<?php /* * http.php * * @(#) $Header: /opt2/ena/metal/http/http.php,v 1.92 2014/08/14 23:17:34 mlemos Exp $ * */ defined('HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR') || define('HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR', -1); defined('HTTP_CLIENT_ERROR_NO_ERROR') || define('HTTP_CLIENT_ERROR_NO_ERROR', 0); defined('HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS') || define('HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS', 1); defined('HTTP_CLIENT_ERROR_CANNOT_CONNECT') || define('HTTP_CLIENT_ERROR_CANNOT_CONNECT', 2); defined('HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE') || define('HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE', 3); defined('HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE') || define('HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE', 4); defined('HTTP_CLIENT_ERROR_PROTOCOL_FAILURE') || define('HTTP_CLIENT_ERROR_PROTOCOL_FAILURE', 5); defined('HTTP_CLIENT_ERROR_INVALID_PARAMETERS') || define('HTTP_CLIENT_ERROR_INVALID_PARAMETERS', 6); function N2PHPErrorMessage() { $lastError = error_get_last(); if ($lastError === null) { return ''; } return $lastError['message']; } class N2HTTP { var $host_name = ""; var $host_port = 0; var $proxy_host_name = ""; var $proxy_host_port = 80; var $socks_host_name = ''; var $socks_host_port = 1080; var $socks_version = '5'; var $protocol = "http"; var $request_method = "GET"; var $user_agent = 'httpclient (http://www.phpclasses.org/httpclient $Revision: 1.92 $)'; var $accept = ''; var $authentication_mechanism = ""; var $user; var $password; var $realm; var $workstation; var $proxy_authentication_mechanism = ""; var $proxy_user; var $proxy_password; var $proxy_realm; var $proxy_workstation; var $request_uri = ""; var $request = ""; var $request_headers = array(); var $request_user; var $request_password; var $request_realm; var $request_workstation; var $proxy_request_user; var $proxy_request_password; var $proxy_request_realm; var $proxy_request_workstation; var $request_body = ""; var $request_arguments = array(); var $protocol_version = "1.1"; var $timeout = 0; var $data_timeout = 0; var $debug = 0; var $log_debug = 0; var $debug_response_body = 1; var $html_debug = 0; var $support_cookies = 1; var $cookies = array(); var $error = ""; var $error_code = HTTP_CLIENT_ERROR_NO_ERROR; var $exclude_address = ""; var $follow_redirect = 0; var $redirection_limit = 5; var $response_status = ""; var $response_message = ""; var $file_buffer_length = 8000; var $force_multipart_form_post = 0; var $prefer_curl = 1; var $keep_alive = 1; var $sasl_authenticate = 1; /* private variables - DO NOT ACCESS */ var $state = "Disconnected"; var $use_curl = 0; var $connection = 0; var $content_length = 0; var $response = ""; var $read_response = 0; var $read_length = 0; var $request_host = ""; var $next_token = ""; var $redirection_level = 0; var $chunked = 0; var $remaining_chunk = 0; var $last_chunk_read = 0; var $months = array( "Jan" => "01", "Feb" => "02", "Mar" => "03", "Apr" => "04", "May" => "05", "Jun" => "06", "Jul" => "07", "Aug" => "08", "Sep" => "09", "Oct" => "10", "Nov" => "11", "Dec" => "12" ); var $session = ''; var $connection_close = 0; var $force_close = 0; var $connected_host = ''; var $connected_port = -1; var $connected_ssl = 0; /* Private methods - DO NOT CALL */ Function Tokenize($string, $separator = "") { if (!strcmp($separator, "")) { $separator = $string; $string = $this->next_token; } for ($character = 0; $character < strlen($separator); $character++) { $position = strpos($string, $separator[$character]); if (GetType($position) == "integer") $found = (IsSet($found) ? min($found, $position) : $position); } if (IsSet($found)) { $this->next_token = substr($string, $found + 1); return (substr($string, 0, $found)); } else { $this->next_token = ""; return ($string); } } Function CookieEncode($value, $name) { return ($name ? str_replace("=", "%25", $value) : str_replace(";", "%3B", $value)); } Function SetError($error, $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR) { $this->error_code = $error_code; return ($this->error = $error); } Function SetPHPError($error, $php_error_message, $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR) { if (IsSet($php_error_message) && strlen($php_error_message)) $error .= ": " . $php_error_message; return ($this->SetError($error, $error_code)); } Function SetDataAccessError($error, $check_connection = 0) { $this->error = $error; $this->error_code = HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE; if (!$this->use_curl && function_exists("socket_get_status")) { $status = socket_get_status($this->connection); if ($status["timed_out"]) $this->error .= ": data access time out"; elseif ($status["eof"]) { if ($check_connection) $this->error = ""; else $this->error .= ": the server disconnected"; } } } Function OutputDebug($message) { if ($this->log_debug) error_log($message); else { $message .= "\n"; if ($this->html_debug) $message = str_replace("\n", "<br />\n", HtmlEntities($message)); echo $message; flush(); } } Function GetLine() { for ($line = ""; ;) { if ($this->use_curl) { $eol = strpos($this->response, "\n", $this->read_response); $data = ($eol ? substr($this->response, $this->read_response, $eol + 1 - $this->read_response) : ""); $this->read_response += strlen($data); } else { if (feof($this->connection)) { $this->SetDataAccessError("reached the end of data while reading from the HTTP server connection"); return (0); } $data = fgets($this->connection, 100); } if (GetType($data) != "string" || strlen($data) == 0) { $this->SetDataAccessError("it was not possible to read line from the HTTP server"); return (0); } $line .= $data; $length = strlen($line); if ($length && !strcmp(substr($line, $length - 1, 1), "\n")) { $length -= (($length >= 2 && !strcmp(substr($line, $length - 2, 1), "\r")) ? 2 : 1); $line = substr($line, 0, $length); if ($this->debug) $this->OutputDebug("S $line"); return ($line); } } } Function PutLine($line) { if ($this->debug) $this->OutputDebug("C $line"); if (!fputs($this->connection, $line . "\r\n")) { $this->SetDataAccessError("it was not possible to send a line to the HTTP server"); return (0); } return (1); } Function PutData($data) { if (strlen($data)) { if ($this->debug) $this->OutputDebug('C ' . $data); if (!fputs($this->connection, $data)) { $this->SetDataAccessError("it was not possible to send data to the HTTP server"); return (0); } } return (1); } Function FlushData() { if (!fflush($this->connection)) { $this->SetDataAccessError("it was not possible to send data to the HTTP server"); return (0); } return (1); } Function ReadChunkSize() { if ($this->remaining_chunk == 0) { $debug = $this->debug; if (!$this->debug_response_body) $this->debug = 0; $line = $this->GetLine(); $this->debug = $debug; if (GetType($line) != "string") return ($this->SetError("could not read chunk start: " . $this->error, $this->error_code)); $this->remaining_chunk = hexdec($line); if ($this->remaining_chunk == 0) { if (!$this->debug_response_body) $this->debug = 0; $line = $this->GetLine(); $this->debug = $debug; if (GetType($line) != "string") return ($this->SetError("could not read chunk end: " . $this->error, $this->error_code)); } } return (""); } Function ReadBytes($length) { if ($this->use_curl) { $bytes = substr($this->response, $this->read_response, min($length, strlen($this->response) - $this->read_response)); $this->read_response += strlen($bytes); if ($this->debug && $this->debug_response_body && strlen($bytes)) $this->OutputDebug("S " . $bytes); } else { if ($this->chunked) { for ($bytes = "", $remaining = $length; $remaining;) { if (strlen($this->ReadChunkSize())) return (""); if ($this->remaining_chunk == 0) { $this->last_chunk_read = 1; break; } $ask = min($this->remaining_chunk, $remaining); $chunk = @fread($this->connection, $ask); $read = strlen($chunk); if ($read == 0) { $this->SetDataAccessError("it was not possible to read data chunk from the HTTP server"); return (""); } if ($this->debug && $this->debug_response_body) $this->OutputDebug("S " . $chunk); $bytes .= $chunk; $this->remaining_chunk -= $read; $remaining -= $read; if ($this->remaining_chunk == 0) { if (feof($this->connection)) return ($this->SetError("reached the end of data while reading the end of data chunk mark from the HTTP server", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); $data = @fread($this->connection, 2); if (strcmp($data, "\r\n")) { $this->SetDataAccessError("it was not possible to read end of data chunk from the HTTP server"); return (""); } } } } else { $bytes = @fread($this->connection, $length); if (strlen($bytes)) { if ($this->debug && $this->debug_response_body) $this->OutputDebug("S " . $bytes); } else $this->SetDataAccessError("it was not possible to read data from the HTTP server", $this->connection_close); } } return ($bytes); } Function EndOfInput() { if ($this->use_curl) return ($this->read_response >= strlen($this->response)); if ($this->chunked) return ($this->last_chunk_read); if ($this->content_length_set) return ($this->content_length <= $this->read_length); return (feof($this->connection)); } Function Resolve($domain, &$ip, $server_type) { if (preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $domain)) $ip = $domain; else { if ($this->debug) $this->OutputDebug('Resolving ' . $server_type . ' server domain "' . $domain . '"...'); if (!strcmp($ip = @gethostbyname($domain), $domain)) $ip = ""; } if (strlen($ip) == 0 || (strlen($this->exclude_address) && !strcmp(@gethostbyname($this->exclude_address), $ip))) return ($this->SetError("could not resolve the host domain \"" . $domain . "\"", HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS)); return (''); } Function Connect($host_name, $host_port, $ssl, $server_type = 'HTTP') { $domain = $host_name; $port = $host_port; if (strlen($error = $this->Resolve($domain, $ip, $server_type))) return ($error); if (strlen($this->socks_host_name)) { switch ($this->socks_version) { case '4': $version = 4; break; case '5': $version = 5; break; default: return ('it was not specified a supported SOCKS protocol version'); break; } $host_ip = $ip; $port = $this->socks_host_port; $host_server_type = $server_type; $server_type = 'SOCKS'; if (strlen($error = $this->Resolve($this->socks_host_name, $ip, $server_type))) return ($error); } if ($this->debug) $this->OutputDebug('Connecting to ' . $server_type . ' server IP ' . $ip . ' port ' . $port . '...'); if ($ssl) $ip = "ssl://" . $host_name; if (($this->connection = ($this->timeout ? @fsockopen($ip, $port, $errno, $error, $this->timeout) : @fsockopen($ip, $port, $errno))) == 0) { $error_code = HTTP_CLIENT_ERROR_CANNOT_CONNECT; switch ($errno) { case -3: return ($this->SetError("socket could not be created", $error_code)); case -4: return ($this->SetError("dns lookup on hostname \"" . $host_name . "\" failed", $error_code)); case -5: return ($this->SetError("connection refused or timed out", $error_code)); case -6: return ($this->SetError("fdopen() call failed", $error_code)); case -7: return ($this->SetError("setvbuf() call failed", $error_code)); default: return ($this->SetPHPError($errno . " could not connect to the host \"" . $host_name . "\"", N2PHPErrorMessage(), $error_code)); } } else { if ($this->data_timeout && function_exists("socket_set_timeout")) socket_set_timeout($this->connection, $this->data_timeout, 0); if (strlen($this->socks_host_name)) { if ($this->debug) $this->OutputDebug('Connected to the SOCKS server ' . $this->socks_host_name); $send_error = 'it was not possible to send data to the SOCKS server'; $receive_error = 'it was not possible to receive data from the SOCKS server'; switch ($version) { case 4: $command = 1; $user = ''; if (!fputs($this->connection, chr($version) . chr($command) . pack('nN', $host_port, ip2long($host_ip)) . $user . Chr(0))) $error = $this->SetDataAccessError($send_error); else { $response = fgets($this->connection, 9); if (strlen($response) != 8) $error = $this->SetDataAccessError($receive_error); else { $socks_errors = array( "\x5a" => '', "\x5b" => 'request rejected', "\x5c" => 'request failed because client is not running identd (or not reachable from the server)', "\x5d" => 'request failed because client\'s identd could not confirm the user ID string in the request', ); $error_code = $response[1]; $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown'); if (strlen($error)) $error = 'SOCKS error: ' . $error; } } break; case 5: if ($this->debug) $this->OutputDebug('Negotiating the authentication method ...'); $methods = 1; $method = 0; if (!fputs($this->connection, chr($version) . chr($methods) . chr($method))) $error = $this->SetDataAccessError($send_error); else { $response = fgets($this->connection, 3); if (strlen($response) != 2) $error = $this->SetDataAccessError($receive_error); elseif (Ord($response[1]) != $method) $error = 'the SOCKS server requires an authentication method that is not yet supported'; else { if ($this->debug) $this->OutputDebug('Connecting to ' . $host_server_type . ' server IP ' . $host_ip . ' port ' . $host_port . '...'); $command = 1; $address_type = 1; if (!fputs($this->connection, chr($version) . chr($command) . "\x00" . chr($address_type) . pack('Nn', ip2long($host_ip), $host_port))) $error = $this->SetDataAccessError($send_error); else { $response = fgets($this->connection, 11); if (strlen($response) != 10) $error = $this->SetDataAccessError($receive_error); else { $socks_errors = array( "\x00" => '', "\x01" => 'general SOCKS server failure', "\x02" => 'connection not allowed by ruleset', "\x03" => 'Network unreachable', "\x04" => 'Host unreachable', "\x05" => 'Connection refused', "\x06" => 'TTL expired', "\x07" => 'Command not supported', "\x08" => 'Address type not supported' ); $error_code = $response[1]; $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown'); if (strlen($error)) $error = 'SOCKS error: ' . $error; } } } } break; default: $error = 'support for SOCKS protocol version ' . $this->socks_version . ' is not yet implemented'; break; } if (strlen($error)) { fclose($this->connection); return ($error); } } if ($this->debug) $this->OutputDebug("Connected to $host_name"); if (strlen($this->proxy_host_name) && !strcmp(strtolower($this->protocol), 'https')) { if (function_exists('stream_socket_enable_crypto') && in_array('ssl', stream_get_transports())) $this->state = "ConnectedToProxy"; else { $this->OutputDebug("It is not possible to start SSL after connecting to the proxy server. If the proxy refuses to forward the SSL request, you may need to upgrade to PHP 5.1 or later with OpenSSL support enabled."); $this->state = "Connected"; } } else $this->state = "Connected"; return (""); } } Function Disconnect() { if ($this->debug) $this->OutputDebug("Disconnected from " . $this->connected_host); if ($this->use_curl) { curl_close($this->connection); $this->response = ""; } else fclose($this->connection); $this->state = "Disconnected"; return (""); } /* Public methods */ Function GetRequestArguments($url, &$arguments) { $this->error = ''; $this->error_code = HTTP_CLIENT_ERROR_NO_ERROR; $arguments = array(); $url = str_replace(' ', '%20', $url); $parameters = @parse_url($url); if (!$parameters) return ($this->SetError("it was not specified a valid URL", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); if (!IsSet($parameters["scheme"])) return ($this->SetError("it was not specified the protocol type argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); switch (strtolower($parameters["scheme"])) { case "http": case "https": $arguments["Protocol"] = $parameters["scheme"]; break; default: return ($parameters["scheme"] . " connection scheme is not yet supported"); } if (!IsSet($parameters["host"])) return ($this->SetError("it was not specified the connection host argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); $arguments["HostName"] = $parameters["host"]; $arguments["Headers"] = array("Host" => $parameters["host"] . (IsSet($parameters["port"]) ? ":" . $parameters["port"] : "")); if (IsSet($parameters["user"])) { $arguments["AuthUser"] = UrlDecode($parameters["user"]); if (!IsSet($parameters["pass"])) $arguments["AuthPassword"] = ""; } if (IsSet($parameters["pass"])) { if (!IsSet($parameters["user"])) $arguments["AuthUser"] = ""; $arguments["AuthPassword"] = UrlDecode($parameters["pass"]); } if (IsSet($parameters["port"])) { if (strcmp($parameters["port"], strval(intval($parameters["port"])))) return ($this->SetError("it was not specified a valid connection host argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); $arguments["HostPort"] = intval($parameters["port"]); } else $arguments["HostPort"] = 0; $arguments["RequestURI"] = (IsSet($parameters["path"]) ? $parameters["path"] : "/") . (IsSet($parameters["query"]) ? "?" . $parameters["query"] : ""); if (strlen($this->user_agent)) $arguments["Headers"]["User-Agent"] = $this->user_agent; if (strlen($this->accept)) $arguments["Headers"]["Accept"] = $this->accept; return (""); } Function Open($arguments) { if (strlen($this->error)) return ($this->error); $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR; if (IsSet($arguments["HostName"])) $this->host_name = $arguments["HostName"]; if (IsSet($arguments["HostPort"])) $this->host_port = $arguments["HostPort"]; if (IsSet($arguments["ProxyHostName"])) $this->proxy_host_name = $arguments["ProxyHostName"]; if (IsSet($arguments["ProxyHostPort"])) $this->proxy_host_port = $arguments["ProxyHostPort"]; if (IsSet($arguments["SOCKSHostName"])) $this->socks_host_name = $arguments["SOCKSHostName"]; if (IsSet($arguments["SOCKSHostPort"])) $this->socks_host_port = $arguments["SOCKSHostPort"]; if (IsSet($arguments["SOCKSVersion"])) $this->socks_version = $arguments["SOCKSVersion"]; if (IsSet($arguments["Protocol"])) $this->protocol = $arguments["Protocol"]; switch (strtolower($this->protocol)) { case "http": $default_port = 80; break; case "https": $default_port = 443; break; default: return ($this->SetError("it was not specified a valid connection protocol", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); } if (strlen($this->proxy_host_name) == 0) { if (strlen($this->host_name) == 0) return ($this->SetError("it was not specified a valid hostname", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); $host_name = $this->host_name; $host_port = ($this->host_port ? $this->host_port : $default_port); $server_type = 'HTTP'; } else { $host_name = $this->proxy_host_name; $host_port = $this->proxy_host_port; $server_type = 'HTTP proxy'; } $ssl = (strtolower($this->protocol) == "https" && strlen($this->proxy_host_name) == 0); if ($ssl && strlen($this->socks_host_name)) return ($this->SetError('establishing SSL connections via a SOCKS server is not yet supported', HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); $this->use_curl = ($ssl && $this->prefer_curl && function_exists("curl_init")); switch ($this->state) { case 'Connected': if (!strcmp($host_name, $this->connected_host) && intval($host_port) == $this->connected_port && intval($ssl) == $this->connected_ssl) { if ($this->debug) $this->OutputDebug("Reusing connection to " . $this->connected_host); return (''); } if (strlen($error = $this->Disconnect())) return ($error); case "Disconnected": break; default: return ("1 already connected"); } if ($this->debug) $this->OutputDebug("Connecting to " . $this->host_name); if ($this->use_curl) { $error = (($this->connection = curl_init($this->protocol . "://" . $this->host_name . ($host_port == $default_port ? "" : ":" . strval($host_port)) . "/")) ? "" : "Could not initialize a CURL session"); if (strlen($error) == 0) { curl_setopt($this->connection, CURLOPT_CAINFO, N2LIBRARY . '/cacert.pem'); if (IsSet($arguments["SSLCertificateFile"])) curl_setopt($this->connection, CURLOPT_SSLCERT, $arguments["SSLCertificateFile"]); if (IsSet($arguments["SSLCertificatePassword"])) curl_setopt($this->connection, CURLOPT_SSLCERTPASSWD, $arguments["SSLCertificatePassword"]); if (IsSet($arguments["SSLKeyFile"])) curl_setopt($this->connection, CURLOPT_SSLKEY, $arguments["SSLKeyFile"]); if (IsSet($arguments["SSLKeyPassword"])) curl_setopt($this->connection, CURLOPT_SSLKEYPASSWD, $arguments["SSLKeyPassword"]); } $this->state = "Connected"; } else { $error = ""; if (strlen($this->proxy_host_name) && (IsSet($arguments["SSLCertificateFile"]) || IsSet($arguments["SSLCertificateFile"]))) $error = "establishing SSL connections using certificates or private keys via non-SSL proxies is not supported"; else { if ($ssl) { if (IsSet($arguments["SSLCertificateFile"])) $error = "establishing SSL connections using certificates is only supported when the cURL extension is enabled"; elseif (IsSet($arguments["SSLKeyFile"])) $error = "establishing SSL connections using a private key is only supported when the cURL extension is enabled"; else { $version = explode(".", function_exists("phpversion") ? phpversion() : "3.0.7"); $php_version = intval($version[0]) * 1000000 + intval($version[1]) * 1000 + intval($version[2]); if ($php_version < 4003000) $error = "establishing SSL connections requires at least PHP version 4.3.0 or having the cURL extension enabled"; elseif (!function_exists("extension_loaded") || !extension_loaded("openssl")) $error = "establishing SSL connections requires the OpenSSL extension enabled"; } } if (strlen($error) == 0) { $error = $this->Connect($host_name, $host_port, $ssl, $server_type); $error_code = $this->error_code; } } } if (strlen($error)) return ($this->SetError($error, $error_code)); $this->session = md5(uniqid("")); $this->connected_host = $host_name; $this->connected_port = intval($host_port); $this->connected_ssl = intval($ssl); return (""); } Function Close($force = 0) { if ($this->state == "Disconnected") return ("1 already disconnected"); if (!$this->force_close && $this->keep_alive && !$force && $this->state == 'ResponseReceived') { if ($this->debug) $this->OutputDebug('Keeping the connection alive to ' . $this->connected_host); $this->state = 'Connected'; return (''); } return ($this->Disconnect()); } Function PickCookies(&$cookies, $secure) { if (IsSet($this->cookies[$secure])) { $now = gmdate("Y-m-d H-i-s"); for ($domain = 0, Reset($this->cookies[$secure]); $domain < count($this->cookies[$secure]); Next($this->cookies[$secure]), $domain++) { $domain_pattern = Key($this->cookies[$secure]); $match = strlen($this->request_host) - strlen($domain_pattern); if ($match >= 0 && !strcmp($domain_pattern, substr($this->request_host, $match)) && ($match == 0 || $domain_pattern[0] == "." || $this->request_host[$match - 1] == ".")) { for (Reset($this->cookies[$secure][$domain_pattern]), $path_part = 0; $path_part < count($this->cookies[$secure][$domain_pattern]); Next($this->cookies[$secure][$domain_pattern]), $path_part++) { $path = Key($this->cookies[$secure][$domain_pattern]); if (strlen($this->request_uri) >= strlen($path) && substr($this->request_uri, 0, strlen($path)) == $path) { for (Reset($this->cookies[$secure][$domain_pattern][$path]), $cookie = 0; $cookie < count($this->cookies[$secure][$domain_pattern][$path]); Next($this->cookies[$secure][$domain_pattern][$path]), $cookie++) { $cookie_name = Key($this->cookies[$secure][$domain_pattern][$path]); $expires = $this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"]; if ($expires == "" || strcmp($now, $expires) < 0) $cookies[$cookie_name] = $this->cookies[$secure][$domain_pattern][$path][$cookie_name]; } } } } } } } Function GetFileDefinition($file, &$definition) { $name = ""; if (IsSet($file["FileName"])) $name = basename($file["FileName"]); if (IsSet($file["Name"])) $name = $file["Name"]; if (strlen($name) == 0) return ("it was not specified the file part name"); if (IsSet($file["Content-Type"])) { $content_type = $file["Content-Type"]; $type = $this->Tokenize(strtolower($content_type), "/"); $sub_type = $this->Tokenize(""); switch ($type) { case "text": case "image": case "audio": case "video": case "application": case "message": break; case "automatic": switch ($sub_type) { case "name": switch (GetType($dot = strrpos($name, ".")) == "integer" ? strtolower(substr($name, $dot)) : "") { case ".xls": $content_type = "application/excel"; break; case ".hqx": $content_type = "application/macbinhex40"; break; case ".doc": case ".dot": case ".wrd": $content_type = "application/msword"; break; case ".pdf": $content_type = "application/pdf"; break; case ".pgp": $content_type = "application/pgp"; break; case ".ps": case ".eps": case ".ai": $content_type = "application/postscript"; break; case ".ppt": $content_type = "application/powerpoint"; break; case ".rtf": $content_type = "application/rtf"; break; case ".tgz": case ".gtar": $content_type = "application/x-gtar"; break; case ".gz": $content_type = "application/x-gzip"; break; case ".php": case ".php3": $content_type = "application/x-httpd-php"; break; case ".js": $content_type = "application/x-javascript"; break; case ".ppd": case ".psd": $content_type = "application/x-photoshop"; break; case ".svg": $content_type = "image/svg+xml"; break; case ".swf": case ".swc": case ".rf": $content_type = "application/x-shockwave-flash"; break; case ".tar": $content_type = "application/x-tar"; break; case ".zip": $content_type = "application/zip"; break; case ".mid": case ".midi": case ".kar": $content_type = "audio/midi"; break; case ".mp2": case ".mp3": case ".mpga": $content_type = "audio/mpeg"; break; case ".ra": $content_type = "audio/x-realaudio"; break; case ".wav": $content_type = "audio/wav"; break; case ".bmp": $content_type = "image/bitmap"; break; case ".gif": $content_type = "image/gif"; break; case ".iff": $content_type = "image/iff"; break; case ".jb2": $content_type = "image/jb2"; break; case ".jpg": case ".jpe": case ".jpeg": $content_type = "image/jpeg"; break; case ".jpx": $content_type = "image/jpx"; break; case ".png": $content_type = "image/png"; break; case ".tif": case ".tiff": $content_type = "image/tiff"; break; case ".wbmp": $content_type = "image/vnd.wap.wbmp"; break; case ".webp": $content_type = "image/webp"; break; case ".xbm": $content_type = "image/xbm"; break; case ".css": $content_type = "text/css"; break; case ".txt": $content_type = "text/plain"; break; case ".htm": case ".html": $content_type = "text/html"; break; case ".xml": $content_type = "text/xml"; break; case ".mpg": case ".mpe": case ".mpeg": $content_type = "video/mpeg"; break; case ".qt": case ".mov": $content_type = "video/quicktime"; break; case ".avi": $content_type = "video/x-ms-video"; break; case ".eml": $content_type = "message/rfc822"; break; default: $content_type = "application/octet-stream"; break; } break; default: return ($content_type . " is not a supported automatic content type detection method"); } break; default: return ($content_type . " is not a supported file content type"); } } else $content_type = "application/octet-stream"; $definition = array( "Content-Type" => $content_type, "NAME" => $name ); if (IsSet($file["FileName"])) { if (GetType($length = @filesize($file["FileName"])) != "integer") { $error = "it was not possible to determine the length of the file " . $file["FileName"]; $errorMessage = N2PHPErrorMessage(); if (strlen($errorMessage)) $error .= ": " . $errorMessage; if (!file_exists($file["FileName"])) $error = "it was not possible to access the file " . $file["FileName"]; return ($error); } $definition["FILENAME"] = $file["FileName"]; $definition["Content-Length"] = $length; } elseif (IsSet($file["Data"])) $definition["Content-Length"] = strlen($definition["DATA"] = $file["Data"]); else return ("it was not specified a valid file name"); return (""); } Function ConnectFromProxy($arguments, &$headers) { if (!$this->PutLine('CONNECT ' . $this->host_name . ':' . ($this->host_port ? $this->host_port : 443) . ' HTTP/1.0') || (strlen($this->user_agent) && !$this->PutLine('User-Agent: ' . $this->user_agent)) || (strlen($this->accept) && !$this->PutLine('Accept: ' . $this->accept)) || (IsSet($arguments['Headers']['Proxy-Authorization']) && !$this->PutLine('Proxy-Authorization: ' . $arguments['Headers']['Proxy-Authorization'])) || !$this->PutLine('')) { $this->Disconnect(); return ($this->error); } $this->state = "ConnectSent"; if (strlen($error = $this->ReadReplyHeadersResponse($headers))) return ($error); $proxy_authorization = ""; while (!strcmp($this->response_status, "100")) { $this->state = "ConnectSent"; if (strlen($error = $this->ReadReplyHeadersResponse($headers))) return ($error); } switch ($this->response_status) { case "200": if (!@stream_socket_enable_crypto($this->connection, 1, STREAM_CRYPTO_METHOD_SSLv23_CLIENT)) { $this->SetPHPError('it was not possible to start a SSL encrypted connection via this proxy', N2PHPErrorMessage(), HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE); $this->Disconnect(); return ($this->error); } $this->state = "Connected"; break; case "407": if (strlen($error = $this->Authenticate($headers, -1, $proxy_authorization, $this->proxy_request_user, $this->proxy_request_password, $this->proxy_request_realm, $this->proxy_request_workstation))) return ($error); break; default: return ($this->SetError("unable to send request via proxy", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); } return (""); } Function SendRequest($arguments) { if (strlen($this->error)) return ($this->error); if (IsSet($arguments["ProxyUser"])) $this->proxy_request_user = $arguments["ProxyUser"]; elseif (IsSet($this->proxy_user)) $this->proxy_request_user = $this->proxy_user; if (IsSet($arguments["ProxyPassword"])) $this->proxy_request_password = $arguments["ProxyPassword"]; elseif (IsSet($this->proxy_password)) $this->proxy_request_password = $this->proxy_password; if (IsSet($arguments["ProxyRealm"])) $this->proxy_request_realm = $arguments["ProxyRealm"]; elseif (IsSet($this->proxy_realm)) $this->proxy_request_realm = $this->proxy_realm; if (IsSet($arguments["ProxyWorkstation"])) $this->proxy_request_workstation = $arguments["ProxyWorkstation"]; elseif (IsSet($this->proxy_workstation)) $this->proxy_request_workstation = $this->proxy_workstation; switch ($this->state) { case "Disconnected": return ($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "Connected": $connect = 0; break; case "ConnectedToProxy": if (strlen($error = $this->ConnectFromProxy($arguments, $headers))) return ($error); $connect = 1; break; default: return ($this->SetError("can not send request in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); } if (IsSet($arguments["RequestMethod"])) $this->request_method = $arguments["RequestMethod"]; if (IsSet($arguments["User-Agent"])) $this->user_agent = $arguments["User-Agent"]; if (!IsSet($arguments["Headers"]["User-Agent"]) && strlen($this->user_agent)) $arguments["Headers"]["User-Agent"] = $this->user_agent; if (IsSet($arguments["KeepAlive"])) $this->keep_alive = intval($arguments["KeepAlive"]); if (!IsSet($arguments["Headers"]["Connection"]) && $this->keep_alive) $arguments["Headers"]["Connection"] = 'Keep-Alive'; if (IsSet($arguments["Accept"])) $this->user_agent = $arguments["Accept"]; if (!IsSet($arguments["Headers"]["Accept"]) && strlen($this->accept)) $arguments["Headers"]["Accept"] = $this->accept; if (strlen($this->request_method) == 0) return ($this->SetError("it was not specified a valid request method", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); if (IsSet($arguments["RequestURI"])) $this->request_uri = $arguments["RequestURI"]; if (strlen($this->request_uri) == 0 || substr($this->request_uri, 0, 1) != "/") return ($this->SetError("it was not specified a valid request URI", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); $this->request_arguments = $arguments; $this->request_headers = (IsSet($arguments["Headers"]) ? $arguments["Headers"] : array()); $body_length = 0; $this->request_body = ""; $get_body = 1; if ($this->request_method == "POST" || $this->request_method == "PUT") { if (IsSet($arguments['StreamRequest'])) { $get_body = 0; $this->request_headers["Transfer-Encoding"] = "chunked"; } elseif (IsSet($arguments["PostFiles"]) || ($this->force_multipart_form_post && IsSet($arguments["PostValues"]))) { $boundary = "--" . md5(uniqid(time())); $this->request_headers["Content-Type"] = "multipart/form-data; boundary=" . $boundary . (IsSet($arguments["CharSet"]) ? "; charset=" . $arguments["CharSet"] : ""); $post_parts = array(); if (IsSet($arguments["PostValues"])) { $values = $arguments["PostValues"]; if (GetType($values) != "array") return ($this->SetError("it was not specified a valid POST method values array", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); for (Reset($values), $value = 0; $value < count($values); Next($values), $value++) { $input = Key($values); $headers = "--" . $boundary . "\r\nContent-Disposition: form-data; name=\"" . $input . "\"\r\n\r\n"; $data = $values[$input]; $post_parts[] = array( "HEADERS" => $headers, "DATA" => $data ); $body_length += strlen($headers) + strlen($data) + strlen("\r\n"); } } $body_length += strlen("--" . $boundary . "--\r\n"); $files = (IsSet($arguments["PostFiles"]) ? $arguments["PostFiles"] : array()); Reset($files); $end = (GetType($input = Key($files)) != "string"); for (; !$end;) { if (strlen($error = $this->GetFileDefinition($files[$input], $definition))) return ("3 " . $error); $headers = "--" . $boundary . "\r\nContent-Disposition: form-data; name=\"" . $input . "\"; filename=\"" . $definition["NAME"] . "\"\r\nContent-Type: " . $definition["Content-Type"] . "\r\n\r\n"; $part = count($post_parts); $post_parts[$part] = array("HEADERS" => $headers); if (IsSet($definition["FILENAME"])) { $post_parts[$part]["FILENAME"] = $definition["FILENAME"]; $data = ""; } else $data = $definition["DATA"]; $post_parts[$part]["DATA"] = $data; $body_length += strlen($headers) + $definition["Content-Length"] + strlen("\r\n"); Next($files); $end = (GetType($input = Key($files)) != "string"); } $get_body = 0; } elseif (IsSet($arguments["PostValues"])) { $values = $arguments["PostValues"]; if (GetType($values) != "array") return ($this->SetError("it was not specified a valid POST method values array", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); for (Reset($values), $value = 0; $value < count($values); Next($values), $value++) { $k = Key($values); if (GetType($values[$k]) == "array") { for ($v = 0; $v < count($values[$k]); $v++) { if ($value + $v > 0) $this->request_body .= "&"; $this->request_body .= UrlEncode($k) . "=" . UrlEncode($values[$k][$v]); } } else { if ($value > 0) $this->request_body .= "&"; $this->request_body .= UrlEncode($k) . "=" . UrlEncode($values[$k]); } } $this->request_headers["Content-Type"] = "application/x-www-form-urlencoded" . (IsSet($arguments["CharSet"]) ? "; charset=" . $arguments["CharSet"] : ""); $get_body = 0; } } if ($get_body && (IsSet($arguments["Body"]) || IsSet($arguments["BodyStream"]))) { if (IsSet($arguments["Body"])) $this->request_body = $arguments["Body"]; else { $stream = $arguments["BodyStream"]; $this->request_body = ""; for ($part = 0; $part < count($stream); $part++) { if (IsSet($stream[$part]["Data"])) $this->request_body .= $stream[$part]["Data"]; elseif (IsSet($stream[$part]["File"])) { if (!($file = @fopen($stream[$part]["File"], "rb"))) return ($this->SetPHPError("could not open upload file " . $stream[$part]["File"], N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE)); while (!feof($file)) { if (GetType($block = @fread($file, $this->file_buffer_length)) != "string") { $error = $this->SetPHPError("could not read body stream file " . $stream[$part]["File"], N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); fclose($file); return ($error); } $this->request_body .= $block; } fclose($file); } else return ("5 it was not specified a valid file or data body stream element at position " . $part); } } if (!IsSet($this->request_headers["Content-Type"])) $this->request_headers["Content-Type"] = "application/octet-stream" . (IsSet($arguments["CharSet"]) ? "; charset=" . $arguments["CharSet"] : ""); } if (IsSet($arguments["AuthUser"])) $this->request_user = $arguments["AuthUser"]; elseif (IsSet($this->user)) $this->request_user = $this->user; if (IsSet($arguments["AuthPassword"])) $this->request_password = $arguments["AuthPassword"]; elseif (IsSet($this->password)) $this->request_password = $this->password; if (IsSet($arguments["AuthRealm"])) $this->request_realm = $arguments["AuthRealm"]; elseif (IsSet($this->realm)) $this->request_realm = $this->realm; if (IsSet($arguments["AuthWorkstation"])) $this->request_workstation = $arguments["AuthWorkstation"]; elseif (IsSet($this->workstation)) $this->request_workstation = $this->workstation; if (strlen($this->proxy_host_name) == 0 || $connect) $request_uri = $this->request_uri; else { switch (strtolower($this->protocol)) { case "http": $default_port = 80; break; case "https": $default_port = 443; break; } $request_uri = strtolower($this->protocol) . "://" . $this->host_name . (($this->host_port == 0 || $this->host_port == $default_port) ? "" : ":" . $this->host_port) . $this->request_uri; } if ($this->use_curl) { $version = (GetType($v = curl_version()) == "array" ? (IsSet($v["version"]) ? $v["version"] : "0.0.0") : (preg_match("/^libcurl\\/([0-9]+\\.[0-9]+\\.[0-9]+)/", $v, $m) ? $m[1] : "0.0.0")); $curl_version = 100000 * intval($this->Tokenize($version, ".")) + 1000 * intval($this->Tokenize(".")) + intval($this->Tokenize("")); $protocol_version = ($curl_version < 713002 ? "1.0" : $this->protocol_version); } else $protocol_version = $this->protocol_version; $this->request = $this->request_method . " " . $request_uri . " HTTP/" . $protocol_version; if ($body_length || ($body_length = strlen($this->request_body)) || !strcmp($this->request_method, 'POST')) $this->request_headers["Content-Length"] = $body_length; for ($headers = array(), $host_set = 0, Reset($this->request_headers), $header = 0; $header < count($this->request_headers); Next($this->request_headers), $header++) { $header_name = Key($this->request_headers); $header_value = $this->request_headers[$header_name]; if (GetType($header_value) == "array") { for (Reset($header_value), $value = 0; $value < count($header_value); Next($header_value), $value++) $headers[] = $header_name . ": " . $header_value[Key($header_value)]; } else $headers[] = $header_name . ": " . $header_value; if (strtolower(Key($this->request_headers)) == "host") { $this->request_host = strtolower($header_value); $host_set = 1; } } if (!$host_set) { $headers[] = "Host: " . $this->host_name; $this->request_host = strtolower($this->host_name); } if (count($this->cookies)) { $cookies = array(); $this->PickCookies($cookies, 0); if (strtolower($this->protocol) == "https") $this->PickCookies($cookies, 1); if (count($cookies)) { $h = count($headers); $headers[$h] = "Cookie:"; for (Reset($cookies), $cookie = 0; $cookie < count($cookies); Next($cookies), $cookie++) { $cookie_name = Key($cookies); $headers[$h] .= " " . $cookie_name . "=" . $cookies[$cookie_name]["value"] . ";"; } } } $next_state = "RequestSent"; if ($this->use_curl) { if (IsSet($arguments['StreamRequest'])) return ($this->SetError("Streaming request data is not supported when using Curl", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); if ($body_length && strlen($this->request_body) == 0) { for ($request_body = "", $success = 1, $part = 0; $part < count($post_parts); $part++) { $request_body .= $post_parts[$part]["HEADERS"] . $post_parts[$part]["DATA"]; if (IsSet($post_parts[$part]["FILENAME"])) { if (!($file = @fopen($post_parts[$part]["FILENAME"], "rb"))) { $this->SetPHPError("could not open upload file " . $post_parts[$part]["FILENAME"], N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); $success = 0; break; } while (!feof($file)) { if (GetType($block = @fread($file, $this->file_buffer_length)) != "string") { $this->SetPHPError("could not read upload file", N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); $success = 0; break; } $request_body .= $block; } fclose($file); if (!$success) break; } $request_body .= "\r\n"; } $request_body .= "--" . $boundary . "--\r\n"; } else $request_body = $this->request_body; curl_setopt($this->connection, CURLOPT_HEADER, 1); curl_setopt($this->connection, CURLOPT_RETURNTRANSFER, 1); if ($this->timeout) curl_setopt($this->connection, CURLOPT_TIMEOUT, $this->timeout); curl_setopt($this->connection, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($this->connection, CURLOPT_SSL_VERIFYHOST, 0); $request = $this->request . "\r\n" . implode("\r\n", $headers) . "\r\n\r\n" . $request_body; curl_setopt($this->connection, CURLOPT_CUSTOMREQUEST, $request); if ($this->debug) $this->OutputDebug("C " . $request); if (!($success = (strlen($this->response = curl_exec($this->connection)) != 0))) { $error = curl_error($this->connection); $this->SetError("Could not execute the request" . (strlen($error) ? ": " . $error : ""), HTTP_CLIENT_ERROR_PROTOCOL_FAILURE); } } else { if (($success = $this->PutLine($this->request))) { for ($header = 0; $header < count($headers); $header++) { if (!$success = $this->PutLine($headers[$header])) break; } if ($success && ($success = $this->PutLine(""))) { if (IsSet($arguments['StreamRequest'])) $next_state = "SendingRequestBody"; elseif ($body_length) { if (strlen($this->request_body)) $success = $this->PutData($this->request_body); else { for ($part = 0; $part < count($post_parts); $part++) { if (!($success = $this->PutData($post_parts[$part]["HEADERS"])) || !($success = $this->PutData($post_parts[$part]["DATA"]))) break; if (IsSet($post_parts[$part]["FILENAME"])) { if (!($file = @fopen($post_parts[$part]["FILENAME"], "rb"))) { $this->SetPHPError("could not open upload file " . $post_parts[$part]["FILENAME"], N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); $success = 0; break; } while (!feof($file)) { if (GetType($block = @fread($file, $this->file_buffer_length)) != "string") { $this->SetPHPError("could not read upload file", N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); $success = 0; break; } if (!($success = $this->PutData($block))) break; } fclose($file); if (!$success) break; } if (!($success = $this->PutLine(""))) break; } if ($success) $success = $this->PutLine("--" . $boundary . "--"); } if ($success) $sucess = $this->FlushData(); } } } } if (!$success) return ($this->SetError("could not send the HTTP request: " . $this->error, $this->error_code)); $this->state = $next_state; return (""); } Function SetCookie($name, $value, $expires = "", $path = "/", $domain = "", $secure = 0, $verbatim = 0) { if (strlen($this->error)) return ($this->error); if (strlen($name) == 0) return ($this->SetError("it was not specified a valid cookie name", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); if (strlen($path) == 0 || strcmp($path[0], "/")) return ($this->SetError($path . " is not a valid path for setting cookie " . $name, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); if ($domain == "" || strpos($domain, ".", $domain[0] == "." ? 1 : 0) === false) return ($this->SetError($domain . " is not a valid domain for setting cookie " . $name, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); $domain = strtolower($domain); if (!strcmp($domain[0], ".")) $domain = substr($domain, 1); if (!$verbatim) { $name = $this->CookieEncode($name, 1); $value = $this->CookieEncode($value, 0); } $secure = intval($secure); $this->cookies[$secure][$domain][$path][$name] = array( "name" => $name, "value" => $value, "domain" => $domain, "path" => $path, "expires" => $expires, "secure" => $secure ); return (""); } Function SendRequestBody($data, $end_of_data) { if (strlen($this->error)) return ($this->error); switch ($this->state) { case "Disconnected": return ($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "Connected": case "ConnectedToProxy": return ($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "SendingRequestBody": break; case "RequestSent": return ($this->SetError("request body was already sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); default: return ($this->SetError("can not send the request body in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); } $length = strlen($data); if ($length) { $size = dechex($length) . "\r\n"; if (!$this->PutData($size) || !$this->PutData($data)) return ($this->error); } if ($end_of_data) { $size = "0\r\n"; if (!$this->PutData($size)) return ($this->error); $this->state = "RequestSent"; } return (""); } Function ReadReplyHeadersResponse(&$headers) { $headers = array(); if (strlen($this->error)) return ($this->error); switch ($this->state) { case "Disconnected": return ($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "Connected": return ($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "ConnectedToProxy": return ($this->SetError("connection from the remote server from the proxy was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "SendingRequestBody": return ($this->SetError("request body data was not completely sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "ConnectSent": $connect = 1; break; case "RequestSent": $connect = 0; break; default: return ($this->SetError("can not get request headers in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); } $this->content_length = $this->read_length = $this->read_response = $this->remaining_chunk = 0; $this->content_length_set = $this->chunked = $this->last_chunk_read = $chunked = 0; $this->force_close = $this->connection_close = 0; for ($this->response_status = ""; ;) { $line = $this->GetLine(); if (GetType($line) != "string") return ($this->SetError("could not read request reply: " . $this->error, $this->error_code)); if (strlen($this->response_status) == 0) { if (!preg_match($match = "/^http\\/[0-9]+\\.[0-9]+[ \t]+([0-9]+)[ \t]*(.*)\$/i", $line, $matches)) return ($this->SetError("it was received an unexpected HTTP response status", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); $this->response_status = $matches[1]; $this->response_message = $matches[2]; if ($this->response_status == 204) { $this->content_length = 0; $this->content_length_set = 1; } } if ($line == "") { if (strlen($this->response_status) == 0) return ($this->SetError("it was not received HTTP response status", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); $this->state = ($connect ? "GotConnectHeaders" : "GotReplyHeaders"); break; } $header_name = strtolower($this->Tokenize($line, ":")); $header_value = Trim(Chop($this->Tokenize("\r\n"))); if (IsSet($headers[$header_name])) { if (GetType($headers[$header_name]) == "string") $headers[$header_name] = array($headers[$header_name]); $headers[$header_name][] = $header_value; } else $headers[$header_name] = $header_value; if (!$connect) { switch ($header_name) { case "content-length": $this->content_length = intval($headers[$header_name]); $this->content_length_set = 1; break; case "transfer-encoding": $encoding = $this->Tokenize($header_value, "; \t"); if (!$this->use_curl && !strcmp($encoding, "chunked")) $chunked = 1; break; case "set-cookie": if ($this->support_cookies) { if (GetType($headers[$header_name]) == "array") $cookie_headers = $headers[$header_name]; else $cookie_headers = array($headers[$header_name]); for ($cookie = 0; $cookie < count($cookie_headers); $cookie++) { $cookie_name = trim($this->Tokenize($cookie_headers[$cookie], "=")); $cookie_value = $this->Tokenize(";"); $domain = $this->request_host; $path = "/"; $expires = ""; $secure = 0; while (($name = strtolower(trim(UrlDecode($this->Tokenize("="))))) != "") { $value = UrlDecode($this->Tokenize(";")); switch ($name) { case "domain": $domain = $value; break; case "path": $path = $value; break; case "expires": if (preg_match("/^((Mon|Monday|Tue|Tuesday|Wed|Wednesday|Thu|Thursday|Fri|Friday|Sat|Saturday|Sun|Sunday), )?([0-9]{2})\\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\-([0-9]{2,4}) ([0-9]{2})\\:([0-9]{2})\\:([0-9]{2}) GMT\$/", $value, $matches)) { $year = intval($matches[5]); if ($year < 1900) $year += ($year < 70 ? 2000 : 1900); $expires = "$year-" . $this->months[$matches[4]] . "-" . $matches[3] . " " . $matches[6] . ":" . $matches[7] . ":" . $matches[8]; } break; case "secure": $secure = 1; break; } } if (strlen($this->SetCookie($cookie_name, $cookie_value, $expires, $path, $domain, $secure, 1))) $this->error = ""; } } break; case "connection": $this->force_close = $this->connection_close = !strcmp(strtolower($header_value), "close"); break; } } } $this->chunked = $chunked; if ($this->content_length_set) $this->connection_close = 0; return (""); } Function Redirect(&$headers) { if ($this->follow_redirect) { if (!IsSet($headers["location"]) || (GetType($headers["location"]) != "array" && strlen($location = $headers["location"]) == 0) || (GetType($headers["location"]) == "array" && strlen($location = $headers["location"][0]) == 0)) return ($this->SetError("it was received a redirect without location URL", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); if (strcmp($location[0], "/")) { if (!($location_arguments = @parse_url($location))) return ($this->SetError("the server did not return a valid redirection location URL", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); if (!IsSet($location_arguments["scheme"])) $location = ((GetType($end = strrpos($this->request_uri, "/")) == "integer" && $end > 1) ? substr($this->request_uri, 0, $end) : "") . "/" . $location; } if (!strcmp($location[0], "/")) $location = $this->protocol . "://" . $this->host_name . ($this->host_port ? ":" . $this->host_port : "") . $location; $error = $this->GetRequestArguments($location, $arguments); if (strlen($error)) return ($this->SetError("could not process redirect url: " . $error, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); $arguments["RequestMethod"] = "GET"; if (strlen($error = $this->Close()) == 0 && strlen($error = $this->Open($arguments)) == 0 && strlen($error = $this->SendRequest($arguments)) == 0) { $this->redirection_level++; if ($this->redirection_level > $this->redirection_limit) { $error = "it was exceeded the limit of request redirections"; $this->error_code = HTTP_CLIENT_ERROR_PROTOCOL_FAILURE; } else $error = $this->ReadReplyHeaders($headers); $this->redirection_level--; } if (strlen($error)) return ($this->SetError($error, $this->error_code)); } return (""); } Function Authenticate(&$headers, $proxy, &$proxy_authorization, &$user, &$password, &$realm, &$workstation) { if ($proxy) { $authenticate_header = "proxy-authenticate"; $authorization_header = "Proxy-Authorization"; $authenticate_status = "407"; $authentication_mechanism = $this->proxy_authentication_mechanism; } else { $authenticate_header = "www-authenticate"; $authorization_header = "Authorization"; $authenticate_status = "401"; $authentication_mechanism = $this->authentication_mechanism; } if (IsSet($headers[$authenticate_header]) && $this->sasl_authenticate) { if (function_exists("class_exists") && !class_exists("sasl_client_class")) return ($this->SetError("the SASL client class needs to be loaded to be able to authenticate" . ($proxy ? " with the proxy server" : "") . " and access this site", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); if (GetType($headers[$authenticate_header]) == "array") $authenticate = $headers[$authenticate_header]; else $authenticate = array($headers[$authenticate_header]); for ($response = "", $mechanisms = array(), $m = 0; $m < count($authenticate); $m++) { $mechanism = $this->Tokenize($authenticate[$m], " "); $response = $this->Tokenize(""); if (strlen($authentication_mechanism)) { if (!strcmp($authentication_mechanism, $mechanism)) { $mechanisms[] = $mechanism; break; } } else $mechanisms[] = $mechanism; } $sasl = new sasl_client_class; if (IsSet($user)) $sasl->SetCredential("user", $user); if (IsSet($password)) $sasl->SetCredential("password", $password); if (IsSet($realm)) $sasl->SetCredential("realm", $realm); if (IsSet($workstation)) $sasl->SetCredential("workstation", $workstation); $sasl->SetCredential("uri", $this->request_uri); $sasl->SetCredential("method", $this->request_method); $sasl->SetCredential("session", $this->session); do { $status = $sasl->Start($mechanisms, $message, $interactions); } while ($status == SASL_INTERACT); switch ($status) { case SASL_CONTINUE: break; case SASL_NOMECH: return ($this->SetError(($proxy ? "proxy " : "") . "authentication error: " . (strlen($authentication_mechanism) ? "authentication mechanism " . $authentication_mechanism . " may not be used: " : "") . $sasl->error, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); default: return ($this->SetError("Could not start the SASL " . ($proxy ? "proxy " : "") . "authentication client: " . $sasl->error, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); } if ($proxy >= 0) { for (; ;) { if (strlen($error = $this->ReadReplyBody($body, $this->file_buffer_length))) return ($error); if (strlen($body) == 0) break; } } $authorization_value = $sasl->mechanism . (IsSet($message) ? " " . ($sasl->encode_response ? n2_base64_encode($message) : $message) : ""); $request_arguments = $this->request_arguments; $arguments = $request_arguments; $arguments["Headers"][$authorization_header] = $authorization_value; if (!$proxy && strlen($proxy_authorization)) $arguments["Headers"]["Proxy-Authorization"] = $proxy_authorization; if (strlen($error = $this->Close()) || strlen($error = $this->Open($arguments))) return ($this->SetError($error, $this->error_code)); $authenticated = 0; if (IsSet($message)) { if ($proxy < 0) { if (strlen($error = $this->ConnectFromProxy($arguments, $headers))) return ($this->SetError($error, $this->error_code)); } else { if (strlen($error = $this->SendRequest($arguments)) || strlen($error = $this->ReadReplyHeadersResponse($headers))) return ($this->SetError($error, $this->error_code)); } if (!IsSet($headers[$authenticate_header])) $authenticate = array(); elseif (GetType($headers[$authenticate_header]) == "array") $authenticate = $headers[$authenticate_header]; else $authenticate = array($headers[$authenticate_header]); for ($mechanism = 0; $mechanism < count($authenticate); $mechanism++) { if (!strcmp($this->Tokenize($authenticate[$mechanism], " "), $sasl->mechanism)) { $response = $this->Tokenize(""); break; } } switch ($this->response_status) { case $authenticate_status: break; case "301": case "302": case "303": case "307": if ($proxy >= 0) return ($this->Redirect($headers)); default: if (intval($this->response_status / 100) == 2) { if ($proxy) $proxy_authorization = $authorization_value; $authenticated = 1; break; } if ($proxy && !strcmp($this->response_status, "401")) { $proxy_authorization = $authorization_value; $authenticated = 1; break; } return ($this->SetError(($proxy ? "proxy " : "") . "authentication error: " . $this->response_status . " " . $this->response_message, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); } } for (; !$authenticated;) { do { $status = $sasl->Step($response, $message, $interactions); } while ($status == SASL_INTERACT); switch ($status) { case SASL_CONTINUE: $authorization_value = $sasl->mechanism . (IsSet($message) ? " " . ($sasl->encode_response ? n2_base64_encode($message) : $message) : ""); $arguments = $request_arguments; $arguments["Headers"][$authorization_header] = $authorization_value; if (!$proxy && strlen($proxy_authorization)) $arguments["Headers"]["Proxy-Authorization"] = $proxy_authorization; if ($proxy < 0) { if (strlen($error = $this->ConnectFromProxy($arguments, $headers))) return ($this->SetError($error, $this->error_code)); } else { if (strlen($error = $this->SendRequest($arguments)) || strlen($error = $this->ReadReplyHeadersResponse($headers))) return ($this->SetError($error, $this->error_code)); } switch ($this->response_status) { case $authenticate_status: if (GetType($headers[$authenticate_header]) == "array") $authenticate = $headers[$authenticate_header]; else $authenticate = array($headers[$authenticate_header]); for ($response = "", $mechanism = 0; $mechanism < count($authenticate); $mechanism++) { if (!strcmp($this->Tokenize($authenticate[$mechanism], " "), $sasl->mechanism)) { $response = $this->Tokenize(""); break; } } if ($proxy >= 0) { for (; ;) { if (strlen($error = $this->ReadReplyBody($body, $this->file_buffer_length))) return ($error); if (strlen($body) == 0) break; } } $this->state = "Connected"; break; case "301": case "302": case "303": case "307": if ($proxy >= 0) return ($this->Redirect($headers)); default: if (intval($this->response_status / 100) == 2) { if ($proxy) $proxy_authorization = $authorization_value; $authenticated = 1; break; } if ($proxy && !strcmp($this->response_status, "401")) { $proxy_authorization = $authorization_value; $authenticated = 1; break; } return ($this->SetError(($proxy ? "proxy " : "") . "authentication error: " . $this->response_status . " " . $this->response_message)); } break; default: return ($this->SetError("Could not process the SASL " . ($proxy ? "proxy " : "") . "authentication step: " . $sasl->error, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); } } } return (""); } Function ReadReplyHeaders(&$headers) { if (strlen($error = $this->ReadReplyHeadersResponse($headers))) return ($error); $proxy_authorization = ""; while (!strcmp($this->response_status, "100")) { $this->state = "RequestSent"; if (strlen($error = $this->ReadReplyHeadersResponse($headers))) return ($error); } switch ($this->response_status) { case "301": case "302": case "303": case "307": if (strlen($error = $this->Redirect($headers))) return ($error); break; case "407": if (strlen($error = $this->Authenticate($headers, 1, $proxy_authorization, $this->proxy_request_user, $this->proxy_request_password, $this->proxy_request_realm, $this->proxy_request_workstation))) return ($error); if (strcmp($this->response_status, "401")) return (""); case "401": return ($this->Authenticate($headers, 0, $proxy_authorization, $this->request_user, $this->request_password, $this->request_realm, $this->request_workstation)); } return (""); } Function ReadReplyBody(&$body, $length) { $body = ""; if (strlen($this->error)) return ($this->error); switch ($this->state) { case "Disconnected": return ($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "Connected": case "ConnectedToProxy": return ($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); case "RequestSent": if (($error = $this->ReadReplyHeaders($headers)) != "") return ($error); break; case "GotReplyHeaders": break; case 'ResponseReceived': $body = ''; return (''); default: return ($this->SetError("can not get request headers in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); } if ($this->content_length_set) $length = min($this->content_length - $this->read_length, $length); $body = ''; if ($length > 0) { if (!$this->EndOfInput() && ($body = $this->ReadBytes($length)) == "") { if (strlen($this->error)) return ($this->SetError("could not get the request reply body: " . $this->error, $this->error_code)); } $this->read_length += strlen($body); if ($this->EndOfInput()) $this->state = 'ResponseReceived'; } return (""); } Function ReadWholeReplyBody(&$body) { $body = ''; for (; ;) { if (strlen($error = $this->ReadReplyBody($block, $this->file_buffer_length))) return ($error); if (strlen($block) == 0) return (''); $body .= $block; } } Function ReadWholeReplyIntoTemporaryFile(&$file) { if (!($file = tmpfile())) return $this->SetPHPError('could not create the temporary file to save the response', N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); for (; ;) { if (strlen($error = $this->ReadReplyBody($block, $this->file_buffer_length))) { fclose($file); return ($error); } if (strlen($block) == 0) { if (@fseek($file, 0) != 0) { $error = $this->SetPHPError('could not seek to the beginning of temporary file with the response', N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); fclose($file); return $error; } return (''); } if (!@fwrite($file, $block)) { $error = $this->SetPHPError('could not write to the temporary file to save the response', N2PHPErrorMessage(), HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); fclose($file); return $error; } } } Function SaveCookies(&$cookies, $domain = '', $secure_only = 0, $persistent_only = 0) { $now = gmdate("Y-m-d H-i-s"); $cookies = array(); for ($secure_cookies = 0, Reset($this->cookies); $secure_cookies < count($this->cookies); Next($this->cookies), $secure_cookies++) { $secure = Key($this->cookies); if (!$secure_only || $secure) { for ($cookie_domain = 0, Reset($this->cookies[$secure]); $cookie_domain < count($this->cookies[$secure]); Next($this->cookies[$secure]), $cookie_domain++) { $domain_pattern = Key($this->cookies[$secure]); $match = strlen($domain) - strlen($domain_pattern); if (strlen($domain) == 0 || ($match >= 0 && !strcmp($domain_pattern, substr($domain, $match)) && ($match == 0 || $domain_pattern[0] == "." || $domain[$match - 1] == "."))) { for (Reset($this->cookies[$secure][$domain_pattern]), $path_part = 0; $path_part < count($this->cookies[$secure][$domain_pattern]); Next($this->cookies[$secure][$domain_pattern]), $path_part++) { $path = Key($this->cookies[$secure][$domain_pattern]); for (Reset($this->cookies[$secure][$domain_pattern][$path]), $cookie = 0; $cookie < count($this->cookies[$secure][$domain_pattern][$path]); Next($this->cookies[$secure][$domain_pattern][$path]), $cookie++) { $cookie_name = Key($this->cookies[$secure][$domain_pattern][$path]); $expires = $this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"]; if ((!$persistent_only && strlen($expires) == 0) || (strlen($expires) && strcmp($now, $expires) < 0)) $cookies[$secure][$domain_pattern][$path][$cookie_name] = $this->cookies[$secure][$domain_pattern][$path][$cookie_name]; } } } } } } } Function SavePersistentCookies(&$cookies, $domain = '', $secure_only = 0) { $this->SaveCookies($cookies, $domain, $secure_only, 1); } Function GetPersistentCookies(&$cookies, $domain = '', $secure_only = 0) { $this->SavePersistentCookies($cookies, $domain, $secure_only); } Function RestoreCookies($cookies, $clear = 1) { $new_cookies = ($clear ? array() : $this->cookies); for ($secure_cookies = 0, Reset($cookies); $secure_cookies < count($cookies); Next($cookies), $secure_cookies++) { $secure = Key($cookies); if (GetType($secure) != "integer") return ($this->SetError("invalid cookie secure value type (" . serialize($secure) . ")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); for ($cookie_domain = 0, Reset($cookies[$secure]); $cookie_domain < count($cookies[$secure]); Next($cookies[$secure]), $cookie_domain++) { $domain_pattern = Key($cookies[$secure]); if (GetType($domain_pattern) != "string") return ($this->SetError("invalid cookie domain value type (" . serialize($domain_pattern) . ")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); for (Reset($cookies[$secure][$domain_pattern]), $path_part = 0; $path_part < count($cookies[$secure][$domain_pattern]); Next($cookies[$secure][$domain_pattern]), $path_part++) { $path = Key($cookies[$secure][$domain_pattern]); if (GetType($path) != "string" || strcmp(substr($path, 0, 1), "/")) return ($this->SetError("invalid cookie path value type (" . serialize($path) . ")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); for (Reset($cookies[$secure][$domain_pattern][$path]), $cookie = 0; $cookie < count($cookies[$secure][$domain_pattern][$path]); Next($cookies[$secure][$domain_pattern][$path]), $cookie++) { $cookie_name = Key($cookies[$secure][$domain_pattern][$path]); $expires = $cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"]; $value = $cookies[$secure][$domain_pattern][$path][$cookie_name]["value"]; if (GetType($expires) != "string" || (strlen($expires) && !preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\$/", $expires))) return ($this->SetError("invalid cookie expiry value type (" . serialize($expires) . ")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); $new_cookies[$secure][$domain_pattern][$path][$cookie_name] = array( "name" => $cookie_name, "value" => $value, "domain" => $domain_pattern, "path" => $path, "expires" => $expires, "secure" => $secure ); } } } } $this->cookies = $new_cookies; return (""); } }