📄 xmlrpc.inc
字号:
// error response
$this->errno = $fcode;
$this->errstr = $fstr;
//$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
}
/*elseif(!is_object($val) || !is_a($val, 'xmlrpcval'))
{
// programmer error
error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
$this->val =& new xmlrpcval();
}*/
else
{
// successful response
$this->val = $val;
if ($valtyp == '')
{
// user did not declare type of response value: try to guess it
if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
{
$this->valtyp = 'xmlrpcvals';
}
else if (is_string($this->val))
{
$this->valtyp = 'xml';
}
else
{
$this->valtyp = 'phpvals';
}
}
else
{
// user declares type of resp value: believe him
$this->valtyp = $valtyp;
}
}
}
function faultCode()
{
return $this->errno;
}
function faultString()
{
return $this->errstr;
}
function value()
{
return $this->val;
}
/*
* Returns an array with the cookies received from the server.
* Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
* with attributes being e.g. 'expires', 'path', domain'.
* NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
* are still present in the array. It is up to the user-defined code to decide
* how to use the received cookies, and wheter they have to be sent back with the next
* request to the server (using xmlrpc_client::setCookie) or not
* @access public
*/
function cookies()
{
return $this->_cookies;
}
function serialize()
{
$result = "<methodResponse>\n";
if($this->errno)
{
// G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
// by xml-encoding non ascii chars
$result .= "<fault>\n" .
"<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
"</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding']) . "</string></value>\n</member>\n" .
"</struct>\n</value>\n</fault>";
}
else
{
if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
{
if (is_string($this->val) && $this->valtyp == 'xml')
{
$result .= "<params>\n<param>\n" .
$this->val .
"</param>\n</params>";
}
else
{
/// @todo try to build something serializable?
die('cannot serialize xmlrpcresp objects whose content is native php values');
}
}
else
{
$result .= "<params>\n<param>\n" .
$this->val->serialize() .
"</param>\n</params>";
}
}
$result .= "\n</methodResponse>";
return $result;
}
}
class xmlrpcmsg
{
var $payload;
var $methodname;
var $params=array();
var $debug=0;
var $content_type = 'text/xml';
function xmlrpcmsg($meth, $pars=0)
{
$this->methodname=$meth;
if(is_array($pars) && sizeof($pars)>0)
{
for($i=0; $i<sizeof($pars); $i++)
{
$this->addParam($pars[$i]);
}
}
}
function xml_header()
{
return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
}
function xml_footer()
{
return "</methodCall>\n";
}
function createPayload()
{
$this->payload=$this->xml_header();
$this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
// if(sizeof($this->params)) {
$this->payload.="<params>\n";
for($i=0; $i<sizeof($this->params); $i++)
{
$p=$this->params[$i];
$this->payload.="<param>\n" . $p->serialize() .
"</param>\n";
}
$this->payload.="</params>\n";
// }
$this->payload.=$this->xml_footer();
//$this->payload=str_replace("\n", "\r\n", $this->payload);
}
/*
* Gets/sets the xmlrpc method to be invoked
* @access public
*/
function method($meth='')
{
if($meth!='')
{
$this->methodname=$meth;
}
return $this->methodname;
}
function serialize()
{
$this->createPayload();
return $this->payload;
}
function addParam($par)
{
// add check: do not add to self params which are not xmlrpcvals
if(is_object($par) && is_a($par, 'xmlrpcval'))
{
$this->params[]=$par;
return true;
}
else
{
return false;
}
}
function getParam($i) { return $this->params[$i]; }
function getNumParams() { return sizeof($this->params); }
/*
* @access private
* @todo add 2nd & 3rd param to be paased to ParseResponse() ???
*/
function &parseResponseFile($fp)
{
$ipd='';
while($data=fread($fp, 32768))
{
$ipd.=$data;
}
//fclose($fp);
$r =& $this->parseResponse($ipd);
return $r;
}
/**
* Parses HTTP headers and separates them from data.
* @access private
*/
function &parseResponseHeaders(&$data, $headers_processed=false)
{
// Strip HTTP 1.1 100 Continue header if present
while(ereg('^HTTP/1\.1 1[0-9]{2} ', $data))
{
$pos = strpos($data, 'HTTP', 12);
// server sent a Continue header without any (valid) content following...
// give the client a chance to know it
if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
{
break;
}
$data = substr($data, $pos);
}
if(!ereg('^HTTP/[0-9.]+ 200 ', $data))
{
$errstr= substr($data, 0, strpos($data, "\n")-1);
error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr);
$r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
return $r;
}
$GLOBALS['_xh']['headers'] = array();
$GLOBALS['_xh']['cookies'] = array();
// be tolerant to usage of \n instead of \r\n to separate headers and data
// (even though it is not valid http)
$pos = strpos($data,"\r\n\r\n");
if($pos || is_int($pos))
{
$bd = $pos+4;
}
else
{
$pos = strpos($data,"\n\n");
if($pos || is_int($pos))
{
$bd = $pos+2;
}
else
{
// No separation between response headers and body: fault?
$bd = 0;
}
}
// be tolerant to line endings, and extra empty lines
$ar = split("\r?\n", trim(substr($data, 0, $pos)));
while(list(,$line) = @each($ar))
{
// take care of multi-line headers and cookies
$arr = explode(':',$line,2);
if(count($arr) > 1)
{
$header_name = strtolower(trim($arr[0]));
/// @todo some other headers (the ones that allow a CSV list of values)
/// do allow many values to be passed using multiple header lines.
/// We should add content to $GLOBALS['_xh']['headers'][$header_name]
/// instead of replacing it for those...
if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
{
if ($header_name == 'set-cookie2')
{
// version 2 cookies:
// there could be many cookies on one line, comma separated
$cookies = explode(',', $arr[1]);
}
else
{
$cookies = array($arr[1]);
}
foreach ($cookies as $cookie)
{
// glue together all received cookies, using a comma to separate them
// (same as php does with getallheaders())
if (isset($GLOBALS['_xh']['headers'][$header_name]))
$GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
else
$GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
// parse cookie attributes, in case user wants to coorectly honour then
// feature creep: only allow rfc-compliant cookie attributes?
$cookie = explode(';', $cookie);
foreach ($cookie as $pos => $val)
{
$val = explode('=', $val, 2);
$tag = trim($val[0]);
$val = trim(@$val[1]);
/// @todo with version 1 cookies, we should strip leading and trailing " chars
if ($pos == 0)
{
$cookiename = $tag;
$GLOBALS['_xh']['cookies'][$tag] = array();
$GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
}
else
{
$GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
}
}
}
}
else
{
$GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
}
}
elseif(isset($header_name))
{
/// @todo version1 cookies might span multiple lines, thus breaking the parsing above
$GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
}
}
// rebuild full cookie set
/*if (isset($GLOBALS['_xh']['headers']['set-cookie']))
{
$cookies = array();
$received = explode(';', $GLOBALS['_xh']['headers']['set-cookie']);
foreach($received as $cookie)
{
list($name, $value) = explode('=', $cookie);
$name = trim($name);
$value = trim($value);
// these values are in fact attributes
if ($name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment')
{
$cookies[$name] = $value;
}
}
}*/
$data = substr($data, $bd);
if($this->debug && count($GLOBALS['_xh']['headers']))
{
print '<PRE>';
foreach($GLOBALS['_xh']['headers'] as $header => $value)
{
print "HEADER: $header: $value\n";
}
foreach($GLOBALS['_xh']['cookies'] as $header => $value)
{
print "COOKIE: $header={$value['value']}\n";
}
print "</PRE>\n";
}
// if CURL was used for the call, http headers have been processed,
// and dechunking + reinflating have been carried out
if(!$headers_processed)
{
// Decode chunked encoding sent by http 1.1 servers
if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
{
if(!$data = decode_chunked($data))
{
error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server');
$r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
return $r;
}
}
// Decode gzip-compressed stuff
// code shamelessly inspired from nusoap library by Dietrich Ayala
if(isset($GLOBALS['_xh']['headers']['content-encoding']))
{
if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
{
// if decoding works, use it. else assume data wasn't gzencoded
if(function_exists('gzinflate'))
{
if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzinflate($data))
{
$data = $degzdata;
if($this->debug)
print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
}
elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
{
$data = $degzdata;
if($this->debug)
print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
}
else
{
error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server');
$r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
return $r;
}
}
else
{
error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
$r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
return $r;
}
}
}
} // end of 'if needed, de-chunk, re-inflate response'
// real stupid hack to avoid PHP 4 complaining about returning NULL by ref
$r = null;
$r =& $r;
return $r;
}
/*
* @param string $data the xmlrpc response, eventually including http headers
* @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and conseuqent decoding
* @param string $return_type decides return type, i.e. content of response->value(). Either '
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -