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);
 }
Beispiel #4
0
 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);
 }