egroupware/phpgwapi/inc/class.http.inc.php

475 lines
14 KiB
PHP

<?
/**************************************************************************\
* phpGroupWare API - HTTP protocol class *
* http://www.phpgroupware.org/api *
* ------------------------------------------------------------------------ *
* This is not part of phpGroupWare, but is used by phpGroupWare. *
* ------------------------------------------------------------------------ *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation; either version 2 of the License, or (at your *
* option) any later version. *
\**************************************************************************/
/* $Id$ */
class http
{
var $host_name="";
var $host_port=80;
var $proxy_host_name="";
var $proxy_host_port=80;
var $request_method="GET";
var $user_agent="Manuel Lemos HTTP class test script";
var $request_uri="";
var $protocol_version="1.0";
var $debug=0;
var $support_cookies=1;
var $cookies=array();
/* private variables - DO NOT ACCESS */
var $state="Disconnected";
var $connection=0;
var $content_length=0;
var $read_length=0;
var $request_host="";
var $months=array(
"Jan"=>"01",
"Feb"=>"02",
"Mar"=>"03",
"Apr"=>"04",
"May"=>"05",
"Jun"=>"06",
"Jul"=>"07",
"Aug"=>"08",
"Sep"=>"09",
"Oct"=>"10",
"Nov"=>"11",
"Dec"=>"12");
/* Private methods - DO NOT CALL */
Function OutputDebug($message)
{
echo $message,"\n";
}
Function GetLine()
{
for($line="";;)
{
if(feof($this->connection)
|| !($part=fgets($this->connection,100)))
return(0);
$line.=$part;
$length=strlen($line);
if($length>=2
&& substr($line,$length-2,2)=="\r\n")
{
$line=substr($line,0,$length-2);
if($this->debug)
$this->OutputDebug("< $line");
return($line);
}
}
}
Function PutLine($line)
{
if($this->debug)
$this->OutputDebug("> $line");
return(fputs($this->connection,"$line\r\n"));
}
Function PutData($data)
{
if($this->debug)
$this->OutputDebug("> $data");
return(fputs($this->connection,$data));
}
Function Readbytes($length)
{
if($this->debug)
{
if(($bytes=fread($this->connection,$length))!="")
$this->OutputDebug("< $bytes");
return($bytes);
}
else
return(fread($this->connection,$length));
}
Function EndOfInput()
{
return(feof($this->connection));
}
Function Connect($host_name,$host_port)
{
if($this->debug)
$this->OutputDebug("Connecting to $host_name...");
if(($this->connection=fsockopen($host_name,$host_port,&$error))==0)
{
switch($error)
{
case -3:
return("-3 socket could not be created");
case -4:
return("-4 dns lookup on hostname \"".$host_name."\" failed");
case -5:
return("-5 connection refused or timed out");
case -6:
return("-6 fdopen() call failed");
case -7:
return("-7 setvbuf() call failed");
default:
return($error." could not connect to the host \"".$host_name."\"");
}
}
else
{
if($this->debug)
$this->OutputDebug("Connected to $host_name");
$this->state="Connected";
return("");
}
}
Function Disconnect()
{
if($this->debug)
$this->OutputDebug("Disconnected from ".$this->host_name);
fclose($this->connection);
return("");
}
/* Public methods */
Function Open($arguments)
{
if($this->state!="Disconnected")
return("1 already connected");
if(IsSet($arguments["HostName"]))
$this->host_name=$arguments["HostName"];
if(IsSet($arguments["HostPort"]))
$this->host_port=$arguments["HostPort"];
if(IsSet($arguments["ProxyHostName"]))
$this->proxy_host_name=$arguments["ProxyHostName"];
if(IsSet($arguments["ProxyHostPort"]))
$this->proxy_host_port=$arguments["ProxyHostPort"];
if(strlen($this->proxy_host_name)==0)
{
if(strlen($this->host_name)==0)
return("2 it was not specified a valid hostname");
$host_name=$this->host_name;
$host_port=$this->host_port;
}
else
{
$host_name=$this->proxy_host_name;
$host_port=$this->proxy_host_port;
}
$error=$this->Connect($host_name,$host_port);
if(strlen($error)==0)
$this->state="Connected";
return($error);
}
Function Close()
{
if($this->state=="Disconnected")
return("1 already disconnected");
$error=$this->Disconnect();
if(strlen($error)==0)
$this->state="Disconnected";
return($error);
}
Function SendRequest($arguments)
{
switch($this->state)
{
case "Disconnected":
return("1 connection was not yet established");
case "Connected":
break;
default:
return("2 can not send request in the current connection state");
}
if(IsSet($arguments["RequestMethod"]))
$this->request_method=$arguments["RequestMethod"];
if(IsSet($arguments["User-Agent"]))
$this->user_agent=$arguments["User-Agent"];
if(strlen($this->request_method)==0)
return("3 it was not specified a valid request method");
if(IsSet($arguments["RequestURI"]))
$this->request_uri=$arguments["RequestURI"];
if(strlen($this->request_uri)==0
|| substr($this->request_uri,0,1)!="/")
return("4 it was not specified a valid request URI");
$request_body="";
$headers=(IsSet($arguments["Headers"]) ? $arguments["Headers"] : array());
if($this->request_method=="POST")
{
if(IsSet($arguments["PostValues"]))
{
$values=$arguments["PostValues"];
if(GetType($values)!="array")
return("5 it was not specified a valid POST method values array");
for($request_body="",Reset($values),$value=0;$value<count($values);Next($values),$value++)
{
if($value>0)
$request_body.="&";
$request_body.=Key($values)."=".UrlEncode($values[Key($values)]);
}
$headers["Content-type"]="application/x-www-form-urlencoded";
}
}
if(strlen($this->proxy_host_name)==0)
$request_uri=$this->request_uri;
else
$request_uri="http://".$this->host_name.($this->host_port==80 ? "" : ":".$this->host_port).$this->request_uri;
if(($success=$this->PutLine($this->request_method." ".$request_uri." HTTP/".$this->protocol_version)))
{
if(($body_length=strlen($request_body)))
$headers["Content-length"]=$body_length;
for($host_set=0,Reset($headers),$header=0;$header<count($headers);Next($headers),$header++)
{
$header_name=Key($headers);
$header_value=$headers[$header_name];
if(GetType($header_value)=="array")
{
for(Reset($header_value),$value=0;$value<count($header_value);Next($header_value),$value++)
{
if(!$success=$this->PutLine("$header_name: ".$header_value[Key($header_value)]))
break 2;
}
}
else
{
if(!$success=$this->PutLine("$header_name: $header_value"))
break;
}
if(strtolower(Key($headers))=="host")
{
$this->request_host=strtolower($header_value);
$host_set=1;
}
}
if($success)
{
if(!$host_set)
{
$success=$this->PutLine("Host: ".$this->host_name);
$this->request_host=strtolower($this->host_name);
}
if(count($this->cookies)
&& IsSet($this->cookies[0]))
{
$now=gmdate("Y-m-d H-i-s");
for($cookies=array(),$domain=0,Reset($this->cookies[0]);$domain<count($this->cookies[0]);Next($this->cookies[0]),$domain++)
{
$domain_pattern=Key($this->cookies[0]);
$match=strlen($this->request_host)-strlen($domain_pattern);
if($match>=0
&& !strcmp($domain_pattern,substr($this->request_host,$match))
&& ($match==0
|| $domain_pattern[0]=="."
|| $this->request_host[$match-1]=="."))
{
for(Reset($this->cookies[0][$domain_pattern]),$path_part=0;$path_part<count($this->cookies[0][$domain_pattern]);Next($this->cookies[0][$domain_pattern]),$path_part++)
{
$path=Key($this->cookies[0][$domain_pattern]);
if(strlen($this->request_uri)>=strlen($path)
&& substr($this->request_uri,0,strlen($path))==$path)
{
for(Reset($this->cookies[0][$domain_pattern][$path]),$cookie=0;$cookie<count($this->cookies[0][$domain_pattern][$path]);Next($this->cookies[0][$domain_pattern][$path]),$cookie++)
{
$cookie_name=Key($this->cookies[0][$domain_pattern][$path]);
$expires=$this->cookies[0][$domain_pattern][$path][$cookie_name]["expires"];
if($expires==""
|| strcmp($now,$expires)<0)
$cookies[$cookie_name]=$this->cookies[0][$domain_pattern][$path][$cookie_name];
}
}
}
}
}
for(Reset($cookies),$cookie=0;$cookie<count($cookies);Next($cookies),$cookie++)
{
$cookie_name=Key($cookies);
if(!($success=$this->PutLine("Cookie: ".UrlEncode($cookie_name)."=".$cookies[$cookie_name]["value"].";")))
break;
}
}
if($success)
{
if($success)
{
$success=$this->PutLine("");
if($body_length
&& $success)
$success=$this->PutData($request_body);
}
}
}
}
if(!$success)
return("5 could not send the HTTP request");
$this->state="RequestSent";
return("");
}
Function ReadReplyHeaders(&$headers)
{
switch($this->state)
{
case "Disconnected":
return("1 connection was not yet established");
case "Connected":
return("2 request was not sent");
case "RequestSent":
break;
default:
return("3 can not get request headers in the current connection state");
}
$headers=array();
$this->content_length=$this->read_length=0;
$this->content_length_set=0;
for(;;)
{
$line=$this->GetLine();
if(GetType($line)!="string")
return("4 could not read request reply");
if($line=="")
{
$this->state="GotReplyHeaders";
return("");
}
$header_name=strtolower(strtok($line,":"));
$header_value=Trim(Chop(strtok("\r\n")));
if(IsSet($headers[$header_name]))
{
if(GetType($headers[$header_name])=="string")
$headers[$header_name]=array($headers[$header_name]);
$headers[$header_name][]=$header_value;
}
else
$headers[$header_name]=$header_value;
switch($header_name)
{
case "content-length":
$this->content_length=intval($headers[$header_name]);
$this->content_length_set=1;
break;
case "set-cookie":
if($this->support_cookies)
{
$cookie_name=trim(strtok($headers[$header_name],"="));
$cookie_value=strtok(";");
$domain=$this->request_host;
$path="/";
$expires="";
$secure=0;
while(($name=strtolower(trim(strtok("="))))!="")
{
$value=UrlDecode(strtok(";"));
switch($name)
{
case "domain":
if($value==""
|| !strpos($value,".",$value[0]=="."))
break;
$domain=strtolower($value);
break;
case "path":
if($value!=""
&& $value[0]=="/")
$path=$value;
break;
case "expires":
if(ereg("^((Mon|Monday|Tue|Tuesday|Wed|Wednesday|Thu|Thursday|Fri|Friday|Sat|Saturday|Sun|Sunday), )?([0-9]{2})\\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\-([0-9]{2,4}) ([0-9]{2})\\:([0-9]{2})\\:([0-9]{2}) GMT$",$value,$matches))
{
$year=intval($matches[5]);
if($year<1900)
$year+=($year<70 ? 2000 : 1900);
$expires="$year-".$this->months[$matches[4]]."-".$matches[3]." ".$matches[6].":".$matches[7].":".$matches[8];
}
break;
case "secure":
$secure=1;
break;
}
}
$this->cookies[$secure][$domain][$path][$cookie_name]=array(
"name"=>$cookie_name,
"value"=>$cookie_value,
"domain"=>$domain,
"path"=>$path,
"expires"=>$expires,
"secure"=>$secure
);
}
}
}
}
Function ReadReplyBody(&$body,$length)
{
switch($this->state)
{
case "Disconnected":
return("1 connection was not yet established");
case "Connected":
return("2 request was not sent");
case "RequestSent":
if(($error=$this->ReadReplyHeaders(&$headers))!="")
return($error);
break;
case "GotReplyHeaders":
break;
default:
return("3 can not get request headers in the current connection state");
}
$body="";
if($this->content_length_set)
$length=min($this->content_length-$this->read_length,$length);
if($length>0
&& !$this->EndOfInput()
&& ($body=$this->ReadBytes($length))=="")
return("4 could not get the request reply body");
return("");
}
Function GetPersistentCookies(&$cookies)
{
$now=gmdate("Y-m-d H-i-s");
$cookies=array();
for($secure_cookies=0,Reset($this->cookies);$secure_cookies<count($this->cookies);Next($this->cookies),$secure_cookies++)
{
$secure=Key($this->cookies);
for($domain=0,Reset($this->cookies[$secure]);$domain<count($this->cookies[$secure]);Next($this->cookies[$secure]),$domain++)
{
$domain_pattern=Key($this->cookies[$secure]);
for(Reset($this->cookies[$secure][$domain_pattern]),$path_part=0;$path_part<count($this->cookies[$secure][$domain_pattern]);Next($this->cookies[$secure][$domain_pattern]),$path_part++)
{
$path=Key($this->cookies[$secure][$domain_pattern]);
for(Reset($this->cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookie<count($this->cookies[$secure][$domain_pattern][$path]);Next($this->cookies[$secure][$domain_pattern][$path]),$cookie++)
{
$cookie_name=Key($this->cookies[$secure][$domain_pattern][$path]);
$expires=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"];
if($expires!=""
&& strcmp($now,$expires)<0)
$cookies[$secure][$domain_pattern][$path][$cookie_name]=$this->cookies[$secure][$domain_pattern][$path][$cookie_name];
}
}
}
}
}
};