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); }
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; }
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; }