2144 lines
96 KiB
PHP
2144 lines
96 KiB
PHP
<?php
|
|
/*
|
|
* http.php
|
|
*
|
|
* @(#) $Header: /opt2/ena/metal/http/http.php,v 1.92 2014/08/14 23:17:34 mlemos Exp $
|
|
*
|
|
*/
|
|
|
|
define('HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR', -1);
|
|
define('HTTP_CLIENT_ERROR_NO_ERROR', 0);
|
|
define('HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS', 1);
|
|
define('HTTP_CLIENT_ERROR_CANNOT_CONNECT', 2);
|
|
define('HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE', 3);
|
|
define('HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE', 4);
|
|
define('HTTP_CLIENT_ERROR_PROTOCOL_FAILURE', 5);
|
|
define('HTTP_CLIENT_ERROR_INVALID_PARAMETERS', 6);
|
|
|
|
if (!class_exists('sasl_client_class')) {
|
|
include _PS_MODULE_DIR_.'onepagecheckoutps/lib/social_network/sasl.php';
|
|
}
|
|
|
|
class http_class
|
|
{
|
|
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 = 0;
|
|
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++) {
|
|
if (GetType($position=strpos($string, $separator[$character]))=="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."\"", $php_errormsg, $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) {
|
|
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 ".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 ".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"];
|
|
if (IsSet($php_errormsg)
|
|
&& strlen($php_errormsg)) {
|
|
$error.=": ".$php_errormsg;
|
|
}
|
|
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', $php_errormsg, 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"], $php_errormsg, 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"], $php_errormsg, 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"], $php_errormsg, 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", $php_errormsg, 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"], $php_errormsg, 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", $php_errormsg, 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)) {
|
|
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 ? 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 ? 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', $php_errormsg, 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', $php_errormsg, 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', $php_errormsg, 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("");
|
|
}
|
|
};
|
|
|
|
?>
|