]> git.mxchange.org Git - friendica-addons.git/blob - pumpio/oauth/http.php
Fix typo in translation string in twitter
[friendica-addons.git] / pumpio / oauth / http.php
1 <?php
2 /*
3  * http.php
4  *
5  * @(#) $Header: /opt2/ena/metal/http/http.php,v 1.90 2013/02/20 11:45:28 mlemos Exp $
6  *
7  */
8
9 define('HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR',       -1);
10 define('HTTP_CLIENT_ERROR_NO_ERROR',                 0);
11 define('HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS',   1);
12 define('HTTP_CLIENT_ERROR_CANNOT_CONNECT',           2);
13 define('HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE',    3);
14 define('HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE', 4);
15 define('HTTP_CLIENT_ERROR_PROTOCOL_FAILURE',         5);
16 define('HTTP_CLIENT_ERROR_INVALID_PARAMETERS',       6);
17
18 class http_class
19 {
20         var $host_name="";
21         var $host_port=0;
22         var $proxy_host_name="";
23         var $proxy_host_port=80;
24         var $socks_host_name = '';
25         var $socks_host_port = 1080;
26         var $socks_version = '5';
27
28         var $protocol="http";
29         var $request_method="GET";
30         var $user_agent='httpclient (http://www.phpclasses.org/httpclient $Revision: 1.90 $)';
31         var $accept='';
32         var $authentication_mechanism="";
33         var $user;
34         var $password;
35         var $realm;
36         var $workstation;
37         var $proxy_authentication_mechanism="";
38         var $proxy_user;
39         var $proxy_password;
40         var $proxy_realm;
41         var $proxy_workstation;
42         var $request_uri="";
43         var $request="";
44         var $request_headers=array();
45         var $request_user;
46         var $request_password;
47         var $request_realm;
48         var $request_workstation;
49         var $proxy_request_user;
50         var $proxy_request_password;
51         var $proxy_request_realm;
52         var $proxy_request_workstation;
53         var $request_body="";
54         var $request_arguments=array();
55         var $protocol_version="1.1";
56         var $timeout=0;
57         var $data_timeout=0;
58         var $debug=0;
59         var $log_debug=0;
60         var $debug_response_body=1;
61         var $html_debug=0;
62         var $support_cookies=1;
63         var $cookies=array();
64         var $error="";
65         var $error_code = HTTP_CLIENT_ERROR_NO_ERROR;
66         var $exclude_address="";
67         var $follow_redirect=0;
68         var $redirection_limit=5;
69         var $response_status="";
70         var $response_message="";
71         var $file_buffer_length=8000;
72         var $force_multipart_form_post=0;
73         var $prefer_curl = 0;
74         var $keep_alive = 1;
75         var $sasl_authenticate = 1;
76
77         /* private variables - DO NOT ACCESS */
78
79         var $state="Disconnected";
80         var $use_curl=0;
81         var $connection=0;
82         var $content_length=0;
83         var $response="";
84         var $read_response=0;
85         var $read_length=0;
86         var $request_host="";
87         var $next_token="";
88         var $redirection_level=0;
89         var $chunked=0;
90         var $remaining_chunk=0;
91         var $last_chunk_read=0;
92         var $months=array(
93                 "Jan"=>"01",
94                 "Feb"=>"02",
95                 "Mar"=>"03",
96                 "Apr"=>"04",
97                 "May"=>"05",
98                 "Jun"=>"06",
99                 "Jul"=>"07",
100                 "Aug"=>"08",
101                 "Sep"=>"09",
102                 "Oct"=>"10",
103                 "Nov"=>"11",
104                 "Dec"=>"12");
105         var $session='';
106         var $connection_close=0;
107         var $force_close = 0;
108         var $connected_host = '';
109         var $connected_port = -1;
110         var $connected_ssl = 0;
111
112         /* Private methods - DO NOT CALL */
113
114         Function Tokenize($string,$separator="")
115         {
116                 if(!strcmp($separator,""))
117                 {
118                         $separator=$string;
119                         $string=$this->next_token;
120                 }
121                 for($character=0;$character<strlen($separator);$character++)
122                 {
123                         if(GetType($position=strpos($string,$separator[$character]))=="integer")
124                                 $found=(IsSet($found) ? min($found,$position) : $position);
125                 }
126                 if(IsSet($found))
127                 {
128                         $this->next_token=substr($string,$found+1);
129                         return(substr($string,0,$found));
130                 }
131                 else
132                 {
133                         $this->next_token="";
134                         return($string);
135                 }
136         }
137
138         Function CookieEncode($value, $name)
139         {
140                 return($name ? str_replace("=", "%25", $value) : str_replace(";", "%3B", $value));
141         }
142
143         Function SetError($error, $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR)
144         {
145                 $this->error_code = $error_code;
146                 return($this->error=$error);
147         }
148
149         Function SetPHPError($error, &$php_error_message, $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR)
150         {
151                 if(IsSet($php_error_message)
152                 && strlen($php_error_message))
153                         $error.=": ".$php_error_message;
154                 return($this->SetError($error, $error_code));
155         }
156
157         Function SetDataAccessError($error,$check_connection=0)
158         {
159                 $this->error=$error;
160                 $this->error_code = HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE;
161                 if(!$this->use_curl
162                 && function_exists("socket_get_status"))
163                 {
164                         $status=socket_get_status($this->connection);
165                         if($status["timed_out"])
166                                 $this->error.=": data access time out";
167                         elseif($status["eof"])
168                         {
169                                 if($check_connection)
170                                         $this->error="";
171                                 else
172                                         $this->error.=": the server disconnected";
173                         }
174                 }
175         }
176
177         Function OutputDebug($message)
178         {
179                 if($this->log_debug)
180                         error_log($message);
181                 else
182                 {
183                         $message.="\n";
184                         if($this->html_debug)
185                                 $message=str_replace("\n","<br />\n",HtmlEntities($message));
186                         echo $message;
187                         flush();
188                 }
189         }
190
191         Function GetLine()
192         {
193                 for($line="";;)
194                 {
195                         if($this->use_curl)
196                         {
197                                 $eol=strpos($this->response,"\n",$this->read_response);
198                                 $data=($eol ? substr($this->response,$this->read_response,$eol+1-$this->read_response) : "");
199                                 $this->read_response+=strlen($data);
200                         }
201                         else
202                         {
203                                 if(feof($this->connection))
204                                 {
205                                         $this->SetDataAccessError("reached the end of data while reading from the HTTP server connection");
206                                         return(0);
207                                 }
208                                 $data=fgets($this->connection,100);
209                         }
210                         if(GetType($data)!="string"
211                         || strlen($data)==0)
212                         {
213                                 $this->SetDataAccessError("it was not possible to read line from the HTTP server");
214                                 return(0);
215                         }
216                         $line.=$data;
217                         $length=strlen($line);
218                         if($length
219                         && !strcmp(substr($line,$length-1,1),"\n"))
220                         {
221                                 $length-=(($length>=2 && !strcmp(substr($line,$length-2,1),"\r")) ? 2 : 1);
222                                 $line=substr($line,0,$length);
223                                 if($this->debug)
224                                         $this->OutputDebug("S $line");
225                                 return($line);
226                         }
227                 }
228         }
229
230         Function PutLine($line)
231         {
232                 if($this->debug)
233                         $this->OutputDebug("C $line");
234                 if(!fputs($this->connection,$line."\r\n"))
235                 {
236                         $this->SetDataAccessError("it was not possible to send a line to the HTTP server");
237                         return(0);
238                 }
239                 return(1);
240         }
241
242         Function PutData($data)
243         {
244                 if(strlen($data))
245                 {
246                         if($this->debug)
247                                 $this->OutputDebug('C '.$data);
248                         if(!fputs($this->connection,$data))
249                         {
250                                 $this->SetDataAccessError("it was not possible to send data to the HTTP server");
251                                 return(0);
252                         }
253                 }
254                 return(1);
255         }
256
257         Function FlushData()
258         {
259                 if(!fflush($this->connection))
260                 {
261                         $this->SetDataAccessError("it was not possible to send data to the HTTP server");
262                         return(0);
263                 }
264                 return(1);
265         }
266
267         Function ReadChunkSize()
268         {
269                 if($this->remaining_chunk==0)
270                 {
271                         $debug=$this->debug;
272                         if(!$this->debug_response_body)
273                                 $this->debug=0;
274                         $line=$this->GetLine();
275                         $this->debug=$debug;
276                         if(GetType($line)!="string")
277                                 return($this->SetError("could not read chunk start: ".$this->error, $this->error_code));
278                         $this->remaining_chunk=hexdec($line);
279                         if($this->remaining_chunk == 0)
280                         {
281                                 if(!$this->debug_response_body)
282                                         $this->debug=0;
283                                 $line=$this->GetLine();
284                                 $this->debug=$debug;
285                                 if(GetType($line)!="string")
286                                         return($this->SetError("could not read chunk end: ".$this->error, $this->error_code));
287                         }
288                 }
289                 return("");
290         }
291
292         Function ReadBytes($length)
293         {
294                 if($this->use_curl)
295                 {
296                         $bytes=substr($this->response,$this->read_response,min($length,strlen($this->response)-$this->read_response));
297                         $this->read_response+=strlen($bytes);
298                         if($this->debug
299                         && $this->debug_response_body
300                         && strlen($bytes))
301                                 $this->OutputDebug("S ".$bytes);
302                 }
303                 else
304                 {
305                         if($this->chunked)
306                         {
307                                 for($bytes="",$remaining=$length;$remaining;)
308                                 {
309                                         if(strlen($this->ReadChunkSize()))
310                                                 return("");
311                                         if($this->remaining_chunk==0)
312                                         {
313                                                 $this->last_chunk_read=1;
314                                                 break;
315                                         }
316                                         $ask=min($this->remaining_chunk,$remaining);
317                                         $chunk=@fread($this->connection,$ask);
318                                         $read=strlen($chunk);
319                                         if($read==0)
320                                         {
321                                                 $this->SetDataAccessError("it was not possible to read data chunk from the HTTP server");
322                                                 return("");
323                                         }
324                                         if($this->debug
325                                         && $this->debug_response_body)
326                                                 $this->OutputDebug("S ".$chunk);
327                                         $bytes.=$chunk;
328                                         $this->remaining_chunk-=$read;
329                                         $remaining-=$read;
330                                         if($this->remaining_chunk==0)
331                                         {
332                                                 if(feof($this->connection))
333                                                         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));
334                                                 $data=@fread($this->connection,2);
335                                                 if(strcmp($data,"\r\n"))
336                                                 {
337                                                         $this->SetDataAccessError("it was not possible to read end of data chunk from the HTTP server");
338                                                         return("");
339                                                 }
340                                         }
341                                 }
342                         }
343                         else
344                         {
345                                 $bytes=@fread($this->connection,$length);
346                                 if(strlen($bytes))
347                                 {
348                                         if($this->debug
349                                         && $this->debug_response_body)
350                                                 $this->OutputDebug("S ".$bytes);
351                                 }
352                                 else
353                                         $this->SetDataAccessError("it was not possible to read data from the HTTP server", $this->connection_close);
354                         }
355                 }
356                 return($bytes);
357         }
358
359         Function EndOfInput()
360         {
361                 if($this->use_curl)
362                         return($this->read_response>=strlen($this->response));
363                 if($this->chunked)
364                         return($this->last_chunk_read);
365                 if($this->content_length_set)
366                         return($this->content_length <= $this->read_length);
367                 return(feof($this->connection));
368         }
369
370         Function Resolve($domain, &$ip, $server_type)
371         {
372                 if(preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',$domain))
373                         $ip=$domain;
374                 else
375                 {
376                         if($this->debug)
377                                 $this->OutputDebug('Resolving '.$server_type.' server domain "'.$domain.'"...');
378                         if(!strcmp($ip=@gethostbyname($domain),$domain))
379                                 $ip="";
380                 }
381                 if(strlen($ip)==0
382                 || (strlen($this->exclude_address)
383                 && !strcmp(@gethostbyname($this->exclude_address),$ip)))
384                         return($this->SetError("could not resolve the host domain \"".$domain."\"", HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS));
385                 return('');
386         }
387
388         Function Connect($host_name, $host_port, $ssl, $server_type = 'HTTP')
389         {
390                 $domain=$host_name;
391                 $port = $host_port;
392                 if(strlen($error = $this->Resolve($domain, $ip, $server_type)))
393                         return($error);
394                 if(strlen($this->socks_host_name))
395                 {
396                         switch($this->socks_version)
397                         {
398                                 case '4':
399                                         $version = 4;
400                                         break;
401                                 case '5':
402                                         $version = 5;
403                                         break;
404                                 default:
405                                         return('it was not specified a supported SOCKS protocol version');
406                                         break;
407                         }
408                         $host_ip = $ip;
409                         $port = $this->socks_host_port;
410                         $host_server_type = $server_type;
411                         $server_type = 'SOCKS';
412                         if(strlen($error = $this->Resolve($this->socks_host_name, $ip, $server_type)))
413                                 return($error);
414                 }
415                 if($this->debug)
416                         $this->OutputDebug('Connecting to '.$server_type.' server IP '.$ip.' port '.$port.'...');
417                 if($ssl)
418                         $ip="ssl://".$host_name;
419                 if(($this->connection=($this->timeout ? @fsockopen($ip, $port, $errno, $error, $this->timeout) : @fsockopen($ip, $port, $errno)))==0)
420                 {
421                         $error_code = HTTP_CLIENT_ERROR_CANNOT_CONNECT;
422                         switch($errno)
423                         {
424                                 case -3:
425                                         return($this->SetError("socket could not be created", $error_code));
426                                 case -4:
427                                         return($this->SetError("dns lookup on hostname \"".$host_name."\" failed", $error_code));
428                                 case -5:
429                                         return($this->SetError("connection refused or timed out", $error_code));
430                                 case -6:
431                                         return($this->SetError("fdopen() call failed", $error_code));
432                                 case -7:
433                                         return($this->SetError("setvbuf() call failed", $error_code));
434                                 default:
435                                         return($this->SetPHPError($errno." could not connect to the host \"".$host_name."\"",$php_errormsg, $error_code));
436                         }
437                 }
438                 else
439                 {
440                         if($this->data_timeout
441                         && function_exists("socket_set_timeout"))
442                                 socket_set_timeout($this->connection,$this->data_timeout,0);
443                         if(strlen($this->socks_host_name))
444                         {
445                                 if($this->debug)
446                                         $this->OutputDebug('Connected to the SOCKS server '.$this->socks_host_name);
447                                 $send_error = 'it was not possible to send data to the SOCKS server';
448                                 $receive_error = 'it was not possible to receive data from the SOCKS server';
449                                 switch($version)
450                                 {
451                                         case 4:
452                                                 $command = 1;
453                                                 $user = '';
454                                                 if(!fputs($this->connection, chr($version).chr($command).pack('nN', $host_port, ip2long($host_ip)).$user.Chr(0)))
455                                                         $error = $this->SetDataAccessError($send_error);
456                                                 else
457                                                 {
458                                                         $response = fgets($this->connection, 9);
459                                                         if(strlen($response) != 8)
460                                                                 $error = $this->SetDataAccessError($receive_error);
461                                                         else
462                                                         {
463                                                                 $socks_errors = array(
464                                                                         "\x5a"=>'',
465                                                                         "\x5b"=>'request rejected',
466                                                                         "\x5c"=>'request failed because client is not running identd (or not reachable from the server)',
467                                                                         "\x5d"=>'request failed because client\'s identd could not confirm the user ID string in the request',
468                                                                 );
469                                                                 $error_code = $response[1];
470                                                                 $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown');
471                                                                 if(strlen($error))
472                                                                         $error = 'SOCKS error: '.$error;
473                                                         }
474                                                 }
475                                                 break;
476                                         case 5:
477                                                 if($this->debug)
478                                                         $this->OutputDebug('Negotiating the authentication method ...');
479                                                 $methods = 1;
480                                                 $method = 0;
481                                                 if(!fputs($this->connection, chr($version).chr($methods).chr($method)))
482                                                         $error = $this->SetDataAccessError($send_error);
483                                                 else
484                                                 {
485                                                         $response = fgets($this->connection, 3);
486                                                         if(strlen($response) != 2)
487                                                                 $error = $this->SetDataAccessError($receive_error);
488                                                         elseif(Ord($response[1]) != $method)
489                                                                 $error = 'the SOCKS server requires an authentication method that is not yet supported';
490                                                         else
491                                                         {
492                                                                 if($this->debug)
493                                                                         $this->OutputDebug('Connecting to '.$host_server_type.' server IP '.$host_ip.' port '.$host_port.'...');
494                                                                 $command = 1;
495                                                                 $address_type = 1;
496                                                                 if(!fputs($this->connection, chr($version).chr($command)."\x00".chr($address_type).pack('Nn', ip2long($host_ip), $host_port)))
497                                                                         $error = $this->SetDataAccessError($send_error);
498                                                                 else
499                                                                 {
500                                                                         $response = fgets($this->connection, 11);
501                                                                         if(strlen($response) != 10)
502                                                                                 $error = $this->SetDataAccessError($receive_error);
503                                                                         else
504                                                                         {
505                                                                                 $socks_errors = array(
506                                                                                         "\x00"=>'',
507                                                                                         "\x01"=>'general SOCKS server failure',
508                                                                                         "\x02"=>'connection not allowed by ruleset',
509                                                                                         "\x03"=>'Network unreachable',
510                                                                                         "\x04"=>'Host unreachable',
511                                                                                         "\x05"=>'Connection refused',
512                                                                                         "\x06"=>'TTL expired',
513                                                                                         "\x07"=>'Command not supported',
514                                                                                         "\x08"=>'Address type not supported'
515                                                                                 );
516                                                                                 $error_code = $response[1];
517                                                                                 $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown');
518                                                                                 if(strlen($error))
519                                                                                         $error = 'SOCKS error: '.$error;
520                                                                         }
521                                                                 }
522                                                         }
523                                                 }
524                                                 break;
525                                         default:
526                                                 $error = 'support for SOCKS protocol version '.$this->socks_version.' is not yet implemented';
527                                                 break;
528                                 }
529                                 if(strlen($error))
530                                 {
531                                         fclose($this->connection);
532                                         return($error);
533                                 }
534                         }
535                         if($this->debug)
536                                 $this->OutputDebug("Connected to $host_name");
537                         if(strlen($this->proxy_host_name)
538                         && !strcmp(strtolower($this->protocol), 'https'))
539                         {
540                                 if(function_exists('stream_socket_enable_crypto')
541                                 && in_array('ssl', stream_get_transports()))
542                                         $this->state = "ConnectedToProxy";
543                                 else
544                                 {
545                                         $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.");
546                                         $this->state="Connected";
547                                 }
548                         }
549                         else
550                                 $this->state="Connected";
551                         return("");
552                 }
553         }
554
555         Function Disconnect()
556         {
557                 if($this->debug)
558                         $this->OutputDebug("Disconnected from ".$this->connected_host);
559                 if($this->use_curl)
560                 {
561                         curl_close($this->connection);
562                         $this->response="";
563                 }
564                 else
565                         fclose($this->connection);
566                 $this->state="Disconnected";
567                 return("");
568         }
569
570         /* Public methods */
571
572         Function GetRequestArguments($url, &$arguments)
573         {
574                 $this->error = '';
575                 $this->error_code = HTTP_CLIENT_ERROR_NO_ERROR;
576                 $arguments=array();
577                 $url = str_replace(' ', '%20', $url);
578                 $parameters=@parse_url($url);
579                 if(!$parameters)
580                         return($this->SetError("it was not specified a valid URL", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
581                 if(!IsSet($parameters["scheme"]))
582                         return($this->SetError("it was not specified the protocol type argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
583                 switch(strtolower($parameters["scheme"]))
584                 {
585                         case "http":
586                         case "https":
587                                 $arguments["Protocol"]=$parameters["scheme"];
588                                 break;
589                         default:
590                                 return($parameters["scheme"]." connection scheme is not yet supported");
591                 }
592                 if(!IsSet($parameters["host"]))
593                         return($this->SetError("it was not specified the connection host argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
594                 $arguments["HostName"]=$parameters["host"];
595                 $arguments["Headers"]=array("Host"=>$parameters["host"].(IsSet($parameters["port"]) ? ":".$parameters["port"] : ""));
596                 if(IsSet($parameters["user"]))
597                 {
598                         $arguments["AuthUser"]=UrlDecode($parameters["user"]);
599                         if(!IsSet($parameters["pass"]))
600                                 $arguments["AuthPassword"]="";
601                 }
602                 if(IsSet($parameters["pass"]))
603                 {
604                         if(!IsSet($parameters["user"]))
605                                 $arguments["AuthUser"]="";
606                         $arguments["AuthPassword"]=UrlDecode($parameters["pass"]);
607                 }
608                 if(IsSet($parameters["port"]))
609                 {
610                         if(strcmp($parameters["port"],strval(intval($parameters["port"]))))
611                                 return($this->SetError("it was not specified a valid connection host argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
612                         $arguments["HostPort"]=intval($parameters["port"]);
613                 }
614                 else
615                         $arguments["HostPort"]=0;
616                 $arguments["RequestURI"]=(IsSet($parameters["path"]) ? $parameters["path"] : "/").(IsSet($parameters["query"]) ? "?".$parameters["query"] : "");
617                 if(strlen($this->user_agent))
618                         $arguments["Headers"]["User-Agent"]=$this->user_agent;
619                 if(strlen($this->accept))
620                         $arguments["Headers"]["Accept"]=$this->accept;
621                 return("");
622         }
623
624         Function Open($arguments)
625         {
626                 if(strlen($this->error))
627                         return($this->error);
628                 $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR;
629                 if(IsSet($arguments["HostName"]))
630                         $this->host_name=$arguments["HostName"];
631                 if(IsSet($arguments["HostPort"]))
632                         $this->host_port=$arguments["HostPort"];
633                 if(IsSet($arguments["ProxyHostName"]))
634                         $this->proxy_host_name=$arguments["ProxyHostName"];
635                 if(IsSet($arguments["ProxyHostPort"]))
636                         $this->proxy_host_port=$arguments["ProxyHostPort"];
637                 if(IsSet($arguments["SOCKSHostName"]))
638                         $this->socks_host_name=$arguments["SOCKSHostName"];
639                 if(IsSet($arguments["SOCKSHostPort"]))
640                         $this->socks_host_port=$arguments["SOCKSHostPort"];
641                 if(IsSet($arguments["SOCKSVersion"]))
642                         $this->socks_version=$arguments["SOCKSVersion"];
643                 if(IsSet($arguments["Protocol"]))
644                         $this->protocol=$arguments["Protocol"];
645                 switch(strtolower($this->protocol))
646                 {
647                         case "http":
648                                 $default_port=80;
649                                 break;
650                         case "https":
651                                 $default_port=443;
652                                 break;
653                         default:
654                                 return($this->SetError("it was not specified a valid connection protocol", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
655                 }
656                 if(strlen($this->proxy_host_name)==0)
657                 {
658                         if(strlen($this->host_name)==0)
659                                 return($this->SetError("it was not specified a valid hostname", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
660                         $host_name=$this->host_name;
661                         $host_port=($this->host_port ? $this->host_port : $default_port);
662                         $server_type = 'HTTP';
663                 }
664                 else
665                 {
666                         $host_name=$this->proxy_host_name;
667                         $host_port=$this->proxy_host_port;
668                         $server_type = 'HTTP proxy';
669                 }
670                 $ssl=(strtolower($this->protocol)=="https" && strlen($this->proxy_host_name)==0);
671                 if($ssl
672                 && strlen($this->socks_host_name))
673                         return($this->SetError('establishing SSL connections via a SOCKS server is not yet supported', HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
674                 $this->use_curl=($ssl && $this->prefer_curl && function_exists("curl_init"));
675                 switch($this->state)
676                 {
677                         case 'Connected':
678                                 if(!strcmp($host_name, $this->connected_host)
679                                 && intval($host_port) == $this->connected_port
680                                 && intval($ssl) == $this->connected_ssl)
681                                 {
682                                         if($this->debug)
683                                                 $this->OutputDebug("Reusing connection to ".$this->connected_host);
684                                         return('');
685                                 }
686                                 if(strlen($error = $this->Disconnect()))
687                                         return($error);
688                         case "Disconnected":
689                                 break;
690                         default:
691                                 return("1 already connected");
692                 }
693                 if($this->debug)
694                         $this->OutputDebug("Connecting to ".$this->host_name);
695                 if($this->use_curl)
696                 {
697                         $error=(($this->connection=curl_init($this->protocol."://".$this->host_name.($host_port==$default_port ? "" : ":".strval($host_port))."/")) ? "" : "Could not initialize a CURL session");
698                         if(strlen($error)==0)
699                         {
700                                 if(IsSet($arguments["SSLCertificateFile"]))
701                                         curl_setopt($this->connection,CURLOPT_SSLCERT,$arguments["SSLCertificateFile"]);
702                                 if(IsSet($arguments["SSLCertificatePassword"]))
703                                         curl_setopt($this->connection,CURLOPT_SSLCERTPASSWD,$arguments["SSLCertificatePassword"]);
704                                 if(IsSet($arguments["SSLKeyFile"]))
705                                         curl_setopt($this->connection,CURLOPT_SSLKEY,$arguments["SSLKeyFile"]);
706                                 if(IsSet($arguments["SSLKeyPassword"]))
707                                         curl_setopt($this->connection,CURLOPT_SSLKEYPASSWD,$arguments["SSLKeyPassword"]);
708                         }
709                         $this->state="Connected";
710                 }
711                 else
712                 {
713                         $error="";
714                         if(strlen($this->proxy_host_name)
715                         && (IsSet($arguments["SSLCertificateFile"])
716                         || IsSet($arguments["SSLCertificateFile"])))
717                                 $error="establishing SSL connections using certificates or private keys via non-SSL proxies is not supported";
718                         else
719                         {
720                                 if($ssl)
721                                 {
722                                         if(IsSet($arguments["SSLCertificateFile"]))
723                                                 $error="establishing SSL connections using certificates is only supported when the cURL extension is enabled";
724                                         elseif(IsSet($arguments["SSLKeyFile"]))
725                                                 $error="establishing SSL connections using a private key is only supported when the cURL extension is enabled";
726                                         else
727                                         {
728                                                 $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
729                                                 $php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]);
730                                                 if($php_version<4003000)
731                                                         $error="establishing SSL connections requires at least PHP version 4.3.0 or having the cURL extension enabled";
732                                                 elseif(!function_exists("extension_loaded")
733                                                 || !extension_loaded("openssl"))
734                                                         $error="establishing SSL connections requires the OpenSSL extension enabled";
735                                         }
736                                 }
737                                 if(strlen($error)==0)
738                                 {
739                                         $error=$this->Connect($host_name, $host_port, $ssl, $server_type);
740                                         $error_code = $this->error_code;
741                                 }
742                         }
743                 }
744                 if(strlen($error))
745                         return($this->SetError($error, $error_code));
746                 $this->session=md5(uniqid(""));
747                 $this->connected_host = $host_name;
748                 $this->connected_port = intval($host_port);
749                 $this->connected_ssl = intval($ssl);
750                 return("");
751         }
752
753         Function Close($force = 0)
754         {
755                 if($this->state=="Disconnected")
756                         return("1 already disconnected");
757                 if(!$this->force_close
758                 && $this->keep_alive
759                 && !$force
760                 && $this->state == 'ResponseReceived')
761                 {
762                         if($this->debug)
763                                 $this->OutputDebug('Keeping the connection alive to '.$this->connected_host);
764                         $this->state = 'Connected';
765                         return('');
766                 }
767                 return($this->Disconnect());
768         }
769
770         Function PickCookies(&$cookies,$secure)
771         {
772                 if(IsSet($this->cookies[$secure]))
773                 {
774                         $now=gmdate("Y-m-d H-i-s");
775                         for($domain=0,Reset($this->cookies[$secure]);$domain<count($this->cookies[$secure]);Next($this->cookies[$secure]),$domain++)
776                         {
777                                 $domain_pattern=Key($this->cookies[$secure]);
778                                 $match=strlen($this->request_host)-strlen($domain_pattern);
779                                 if($match>=0
780                                 && !strcmp($domain_pattern,substr($this->request_host,$match))
781                                 && ($match==0
782                                 || $domain_pattern[0]=="."
783                                 || $this->request_host[$match-1]=="."))
784                                 {
785                                         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++)
786                                         {
787                                                 $path=Key($this->cookies[$secure][$domain_pattern]);
788                                                 if(strlen($this->request_uri)>=strlen($path)
789                                                 && substr($this->request_uri,0,strlen($path))==$path)
790                                                 {
791                                                         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++)
792                                                         {
793                                                                 $cookie_name=Key($this->cookies[$secure][$domain_pattern][$path]);
794                                                                 $expires=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"];
795                                                                 if($expires==""
796                                                                 || strcmp($now,$expires)<0)
797                                                                         $cookies[$cookie_name]=$this->cookies[$secure][$domain_pattern][$path][$cookie_name];
798                                                         }
799                                                 }
800                                         }
801                                 }
802                         }
803                 }
804         }
805
806         Function GetFileDefinition($file, &$definition)
807         {
808                 $name="";
809                 if(IsSet($file["FileName"]))
810                         $name=basename($file["FileName"]);
811                 if(IsSet($file["Name"]))
812                         $name=$file["Name"];
813                 if(strlen($name)==0)
814                         return("it was not specified the file part name");
815                 if(IsSet($file["Content-Type"]))
816                 {
817                         $content_type=$file["Content-Type"];
818                         $type=$this->Tokenize(strtolower($content_type),"/");
819                         $sub_type=$this->Tokenize("");
820                         switch($type)
821                         {
822                                 case "text":
823                                 case "image":
824                                 case "audio":
825                                 case "video":
826                                 case "application":
827                                 case "message":
828                                         break;
829                                 case "automatic":
830                                         switch($sub_type)
831                                         {
832                                                 case "name":
833                                                         switch(GetType($dot=strrpos($name,"."))=="integer" ? strtolower(substr($name,$dot)) : "")
834                                                         {
835                                                                 case ".xls":
836                                                                         $content_type="application/excel";
837                                                                         break;
838                                                                 case ".hqx":
839                                                                         $content_type="application/macbinhex40";
840                                                                         break;
841                                                                 case ".doc":
842                                                                 case ".dot":
843                                                                 case ".wrd":
844                                                                         $content_type="application/msword";
845                                                                         break;
846                                                                 case ".pdf":
847                                                                         $content_type="application/pdf";
848                                                                         break;
849                                                                 case ".pgp":
850                                                                         $content_type="application/pgp";
851                                                                         break;
852                                                                 case ".ps":
853                                                                 case ".eps":
854                                                                 case ".ai":
855                                                                         $content_type="application/postscript";
856                                                                         break;
857                                                                 case ".ppt":
858                                                                         $content_type="application/powerpoint";
859                                                                         break;
860                                                                 case ".rtf":
861                                                                         $content_type="application/rtf";
862                                                                         break;
863                                                                 case ".tgz":
864                                                                 case ".gtar":
865                                                                         $content_type="application/x-gtar";
866                                                                         break;
867                                                                 case ".gz":
868                                                                         $content_type="application/x-gzip";
869                                                                         break;
870                                                                 case ".php":
871                                                                 case ".php3":
872                                                                         $content_type="application/x-httpd-php";
873                                                                         break;
874                                                                 case ".js":
875                                                                         $content_type="application/x-javascript";
876                                                                         break;
877                                                                 case ".ppd":
878                                                                 case ".psd":
879                                                                         $content_type="application/x-photoshop";
880                                                                         break;
881                                                                 case ".swf":
882                                                                 case ".swc":
883                                                                 case ".rf":
884                                                                         $content_type="application/x-shockwave-flash";
885                                                                         break;
886                                                                 case ".tar":
887                                                                         $content_type="application/x-tar";
888                                                                         break;
889                                                                 case ".zip":
890                                                                         $content_type="application/zip";
891                                                                         break;
892                                                                 case ".mid":
893                                                                 case ".midi":
894                                                                 case ".kar":
895                                                                         $content_type="audio/midi";
896                                                                         break;
897                                                                 case ".mp2":
898                                                                 case ".mp3":
899                                                                 case ".mpga":
900                                                                         $content_type="audio/mpeg";
901                                                                         break;
902                                                                 case ".ra":
903                                                                         $content_type="audio/x-realaudio";
904                                                                         break;
905                                                                 case ".wav":
906                                                                         $content_type="audio/wav";
907                                                                         break;
908                                                                 case ".bmp":
909                                                                         $content_type="image/bitmap";
910                                                                         break;
911                                                                 case ".gif":
912                                                                         $content_type="image/gif";
913                                                                         break;
914                                                                 case ".iff":
915                                                                         $content_type="image/iff";
916                                                                         break;
917                                                                 case ".jb2":
918                                                                         $content_type="image/jb2";
919                                                                         break;
920                                                                 case ".jpg":
921                                                                 case ".jpe":
922                                                                 case ".jpeg":
923                                                                         $content_type="image/jpeg";
924                                                                         break;
925                                                                 case ".jpx":
926                                                                         $content_type="image/jpx";
927                                                                         break;
928                                                                 case ".png":
929                                                                         $content_type="image/png";
930                                                                         break;
931                                                                 case ".tif":
932                                                                 case ".tiff":
933                                                                         $content_type="image/tiff";
934                                                                         break;
935                                                                 case ".wbmp":
936                                                                         $content_type="image/vnd.wap.wbmp";
937                                                                         break;
938                                                                 case ".xbm":
939                                                                         $content_type="image/xbm";
940                                                                         break;
941                                                                 case ".css":
942                                                                         $content_type="text/css";
943                                                                         break;
944                                                                 case ".txt":
945                                                                         $content_type="text/plain";
946                                                                         break;
947                                                                 case ".htm":
948                                                                 case ".html":
949                                                                         $content_type="text/html";
950                                                                         break;
951                                                                 case ".xml":
952                                                                         $content_type="text/xml";
953                                                                         break;
954                                                                 case ".mpg":
955                                                                 case ".mpe":
956                                                                 case ".mpeg":
957                                                                         $content_type="video/mpeg";
958                                                                         break;
959                                                                 case ".qt":
960                                                                 case ".mov":
961                                                                         $content_type="video/quicktime";
962                                                                         break;
963                                                                 case ".avi":
964                                                                         $content_type="video/x-ms-video";
965                                                                         break;
966                                                                 case ".eml":
967                                                                         $content_type="message/rfc822";
968                                                                         break;
969                                                                 default:
970                                                                         $content_type="application/octet-stream";
971                                                                         break;
972                                                         }
973                                                         break;
974                                                 default:
975                                                         return($content_type." is not a supported automatic content type detection method");
976                                         }
977                                         break;
978                                 default:
979                                         return($content_type." is not a supported file content type");
980                         }
981                 }
982                 else
983                         $content_type="application/octet-stream";
984                 $definition=array(
985                         "Content-Type"=>$content_type,
986                         "NAME"=>$name
987                 );
988                 if(IsSet($file["FileName"]))
989                 {
990                         if(GetType($length=@filesize($file["FileName"]))!="integer")
991                         {
992                                 $error="it was not possible to determine the length of the file ".$file["FileName"];
993                                 if(IsSet($php_errormsg)
994                                 && strlen($php_errormsg))
995                                         $error.=": ".$php_errormsg;
996                                 if(!file_exists($file["FileName"]))
997                                         $error="it was not possible to access the file ".$file["FileName"];
998                                 return($error);
999                         }
1000                         $definition["FILENAME"]=$file["FileName"];
1001                         $definition["Content-Length"]=$length;
1002                 }
1003                 elseif(IsSet($file["Data"]))
1004                         $definition["Content-Length"]=strlen($definition["DATA"]=$file["Data"]);
1005                 else
1006                         return("it was not specified a valid file name");
1007                 return("");
1008         }
1009
1010         Function ConnectFromProxy($arguments, &$headers)
1011         {
1012                 if(!$this->PutLine('CONNECT '.$this->host_name.':'.($this->host_port ? $this->host_port : 443).' HTTP/1.0')
1013                 || (strlen($this->user_agent)
1014                 && !$this->PutLine('User-Agent: '.$this->user_agent))
1015                 || (strlen($this->accept)
1016                 && !$this->PutLine('Accept: '.$this->accept))
1017                 || (IsSet($arguments['Headers']['Proxy-Authorization'])
1018                 && !$this->PutLine('Proxy-Authorization: '.$arguments['Headers']['Proxy-Authorization']))
1019                 || !$this->PutLine(''))
1020                 {
1021                         $this->Disconnect();
1022                         return($this->error);
1023                 }
1024                 $this->state = "ConnectSent";
1025                 if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
1026                         return($error);
1027                 $proxy_authorization="";
1028                 while(!strcmp($this->response_status, "100"))
1029                 {
1030                         $this->state="ConnectSent";
1031                         if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
1032                                 return($error);
1033                 }
1034                 switch($this->response_status)
1035                 {
1036                         case "200":
1037                                 if(!@stream_socket_enable_crypto($this->connection, 1, STREAM_CRYPTO_METHOD_SSLv23_CLIENT))
1038                                 {
1039                                         $this->SetPHPError('it was not possible to start a SSL encrypted connection via this proxy', $php_errormsg, HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE);
1040                                         $this->Disconnect();
1041                                         return($this->error);
1042                                 }
1043                                 $this->state = "Connected";
1044                                 break;
1045                         case "407":
1046                                 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)))
1047                                         return($error);
1048                                 break;
1049                         default:
1050                                 return($this->SetError("unable to send request via proxy", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1051                 }
1052                 return("");
1053         }
1054
1055         Function SendRequest($arguments)
1056         {
1057                 if(strlen($this->error))
1058                         return($this->error);
1059                 if(IsSet($arguments["ProxyUser"]))
1060                         $this->proxy_request_user=$arguments["ProxyUser"];
1061                 elseif(IsSet($this->proxy_user))
1062                         $this->proxy_request_user=$this->proxy_user;
1063                 if(IsSet($arguments["ProxyPassword"]))
1064                         $this->proxy_request_password=$arguments["ProxyPassword"];
1065                 elseif(IsSet($this->proxy_password))
1066                         $this->proxy_request_password=$this->proxy_password;
1067                 if(IsSet($arguments["ProxyRealm"]))
1068                         $this->proxy_request_realm=$arguments["ProxyRealm"];
1069                 elseif(IsSet($this->proxy_realm))
1070                         $this->proxy_request_realm=$this->proxy_realm;
1071                 if(IsSet($arguments["ProxyWorkstation"]))
1072                         $this->proxy_request_workstation=$arguments["ProxyWorkstation"];
1073                 elseif(IsSet($this->proxy_workstation))
1074                         $this->proxy_request_workstation=$this->proxy_workstation;
1075                 switch($this->state)
1076                 {
1077                         case "Disconnected":
1078                                 return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1079                         case "Connected":
1080                                 $connect = 0;
1081                                 break;
1082                         case "ConnectedToProxy":
1083                                 if(strlen($error = $this->ConnectFromProxy($arguments, $headers)))
1084                                         return($error);
1085                                 $connect = 1;
1086                                 break;
1087                         default:
1088                                 return($this->SetError("can not send request in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1089                 }
1090                 if(IsSet($arguments["RequestMethod"]))
1091                         $this->request_method=$arguments["RequestMethod"];
1092                 if(IsSet($arguments["User-Agent"]))
1093                         $this->user_agent=$arguments["User-Agent"];
1094                 if(!IsSet($arguments["Headers"]["User-Agent"])
1095                 && strlen($this->user_agent))
1096                         $arguments["Headers"]["User-Agent"]=$this->user_agent;
1097                 if(IsSet($arguments["KeepAlive"]))
1098                         $this->keep_alive=intval($arguments["KeepAlive"]);
1099                 if(!IsSet($arguments["Headers"]["Connection"])
1100                 && $this->keep_alive)
1101                         $arguments["Headers"]["Connection"]='Keep-Alive';
1102                 if(IsSet($arguments["Accept"]))
1103                         $this->user_agent=$arguments["Accept"];
1104                 if(!IsSet($arguments["Headers"]["Accept"])
1105                 && strlen($this->accept))
1106                         $arguments["Headers"]["Accept"]=$this->accept;
1107                 if(strlen($this->request_method)==0)
1108                         return($this->SetError("it was not specified a valid request method", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1109                 if(IsSet($arguments["RequestURI"]))
1110                         $this->request_uri=$arguments["RequestURI"];
1111                 if(strlen($this->request_uri)==0
1112                 || substr($this->request_uri,0,1)!="/")
1113                         return($this->SetError("it was not specified a valid request URI", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1114                 $this->request_arguments=$arguments;
1115                 $this->request_headers=(IsSet($arguments["Headers"]) ? $arguments["Headers"] : array());
1116                 $body_length=0;
1117                 $this->request_body="";
1118                 $get_body=1;
1119                 if($this->request_method=="POST"
1120                 || $this->request_method=="PUT")
1121                 {
1122                         if(IsSet($arguments['StreamRequest']))
1123                         {
1124                                 $get_body = 0;
1125                                 $this->request_headers["Transfer-Encoding"]="chunked";
1126                         }
1127                         elseif(IsSet($arguments["PostFiles"])
1128                         || ($this->force_multipart_form_post
1129                         && IsSet($arguments["PostValues"])))
1130                         {
1131                                 $boundary="--".md5(uniqid(time()));
1132                                 $this->request_headers["Content-Type"]="multipart/form-data; boundary=".$boundary.(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : "");
1133                                 $post_parts=array();
1134                                 if(IsSet($arguments["PostValues"]))
1135                                 {
1136                                         $values=$arguments["PostValues"];
1137                                         if(GetType($values)!="array")
1138                                                 return($this->SetError("it was not specified a valid POST method values array", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1139                                         for(Reset($values),$value=0;$value<count($values);Next($values),$value++)
1140                                         {
1141                                                 $input=Key($values);
1142                                                 $headers="--".$boundary."\r\nContent-Disposition: form-data; name=\"".$input."\"\r\n\r\n";
1143                                                 $data=$values[$input];
1144                                                 $post_parts[]=array("HEADERS"=>$headers,"DATA"=>$data);
1145                                                 $body_length+=strlen($headers)+strlen($data)+strlen("\r\n");
1146                                         }
1147                                 }
1148                                 $body_length+=strlen("--".$boundary."--\r\n");
1149                                 $files=(IsSet($arguments["PostFiles"]) ? $arguments["PostFiles"] : array());
1150                                 Reset($files);
1151                                 $end=(GetType($input=Key($files))!="string");
1152                                 for(;!$end;)
1153                                 {
1154                                         if(strlen($error=$this->GetFileDefinition($files[$input],$definition)))
1155                                                 return("3 ".$error);
1156                                         $headers="--".$boundary."\r\nContent-Disposition: form-data; name=\"".$input."\"; filename=\"".$definition["NAME"]."\"\r\nContent-Type: ".$definition["Content-Type"]."\r\n\r\n";
1157                                         $part=count($post_parts);
1158                                         $post_parts[$part]=array("HEADERS"=>$headers);
1159                                         if(IsSet($definition["FILENAME"]))
1160                                         {
1161                                                 $post_parts[$part]["FILENAME"]=$definition["FILENAME"];
1162                                                 $data="";
1163                                         }
1164                                         else
1165                                                 $data=$definition["DATA"];
1166                                         $post_parts[$part]["DATA"]=$data;
1167                                         $body_length+=strlen($headers)+$definition["Content-Length"]+strlen("\r\n");
1168                                         Next($files);
1169                                         $end=(GetType($input=Key($files))!="string");
1170                                 }
1171                                 $get_body=0;
1172                         }
1173                         elseif(IsSet($arguments["PostValues"]))
1174                         {
1175                                 $values=$arguments["PostValues"];
1176                                 if(GetType($values)!="array")
1177                                         return($this->SetError("it was not specified a valid POST method values array", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1178                                 for(Reset($values),$value=0;$value<count($values);Next($values),$value++)
1179                                 {
1180                                         $k=Key($values);
1181                                         if(GetType($values[$k])=="array")
1182                                         {
1183                                                 for($v = 0; $v < count($values[$k]); $v++)
1184                                                 {
1185                                                         if($value+$v>0)
1186                                                                 $this->request_body.="&";
1187                                                         $this->request_body.=UrlEncode($k)."=".UrlEncode($values[$k][$v]);
1188                                                 }
1189                                         }
1190                                         else
1191                                         {
1192                                                 if($value>0)
1193                                                         $this->request_body.="&";
1194                                                 $this->request_body.=UrlEncode($k)."=".UrlEncode($values[$k]);
1195                                         }
1196                                 }
1197                                 $this->request_headers["Content-Type"]="application/x-www-form-urlencoded".(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : "");
1198                                 $get_body=0;
1199                         }
1200                 }
1201                 if($get_body
1202                 && (IsSet($arguments["Body"])
1203                 || IsSet($arguments["BodyStream"])))
1204                 {
1205                         if(IsSet($arguments["Body"]))
1206                                 $this->request_body=$arguments["Body"];
1207                         else
1208                         {
1209                                 $stream=$arguments["BodyStream"];
1210                                 $this->request_body="";
1211                                 for($part=0; $part<count($stream); $part++)
1212                                 {
1213                                         if(IsSet($stream[$part]["Data"]))
1214                                                 $this->request_body.=$stream[$part]["Data"];
1215                                         elseif(IsSet($stream[$part]["File"]))
1216                                         {
1217                                                 if(!($file=@fopen($stream[$part]["File"],"rb")))
1218                                                         return($this->SetPHPError("could not open upload file ".$stream[$part]["File"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE));
1219                                                 while(!feof($file))
1220                                                 {
1221                                                         if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
1222                                                         {
1223                                                                 $error=$this->SetPHPError("could not read body stream file ".$stream[$part]["File"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE);
1224                                                                 fclose($file);
1225                                                                 return($error);
1226                                                         }
1227                                                         $this->request_body.=$block;
1228                                                 }
1229                                                 fclose($file);
1230                                         }
1231                                         else
1232                                                 return("5 it was not specified a valid file or data body stream element at position ".$part);
1233                                 }
1234                         }
1235                         if(!IsSet($this->request_headers["Content-Type"]))
1236                                 $this->request_headers["Content-Type"]="application/octet-stream".(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : "");
1237                 }
1238                 if(IsSet($arguments["AuthUser"]))
1239                         $this->request_user=$arguments["AuthUser"];
1240                 elseif(IsSet($this->user))
1241                         $this->request_user=$this->user;
1242                 if(IsSet($arguments["AuthPassword"]))
1243                         $this->request_password=$arguments["AuthPassword"];
1244                 elseif(IsSet($this->password))
1245                         $this->request_password=$this->password;
1246                 if(IsSet($arguments["AuthRealm"]))
1247                         $this->request_realm=$arguments["AuthRealm"];
1248                 elseif(IsSet($this->realm))
1249                         $this->request_realm=$this->realm;
1250                 if(IsSet($arguments["AuthWorkstation"]))
1251                         $this->request_workstation=$arguments["AuthWorkstation"];
1252                 elseif(IsSet($this->workstation))
1253                         $this->request_workstation=$this->workstation;
1254                 if(strlen($this->proxy_host_name)==0
1255                 || $connect)
1256                         $request_uri=$this->request_uri;
1257                 else
1258                 {
1259                         switch(strtolower($this->protocol))
1260                         {
1261                                 case "http":
1262                                         $default_port=80;
1263                                         break;
1264                                 case "https":
1265                                         $default_port=443;
1266                                         break;
1267                         }
1268                         $request_uri=strtolower($this->protocol)."://".$this->host_name.(($this->host_port==0 || $this->host_port==$default_port) ? "" : ":".$this->host_port).$this->request_uri;
1269                 }
1270                 if($this->use_curl)
1271                 {
1272                         $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"));
1273                         $curl_version=100000*intval($this->Tokenize($version,"."))+1000*intval($this->Tokenize("."))+intval($this->Tokenize(""));
1274                         $protocol_version=($curl_version<713002 ? "1.0" : $this->protocol_version);
1275                 }
1276                 else
1277                         $protocol_version=$this->protocol_version;
1278                 $this->request=$this->request_method." ".$request_uri." HTTP/".$protocol_version;
1279                 if($body_length
1280                 || ($body_length=strlen($this->request_body))
1281                 || !strcmp($this->request_method, 'POST'))
1282                         $this->request_headers["Content-Length"]=$body_length;
1283                 for($headers=array(),$host_set=0,Reset($this->request_headers),$header=0;$header<count($this->request_headers);Next($this->request_headers),$header++)
1284                 {
1285                         $header_name=Key($this->request_headers);
1286                         $header_value=$this->request_headers[$header_name];
1287                         if(GetType($header_value)=="array")
1288                         {
1289                                 for(Reset($header_value),$value=0;$value<count($header_value);Next($header_value),$value++)
1290                                         $headers[]=$header_name.": ".$header_value[Key($header_value)];
1291                         }
1292                         else
1293                                 $headers[]=$header_name.": ".$header_value;
1294                         if(strtolower(Key($this->request_headers))=="host")
1295                         {
1296                                 $this->request_host=strtolower($header_value);
1297                                 $host_set=1;
1298                         }
1299                 }
1300                 if(!$host_set)
1301                 {
1302                         $headers[]="Host: ".$this->host_name;
1303                         $this->request_host=strtolower($this->host_name);
1304                 }
1305                 if(count($this->cookies))
1306                 {
1307                         $cookies=array();
1308                         $this->PickCookies($cookies,0);
1309                         if(strtolower($this->protocol)=="https")
1310                                 $this->PickCookies($cookies,1);
1311                         if(count($cookies))
1312                         {
1313                                 $h=count($headers);
1314                                 $headers[$h]="Cookie:";
1315                                 for(Reset($cookies),$cookie=0;$cookie<count($cookies);Next($cookies),$cookie++)
1316                                 {
1317                                         $cookie_name=Key($cookies);
1318                                         $headers[$h].=" ".$cookie_name."=".$cookies[$cookie_name]["value"].";";
1319                                 }
1320                         }
1321                 }
1322                 $next_state = "RequestSent";
1323                 if($this->use_curl)
1324                 {
1325                         if(IsSet($arguments['StreamRequest']))
1326                                 return($this->SetError("Streaming request data is not supported when using Curl", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1327                         if($body_length
1328                         && strlen($this->request_body)==0)
1329                         {
1330                                 for($request_body="",$success=1,$part=0;$part<count($post_parts);$part++)
1331                                 {
1332                                         $request_body.=$post_parts[$part]["HEADERS"].$post_parts[$part]["DATA"];
1333                                         if(IsSet($post_parts[$part]["FILENAME"]))
1334                                         {
1335                                                 if(!($file=@fopen($post_parts[$part]["FILENAME"],"rb")))
1336                                                 {
1337                                                         $this->SetPHPError("could not open upload file ".$post_parts[$part]["FILENAME"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE);
1338                                                         $success=0;
1339                                                         break;
1340                                                 }
1341                                                 while(!feof($file))
1342                                                 {
1343                                                         if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
1344                                                         {
1345                                                                 $this->SetPHPError("could not read upload file", $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE);
1346                                                                 $success=0;
1347                                                                 break;
1348                                                         }
1349                                                         $request_body.=$block;
1350                                                 }
1351                                                 fclose($file);
1352                                                 if(!$success)
1353                                                         break;
1354                                         }
1355                                         $request_body.="\r\n";
1356                                 }
1357                                 $request_body.="--".$boundary."--\r\n";
1358                         }
1359                         else
1360                                 $request_body=$this->request_body;
1361                         curl_setopt($this->connection,CURLOPT_HEADER,1);
1362                         curl_setopt($this->connection,CURLOPT_RETURNTRANSFER,1);
1363                         if($this->timeout)
1364                                 curl_setopt($this->connection,CURLOPT_TIMEOUT,$this->timeout);
1365                         curl_setopt($this->connection,CURLOPT_SSL_VERIFYPEER,0);
1366                         curl_setopt($this->connection,CURLOPT_SSL_VERIFYHOST,0);
1367                         $request=$this->request."\r\n".implode("\r\n",$headers)."\r\n\r\n".$request_body;
1368                         curl_setopt($this->connection,CURLOPT_CUSTOMREQUEST,$request);
1369                         if($this->debug)
1370                                 $this->OutputDebug("C ".$request);
1371                         if(!($success=(strlen($this->response=curl_exec($this->connection))!=0)))
1372                         {
1373                                 $error=curl_error($this->connection);
1374                                 $this->SetError("Could not execute the request".(strlen($error) ? ": ".$error : ""), HTTP_CLIENT_ERROR_PROTOCOL_FAILURE);
1375                         }
1376                 }
1377                 else
1378                 {
1379                         if(($success=$this->PutLine($this->request)))
1380                         {
1381                                 for($header=0;$header<count($headers);$header++)
1382                                 {
1383                                         if(!$success=$this->PutLine($headers[$header]))
1384                                                 break;
1385                                 }
1386                                 if($success
1387                                 && ($success=$this->PutLine("")))
1388                                 {
1389                                         if(IsSet($arguments['StreamRequest']))
1390                                                 $next_state = "SendingRequestBody";
1391                                         elseif($body_length)
1392                                         {
1393                                                 if(strlen($this->request_body))
1394                                                         $success=$this->PutData($this->request_body);
1395                                                 else
1396                                                 {
1397                                                         for($part=0;$part<count($post_parts);$part++)
1398                                                         {
1399                                                                 if(!($success=$this->PutData($post_parts[$part]["HEADERS"]))
1400                                                                 || !($success=$this->PutData($post_parts[$part]["DATA"])))
1401                                                                         break;
1402                                                                 if(IsSet($post_parts[$part]["FILENAME"]))
1403                                                                 {
1404                                                                         if(!($file=@fopen($post_parts[$part]["FILENAME"],"rb")))
1405                                                                         {
1406                                                                                 $this->SetPHPError("could not open upload file ".$post_parts[$part]["FILENAME"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE);
1407                                                                                 $success=0;
1408                                                                                 break;
1409                                                                         }
1410                                                                         while(!feof($file))
1411                                                                         {
1412                                                                                 if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
1413                                                                                 {
1414                                                                                         $this->SetPHPError("could not read upload file", $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE);
1415                                                                                         $success=0;
1416                                                                                         break;
1417                                                                                 }
1418                                                                                 if(!($success=$this->PutData($block)))
1419                                                                                         break;
1420                                                                         }
1421                                                                         fclose($file);
1422                                                                         if(!$success)
1423                                                                                 break;
1424                                                                 }
1425                                                                 if(!($success=$this->PutLine("")))
1426                                                                         break;
1427                                                         }
1428                                                         if($success)
1429                                                                 $success=$this->PutLine("--".$boundary."--");
1430                                                 }
1431                                                 if($success)
1432                                                         $sucess=$this->FlushData();
1433                                         }
1434                                 }
1435                         }
1436                 }
1437                 if(!$success)
1438                         return($this->SetError("could not send the HTTP request: ".$this->error, $this->error_code));
1439                 $this->state=$next_state;
1440                 return("");
1441         }
1442
1443         Function SetCookie($name, $value, $expires="" , $path="/" , $domain="" , $secure=0, $verbatim=0)
1444         {
1445                 if(strlen($this->error))
1446                         return($this->error);
1447                 if(strlen($name)==0)
1448                         return($this->SetError("it was not specified a valid cookie name", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1449                 if(strlen($path)==0
1450                 || strcmp($path[0],"/"))
1451                         return($this->SetError($path." is not a valid path for setting cookie ".$name, HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1452                 if($domain==""
1453                 || !strpos($domain,".",$domain[0]=="." ? 1 : 0))
1454                         return($this->SetError($domain." is not a valid domain for setting cookie ".$name, HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1455                 $domain=strtolower($domain);
1456                 if(!strcmp($domain[0],"."))
1457                         $domain=substr($domain,1);
1458                 if(!$verbatim)
1459                 {
1460                         $name=$this->CookieEncode($name,1);
1461                         $value=$this->CookieEncode($value,0);
1462                 }
1463                 $secure=intval($secure);
1464                 $this->cookies[$secure][$domain][$path][$name]=array(
1465                         "name"=>$name,
1466                         "value"=>$value,
1467                         "domain"=>$domain,
1468                         "path"=>$path,
1469                         "expires"=>$expires,
1470                         "secure"=>$secure
1471                 );
1472                 return("");
1473         }
1474
1475         Function SendRequestBody($data, $end_of_data)
1476         {
1477                 if(strlen($this->error))
1478                         return($this->error);
1479                 switch($this->state)
1480                 {
1481                         case "Disconnected":
1482                                 return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1483                         case "Connected":
1484                         case "ConnectedToProxy":
1485                                 return($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1486                         case "SendingRequestBody":
1487                                 break;
1488                         case "RequestSent":
1489                                 return($this->SetError("request body was already sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1490                         default:
1491                                 return($this->SetError("can not send the request body in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1492                 }
1493                 $length = strlen($data);
1494                 if($length)
1495                 {
1496                         $size = dechex($length)."\r\n";
1497                         if(!$this->PutData($size)
1498                         || !$this->PutData($data))
1499                                 return($this->error);
1500                 }
1501                 if($end_of_data)
1502                 {
1503                         $size = "0\r\n";
1504                         if(!$this->PutData($size))
1505                                 return($this->error);
1506                         $this->state = "RequestSent";
1507                 }
1508                 return("");
1509         }
1510
1511         Function ReadReplyHeadersResponse(&$headers)
1512         {
1513                 $headers=array();
1514                 if(strlen($this->error))
1515                         return($this->error);
1516                 switch($this->state)
1517                 {
1518                         case "Disconnected":
1519                                 return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1520                         case "Connected":
1521                                 return($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1522                         case "ConnectedToProxy":
1523                                 return($this->SetError("connection from the remote server from the proxy was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1524                         case "SendingRequestBody":
1525                                 return($this->SetError("request body data was not completely sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1526                         case "ConnectSent":
1527                                 $connect = 1;
1528                                 break;
1529                         case "RequestSent":
1530                                 $connect = 0;
1531                                 break;
1532                         default:
1533                                 return($this->SetError("can not get request headers in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1534                 }
1535                 $this->content_length=$this->read_length=$this->read_response=$this->remaining_chunk=0;
1536                 $this->content_length_set=$this->chunked=$this->last_chunk_read=$chunked=0;
1537                 $this->force_close = $this->connection_close=0;
1538                 for($this->response_status="";;)
1539                 {
1540                         $line=$this->GetLine();
1541                         if(GetType($line)!="string")
1542                                 return($this->SetError("could not read request reply: ".$this->error, $this->error_code));
1543                         if(strlen($this->response_status)==0)
1544                         {
1545                                 if(!preg_match($match="/^http\\/[0-9]+\\.[0-9]+[ \t]+([0-9]+)[ \t]*(.*)\$/i",$line,$matches))
1546                                         return($this->SetError("it was received an unexpected HTTP response status", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1547                                 $this->response_status=$matches[1];
1548                                 $this->response_message=$matches[2];
1549                         }
1550                         if($line=="")
1551                         {
1552                                 if(strlen($this->response_status)==0)
1553                                         return($this->SetError("it was not received HTTP response status", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1554                                 $this->state=($connect ? "GotConnectHeaders" : "GotReplyHeaders");
1555                                 break;
1556                         }
1557                         $header_name=strtolower($this->Tokenize($line,":"));
1558                         $header_value=Trim(Chop($this->Tokenize("\r\n")));
1559                         if(IsSet($headers[$header_name]))
1560                         {
1561                                 if(GetType($headers[$header_name])=="string")
1562                                         $headers[$header_name]=array($headers[$header_name]);
1563                                 $headers[$header_name][]=$header_value;
1564                         }
1565                         else
1566                                 $headers[$header_name]=$header_value;
1567                         if(!$connect)
1568                         {
1569                                 switch($header_name)
1570                                 {
1571                                         case "content-length":
1572                                                 $this->content_length=intval($headers[$header_name]);
1573                                                 $this->content_length_set=1;
1574                                                 break;
1575                                         case "transfer-encoding":
1576                                                 $encoding=$this->Tokenize($header_value,"; \t");
1577                                                 if(!$this->use_curl
1578                                                 && !strcmp($encoding,"chunked"))
1579                                                         $chunked=1;
1580                                                 break;
1581                                         case "set-cookie":
1582                                                 if($this->support_cookies)
1583                                                 {
1584                                                         if(GetType($headers[$header_name])=="array")
1585                                                                 $cookie_headers=$headers[$header_name];
1586                                                         else
1587                                                                 $cookie_headers=array($headers[$header_name]);
1588                                                         for($cookie=0;$cookie<count($cookie_headers);$cookie++)
1589                                                         {
1590                                                                 $cookie_name=trim($this->Tokenize($cookie_headers[$cookie],"="));
1591                                                                 $cookie_value=$this->Tokenize(";");
1592                                                                 $domain=$this->request_host;
1593                                                                 $path="/";
1594                                                                 $expires="";
1595                                                                 $secure=0;
1596                                                                 while(($name = strtolower(trim(UrlDecode($this->Tokenize("=")))))!="")
1597                                                                 {
1598                                                                         $value=UrlDecode($this->Tokenize(";"));
1599                                                                         switch($name)
1600                                                                         {
1601                                                                                 case "domain":
1602                                                                                         $domain=$value;
1603                                                                                         break;
1604                                                                                 case "path":
1605                                                                                         $path=$value;
1606                                                                                         break;
1607                                                                                 case "expires":
1608                                                                                         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))
1609                                                                                         {
1610                                                                                                 $year=intval($matches[5]);
1611                                                                                                 if($year<1900)
1612                                                                                                         $year+=($year<70 ? 2000 : 1900);
1613                                                                                                 $expires="$year-".$this->months[$matches[4]]."-".$matches[3]." ".$matches[6].":".$matches[7].":".$matches[8];
1614                                                                                         }
1615                                                                                         break;
1616                                                                                 case "secure":
1617                                                                                         $secure=1;
1618                                                                                         break;
1619                                                                         }
1620                                                                 }
1621                                                                 if(strlen($this->SetCookie($cookie_name, $cookie_value, $expires, $path , $domain, $secure, 1)))
1622                                                                         $this->error="";
1623                                                         }
1624                                                 }
1625                                                 break;
1626                                         case "connection":
1627                                                 $this->force_close = $this->connection_close=!strcmp(strtolower($header_value),"close");
1628                                                 break;
1629                                 }
1630                         }
1631                 }
1632                 $this->chunked=$chunked;
1633                 if($this->content_length_set)
1634                         $this->connection_close=0;
1635                 return("");
1636         }
1637
1638         Function Redirect(&$headers)
1639         {
1640                 if($this->follow_redirect)
1641                 {
1642                         if(!IsSet($headers["location"])
1643                         || (GetType($headers["location"])!="array"
1644                         && strlen($location=$headers["location"])==0)
1645                         || (GetType($headers["location"])=="array"
1646                         && strlen($location=$headers["location"][0])==0))
1647                                 return($this->SetError("it was received a redirect without location URL", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1648                         if(strcmp($location[0],"/"))
1649                         {
1650                                 if(!($location_arguments=@parse_url($location)))
1651                                         return($this->SetError("the server did not return a valid redirection location URL", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1652                                 if(!IsSet($location_arguments["scheme"]))
1653                                         $location=((GetType($end=strrpos($this->request_uri,"/"))=="integer" && $end>1) ? substr($this->request_uri,0,$end) : "")."/".$location;
1654                         }
1655                         if(!strcmp($location[0],"/"))
1656                                 $location=$this->protocol."://".$this->host_name.($this->host_port ? ":".$this->host_port : "").$location;
1657                         $error=$this->GetRequestArguments($location,$arguments);
1658                         if(strlen($error))
1659                                 return($this->SetError("could not process redirect url: ".$error, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1660                         $arguments["RequestMethod"]="GET";
1661                         if(strlen($error=$this->Close())==0
1662                         && strlen($error=$this->Open($arguments))==0
1663                         && strlen($error=$this->SendRequest($arguments))==0)
1664                         {
1665                                 $this->redirection_level++;
1666                                 if($this->redirection_level>$this->redirection_limit)
1667                                 {
1668                                         $error="it was exceeded the limit of request redirections";
1669                                         $this->error_code = HTTP_CLIENT_ERROR_PROTOCOL_FAILURE;
1670                                 }
1671                                 else
1672                                         $error=$this->ReadReplyHeaders($headers);
1673                                 $this->redirection_level--;
1674                         }
1675                         if(strlen($error))
1676                                 return($this->SetError($error, $this->error_code));
1677                 }
1678                 return("");
1679         }
1680
1681         Function Authenticate(&$headers, $proxy, &$proxy_authorization, &$user, &$password, &$realm, &$workstation)
1682         {
1683                 if($proxy)
1684                 {
1685                         $authenticate_header="proxy-authenticate";
1686                         $authorization_header="Proxy-Authorization";
1687                         $authenticate_status="407";
1688                         $authentication_mechanism=$this->proxy_authentication_mechanism;
1689                 }
1690                 else
1691                 {
1692                         $authenticate_header="www-authenticate";
1693                         $authorization_header="Authorization";
1694                         $authenticate_status="401";
1695                         $authentication_mechanism=$this->authentication_mechanism;
1696                 }
1697                 if(IsSet($headers[$authenticate_header])
1698                 && $this->sasl_authenticate)
1699                 {
1700                         if(function_exists("class_exists")
1701                         && !class_exists("sasl_client_class"))
1702                                 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));
1703                         if(GetType($headers[$authenticate_header])=="array")
1704                                 $authenticate=$headers[$authenticate_header];
1705                         else
1706                                 $authenticate=array($headers[$authenticate_header]);
1707                         for($response="", $mechanisms=array(),$m=0;$m<count($authenticate);$m++)
1708                         {
1709                                 $mechanism=$this->Tokenize($authenticate[$m]," ");
1710                                 $response=$this->Tokenize("");
1711                                 if(strlen($authentication_mechanism))
1712                                 {
1713                                         if(!strcmp($authentication_mechanism,$mechanism))
1714                                         {
1715                                                 $mechanisms[]=$mechanism;
1716                                                 break;
1717                                         }
1718                                 }
1719                                 else
1720                                         $mechanisms[]=$mechanism;
1721                         }
1722                         $sasl=new sasl_client_class;
1723                         if(IsSet($user))
1724                                 $sasl->SetCredential("user",$user);
1725                         if(IsSet($password))
1726                                 $sasl->SetCredential("password",$password);
1727                         if(IsSet($realm))
1728                                 $sasl->SetCredential("realm",$realm);
1729                         if(IsSet($workstation))
1730                                 $sasl->SetCredential("workstation",$workstation);
1731                         $sasl->SetCredential("uri",$this->request_uri);
1732                         $sasl->SetCredential("method",$this->request_method);
1733                         $sasl->SetCredential("session",$this->session);
1734                         do
1735                         {
1736                                 $status=$sasl->Start($mechanisms,$message,$interactions);
1737                         }
1738                         while($status==SASL_INTERACT);
1739                         switch($status)
1740                         {
1741                                 case SASL_CONTINUE:
1742                                         break;
1743                                 case SASL_NOMECH:
1744                                         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));
1745                                 default:
1746                                         return($this->SetError("Could not start the SASL ".($proxy ? "proxy " : "")."authentication client: ".$sasl->error, HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1747                         }
1748                         if($proxy >= 0)
1749                         {
1750                                 for(;;)
1751                                 {
1752                                         if(strlen($error=$this->ReadReplyBody($body,$this->file_buffer_length)))
1753                                                 return($error);
1754                                         if(strlen($body)==0)
1755                                                 break;
1756                                 }
1757                         }
1758                         $authorization_value=$sasl->mechanism.(IsSet($message) ? " ".($sasl->encode_response ? base64_encode($message) : $message) : "");
1759                         $request_arguments=$this->request_arguments;
1760                         $arguments=$request_arguments;
1761                         $arguments["Headers"][$authorization_header]=$authorization_value;
1762                         if(!$proxy
1763                         && strlen($proxy_authorization))
1764                                 $arguments["Headers"]["Proxy-Authorization"]=$proxy_authorization;
1765                         if(strlen($error=$this->Close())
1766                         || strlen($error=$this->Open($arguments)))
1767                                 return($this->SetError($error, $this->error_code));
1768                         $authenticated=0;
1769                         if(IsSet($message))
1770                         {
1771                                 if($proxy < 0)
1772                                 {
1773                                         if(strlen($error=$this->ConnectFromProxy($arguments, $headers)))
1774                                                 return($this->SetError($error, $this->error_code));
1775                                 }
1776                                 else
1777                                 {
1778                                         if(strlen($error=$this->SendRequest($arguments))
1779                                         || strlen($error=$this->ReadReplyHeadersResponse($headers)))
1780                                                 return($this->SetError($error, $this->error_code));
1781                                 }
1782                                 if(!IsSet($headers[$authenticate_header]))
1783                                         $authenticate=array();
1784                                 elseif(GetType($headers[$authenticate_header])=="array")
1785                                         $authenticate=$headers[$authenticate_header];
1786                                 else
1787                                         $authenticate=array($headers[$authenticate_header]);
1788                                 for($mechanism=0;$mechanism<count($authenticate);$mechanism++)
1789                                 {
1790                                         if(!strcmp($this->Tokenize($authenticate[$mechanism]," "),$sasl->mechanism))
1791                                         {
1792                                                 $response=$this->Tokenize("");
1793                                                 break;
1794                                         }
1795                                 }
1796                                 switch($this->response_status)
1797                                 {
1798                                         case $authenticate_status:
1799                                                 break;
1800                                         case "301":
1801                                         case "302":
1802                                         case "303":
1803                                         case "307":
1804                                                 if($proxy >= 0)
1805                                                         return($this->Redirect($headers));
1806                                         default:
1807                                                 if(intval($this->response_status/100)==2)
1808                                                 {
1809                                                         if($proxy)
1810                                                                 $proxy_authorization=$authorization_value;
1811                                                         $authenticated=1;
1812                                                         break;
1813                                                 }
1814                                                 if($proxy
1815                                                 && !strcmp($this->response_status,"401"))
1816                                                 {
1817                                                         $proxy_authorization=$authorization_value;
1818                                                         $authenticated=1;
1819                                                         break;
1820                                                 }
1821                                                 return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".$this->response_status." ".$this->response_message, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1822                                 }
1823                         }
1824                         for(;!$authenticated;)
1825                         {
1826                                 do
1827                                 {
1828                                         $status=$sasl->Step($response,$message,$interactions);
1829                                 }
1830                                 while($status==SASL_INTERACT);
1831                                 switch($status)
1832                                 {
1833                                         case SASL_CONTINUE:
1834                                                 $authorization_value=$sasl->mechanism.(IsSet($message) ? " ".($sasl->encode_response ? base64_encode($message) : $message) : "");
1835                                                 $arguments=$request_arguments;
1836                                                 $arguments["Headers"][$authorization_header]=$authorization_value;
1837                                                 if(!$proxy
1838                                                 && strlen($proxy_authorization))
1839                                                         $arguments["Headers"]["Proxy-Authorization"]=$proxy_authorization;
1840                                                 if($proxy < 0)
1841                                                 {
1842                                                         if(strlen($error=$this->ConnectFromProxy($arguments, $headers)))
1843                                                                 return($this->SetError($error, $this->error_code));
1844                                                 }
1845                                                 else
1846                                                 {
1847                                                         if(strlen($error=$this->SendRequest($arguments))
1848                                                         || strlen($error=$this->ReadReplyHeadersResponse($headers)))
1849                                                                 return($this->SetError($error, $this->error_code));
1850                                                 }
1851                                                 switch($this->response_status)
1852                                                 {
1853                                                         case $authenticate_status:
1854                                                                 if(GetType($headers[$authenticate_header])=="array")
1855                                                                         $authenticate=$headers[$authenticate_header];
1856                                                                 else
1857                                                                         $authenticate=array($headers[$authenticate_header]);
1858                                                                 for($response="",$mechanism=0;$mechanism<count($authenticate);$mechanism++)
1859                                                                 {
1860                                                                         if(!strcmp($this->Tokenize($authenticate[$mechanism]," "),$sasl->mechanism))
1861                                                                         {
1862                                                                                 $response=$this->Tokenize("");
1863                                                                                 break;
1864                                                                         }
1865                                                                 }
1866                                                                 if($proxy >= 0)
1867                                                                 {
1868                                                                         for(;;)
1869                                                                         {
1870                                                                                 if(strlen($error=$this->ReadReplyBody($body,$this->file_buffer_length)))
1871                                                                                         return($error);
1872                                                                                 if(strlen($body)==0)
1873                                                                                         break;
1874                                                                         }
1875                                                                 }
1876                                                                 $this->state="Connected";
1877                                                                 break;
1878                                                         case "301":
1879                                                         case "302":
1880                                                         case "303":
1881                                                         case "307":
1882                                                                 if($proxy >= 0)
1883                                                                         return($this->Redirect($headers));
1884                                                         default:
1885                                                                 if(intval($this->response_status/100)==2)
1886                                                                 {
1887                                                                         if($proxy)
1888                                                                                 $proxy_authorization=$authorization_value;
1889                                                                         $authenticated=1;
1890                                                                         break;
1891                                                                 }
1892                                                                 if($proxy
1893                                                                 && !strcmp($this->response_status,"401"))
1894                                                                 {
1895                                                                         $proxy_authorization=$authorization_value;
1896                                                                         $authenticated=1;
1897                                                                         break;
1898                                                                 }
1899                                                                 return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".$this->response_status." ".$this->response_message));
1900                                                 }
1901                                                 break;
1902                                         default:
1903                                                 return($this->SetError("Could not process the SASL ".($proxy ? "proxy " : "")."authentication step: ".$sasl->error, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE));
1904                                 }
1905                         }
1906                 }
1907                 return("");
1908         }
1909         
1910         Function ReadReplyHeaders(&$headers)
1911         {
1912                 if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
1913                         return($error);
1914                 $proxy_authorization="";
1915                 while(!strcmp($this->response_status, "100"))
1916                 {
1917                         $this->state="RequestSent";
1918                         if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
1919                                 return($error);
1920                 }
1921                 switch($this->response_status)
1922                 {
1923                         case "301":
1924                         case "302":
1925                         case "303":
1926                         case "307":
1927                                 if(strlen($error=$this->Redirect($headers)))
1928                                         return($error);
1929                                 break;
1930                         case "407":
1931                                 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)))
1932                                         return($error);
1933                                 if(strcmp($this->response_status,"401"))
1934                                         return("");
1935                         case "401":
1936                                 return($this->Authenticate($headers, 0, $proxy_authorization, $this->request_user, $this->request_password, $this->request_realm, $this->request_workstation));
1937                 }
1938                 return("");
1939         }
1940
1941         Function ReadReplyBody(&$body,$length)
1942         {
1943                 $body="";
1944                 if(strlen($this->error))
1945                         return($this->error);
1946                 switch($this->state)
1947                 {
1948                         case "Disconnected":
1949                                 return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1950                         case "Connected":
1951                         case "ConnectedToProxy":
1952                                 return($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1953                         case "RequestSent":
1954                                 if(($error=$this->ReadReplyHeaders($headers))!="")
1955                                         return($error);
1956                                 break;
1957                         case "GotReplyHeaders":
1958                                 break;
1959                         case 'ResponseReceived':
1960                                 $body = '';
1961                                 return('');
1962                         default:
1963                                 return($this->SetError("can not get request headers in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
1964                 }
1965                 if($this->content_length_set)
1966                         $length=min($this->content_length-$this->read_length,$length);
1967                 $body = '';
1968                 if($length>0)
1969                 {
1970                         if(!$this->EndOfInput()
1971                         && ($body=$this->ReadBytes($length))=="")
1972                         {
1973                                 if(strlen($this->error))
1974                                         return($this->SetError("could not get the request reply body: ".$this->error, $this->error_code));
1975                         }
1976                         $this->read_length+=strlen($body);
1977                         if($this->EndOfInput())
1978                                 $this->state = 'ResponseReceived';
1979                 }
1980                 return("");
1981         }
1982
1983         Function ReadWholeReplyBody(&$body)
1984         {
1985                 $body = '';
1986                 for(;;)
1987                 {
1988                         if(strlen($error = $this->ReadReplyBody($block, $this->file_buffer_length)))
1989                                 return($error);
1990                         if(strlen($block) == 0)
1991                                 return('');
1992                         $body .= $block;
1993                 }
1994         }
1995
1996         Function SaveCookies(&$cookies, $domain='', $secure_only=0, $persistent_only=0)
1997         {
1998                 $now=gmdate("Y-m-d H-i-s");
1999                 $cookies=array();
2000                 for($secure_cookies=0,Reset($this->cookies);$secure_cookies<count($this->cookies);Next($this->cookies),$secure_cookies++)
2001                 {
2002                         $secure=Key($this->cookies);
2003                         if(!$secure_only
2004                         || $secure)
2005                         {
2006                                 for($cookie_domain=0,Reset($this->cookies[$secure]);$cookie_domain<count($this->cookies[$secure]);Next($this->cookies[$secure]),$cookie_domain++)
2007                                 {
2008                                         $domain_pattern=Key($this->cookies[$secure]);
2009                                         $match=strlen($domain)-strlen($domain_pattern);
2010                                         if(strlen($domain)==0
2011                                         || ($match>=0
2012                                         && !strcmp($domain_pattern,substr($domain,$match))
2013                                         && ($match==0
2014                                         || $domain_pattern[0]=="."
2015                                         || $domain[$match-1]==".")))
2016                                         {
2017                                                 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++)
2018                                                 {
2019                                                         $path=Key($this->cookies[$secure][$domain_pattern]);
2020                                                         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++)
2021                                                         {
2022                                                                 $cookie_name=Key($this->cookies[$secure][$domain_pattern][$path]);
2023                                                                 $expires=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"];
2024                                                                 if((!$persistent_only
2025                                                                 && strlen($expires)==0)
2026                                                                 || (strlen($expires)
2027                                                                 && strcmp($now,$expires)<0))
2028                                                                         $cookies[$secure][$domain_pattern][$path][$cookie_name]=$this->cookies[$secure][$domain_pattern][$path][$cookie_name];
2029                                                         }
2030                                                 }
2031                                         }
2032                                 }
2033                         }
2034                 }
2035         }
2036
2037         Function SavePersistentCookies(&$cookies, $domain='', $secure_only=0)
2038         {
2039                 $this->SaveCookies($cookies, $domain, $secure_only, 1);
2040         }
2041
2042         Function GetPersistentCookies(&$cookies, $domain='', $secure_only=0)
2043         {
2044                 $this->SavePersistentCookies($cookies, $domain, $secure_only);
2045         }
2046
2047         Function RestoreCookies($cookies, $clear=1)
2048         {
2049                 $new_cookies=($clear ? array() : $this->cookies);
2050                 for($secure_cookies=0, Reset($cookies); $secure_cookies<count($cookies); Next($cookies), $secure_cookies++)
2051                 {
2052                         $secure=Key($cookies);
2053                         if(GetType($secure)!="integer")
2054                                 return($this->SetError("invalid cookie secure value type (".serialize($secure).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
2055                         for($cookie_domain=0,Reset($cookies[$secure]);$cookie_domain<count($cookies[$secure]);Next($cookies[$secure]),$cookie_domain++)
2056                         {
2057                                 $domain_pattern=Key($cookies[$secure]);
2058                                 if(GetType($domain_pattern)!="string")
2059                                         return($this->SetError("invalid cookie domain value type (".serialize($domain_pattern).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
2060                                 for(Reset($cookies[$secure][$domain_pattern]),$path_part=0;$path_part<count($cookies[$secure][$domain_pattern]);Next($cookies[$secure][$domain_pattern]),$path_part++)
2061                                 {
2062                                         $path=Key($cookies[$secure][$domain_pattern]);
2063                                         if(GetType($path)!="string"
2064                                         || strcmp(substr($path, 0, 1), "/"))
2065                                                 return($this->SetError("invalid cookie path value type (".serialize($path).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
2066                                         for(Reset($cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookie<count($cookies[$secure][$domain_pattern][$path]);Next($cookies[$secure][$domain_pattern][$path]),$cookie++)
2067                                         {
2068                                                 $cookie_name=Key($cookies[$secure][$domain_pattern][$path]);
2069                                                 $expires=$cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"];
2070                                                 $value=$cookies[$secure][$domain_pattern][$path][$cookie_name]["value"];
2071                                                 if(GetType($expires)!="string"
2072                                                 || (strlen($expires)
2073                                                 && !preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\$/", $expires)))
2074                                                         return($this->SetError("invalid cookie expiry value type (".serialize($expires).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS));
2075                                                 $new_cookies[$secure][$domain_pattern][$path][$cookie_name]=array(
2076                                                         "name"=>$cookie_name,
2077                                                         "value"=>$value,
2078                                                         "domain"=>$domain_pattern,
2079                                                         "path"=>$path,
2080                                                         "expires"=>$expires,
2081                                                         "secure"=>$secure
2082                                                 );
2083                                         }
2084                                 }
2085                         }
2086                 }
2087                 $this->cookies=$new_cookies;
2088                 return("");
2089         }
2090 };
2091
2092 ?>