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
9define('HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR',       -1);
10define('HTTP_CLIENT_ERROR_NO_ERROR',                 0);
11define('HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS',   1);
12define('HTTP_CLIENT_ERROR_CANNOT_CONNECT',           2);
13define('HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE',    3);
14define('HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE', 4);
15define('HTTP_CLIENT_ERROR_PROTOCOL_FAILURE',         5);
16define('HTTP_CLIENT_ERROR_INVALID_PARAMETERS',       6);
17
18class 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?>