public function Wait($timeout = false, $readfps = array(), $writefps = array(), $exceptfps = NULL)
 {
     $readfps["ws_s"] = $this->fp;
     if ($timeout === false || $timeout > $this->defaultkeepalive) {
         $timeout = $this->defaultkeepalive;
     }
     foreach ($this->clients as $id => &$client) {
         if ($client["writedata"] === "") {
             $readfps["ws_c_" . $id] = $client["fp"];
         }
         if ($client["writedata"] !== "" || $client["websocket"] !== false && $client["websocket"]->NeedsWrite()) {
             $writefps["ws_c_" . $id] = $client["fp"];
         }
         if ($client["websocket"] !== false) {
             $timeout2 = $client["websocket"]->GetKeepAliveTimeout();
             if ($timeout > $timeout2) {
                 $timeout = $timeout2;
             }
         }
     }
     $result = array("success" => true, "clients" => array(), "removed" => array(), "readfps" => array(), "writefps" => array(), "exceptfps" => array());
     $result2 = @stream_select($readfps, $writefps, $exceptfps, $timeout);
     if ($result2 === false) {
         return array("success" => false, "error" => HTTP::HTTPTranslate("Wait() failed due to stream_select() failure.  Most likely cause:  Connection failure."), "errorcode" => "stream_select_failed");
     }
     // Handle new connections.
     if (isset($readfps["ws_s"])) {
         while (($fp = @stream_socket_accept($this->fp, 0)) !== false) {
             // Enable non-blocking mode.
             stream_set_blocking($fp, 0);
             $this->clients[$this->nextclientid] = array("id" => $this->nextclientid, "readdata" => "", "writedata" => "", "request" => false, "path" => "", "url" => "", "headers" => array(), "lastheader" => "", "websocket" => false, "fp" => $fp);
             $this->nextclientid++;
         }
         unset($readfps["s"]);
     }
     // Handle clients in the read queue.
     foreach ($readfps as $cid => $fp) {
         if (!is_string($cid) || strlen($cid) < 6 || substr($cid, 0, 5) !== "ws_c_") {
             continue;
         }
         $id = (int) substr($cid, 5);
         if (!isset($this->clients[$id])) {
             continue;
         }
         if ($this->clients[$id]["websocket"] !== false) {
             $this->ProcessClientQueuesAndTimeoutState($result, $id, true, isset($writefps[$cid]));
             // Remove active WebSocket clients from the write queue.
             unset($writefps[$cid]);
         } else {
             $result2 = @fread($fp, 8192);
             if ($result2 === false || feof($fp)) {
                 @fclose($fp);
                 unset($this->clients[$id]);
             } else {
                 $this->clients[$id]["readdata"] .= $result2;
                 if (strlen($this->clients[$id]["readdata"]) > 100000) {
                     // Bad header size.  Just kill the connection.
                     @fclose($fp);
                     unset($this->clients[$id]);
                 } else {
                     while (($pos = strpos($this->clients[$id]["readdata"], "\n")) !== false) {
                         // Retrieve the next line of input.
                         $line = rtrim(substr($this->clients[$id]["readdata"], 0, $pos));
                         $this->clients[$id]["readdata"] = (string) substr($this->clients[$id]["readdata"], $pos + 1);
                         if ($this->clients[$id]["request"] === false) {
                             $this->clients[$id]["request"] = trim($line);
                         } else {
                             if ($line !== "") {
                                 // Process the header.
                                 if ($this->clients[$id]["lastheader"] != "" && (substr($line, 0, 1) == " " || substr($line, 0, 1) == "\t")) {
                                     $this->clients[$id]["headers"][$this->clients[$id]["lastheader"]] .= $header;
                                 } else {
                                     $pos = strpos($line, ":");
                                     if ($pos === false) {
                                         $pos = strlen($line);
                                     }
                                     $this->clients[$id]["lastheader"] = HTTP::HeaderNameCleanup(substr($line, 0, $pos));
                                     $this->clients[$id]["headers"][$this->clients[$id]["lastheader"]] = ltrim(substr($line, $pos + 1));
                                 }
                             } else {
                                 // Headers have all been received.  Process the client request.
                                 $request = $this->clients[$id]["request"];
                                 $pos = strpos($request, " ");
                                 if ($pos === false) {
                                     $pos = strlen($request);
                                 }
                                 $method = (string) substr($request, 0, $pos);
                                 $request = trim(substr($request, $pos));
                                 $pos = strrpos($request, " ");
                                 if ($pos === false) {
                                     $pos = strlen($request);
                                 }
                                 $path = (string) substr($request, 0, $pos);
                                 if ($path === "") {
                                     $path = "/";
                                 }
                                 $this->clients[$id]["path"] = $path;
                                 $this->clients[$id]["url"] = "ws://" . $client["headers"]["Host"] . $path;
                                 // Let a derived class handle the new connection (e.g. processing Origin and Host).
                                 // Since the 'websocketclass' is instantiated AFTER this function, it is possible to switch classes on the fly.
                                 $this->clients[$id]["writedata"] .= $this->ProcessNewConnection($method, $path, $this->clients[$id]);
                                 // If an error occurs, the connection will still terminate.
                                 $this->clients[$id]["websocket"] = new $this->websocketclass();
                                 $this->clients[$id]["websocket"]->SetCloseMode($this->defaultclosemode);
                                 $this->clients[$id]["websocket"]->SetKeepAliveTimeout($this->defaultkeepalive);
                                 // If nothing was output, accept the connection.
                                 if ($this->clients[$id]["writedata"] === "") {
                                     $this->clients[$id]["writedata"] .= "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n";
                                     $this->clients[$id]["writedata"] .= "Sec-WebSocket-Accept: " . base64_encode(sha1($this->clients[$id]["headers"]["Sec-Websocket-Key"] . WebSocket::KEY_GUID, true)) . "\r\n";
                                     $this->clients[$id]["writedata"] .= $this->ProcessAcceptedConnection($method, $path, $this->clients[$id]);
                                     $this->clients[$id]["writedata"] .= "\r\n";
                                     // Finish class initialization.
                                     $this->clients[$id]["websocket"]->SetServerMode();
                                     $this->clients[$id]["websocket"]->SetMaxReadFrameSize($this->defaultmaxreadframesize);
                                     $this->clients[$id]["websocket"]->SetMaxReadMessageSize($this->defaultmaxreadmessagesize);
                                     // Set the socket in the WebSocket class.
                                     $this->clients[$id]["websocket"]->Connect("", "", "", array("fp" => $fp));
                                 }
                                 break;
                             }
                         }
                     }
                 }
             }
         }
         unset($readfps[$cid]);
     }
     // Handle remaining clients in the write queue.
     foreach ($writefps as $cid => $fp) {
         if (!is_string($cid) || strlen($cid) < 6 || substr($cid, 0, 5) !== "ws_c_") {
             continue;
         }
         $id = (int) substr($cid, 5);
         if (!isset($this->clients[$id])) {
             continue;
         }
         if ($this->clients[$id]["writedata"] === "") {
             $this->ProcessClientQueuesAndTimeoutState($result, $id, false, true);
         } else {
             $result2 = @fwrite($fp, $this->clients[$id]["writedata"]);
             if ($result2 === false || feof($fp)) {
                 @fclose($fp);
                 unset($this->clients[$id]);
             } else {
                 $this->clients[$id]["writedata"] = (string) substr($this->clients[$id]["writedata"], $result2);
                 // Let the application know about the new client.
                 if ($this->clients[$id]["writedata"] === "") {
                     $result["clients"][$id] = $this->clients[$id];
                 }
             }
         }
         unset($writefps[$cid]);
     }
     // Handle client timeouts.
     foreach ($this->clients as $id => &$client) {
         if (!isset($result["clients"][$id]) && $client["writedata"] === "" && $client["websocket"] !== false) {
             $this->ProcessClientQueuesAndTimeoutState($result, $id, false, false);
         }
     }
     // Return any extra handles that were being waited on.
     $result["readfps"] = $readfps;
     $result["writefps"] = $writefps;
     $result["exceptfps"] = $exceptfps;
     return $result;
 }
 protected function ProcessReadData()
 {
     while (($frame = $this->ReadFrame()) !== false) {
         // Verify that the opcode is probably valid.
         if ($frame["opcode"] >= 0x3 && $frame["opcode"] <= 0x7 || $frame["opcode"] >= 0xb) {
             return array("success" => false, "error" => HTTP::HTTPTranslate("Invalid frame detected.  Bad opcode 0x%02X.", $frame["opcode"]), "errorcode" => "bad_frame_opcode");
         }
         // No extension support (yet).
         if ($frame["rsv1"] || $frame["rsv2"] || $frame["rsv3"]) {
             return array("success" => false, "error" => HTTP::HTTPTranslate("Invalid frame detected.  One or more reserved extension bits are set."), "errorcode" => "bad_reserved_bits_set");
         }
         if ($frame["opcode"] >= 0x8) {
             // Handle the control frame.
             if (!$frame["fin"]) {
                 return array("success" => false, "error" => HTTP::HTTPTranslate("Invalid frame detected.  Fragmented control frame was received."), "errorcode" => "bad_control_frame");
             }
             if ($frame["opcode"] === self::FRAMETYPE_CONNECTION_CLOSE) {
                 if ($this->state === self::STATE_CLOSE) {
                     // Already sent the close state.
                     @fclose($this->fp);
                     $this->fp = false;
                     return array("success" => false, "error" => HTTP::HTTPTranslate("Connection closed by peer."), "errorcode" => "connection_closed");
                 } else {
                     // Change the state to close and send the connection close response to the peer at the appropriate time.
                     if ($this->closemode === self::CLOSE_IMMEDIATELY) {
                         $this->writemessages = array();
                     } else {
                         if ($this->closemode === self::CLOSE_AFTER_CURRENT_MESSAGE) {
                             $this->writemessages = array_slice($this->writemessages, 0, 1);
                         }
                     }
                     $this->state = self::STATE_CLOSE;
                     $result = $this->Write("", self::FRAMETYPE_CONNECTION_CLOSE);
                     if (!$result["success"]) {
                         return $result;
                     }
                 }
             } else {
                 if ($frame["opcode"] === self::FRAMETYPE_PING) {
                     if ($this->state !== self::STATE_CLOSE) {
                         // Received a ping.  Respond with a pong with the same payload.
                         $result = $this->Write($frame["payload"], self::FRAMETYPE_PONG, true, false, 0);
                         if (!$result["success"]) {
                             return $result;
                         }
                     }
                 } else {
                     if ($frame["opcode"] === self::FRAMETYPE_PONG) {
                         // Do nothing.
                     }
                 }
             }
         } else {
             // Add this frame to the read message queue.
             $lastcompleted = !count($this->readmessages) || $this->readmessages[count($this->readmessages) - 1]["fin"];
             if ($lastcompleted) {
                 // Make sure the new frame is the start of a fragment or is not fragemented.
                 if ($frame["opcode"] === self::FRAMETYPE_CONTINUATION) {
                     return array("success" => false, "error" => HTTP::HTTPTranslate("Invalid frame detected.  Fragment continuation frame was received at the start of a fragment."), "errorcode" => "bad_continuation_frame");
                 }
                 $this->readmessages[] = $frame;
             } else {
                 // Make sure the frame is a continuation frame.
                 if ($frame["opcode"] !== self::FRAMETYPE_CONTINUATION) {
                     return array("success" => false, "error" => HTTP::HTTPTranslate("Invalid frame detected.  Fragment continuation frame was not received for a fragment."), "errorcode" => "missing_continuation_frame");
                 }
                 $this->readmessages[count($this->readmessages) - 1]["fin"] = $frame["fin"];
                 $this->readmessages[count($this->readmessages) - 1]["payload"] .= $frame["payload"];
             }
             if ($this->maxreadmessagesize !== false && strlen($this->readmessages[count($this->readmessages) - 1]["payload"]) > $this->maxreadmessagesize) {
                 return array("success" => false, "error" => HTTP::HTTPTranslate("Peer sent a single message exceeding %s bytes of data.", $this->maxreadmessagesize), "errorcode" => "max_read_message_size_exceeded");
             }
         }
         //var_dump($frame);
     }
     return array("success" => true);
 }
示例#3
0
 function get_check_curl_multi_init_key($ch)
 {
     global $curl_init__map;
     $key = get_curl_multi_init_key($ch);
     if (!isset($curl_multi_init__map[$key])) {
         throw new Exception(HTTP::HTTPTranslate("cURL Emulator:  Unable to find key mapping for resource."));
     }
     return $key;
 }
示例#4
0
 public function Process($url, $profile = "auto", $tempoptions = array())
 {
     $startts = microtime(true);
     $redirectts = $startts;
     if (isset($tempoptions["timeout"])) {
         $timeout = $tempoptions["timeout"];
     } else {
         if (isset($this->data["httpopts"]["timeout"])) {
             $timeout = $this->data["httpopts"]["timeout"];
         } else {
             $timeout = false;
         }
     }
     if (!isset($this->data["httpopts"]["headers"])) {
         $this->data["httpopts"]["headers"] = array();
     }
     $this->data["httpopts"]["headers"] = HTTP::NormalizeHeaders($this->data["httpopts"]["headers"]);
     unset($this->data["httpopts"]["method"]);
     unset($this->data["httpopts"]["write_body_callback"]);
     unset($this->data["httpopts"]["body"]);
     unset($this->data["httpopts"]["postvars"]);
     unset($this->data["httpopts"]["files"]);
     $httpopts = $this->data["httpopts"];
     $numfollow = $this->data["maxfollow"];
     $numredirects = 0;
     $totalrawsendsize = 0;
     if (!isset($tempoptions["headers"])) {
         $tempoptions["headers"] = array();
     }
     $tempoptions["headers"] = HTTP::NormalizeHeaders($tempoptions["headers"]);
     if (isset($tempoptions["headers"]["Referer"])) {
         $this->data["referer"] = $tempoptions["headers"]["Referer"];
     }
     // If a referrer is specified, use it to generate an absolute URL.
     if ($this->data["referer"] != "") {
         $url = HTTP::ConvertRelativeToAbsoluteURL($this->data["referer"], $url);
     }
     $urlinfo = HTTP::ExtractURL($url);
     do {
         if (!isset($this->data["allowedprotocols"][$urlinfo["scheme"]]) || !$this->data["allowedprotocols"][$urlinfo["scheme"]]) {
             return array("success" => false, "error" => HTTP::HTTPTranslate("Protocol '%s' is not allowed in '%s'.", $urlinfo["scheme"], $url), "errorcode" => "allowed_protocols");
         }
         $filename = HTTP::ExtractFilename($urlinfo["path"]);
         $pos = strrpos($filename, ".");
         $fileext = $pos !== false ? strtolower(substr($filename, $pos + 1)) : "";
         // Set up some standard headers.
         $headers = array();
         $profile = strtolower($profile);
         $tempprofile = explode("-", $profile);
         if (count($tempprofile) == 2) {
             $profile = $tempprofile[0];
             $fileext = $tempprofile[1];
         }
         if (substr($profile, 0, 2) == "ie" || $profile == "auto" && substr($this->data["useragent"], 0, 2) == "ie") {
             if ($fileext == "css") {
                 $headers["Accept"] = "text/css";
             } else {
                 if ($fileext == "png" || $fileext == "jpg" || $fileext == "jpeg" || $fileext == "gif" || $fileext == "svg") {
                     $headers["Accept"] = "image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5";
                 } else {
                     if ($fileext == "js") {
                         $headers["Accept"] = "application/javascript, */*;q=0.8";
                     } else {
                         if ($this->data["referer"] != "" || $fileext == "" || $fileext == "html" || $fileext == "xhtml" || $fileext == "xml") {
                             $headers["Accept"] = "text/html, application/xhtml+xml, */*";
                         } else {
                             $headers["Accept"] = "*/*";
                         }
                     }
                 }
             }
             $headers["Accept-Language"] = "en-US";
             $headers["User-Agent"] = HTTP::GetUserAgent(substr($profile, 0, 2) == "ie" ? $profile : $this->data["useragent"]);
         } else {
             if ($profile == "firefox" || $profile == "auto" && $this->data["useragent"] == "firefox") {
                 if ($fileext == "css") {
                     $headers["Accept"] = "text/css,*/*;q=0.1";
                 } else {
                     if ($fileext == "png" || $fileext == "jpg" || $fileext == "jpeg" || $fileext == "gif" || $fileext == "svg") {
                         $headers["Accept"] = "image/png,image/*;q=0.8,*/*;q=0.5";
                     } else {
                         if ($fileext == "js") {
                             $headers["Accept"] = "*/*";
                         } else {
                             $headers["Accept"] = "text/html, application/xhtml+xml, */*";
                         }
                     }
                 }
                 $headers["Accept-Language"] = "en-us,en;q=0.5";
                 $headers["Cache-Control"] = "max-age=0";
                 $headers["User-Agent"] = HTTP::GetUserAgent("firefox");
             } else {
                 if ($profile == "opera" || $profile == "auto" && $this->data["useragent"] == "opera") {
                     // Opera has the right idea:  Just send the same thing regardless of the request type.
                     $headers["Accept"] = "text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1";
                     $headers["Accept-Language"] = "en-US,en;q=0.9";
                     $headers["Cache-Control"] = "no-cache";
                     $headers["User-Agent"] = HTTP::GetUserAgent("opera");
                 } else {
                     if ($profile == "safari" || $profile == "chrome" || $profile == "auto" && ($this->data["useragent"] == "safari" || $this->data["useragent"] == "chrome")) {
                         if ($fileext == "css") {
                             $headers["Accept"] = "text/css,*/*;q=0.1";
                         } else {
                             if ($fileext == "png" || $fileext == "jpg" || $fileext == "jpeg" || $fileext == "gif" || $fileext == "svg" || $fileext == "js") {
                                 $headers["Accept"] = "*/*";
                             } else {
                                 $headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
                             }
                         }
                         $headers["Accept-Charset"] = "ISO-8859-1,utf-8;q=0.7,*;q=0.3";
                         $headers["Accept-Language"] = "en-US,en;q=0.8";
                         $headers["User-Agent"] = HTTP::GetUserAgent($profile == "safari" || $profile == "chrome" ? $profile : $this->data["useragent"]);
                     }
                 }
             }
         }
         if ($this->data["referer"] != "") {
             $headers["Referer"] = $this->data["referer"];
         }
         // Generate the final headers array.
         $headers = array_merge($headers, $httpopts["headers"], $tempoptions["headers"]);
         // Calculate the host and reverse host and remove port information.
         $host = isset($headers["Host"]) ? $headers["Host"] : $urlinfo["host"];
         $pos = strpos($host, "]");
         if (substr($host, 0, 1) == "[" && $pos !== false) {
             $host = substr($host, 0, $pos + 1);
         } else {
             $pos = strpos($host, ":");
             if ($pos !== false) {
                 $host = substr($host, 0, $pos);
             }
         }
         $dothost = $host;
         if (substr($dothost, 0, 1) != ".") {
             $dothost = "." . $dothost;
         }
         // Append cookies and delete old, invalid cookies.
         $secure = $urlinfo["scheme"] == "https";
         $cookiepath = $urlinfo["path"];
         if ($cookiepath == "") {
             $cookiepath = "/";
         }
         $pos = strrpos($cookiepath, "/");
         if ($pos !== false) {
             $cookiepath = substr($cookiepath, 0, $pos + 1);
         }
         $cookies = array();
         foreach ($this->data["cookies"] as $domain => $paths) {
             if (substr($domain, -strlen($dothost)) == $dothost) {
                 foreach ($paths as $path => $cookies2) {
                     if (substr($cookiepath, 0, strlen($path)) == $path) {
                         foreach ($cookies2 as $num => $info) {
                             if (isset($info["expires_ts"]) && $this->GetExpiresTimestamp($info["expires_ts"]) < time()) {
                                 unset($this->data["cookies"][$domain][$path][$num]);
                             } else {
                                 if ($secure || !isset($info["secure"])) {
                                     $cookies[$info["name"]] = $info["value"];
                                 }
                             }
                         }
                         if (!count($this->data["cookies"][$domain][$path])) {
                             unset($this->data["cookies"][$domain][$path]);
                         }
                     }
                 }
                 if (!count($this->data["cookies"][$domain])) {
                     unset($this->data["cookies"][$domain]);
                 }
             }
         }
         $cookies2 = array();
         foreach ($cookies as $name => $value) {
             $cookies2[] = rawurlencode($name) . "=" . rawurlencode($value);
         }
         $headers["Cookie"] = implode("; ", $cookies2);
         if ($headers["Cookie"] == "") {
             unset($headers["Cookie"]);
         }
         // Generate the final options array.
         $options = array_merge($httpopts, $tempoptions);
         $options["headers"] = $headers;
         if ($timeout !== false) {
             $options["timeout"] = HTTP::GetTimeLeft($startts, $timeout);
         }
         // Process the request.
         $result = HTTP::RetrieveWebpage($url, $options);
         $result["url"] = $url;
         $result["options"] = $options;
         $result["firstreqts"] = $startts;
         $result["numredirects"] = $numredirects;
         $result["redirectts"] = $redirectts;
         if (isset($result["rawsendsize"])) {
             $totalrawsendsize += $result["rawsendsize"];
         }
         $result["totalrawsendsize"] = $totalrawsendsize;
         unset($result["options"]["files"]);
         unset($result["options"]["body"]);
         if (!$result["success"]) {
             return array("success" => false, "error" => HTTP::HTTPTranslate("Unable to retrieve content.  %s", $result["error"]), "info" => $result, "errorcode" => "retrievewebpage");
         }
         // Set up structures for another round.
         if ($this->data["autoreferer"]) {
             $this->data["referer"] = $url;
         }
         if (isset($result["headers"]["Location"]) && $this->data["followlocation"]) {
             $redirectts = microtime(true);
             unset($tempoptions["method"]);
             unset($tempoptions["write_body_callback"]);
             unset($tempoptions["body"]);
             unset($tempoptions["postvars"]);
             unset($tempoptions["files"]);
             $tempoptions["headers"]["Referer"] = $url;
             $url = $result["headers"]["Location"][0];
             // Generate an absolute URL.
             if ($this->data["referer"] != "") {
                 $url = HTTP::ConvertRelativeToAbsoluteURL($this->data["referer"], $url);
             }
             $urlinfo2 = HTTP::ExtractURL($url);
             if (!isset($this->data["allowedredirprotocols"][$urlinfo2["scheme"]]) || !$this->data["allowedredirprotocols"][$urlinfo2["scheme"]]) {
                 return array("success" => false, "error" => HTTP::HTTPTranslate("Protocol '%s' is not allowed.  Server attempted to redirect to '%s'.", $urlinfo2["scheme"], $url), "info" => $result, "errorcode" => "allowed_redir_protocols");
             }
             if ($urlinfo2["host"] != $urlinfo["host"]) {
                 unset($tempoptions["headers"]["Host"]);
                 unset($httpopts["headers"]["Host"]);
             }
             $urlinfo = $urlinfo2;
             $numredirects++;
         }
         // Handle any 'Set-Cookie' headers.
         if (isset($result["headers"]["Set-Cookie"])) {
             foreach ($result["headers"]["Set-Cookie"] as $cookie) {
                 $items = explode("; ", $cookie);
                 $item = trim(array_shift($items));
                 if ($item != "") {
                     $cookie2 = array();
                     $pos = strpos($item, "=");
                     if ($pos === false) {
                         $cookie2["name"] = urldecode($item);
                         $cookie2["value"] = "";
                     } else {
                         $cookie2["name"] = urldecode(substr($item, 0, $pos));
                         $cookie2["value"] = urldecode(substr($item, $pos + 1));
                     }
                     $cookie = array();
                     foreach ($items as $item) {
                         $item = trim($item);
                         if ($item != "") {
                             $pos = strpos($item, "=");
                             if ($pos === false) {
                                 $cookie[strtolower(trim(urldecode($item)))] = "";
                             } else {
                                 $cookie[strtolower(trim(urldecode(substr($item, 0, $pos))))] = urldecode(substr($item, $pos + 1));
                             }
                         }
                     }
                     $cookie = array_merge($cookie, $cookie2);
                     if (isset($cookie["expires"])) {
                         $ts = HTTP::GetDateTimestamp($cookie["expires"]);
                         $cookie["expires_ts"] = gmdate("Y-m-d H:i:s", $ts === false ? time() - 24 * 60 * 60 : $ts);
                     } else {
                         if (isset($cookie["max-age"])) {
                             $cookie["expires_ts"] = gmdate("Y-m-d H:i:s", time() + (int) $cookie["max-age"]);
                         } else {
                             unset($cookie["expires_ts"]);
                         }
                     }
                     if (!isset($cookie["domain"])) {
                         $cookie["domain"] = $dothost;
                     }
                     if (substr($cookie["domain"], 0, 1) != ".") {
                         $cookie["domain"] = "." . $cookie["domain"];
                     }
                     if (!isset($cookie["path"])) {
                         $cookie["path"] = $cookiepath;
                     }
                     $cookie["path"] = str_replace("\\", "/", $cookie["path"]);
                     if (substr($cookie["path"], -1) != "/") {
                         $cookie["path"] = "/";
                     }
                     if (!isset($this->data["cookies"][$cookie["domain"]])) {
                         $this->data["cookies"][$cookie["domain"]] = array();
                     }
                     if (!isset($this->data["cookies"][$cookie["domain"]][$cookie["path"]])) {
                         $this->data["cookies"][$cookie["domain"]][$cookie["path"]] = array();
                     }
                     $this->data["cookies"][$cookie["domain"]][$cookie["path"]][] = $cookie;
                 }
             }
         }
         if ($numfollow > 0) {
             $numfollow--;
         }
     } while (isset($result["headers"]["Location"]) && $this->data["followlocation"] && $numfollow);
     $result["numredirects"] = $numredirects;
     $result["redirectts"] = $redirectts;
     // Extract the forms from the page in a parsed format.
     // Call WebBrowser::GenerateFormRequest() to prepare an actual request for Process().
     if ($this->data["extractforms"]) {
         $result["forms"] = $this->ExtractForms($result["url"], $result["body"]);
     }
     return $result;
 }