public function GenerateFormRequest($submitname = false, $submitvalue = false) { $method = $this->info["method"]; $fields = array(); $files = array(); foreach ($this->fields as $field) { if ($field["type"] == "input.file") { if (is_array($field["value"])) { $field["value"]["name"] = $field["name"]; $files[] = $field["value"]; $method = "post"; } } else { if ($field["type"] == "input.reset" || $field["type"] == "button.reset") { } else { if ($field["type"] == "input.submit" || $field["type"] == "input.image" || $field["type"] == "button.submit") { if (($submitname === false || $field["name"] === $submitname) && ($submitvalue === false || $field["value"] === $submitvalue)) { if ($submitname !== "") { if (!isset($fields[$field["name"]])) { $fields[$field["name"]] = array(); } $fields[$field["name"]][] = $field["value"]; } if ($field["type"] == "input.image") { if (!isset($fields["x"])) { $fields["x"] = array(); } $fields["x"][] = "1"; if (!isset($fields["y"])) { $fields["y"] = array(); } $fields["y"][] = "1"; } } } else { if ($field["type"] != "input.radio" && $field["type"] != "input.checkbox" || $field["checked"]) { if (!isset($fields[$field["name"]])) { $fields[$field["name"]] = array(); } $fields[$field["name"]][] = $field["value"]; } } } } } if ($method == "get") { $url = \CubicleSoft\HTTP::ExtractURL($this->info["action"]); unset($url["query"]); $url["queryvars"] = $fields; $result = array("url" => \CubicleSoft\HTTP::CondenseURL($url), "options" => array()); } else { $result = array("url" => $this->info["action"], "options" => array("postvars" => $fields, "files" => $files)); } return $result; }
public function Wait($timeout = false, $readfps = array(), $writefps = array(), $exceptfps = NULL) { $this->UpdateStreamsAndTimeout("", $timeout, $readfps, $writefps); $result = array("success" => true, "clients" => array(), "removed" => array(), "readfps" => array(), "writefps" => array(), "exceptfps" => array()); if (!count($readfps) && !count($writefps)) { return $result; } $result2 = self::FixedStreamSelect($readfps, $writefps, $exceptfps, $timeout); if ($result2 === false) { return array("success" => false, "error" => \CubicleSoft\HTTP::HTTPTranslate("Wait() failed due to stream_select() failure. Most likely cause: Connection failure."), "errorcode" => "stream_select_failed"); } // Handle new connections. if (isset($readfps["http_s"])) { while (($fp = @stream_socket_accept($this->fp, 0)) !== false) { // Enable non-blocking mode. stream_set_blocking($fp, 0); $client = $this->InitNewClient(); $client->fp = $fp; $client->ipaddr = stream_socket_get_name($fp, true); } unset($readfps["http_s"]); } // Handle clients in the read queue. foreach ($readfps as $cid => $fp) { if (!is_string($cid) || strlen($cid) < 6 || substr($cid, 0, 7) !== "http_c_") { continue; } $id = (int) substr($cid, 7); if (!isset($this->clients[$id])) { continue; } $client = $this->clients[$id]; $client->lastts = microtime(true); if ($client->httpstate !== false) { $result2 = \CubicleSoft\HTTP::ProcessState($client->httpstate); if ($result2["success"]) { // Trigger the last variable to process when extracting form variables. if ($client->contenttype !== false && $client->contenttype[""] === "application/x-www-form-urlencoded") { $this->ProcessClientRequestBody($result2["request"], "&", $id); } if ($client->currfile !== false) { $client->files[$client->currfile]->Close(); $client->currfile = false; } $result["clients"][$id] = $client; $client->requestcomplete = true; $client->requests++; $client->mode = "init_response"; $client->responseheaders = array(); $client->responsefinalized = false; $client->responsebodysize = false; $client->httpstate["type"] = "request"; $client->httpstate["startts"] = microtime(true); $client->httpstate["waituntil"] = -1.0; $client->httpstate["data"] = ""; $client->httpstate["bodysize"] = false; $client->httpstate["chunked"] = false; $client->httpstate["secure"] = $this->ssl; $client->httpstate["state"] = "send_data"; $client->SetResponseCode(200); $client->SetResponseContentType("text/html; charset=UTF-8"); if (isset($client->headers["Connection"])) { $connection = \CubicleSoft\HTTP::ExtractHeader($client->headers["Connection"]); if (strtolower($connection[""]) === "close") { $client->keepalive = false; } } $ver = explode("/", $client->request["httpver"]); $ver = (double) array_pop($ver); if ($ver < 1.1) { $client->keepalive = false; } if ($client->requests >= $this->maxrequests) { $client->keepalive = false; } if ($this->usegzip && isset($client->headers["Accept-Encoding"])) { $encodings = \CubicleSoft\HTTP::ExtractHeader($client->headers["Accept-Encoding"]); $encodings = explode(",", $encodings[""]); $gzip = false; foreach ($encodings as $encoding) { if (strtolower(trim($encoding)) === "gzip") { $gzip = true; } } if ($gzip) { $client->deflate = new \CubicleSoft\DeflateStream(); $client->deflate->Init("wb", -1, array("type" => "gzip")); $client->AddResponseHeader("Content-Encoding", "gzip", true); } } } else { if ($result2["errorcode"] !== "no_data") { if ($client->requests) { $result["removed"][$id] = array("result" => $result2, "client" => $client); } $this->RemoveClient($id); } else { if ($client->requestcomplete === false && $client->httpstate["state"] !== "request_line" && $client->httpstate["state"] !== "headers") { // Allows the caller an opportunity to adjust some client options based on inputs on a per-client basis (e.g. recvlimit). $result["clients"][$id] = $client; } } } } unset($readfps[$cid]); } // Handle clients in the write queue. foreach ($writefps as $cid => $fp) { if (!is_string($cid) || strlen($cid) < 6 || substr($cid, 0, 7) !== "http_c_") { continue; } $id = (int) substr($cid, 7); if (!isset($this->clients[$id])) { continue; } $client = $this->clients[$id]; $client->lastts = microtime(true); if ($client->httpstate !== false) { // Transform the client response into real data. if ($client->mode === "response_ready") { if ($client->responsefinalized) { $client->AddResponseHeader("Content-Length", (string) strlen($client->writedata), true); $client->httpstate["bodysize"] = strlen($client->writedata); } else { if ($client->responsebodysize !== false) { $client->AddResponseHeader("Content-Length", (string) $client->responsebodysize, true); $client->httpstate["bodysize"] = $client->responsebodysize; } else { if ($client->keepalive) { $client->AddResponseHeader("Transfer-Encoding", "chunked", true); $client->httpstate["chunked"] = true; } } } $client->AddResponseHeader("Date", gmdate("D, d M Y H:i:s T"), true); if (!$client->keepalive || $client->requests >= $this->maxrequests) { $client->AddResponseHeader("Connection", "close", true); } foreach ($client->responseheaders as $name => $vals) { foreach ($vals as $val) { $client->httpstate["data"] .= $name . ": " . $val . "\r\n"; } } $client->responseheaders = false; $client->httpstate["data"] .= "\r\n"; $client->mode = "handle_response"; } $result2 = \CubicleSoft\HTTP::ProcessState($client->httpstate); if ($result2["success"]) { if (!$client->responsefinalized) { $result["clients"][$id] = $client; } else { if ($client->keepalive && $client->requests < $this->maxrequests) { // Reset client. $client->mode = "init_request"; $client->httpstate = false; $client->readdata = ""; $client->request = false; $client->url = ""; $client->headers = false; $client->contenttype = false; $client->contenthandled = true; $client->cookievars = false; $client->requestvars = false; $client->requestcomplete = false; $client->deflate = false; $client->writedata = ""; foreach ($client->files as $filename => $tempfile) { unset($client->files[$filename]); } $client->files = array(); $this->initclients[$id] = $client; unset($this->clients[$id]); } else { $result["removed"][$id] = array("result" => array("success" => true), "client" => $client); $this->RemoveClient($id); } } } else { if ($result2["errorcode"] !== "no_data") { $result["removed"][$id] = array("result" => $result2, "client" => $client); $this->RemoveClient($id); } } } unset($writefps[$cid]); } // Initialize new clients. foreach ($this->initclients as $id => $client) { do { $origmode = $client->mode; switch ($client->mode) { case "init": $result2 = $this->ssl ? @stream_socket_enable_crypto($client->fp, true, STREAM_CRYPTO_METHOD_TLS_SERVER) : true; if ($result2 === true) { $client->mode = "init_request"; } else { if ($result2 === false) { @fclose($client->fp); unset($this->initclients[$id]); } } break; case "init_request": // Use the HTTP class in server mode to handle state. // The callback functions are located in WebServer to avoid the issue of pass-by-reference memory leaks. $options = $this->defaultclientoptions; $options["async"] = true; $options["read_headers_callback"] = array($this, "ProcessClientRequestHeaders"); $options["read_headers_callback_opts"] = $id; $options["read_body_callback"] = array($this, "ProcessClientRequestBody"); $options["read_body_callback_opts"] = $id; $options["write_body_callback"] = array($this, "ProcessClientResponseBody"); $options["write_body_callback_opts"] = $id; if (!isset($options["readlinelimit"])) { $options["readlinelimit"] = 116000; } if (!isset($options["maxheaders"])) { $options["maxheaders"] = 1000; } if (!isset($options["recvlimit"])) { $options["recvlimit"] = 1000000; } $startts = microtime(true); $timeout = isset($options["timeout"]) ? $options["timeout"] : false; $result2 = array("success" => true, "rawsendsize" => 0, "rawsendheadersize" => 0, "rawrecvsize" => 0, "rawrecvheadersize" => 0, "startts" => $startts); $debug = isset($options["debug"]) && $options["debug"]; if ($debug) { $result2["rawsend"] = ""; $result2["rawrecv"] = ""; } $client->httpstate = \CubicleSoft\HTTP::InitResponseState($client->fp, $debug, $options, $startts, $timeout, $result2, false, false); $client->mode = "handle_request"; $client->lastts = microtime(true); $this->clients[$id] = $client; unset($this->initclients[$id]); break; } } while (isset($this->initclients[$id]) && $origmode !== $client->mode); } // Handle client timeouts. $ts = microtime(true); foreach ($this->clients as $id => $client) { if ($client->lastts + $this->defaultclienttimeout < $ts) { if ($client->requests) { $result["removed"][$id] = array("result" => $result2, "client" => $client); } $this->RemoveClient($id); } } // Return any extra handles that were being waited on. $result["readfps"] = $readfps; $result["writefps"] = $writefps; $result["exceptfps"] = $exceptfps; return $result; }
public function InteractiveLogin() { echo self::DO_Translate("***************\n"); echo self::DO_Translate("Starting interactive login for DigitalOcean.\n\n"); echo self::DO_Translate("During the next few minutes, you will be asked to sign into your DigitalOcean account here on the command-line and then approve this application to have access to perform actions on your behalf within your DigitalOcean account. You may press Ctrl+C at any time to terminate this application.\n\n"); echo self::DO_Translate("Every web request made from this point on will be dumped to your console and take the form of \"Retrieving '[URL being retrieved]'...\".\n"); echo self::DO_Translate("***************\n\n"); $html = new \CubicleSoft\simple_html_dom(); $web = new \CubicleSoft\WebBrowser(array("httpopts" => array("pre_retrievewebpage_callback" => array($this, "InteractiveLogin__HandleRequest")))); $filteropts = \CubicleSoft\TagFilter::GetHTMLOptions(); $this->accesstokens["refreshtoken"] = false; $this->accesstokens["bearertoken"] = false; $this->accesstokens["bearerexpirets"] = -1; $result = array("url" => $this->GetLoginURL(), "options" => array()); do { $result["options"]["sslopts"] = self::InitSSLOpts(array()); $result2 = $web->Process($result["url"], "auto", $result["options"]); if (!$result2["success"]) { if ($this->accesstokens["refreshtoken"] === false) { return $result2; } echo self::DO_Translate("***************\n"); echo self::DO_Translate("Interactive login completed successfully. Resuming where you left off.\n"); echo self::DO_Translate("***************\n\n"); return array("success" => true); } else { if ($result2["response"]["code"] != 200) { return array("success" => false, "error" => self::DO_Translate("Expected a 200 response from DigitalOcean. Received '%s'.", $result2["response"]["line"]), "errorcode" => "unexpected_digitalocean_response", "info" => $result2); } else { $body = \CubicleSoft\TagFilter::Run($result2["body"], $filteropts); $html->load($body); echo "-----------------------\n\n"; $title = $html->find('title', 0); if ($title) { echo trim($title->plaintext) . "\n\n"; } $h1 = $html->find('h1', 0); if ($h1) { echo trim($h1->plaintext) . "\n\n"; } $h2 = $html->find('h2', 0); if ($h2) { echo trim($h1->plaintext) . "\n\n"; } $error = $html->find('.errors', 0); if ($error) { echo trim(preg_replace('/\\s+/', " ", $error->plaintext)) . "\n\n"; } $forms = $web->ExtractForms($result2["url"], $body); foreach ($forms as $num => $form) { if ($form->info["action"] === "https://cloud.digitalocean.com/login/refresh") { unset($forms[$num]); } } if (!count($forms)) { $url = \CubicleSoft\HTTP::ExtractURL($result2["url"]); if ($url["host"] === "cloud.digitalocean.com" && $url["path"] === "/v1/oauth/authorize") { // Construct a fake form. Might be a touch fragile. // Find the window.currentUser Javascript object. $user = false; $preauth = false; $lines = explode("\n", $body); foreach ($lines as $line) { $line = trim($line); if (preg_match('/window\\.currentUser\\s*=\\s*(\\{.*\\})/', $line, $matches)) { $user = json_decode($matches[1], true); } if ($preauth === false && substr($line, 0, 14) === "window.preAuth") { $preauth = true; } else { if (substr($line, 0, 5) === "name:" || substr($line, 0, 12) === "description:" || substr($line, 0, 4) === "url:") { echo $line . "\n\n"; } } } if ($user !== false) { $html2 = "<body>"; $html2 .= "<form action=\"/oauth/authorize\" method=\"post\">"; $html2 .= "<input type=\"hidden\" name=\"" . htmlspecialchars($html->find('meta[name=csrf-param]', 0)->content) . "\" value=\"" . htmlspecialchars($html->find('meta[name=csrf-token]', 0)->content) . "\">"; foreach ($url["queryvars"] as $key => $vals) { $html2 .= "<input type=\"hidden\" name=\"" . htmlspecialchars($key) . "\" value=\"" . htmlspecialchars($vals[0]) . "\">"; } $html2 .= "<input type=\"hidden\" name=\"context_id\" value=\"" . htmlspecialchars($user["current_context_id"]) . "\">"; $html2 .= "<input type=\"submit\" name=\"cancel\" value=\"Cancel\">"; $html2 .= "<input type=\"submit\" value=\"Authorize application\">"; $html2 .= "</form>"; $html2 .= "</body>"; $forms = $web->ExtractForms($result2["url"], $html2); } } } else { $text = $html->find('p'); if ($text) { foreach ($text as $text2) { echo trim(preg_replace('/\\s+/', " ", $text2->plaintext)) . "\n\n"; } } } $result = $web->InteractiveFormFill($forms); if ($result === false) { return array("success" => false, "error" => self::DO_Translate("Expected at least one form to exist. Received none."), "errorcode" => "invalid_digitalocean_response", "info" => $result2); } } } } while (1); }
public function Connect($url, $origin, $profile = "auto", $options = array(), $web = false) { $this->Disconnect(); if (class_exists("\\CubicleSoft\\CSPRNG", false) && $this->csprng === false) { $this->csprng = new \CubicleSoft\CSPRNG(); } if (isset($options["fp"]) && is_resource($options["fp"])) { $this->fp = $options["fp"]; } else { // Use WebBrowser to initiate the connection. if ($web === false) { $web = new \CubicleSoft\WebBrowser(); } // Transform URL. $url2 = \CubicleSoft\HTTP::ExtractURL($url); if ($url2["scheme"] != "ws" && $url2["scheme"] != "wss") { return array("success" => false, "error" => self::WSTranslate("\\CubicleSoft\\\\CubicleSoft\\WebSocket::Connect() only supports the 'ws' and 'wss' protocols."), "errorcode" => "protocol_check"); } $url2["scheme"] = str_replace("ws", "http", $url2["scheme"]); $url2 = \CubicleSoft\HTTP::CondenseURL($url2); // Generate correct request headers. if (!isset($options["headers"])) { $options["headers"] = array(); } $options["headers"]["Connection"] = "keep-alive, Upgrade"; if ($origin != "") { $options["headers"]["Origin"] = $origin; } $options["headers"]["Pragma"] = "no-cache"; $key = base64_encode($this->PRNGBytes(16)); $options["headers"]["Sec-WebSocket-Key"] = $key; $options["headers"]["Sec-WebSocket-Version"] = "13"; $options["headers"]["Upgrade"] = "websocket"; // No async support for connecting at this time. Async mode is enabled AFTER connecting though. unset($options["async"]); // Connect to the WebSocket. $result = $web->Process($url2, $profile, $options); if (!$result["success"]) { return $result; } if ($result["response"]["code"] != 101) { return array("success" => false, "error" => self::WSTranslate("\\CubicleSoft\\\\CubicleSoft\\WebSocket::Connect() failed to connect to the WebSocket. Server returned: %s %s", $result["response"]["code"], $result["response"]["meaning"]), "errorcode" => "incorrect_server_response"); } if (!isset($result["headers"]["Sec-Websocket-Accept"])) { return array("success" => false, "error" => self::WSTranslate("Server failed to include a 'Sec-WebSocket-Accept' header in its response to the request."), "errorcode" => "missing_server_websocket_accept_header"); } // Verify the Sec-WebSocket-Accept response. if ($result["headers"]["Sec-Websocket-Accept"][0] !== base64_encode(sha1($key . self::KEY_GUID, true))) { return array("success" => false, "error" => self::WSTranslate("The server's 'Sec-WebSocket-Accept' header is invalid."), "errorcode" => "invalid_server_websocket_accept_header"); } $this->fp = $result["fp"]; } // Enable non-blocking mode. stream_set_blocking($this->fp, 0); $this->state = self::STATE_OPEN; $this->readdata = ""; $this->readmessages = array(); $this->writedata = ""; $this->writemessages = array(); $this->lastkeepalive = time(); $this->keepalivesent = false; $this->rawrecvsize = 0; $this->rawsendsize = 0; return array("success" => true); }