HEAD only: Add client from 2.0 lib - adds compression, proxy auth, etc.

This commit is contained in:
Miles Lott 2005-08-16 13:23:03 +00:00
parent e726d3f7ae
commit 7d18902b97

View File

@ -37,20 +37,111 @@
{ {
var $path; var $path;
var $server; var $server;
var $port; var $port=0;
var $method='http';
var $errno; var $errno;
var $errstring; var $errstr;
var $debug=0; var $debug=0;
var $username=''; var $username='';
var $password=''; var $password='';
var $cert=''; var $cert='';
var $certpass=''; var $certpass='';
var $verifypeer=1;
var $verifyhost=1;
var $no_multicall=False;
var $proxy = '';
var $proxyport=0;
var $proxy_user = '';
var $proxy_pass = '';
/**
* List of http compression methods accepted by the client for responses.
* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
*
* NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
* in those cases it will be up to CURL to decide the compression methods
* it supports. You might check for the presence of 'zlib' in the output of
* curl_version() to determine wheter compression is supported or not
*/
var $accepted_compression = array();
/**
* Name of compression scheme to be used for sending requests.
* Either null, gzip or deflate
*/
var $request_compression = '';
/**
* CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
* http://curl.haxx.se/docs/faq.html#7.3)
*/
var $xmlrpc_curl_handle = null;
/// Whether to use persistent connections for http 1.1 and https
var $keepalive = false;
function xmlrpc_client($path='', $server='', $port=0) function xmlrpc_client($path, $server='', $port='', $method='')
{
// allow user to specify all params in $path
if($server == '' and $port == '' and $method == '')
{
$parts = parse_url($path);
$server = $parts['host'];
$path = $parts['path'];
if(isset($parts['query']))
{
$path .= '?'.$parts['query'];
}
if(isset($parts['fragment']))
{
$path .= '#'.$parts['fragment'];
}
if(isset($parts['port']))
{
$port = $parts['port'];
}
if(isset($parts['scheme']))
{
$method = $parts['scheme'];
}
if(isset($parts['user']))
{
$this->username = $parts['user'];
}
if(isset($parts['pass']))
{
$this->password = $parts['pass'];
}
}
if($path == '' || $path[0] != '/')
{
$this->path='/'.$path;
}
else
{
$this->path=$path;
}
$this->server=$server;
if($port != '')
{ {
$this->port=$port; $this->port=$port;
$this->server = $server; }
$this->path = $path; if($method != '')
{
$this->method=$method;
}
// if ZLIB is enabled, let the server by default accept compressed requests
if(function_exists('gzinflate') || (
function_exists('curl_init') && (($info = curl_version()) &&
((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
))
{
$this->accepted_compression = array('gzip', 'deflate');
}
// keepalives: enabled by default ONLY for PHP >= 4.3.8
// (see http://curl.haxx.se/docs/faq.html#7.3)
if(version_compare(phpversion(), '4.3.8') >= 0)
{
$this->keepalive = true;
}
} }
function setDebug($in) function setDebug($in)
@ -77,14 +168,61 @@
$this->certpass = $certpass; $this->certpass = $certpass;
} }
function send($msg, $timeout=0, $method='http') function setSSLVerifyPeer($i)
{ {
/* where msg is an xmlrpcmsg */ $this->verifypeer = $i;
}
function setSSLVerifyHost($i)
{
$this->verifyhost = $i;
}
/**
* set proxy info
*
* @param string $proxyhost
* @param string $proxyport. Defaults to 8080 for HTTP and 443 for HTTPS
* @param string $proxyusername
* @param string $proxypassword
* @access public
*/
function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '')
{
$this->proxy = $proxyhost;
$this->proxyport = $proxyport;
$this->proxy_user = $proxyusername;
$this->proxy_pass = $proxypassword;
}
function& send($msg, $timeout=0, $method='')
{
// if user does not specify http protocol, use native method of this client
// (i.e. method set during call to constructor)
if($method == '')
{
$method = $this->method;
}
if(is_array($msg))
{
// $msg is an array of xmlrpcmsg's
$r =& $this->multicall($msg, $timeout, $method);
return $r;
}
elseif(is_string($msg))
{
$n =& new xmlrpcmsg('');
$n->payload = $msg;
$msg = $n;
}
// where msg is an xmlrpcmsg
$msg->debug=$this->debug; $msg->debug=$this->debug;
if ($method == 'https') switch($method)
{ {
return $this->sendPayloadHTTPS( case 'https':
$r =& $this->sendPayloadHTTPS(
$msg, $msg,
$this->server, $this->server,
$this->port, $this->port,
@ -92,38 +230,161 @@
$this->username, $this->username,
$this->password, $this->password,
$this->cert, $this->cert,
$this->certpass $this->certpass,
$this->proxy,
$this->proxyport,
$this->proxy_user,
$this->proxy_pass,
'https',
$this->keepalive
); );
} break;
else case 'http11':
{ $r =& $this->sendPayloadCURL(
return $this->sendPayloadHTTP10(
$msg, $msg,
$this->server, $this->server,
$this->port, $this->port,
$timeout, $timeout,
$this->username, $this->username,
$this->password $this->password,
null,
null,
$this->proxy,
$this->proxyport,
$this->proxy_user,
$this->proxy_pass,
'http',
$this->keepalive
);
break;
case 'http10':
default:
$r =& $this->sendPayloadHTTP10(
$msg,
$this->server,
$this->port,
$timeout,
$this->username,
$this->password,
$this->proxy,
$this->proxyport,
$this->proxy_user,
$this->proxy_pass
); );
} }
return $r;
} }
function sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='') function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='',
$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='')
{ {
if($port==0) if($port==0)
{ {
$port=80; $port=80;
} }
if($timeout>0)
// Only create the payload if it was not created previously
if(empty($msg->payload))
{ {
$fp = fsockopen($server, $port, &$this->errno, &$this->errstr, $timeout); $msg->createPayload();
}
// Deflate request body and set appropriate request headers
if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
{
if($this->request_compression == 'gzip')
{
$a = @gzencode($msg->payload);
if($a)
{
$msg->payload = $a;
$encoding_hdr = "Content-Encoding: gzip\r\n";
}
} }
else else
{ {
$fp = fsockopen($server, $port, &$this->errno, &$this->errstr); $a = @gzdeflate($msg->payload);
} if($a)
if (!$fp)
{ {
$msg->payload = $a;
$encoding_hdr = "Content-Encoding: deflate\r\n";
}
}
}
else
{
$encoding_hdr = '';
}
// thanks to Grant Rauscher <grant7@firstworld.net>
// for this
$credentials='';
if($username!='')
{
$credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
}
$accepted_encoding = '';
if(is_array($this->accepted_compression) && count($this->accepted_compression))
{
$accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
}
$proxy_credentials = '';
if($proxyhost)
{
if($proxyport == 0)
{
$proxyport = 8080;
}
$connectserver = $proxyhost;
$connectport = $proxyport;
$uri = 'http://'.$server.':'.$port.$this->path;
if($proxyusername != '')
{
$proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
}
}
else
{
$connectserver = $server;
$connectport = $port;
$uri = $this->path;
}
$op= "POST " . $uri. " HTTP/1.0\r\n"
. "User-Agent: " . $GLOBALS['xmlrpcName'] . " " . $GLOBALS['xmlrpcVersion'] . "\r\n"
. 'X-EGW-Server: ' . $this->server . ' ' . "\r\n"
. 'X-EGW-Version: ' . $GLOBALS['egw_info']['server']['versions']['phpgwapi'] . "\r\n"
. "Host: ". $this->server . "\r\n"
. $credentials
. $proxy_credentials
. $accepted_encoding
. $encoding_hdr
. "Accept-Charset: " . $GLOBALS['xmlrpc_defencoding'] . "\r\n"
. "Content-Type: text/xml\r\nContent-Length: "
. strlen($msg->payload) . "\r\n\r\n"
. $msg->payload;
if($timeout>0)
{
$fp = @fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
}
else
{
$fp = @fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
}
if($fp)
{
if($timeout>0 && function_exists('stream_set_timeout'))
{
stream_set_timeout($fp, $timeout);
}
}
else
{
$this->errstr='Connect error: '.$this->errstr;
$r = CreateObject( $r = CreateObject(
'phpgwapi.xmlrpcresp', 'phpgwapi.xmlrpcresp',
'', '',
@ -132,28 +393,6 @@
); );
return $r; return $r;
} }
// Only create the payload if it was not created previously
if(empty($msg->payload))
{
$msg->createPayload();
}
// thanks to Grant Rauscher <grant7@firstworld.net>
// for this
$credentials = '';
if ($username && $password)
{
$credentials = 'Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
}
$op = 'POST ' . $this->path . " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 2.0\r\n"
. 'Host: '. $this->server . "\r\n"
. 'X-EGW-Server: ' . $this->server . ' ' . "\r\n"
. 'X-EGW-Version: ' . $GLOBALS['egw_info']['server']['versions']['phpgwapi'] . "\r\n"
. $credentials
. "Content-Type: text/xml\r\nContent-Length: "
. strlen($msg->payload) . "\r\n\r\n"
. $msg->payload;
if(!fputs($fp, $op, strlen($op))) if(!fputs($fp, $op, strlen($op)))
{ {
@ -165,72 +404,203 @@
$GLOBALS['xmlrpcstr']['http_error'] $GLOBALS['xmlrpcstr']['http_error']
); );
} }
$resp = $msg->parseResponseFile($fp); else
{
// should we reset errno and errstr on succesful socket connection?
}
$resp =& $msg->parseResponseFile($fp);
// shall we move this into parseresponsefile, cuz' we have to close the socket 1st
// and do the parsing second if we want to have recursive calls
// (i.e. redirects)
fclose($fp); fclose($fp);
return $resp; return $resp;
} }
/* contributed by Justin Miller <justin@voxel.net> - requires curl to be built into PHP */ // contributed by Justin Miller <justin@voxel.net>
function sendPayloadHTTPS($msg, $server, $port, $timeout=0,$username='', $password='', $cert='',$certpass='') // requires curl to be built into PHP
// NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
function &sendPayloadHTTPS($msg, $server, $port, $timeout=0,$username='', $password='', $cert='',$certpass='',
$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $keepalive=false)
{
$r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username, $password, $cert, $certpass,
$proxyhost, $proxyport, $proxyusername, $proxypassword, $keepalive);
return $r;
}
function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='', $password='', $cert='', $certpass='',
$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $method='https', $keepalive=false)
{ {
if(!function_exists('curl_init')) if(!function_exists('curl_init'))
{ {
return CreateObject( $r = CreateObject(
'phpgwapi.xmlrpcresp',
'',
$GLOBALS['xmlrpcerr']['no_curl'],
$GLOBALS['xmlrpcstr']['no_curl']
);
return $r;
}
if($method == 'https')
{
if(($info = curl_version()) &&
((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
{
$this->errstr = 'SSL unavailable on this install';
$r = CreateObject(
'phpgwapi.xmlrpcresp', 'phpgwapi.xmlrpcresp',
'', '',
$GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcerr']['no_ssl'],
$GLOBALS['xmlrpcstr']['no_ssl'] $GLOBALS['xmlrpcstr']['no_ssl']
); );
return $r;
}
} }
if($port == 0) if($port == 0)
{
if($method == 'http')
{
$port = 80;
}
else
{ {
$port = 443; $port = 443;
} }
/* Only create the payload if it was not created previously */ }
// Only create the payload if it was not created previously
if(empty($msg->payload)) if(empty($msg->payload))
{ {
$msg->createPayload(); $msg->createPayload();
} }
$curl = curl_init('https://' . $server . ':' . $port . $this->path); // Deflate request body and set appropriate request headers
if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
{
if($this->request_compression == 'gzip')
{
$a = @gzencode($msg->payload);
if($a)
{
$msg->payload = $a;
$encoding_hdr = "Content-Encoding: gzip";
}
}
else
{
$a = @gzdeflate($msg->payload);
if($a)
{
$msg->payload = $a;
$encoding_hdr = "Content-Encoding: deflate";
}
}
}
else
{
$encoding_hdr = '';
}
if(!$keepalive || !$this->xmlrpc_curl_handle)
{
$curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
if($keepalive)
{
$this->xmlrpc_curl_handle = $curl;
}
}
else
{
$curl = $this->xmlrpc_curl_handle;
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// results into variable // results into variable
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
if($this->debug) if($this->debug)
{ {
curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_VERBOSE, 1);
} }
curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC 1.0'); curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']);
// required for XMLRPC // required for XMLRPC: post the data
curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POST, 1);
// post the data
curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload);
// the data // the data
curl_setopt($curl, CURLOPT_HEADER, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload);
// return the header too // return the header too
curl_setopt($curl, CURLOPT_HTTPHEADER, array( curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'X-EGW-Server: ' . $this->server, 'X-EGW-Server: ' . $this->server,
'X-EGW-Version: ' . $GLOBALS['egw_info']['server']['versions']['phpgwapi'], 'X-EGW-Version: ' . $GLOBALS['egw_info']['server']['versions']['phpgwapi'],
'Content-Type: text/xml' 'Content-Type: text/xml'
)); ));
// will only work with PHP >= 5.0
// NB: if we set an empty string, CURL will add http header indicating
// ALL methods it is supporting. This is possibly a better option than
// letting the user tell what curl can / cannot do...
if(is_array($this->accepted_compression) && count($this->accepted_compression))
{
//curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
curl_setopt($curl, CURLOPT_ENCODING, '');
}
// extra headers
$headers = array('Content-Type: text/xml', 'Accept-Charset: '.$GLOBALS['xmlrpc_internalencoding']);
// if no keepalive is wanted, let the server know it in advance
if(!$keepalive)
{
$headers[] = 'Connection: close';
}
// request compression header
if($encoding_hdr)
{
$headers[] = $encoding_hdr;
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
// timeout is borked
if($timeout) if($timeout)
{ {
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1); curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
} }
if($username && $password) if($username && $password)
{ {
curl_setopt($curl, CURLOPT_USERPWD,"$username:$password"); curl_setopt($curl, CURLOPT_USERPWD,"$username:$password");
} }
if($method == 'https')
{
// set cert file
if($cert) if($cert)
{ {
curl_setopt($curl, CURLOPT_SSLCERT, $cert); curl_setopt($curl, CURLOPT_SSLCERT, $cert);
} }
// set cert password
if($certpass) if($certpass)
{ {
curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass); curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
} }
// set cert password // whether to verify remote host's cert
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
// whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
}
// proxy info
if($proxyhost)
{
if($proxyport == 0)
{
$proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
}
curl_setopt($curl, CURLOPT_PROXY,$proxyhost.':'.$proxyport);
//curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
if($proxyusername)
{
curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
}
}
$result = curl_exec($curl); $result = curl_exec($curl);
@ -243,14 +613,130 @@
$GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcerr']['curl_fail'],
$GLOBALS['xmlrpcstr']['curl_fail'] . ': ' . curl_error($curl) $GLOBALS['xmlrpcstr']['curl_fail'] . ': ' . curl_error($curl)
); );
if(!$keepalive)
{
curl_close($curl);
}
} }
else else
{ {
$resp = $msg->parseResponse($result); if(!$keepalive)
} {
curl_close($curl); curl_close($curl);
}
$resp =& $msg->parseResponse($result, true);
}
return $resp; return $resp;
} }
function& multicall($msgs, $timeout=0, $method='http')
{
$results = false;
if(!$this->no_multicall)
{
$results = $this->_try_multicall($msgs, $timeout, $method);
if($results !== false)
{
// Either the system.multicall succeeded, or the send
// failed (e.g. due to HTTP timeout). In either case,
// we're done for now.
return $results;
} }
else
{
// system.multicall unsupported by server,
// don't try it next time...
$this->no_multicall = true;
}
}
// system.multicall is unupported by server:
// Emulate multicall via multiple requests
$results = array();
foreach($msgs as $msg)
{
$results[] =& $this->send($msg, $timeout, $method);
}
return $results;
}
// Attempt to boxcar $msgs via system.multicall.
function _try_multicall($msgs, $timeout, $method)
{
// Construct multicall message
$calls = array();
foreach($msgs as $msg)
{
$call['methodName'] =& new xmlrpcval($msg->method(),'string');
$numParams = $msg->getNumParams();
$params = array();
for($i = 0; $i < $numParams; $i++)
{
$params[$i] = $msg->getParam($i);
}
$call['params'] =& new xmlrpcval($params, 'array');
$calls[] =& new xmlrpcval($call, 'struct');
}
$multicall =& new xmlrpcmsg('system.multicall');
$multicall->addParam(new xmlrpcval($calls, 'array'));
// Attempt RPC call
$result =& $this->send($multicall, $timeout, $method);
//if(!is_object($result))
//{
// return ($result || 0); // transport failed
//}
if($result->faultCode() != 0)
{
return false; // system.multicall failed
}
// Unpack responses.
$rets = $result->value();
if($rets->kindOf() != 'array')
{
return false; // bad return type from system.multicall
}
$numRets = $rets->arraysize();
if($numRets != count($msgs))
{
return false; // wrong number of return values.
}
$response = array();
for($i = 0; $i < $numRets; $i++)
{
$val = $rets->arraymem($i);
switch($val->kindOf())
{
case 'array':
if($val->arraysize() != 1)
{
return false; // Bad value
}
// Normal return value
$response[$i] =& new xmlrpcresp($val->arraymem(0));
break;
case 'struct':
$code = $val->structmem('faultCode');
if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
{
return false;
}
$str = $val->structmem('faultString');
if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
{
return false;
}
$response[$i] =& new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
break;
default:
return false;
}
}
return $response;
}
} // end class xmlrpc_client
?> ?>