From 605938e5d037ba81982bfdd94e14000047901593 Mon Sep 17 00:00:00 2001 From: Bill Marquette Date: Mon, 4 Jul 2005 23:57:31 +0000 Subject: Import PEAR XMLRPC 1.3.1 - this includes the security fix for: http://www.gulftech.org/?node=research&article_id=00088-07022005 --- etc/inc/xmlrpc_client.inc | 290 ++++++++++++++++++++++++++++++---------------- etc/inc/xmlrpc_server.inc | 193 +++++++++++++++++++++++++----- 2 files changed, 356 insertions(+), 127 deletions(-) (limited to 'etc') diff --git a/etc/inc/xmlrpc_client.inc b/etc/inc/xmlrpc_client.inc index 10ed4ac..f33c8cd 100644 --- a/etc/inc/xmlrpc_client.inc +++ b/etc/inc/xmlrpc_client.inc @@ -1,4 +1,5 @@ * @author Martin Jansen * @author Daniel Convissor - * @copyright 1999-2001 Edd Dumbill + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group * @version CVS: $Id$ * @link http://pear.php.net/package/XML_RPC */ if (!function_exists('xml_parser_create')) { - // Win 32 fix. From: "Leo West" - if ($WINDIR) { - dl('php_xml.dll'); - } else { - dl('xml.so'); - } + PEAR::loadExtension('xml'); } /**#@+ * Error constants */ -define('XML_RPC_ERROR_INVALID_TYPE', 101); -define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102); -define('XML_RPC_ERROR_CONNECTION_FAILED', 103); +/** + * Parameter values don't match parameter types + */ +define('XML_RPC_ERROR_INVALID_TYPE', 101); +/** + * Parameter declared to be numeric but the values are not + */ +define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102); +/** + * Communication error + */ +define('XML_RPC_ERROR_CONNECTION_FAILED', 103); +/** + * The array or struct has already been started + */ define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104); +/** + * Incorrect parameters submitted + */ +define('XML_RPC_ERROR_INCORRECT_PARAMS', 105); +/** + * Programming error by developer + */ +define('XML_RPC_ERROR_PROGRAMMING', 106); /**#@-*/ @@ -138,6 +154,7 @@ $GLOBALS['XML_RPC_err'] = array( 'incorrect_params' => 3, 'introspect_unknown' => 4, 'http_error' => 5, + 'not_response_object' => 6, ); /** @@ -150,6 +167,7 @@ $GLOBALS['XML_RPC_str'] = array( 'incorrect_params' => 'Incorrect parameters passed to method', 'introspect_unknown' => 'Can\'t introspect: method unknown', 'http_error' => 'Didn\'t receive 200 OK from remote server.', + 'not_response_object' => 'The requested method didn\'t return an XML_RPC_Response object.', ); @@ -220,7 +238,7 @@ function XML_RPC_se($parser_resource, $name, $attrs) break; case 'NAME': - $XML_RPC_xh[$parser]['st'] .= "'"; + $XML_RPC_xh[$parser]['st'] .= '"'; $XML_RPC_xh[$parser]['ac'] = ''; break; @@ -303,7 +321,7 @@ function XML_RPC_ee($parser_resource, $name) break; case 'NAME': - $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . "' => "; + $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . '" => '; break; case 'BOOLEAN': @@ -328,8 +346,8 @@ function XML_RPC_ee($parser_resource, $name) // we use double quotes rather than single so backslashification works OK $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"'; } elseif ($XML_RPC_xh[$parser]['qt'] == 2) { - $XML_RPC_xh[$parser]['st'] .= "base64_decode('" - . $XML_RPC_xh[$parser]['ac'] . "')"; + $XML_RPC_xh[$parser]['st'] .= 'base64_decode("' + . $XML_RPC_xh[$parser]['ac'] . '")'; } elseif ($name == 'BOOLEAN') { $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac']; } else { @@ -384,20 +402,10 @@ function XML_RPC_ee($parser_resource, $name) break; case 'METHODNAME': + case 'RPCMETHODNAME': $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '', $XML_RPC_xh[$parser]['ac']); break; - - case 'BOOLEAN': - // special case here: we translate boolean 1 or 0 into PHP - // constants true or false - if ($XML_RPC_xh[$parser]['ac'] == '1') { - $XML_RPC_xh[$parser]['ac'] = 'true'; - } else { - $XML_RPC_xh[$parser]['ac'] = 'false'; - } - - $XML_RPC_xh[$parser]['vt'] = strtolower($name); } // if it's a valid type name, set the type @@ -440,17 +448,16 @@ function XML_RPC_cd($parser_resource, $data) } /** - * Base class - * - * This class provides common functions for all of the XML_RPC classes. + * The common methods and properties for all of the XML_RPC classes * * @category Web Services * @package XML_RPC * @author Edd Dumbill * @author Stig Bakken * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill - * @version Release: @package_version@ + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 * @link http://pear.php.net/package/XML_RPC */ class XML_RPC_Base { @@ -486,7 +493,7 @@ class XML_RPC_Base { } /** - * + * The methods and properties for submitting XML RPC requests * * @category Web Services * @package XML_RPC @@ -494,8 +501,8 @@ class XML_RPC_Base { * @author Stig Bakken * @author Martin Jansen * @author Daniel Convissor - * @copyright 1999-2001 Edd Dumbill - * @version Release: @package_version@ + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 * @link http://pear.php.net/package/XML_RPC */ class XML_RPC_Client extends XML_RPC_Base { @@ -594,6 +601,12 @@ class XML_RPC_Client extends XML_RPC_Base { */ var $debug = 0; + /** + * The HTTP headers for the current request. + * @var string + */ + var $headers = ''; + /** * Sets the object's properties @@ -730,6 +743,12 @@ class XML_RPC_Client extends XML_RPC_Base { */ function send($msg, $timeout = 0) { + if (strtolower(get_class($msg)) != 'xml_rpc_message') { + $this->errstr = 'send()\'s $msg parameter must be an' + . ' XML_RPC_Message object.'; + $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING); + return 0; + } $msg->debug = $this->debug; return $this->sendPayloadHTTP10($msg, $this->server, $this->port, $timeout, $this->username, @@ -753,6 +772,7 @@ class XML_RPC_Client extends XML_RPC_Base { * @return object an XML_RPC_Response object. 0 is returned if any * problems happen. * + * @access protected * @see XML_RPC_Client::send() */ function sendPayloadHTTP10($msg, $server, $port, $timeout = 0, @@ -808,60 +828,99 @@ class XML_RPC_Client extends XML_RPC_Base { return 0; } + if ($timeout) { + stream_set_timeout($fp, $timeout); + } + + // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly + if ($username != $this->username) { + $this->setCredentials($username, $password); + } + // Only create the payload if it was not created previously if (empty($msg->payload)) { $msg->createPayload(); } + $this->createHeaders($msg); - // thanks to Grant Rauscher for this - $credentials = ''; - if ($username != '') { - $credentials = 'Authorization: Basic ' . - base64_encode($username . ':' . $password) . "\r\n"; + $op = $this->headers . "\r\n\r\n"; + $op .= $msg->payload; + + if (!fputs($fp, $op, strlen($op))) { + $this->errstr = 'Write error'; + return 0; } + $resp = $msg->parseResponseFile($fp); + + $meta = stream_get_meta_data($fp); + if ($meta['timed_out']) { + fclose($fp); + $this->errstr = 'RPC server did not send response before timeout.'; + $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED); + return 0; + } + + fclose($fp); + return $resp; + } + /** + * Determines the HTTP headers and puts it in the $headers property + * + * @param object $msg the XML_RPC_Message object + * + * @return boolean TRUE if okay, FALSE if the message payload isn't set. + * + * @access protected + */ + function createHeaders($msg) + { + if (empty($msg->payload)) { + return false; + } if ($this->proxy) { - $op = 'POST ' . $this->protocol . $server; + $this->headers = 'POST ' . $this->protocol . $this->server; if ($this->proxy_port) { - $op .= ':' . $this->port; + $this->headers .= ':' . $this->port; } } else { - $op = 'POST '; + $this->headers = 'POST '; } - - $op .= $this->path. " HTTP/1.0\r\n" . - "User-Agent: PEAR XML_RPC\r\n" . - 'Host: ' . $server . "\r\n"; - if ($this->proxy && $this->proxy_user != '') { - $op .= 'Proxy-Authorization: Basic ' . - base64_encode($this->proxy_user . ':' . $this->proxy_pass) . - "\r\n"; + $this->headers .= $this->path. " HTTP/1.0\r\n"; + + $this->headers .= "User-Agent: PEAR XML_RPC\r\n"; + $this->headers .= 'Host: ' . $this->server . "\r\n"; + + if ($this->proxy && $this->proxy_user) { + $this->headers .= 'Proxy-Authorization: Basic ' + . base64_encode("$this->proxy_user:$this->proxy_pass") + . "\r\n"; } - $op .= $credentials . - "Content-Type: text/xml\r\n" . - 'Content-Length: ' . strlen($msg->payload) . "\r\n\r\n" . - $msg->payload; - if (!fputs($fp, $op, strlen($op))) { - $this->errstr = 'Write error'; - return 0; + // thanks to Grant Rauscher for this + if ($this->username) { + $this->headers .= 'Authorization: Basic ' + . base64_encode("$this->username:$this->password") + . "\r\n"; } - $resp = $msg->parseResponseFile($fp); - fclose($fp); - return $resp; + + $this->headers .= "Content-Type: text/xml\r\n"; + $this->headers .= 'Content-Length: ' . strlen($msg->payload); + return true; } } /** - * + * The methods and properties for interpreting responses to XML RPC requests * * @category Web Services * @package XML_RPC * @author Edd Dumbill * @author Stig Bakken * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill - * @version Release: @package_version@ + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 * @link http://pear.php.net/package/XML_RPC */ class XML_RPC_Response extends XML_RPC_Base @@ -943,7 +1002,7 @@ class XML_RPC_Response extends XML_RPC_Base } /** - * + * The methods and properties for composing XML RPC messages * * @category Web Services * @package XML_RPC @@ -951,8 +1010,8 @@ class XML_RPC_Response extends XML_RPC_Base * @author Stig Bakken * @author Martin Jansen * @author Daniel Convissor - * @copyright 1999-2001 Edd Dumbill - * @version Release: @package_version@ + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 * @link http://pear.php.net/package/XML_RPC */ class XML_RPC_Message extends XML_RPC_Base @@ -1082,11 +1141,27 @@ class XML_RPC_Message extends XML_RPC_Base } /** - * @return void + * Obtains an XML_RPC_Value object for the given parameter + * + * @param int $i the index number of the parameter to obtain + * + * @return object the XML_RPC_Value object. + * If the parameter doesn't exist, an XML_RPC_Response object. + * + * @since Returns XML_RPC_Response object on error since Release 1.3.0 */ function getParam($i) { - return $this->params[$i]; + global $XML_RPC_err, $XML_RPC_str; + + if (isset($this->params[$i])) { + return $this->params[$i]; + } else { + $this->raiseError('The submitted request did not contain this parameter', + XML_RPC_ERROR_INCORRECT_PARAMS); + return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'], + $XML_RPC_str['incorrect_params']); + } } /** @@ -1191,10 +1266,12 @@ class XML_RPC_Message extends XML_RPC_Base print "\n---END---\n"; } - // see if we got an HTTP 200 OK, else bomb - // but only do this if we're using the HTTP protocol. + // See if response is a 200 or a 100 then a 200, else raise error. + // But only do this if we're using the HTTP protocol. if (ereg('^HTTP', $data) && - !ereg('^HTTP/[0-9\.]+ 200 ', $data)) { + !ereg('^HTTP/[0-9\.]+ 200 ', $data) && + !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Za-z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data)) + { $errstr = substr($data, 0, strpos($data, "\n") - 1); error_log('HTTP error, got response: ' . $errstr); $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'], @@ -1206,7 +1283,7 @@ class XML_RPC_Message extends XML_RPC_Base // gotta get rid of headers here - if ((!$hdrfnd) && ($brpos = strpos($data,"\r\n\r\n"))) { + if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) { $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos); $data = substr($data, $brpos + 4); $hdrfnd = 1; @@ -1221,7 +1298,7 @@ class XML_RPC_Message extends XML_RPC_Base if (!xml_parse($parser_resource, $data, sizeof($data))) { // thanks to Peter Kocks - if ((xml_get_current_line_number($parser_resource)) == 1) { + if (xml_get_current_line_number($parser_resource) == 1) { $errstr = 'XML error at line 1, check URL'; } else { $errstr = sprintf('XML error: %s at line %d', @@ -1263,15 +1340,16 @@ class XML_RPC_Message extends XML_RPC_Base } /** - * + * The methods and properties that represent data in XML RPC format * * @category Web Services * @package XML_RPC * @author Edd Dumbill * @author Stig Bakken * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill - * @version Release: @package_version@ + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 * @link http://pear.php.net/package/XML_RPC */ class XML_RPC_Value extends XML_RPC_Base @@ -1387,11 +1465,11 @@ class XML_RPC_Value extends XML_RPC_Base function dump($ar) { reset($ar); - while (list($key, $val) = each($ar)) { - echo "$key => $val
"; + foreach ($ar as $key => $val) { + echo "$key => $val
"; if ($key == 'array') { - while (list($key2, $val2) = each($val)) { - echo "-- $key2 => $val2
"; + foreach ($val as $key2 => $val2) { + echo "-- $key2 => $val2
"; } } } @@ -1434,7 +1512,7 @@ class XML_RPC_Value extends XML_RPC_Base // struct $rs .= "\n"; reset($val); - while (list($key2, $val2) = each($val)) { + foreach ($val as $key2 => $val2) { $rs .= "${key2}\n"; $rs .= $this->serializeval($val2); $rs .= "\n"; @@ -1460,7 +1538,6 @@ class XML_RPC_Value extends XML_RPC_Base $rs .= "<${typ}>" . ($val ? '1' : '0') . ""; break; case $XML_RPC_String: - if(is_array($val)) $val = 'array'; $rs .= "<${typ}>" . htmlspecialchars($val). ""; break; default: @@ -1520,12 +1597,13 @@ class XML_RPC_Value extends XML_RPC_Base /** * @return mixed the current value */ - function getval() { + function getval() + { // UNSTABLE global $XML_RPC_BOOLEAN, $XML_RPC_Base64; reset($this->me); - list($a, $b) = each($this->me); + $b = current($this->me); // contributed by I Sofer, 2001-03-24 // add support for nested arrays to scalarval @@ -1560,8 +1638,7 @@ class XML_RPC_Value extends XML_RPC_Base { global $XML_RPC_Boolean, $XML_RPC_Base64; reset($this->me); - list($a, $b) = each($this->me); - return $b; + return current($this->me); } /** @@ -1571,7 +1648,7 @@ class XML_RPC_Value extends XML_RPC_Base { global $XML_RPC_I4, $XML_RPC_Int; reset($this->me); - list($a, $b) = each($this->me); + $a = key($this->me); if ($a == $XML_RPC_I4) { $a = $XML_RPC_Int; } @@ -1595,6 +1672,21 @@ class XML_RPC_Value extends XML_RPC_Base list($a, $b) = each($this->me); return sizeof($b); } + + /** + * Determines if the item submitted is an XML_RPC_Value object + * + * @param mixed $val the variable to be evaluated + * + * @return bool TRUE if the item is an XML_RPC_Value object + * + * @static + * @since Method available since Release 1.3.0 + */ + function isValue($val) + { + return (strtolower(get_class($val)) == 'xml_rpc_value'); + } } /** @@ -1610,7 +1702,8 @@ class XML_RPC_Value extends XML_RPC_Base * * @return string the formatted date */ -function XML_RPC_iso8601_encode($timet, $utc = 0) { +function XML_RPC_iso8601_encode($timet, $utc = 0) +{ if (!$utc) { $t = strftime('%Y%m%dT%H:%M:%S', $timet); } else { @@ -1638,7 +1731,8 @@ function XML_RPC_iso8601_encode($timet, $utc = 0) { * * @return int the unix timestamp of the date submitted */ -function XML_RPC_iso8601_decode($idate, $utc = 0) { +function XML_RPC_iso8601_decode($idate, $utc = 0) +{ $t = 0; if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) { if ($utc) { @@ -1651,12 +1745,11 @@ function XML_RPC_iso8601_decode($idate, $utc = 0) { } /** - * Takes a message in PHP XML_RPC object format and translates it into - * native PHP types + * Converts an XML_RPC_Value object into native PHP types * - * @return mixed + * @param object $XML_RPC_val the XML_RPC_Value object to decode * - * @author Dan Libby + * @return mixed the PHP values */ function XML_RPC_decode($XML_RPC_val) { @@ -1684,17 +1777,16 @@ function XML_RPC_decode($XML_RPC_val) } /** - * Takes native php types and encodes them into XML_RPC PHP object format - * - * Feature creep -- could support more types via optional type argument. + * Converts native PHP types into an XML_RPC_Value object * - * @return string + * @param mixed $php_val the PHP value or variable you want encoded * - * @author Dan Libby + * @return object the XML_RPC_Value object */ -function XML_RPC_encode($php_val) { +function XML_RPC_encode($php_val) +{ global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String, - $XML_RPC_Array, $XML_RPC_Struct; + $XML_RPC_Array, $XML_RPC_Struct; $type = gettype($php_val); $XML_RPC_val = new XML_RPC_Value; diff --git a/etc/inc/xmlrpc_server.inc b/etc/inc/xmlrpc_server.inc index 89ebcae..f7c398f 100644 --- a/etc/inc/xmlrpc_server.inc +++ b/etc/inc/xmlrpc_server.inc @@ -1,9 +1,10 @@ * @author Stig Bakken * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group * @version CVS: $Id$ * @link http://pear.php.net/package/XML_RPC */ @@ -39,11 +41,12 @@ /** * Pull in the XML_RPC class */ -require_once 'xmlrpc_client.inc'; +require_once 'XML/RPC.php'; /** - * listMethods: either a string, or nothing + * signature for system.listMethods: return = array, + * parameters = a string or nothing * @global array $GLOBALS['XML_RPC_Server_listMethods_sig'] */ $GLOBALS['XML_RPC_Server_listMethods_sig'] = array( @@ -54,12 +57,15 @@ $GLOBALS['XML_RPC_Server_listMethods_sig'] = array( ); /** + * docstring for system.listMethods * @global string $GLOBALS['XML_RPC_Server_listMethods_doc'] */ $GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the' . ' methods that the XML-RPC server knows how to dispatch'; /** + * signature for system.methodSignature: return = array, + * parameters = string * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig'] */ $GLOBALS['XML_RPC_Server_methodSignature_sig'] = array( @@ -69,6 +75,7 @@ $GLOBALS['XML_RPC_Server_methodSignature_sig'] = array( ); /** + * docstring for system.methodSignature * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc'] */ $GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known' @@ -77,6 +84,8 @@ $GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known' . ' array to detect missing signature)'; /** + * signature for system.methodHelp: return = string, + * parameters = string * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig'] */ $GLOBALS['XML_RPC_Server_methodHelp_sig'] = array( @@ -86,12 +95,14 @@ $GLOBALS['XML_RPC_Server_methodHelp_sig'] = array( ); /** + * docstring for methodHelp * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc'] */ $GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined' . ' for the method passed, otherwise returns an empty string'; /** + * dispatch map for the automatically declared XML-RPC methods. * @global array $GLOBALS['XML_RPC_Server_dmap'] */ $GLOBALS['XML_RPC_Server_dmap'] = array( @@ -128,13 +139,11 @@ function XML_RPC_Server_listMethods($server, $m) global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; $v = new XML_RPC_Value(); - $dmap = $server->dmap; $outAr = array(); - for (reset($dmap); list($key, $val) = each($dmap); ) { + foreach ($server->dmap as $key => $val) { $outAr[] = new XML_RPC_Value($key, 'string'); } - $dmap = $XML_RPC_Server_dmap; - for (reset($dmap); list($key, $val) = each($dmap); ) { + foreach ($XML_RPC_Server_dmap as $key => $val) { $outAr[] = new XML_RPC_Value($key, 'string'); } $v->addArray($outAr); @@ -232,24 +241,94 @@ function XML_RPC_Server_debugmsg($m) /** + * A server for receiving and replying to XML RPC requests * + * + * $server = new XML_RPC_Server( + * array( + * 'isan8' => + * array( + * 'function' => 'is_8', + * 'signature' => + * array( + * array('boolean', 'int'), + * array('boolean', 'int', 'boolean'), + * array('boolean', 'string'), + * array('boolean', 'string', 'boolean'), + * ), + * 'docstring' => 'Is the value an 8?' + * ), + * ), + * 1, + * 0 + * ); + * * * @category Web Services * @package XML_RPC * @author Edd Dumbill * @author Stig Bakken * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill - * @version Release: @package_version@ + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 * @link http://pear.php.net/package/XML_RPC */ class XML_RPC_Server { + /** + * The dispatch map, listing the methods this server provides. + * @var array + */ var $dmap = array(); + + /** + * The present response's encoding + * @var string + * @see XML_RPC_Message::getEncoding() + */ var $encoding = ''; + + /** + * Debug mode (0 = off, 1 = on) + * @var integer + */ var $debug = 0; /** + * The response's HTTP headers + * @var string + */ + var $server_headers = ''; + + /** + * The response's XML payload + * @var string + */ + var $server_payload = ''; + + + /** + * Constructor for the XML_RPC_Server class + * + * @param array $dispMap the dispatch map. An associative array + * explaining each function. The keys of the main + * array are the procedure names used by the + * clients. The value is another associative array + * that contains up to three elements: + * + The 'function' element's value is the name + * of the function or method that gets called. + * To define a class' method: 'class::method'. + * + The 'signature' element (optional) is an + * array describing the return values and + * parameters + * + The 'docstring' element (optional) is a + * string describing what the method does + * @param int $serviceNow should the HTTP response be sent now? + * (1 = yes, 0 = no) + * @param int $debug should debug output be displayed? + * (1 = yes, 0 = no) + * * @return void */ function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0) @@ -262,14 +341,13 @@ class XML_RPC_Server $this->debug = 0; } - // dispMap is a despatch array of methods - // mapped to function names and signatures - // if a method - // doesn't appear in the map then an unknown - // method error is generated $this->dmap = $dispMap; + if ($serviceNow) { $this->service(); + } else { + $this->createServerPayload(); + $this->createServerHeaders(); } } @@ -296,25 +374,60 @@ class XML_RPC_Server } /** - * Print out the result + * Sends the response * * The encoding and content-type are determined by * XML_RPC_Message::getEncoding() * * @return void * - * @see XML_RPC_Message::getEncoding() + * @uses XML_RPC_Server::createServerPayload(), + * XML_RPC_Server::createServerHeaders() */ function service() { + $this->createServerPayload(); + $this->createServerHeaders(); + header($this->server_headers); + print $this->server_payload; + } + + /** + * Generates the payload and puts it in the $server_payload property + * + * @return void + * + * @uses XML_RPC_Server::parseRequest(), XML_RPC_Server::$encoding, + * XML_RPC_Response::serialize(), XML_RPC_Server::serializeDebug() + */ + function createServerPayload() + { $r = $this->parseRequest(); - $payload = 'encoding . '"?>' . "\n" - . $this->serializeDebug() - . $r->serialize(); - header('Content-Length: ' . strlen($payload)); - header('Content-Type: text/xml; charset=' . $this->encoding); - print $payload; + $this->server_payload = 'encoding . '"?>' . "\n" + . $this->serializeDebug() + . $r->serialize(); + } + + /** + * Determines the HTTP headers and puts them in the $server_headers + * property + * + * @return boolean TRUE if okay, FALSE if $server_payload isn't set. + * + * @uses XML_RPC_Server::createServerPayload(), + * XML_RPC_Server::$server_headers + */ + function createServerHeaders() + { + if (!$this->server_payload) { + return false; + } + $this->server_headers = 'Content-Length: ' + . strlen($this->server_payload) . "\r\n" + . 'Content-Type: text/xml;' + . ' charset=' . $this->encoding; + return true; } /** @@ -336,7 +449,7 @@ class XML_RPC_Server $pt = $p->kindOf(); } // $n+1 as first type of sig is return type - if ($pt != strtolower($cursig[$n+1])) { + if ($pt != $cursig[$n+1]) { $itsOK = 0; $pno = $n+1; $wanted = $cursig[$n+1]; @@ -349,11 +462,30 @@ class XML_RPC_Server } } } - return array(0, "Wanted ${wanted}, got ${got} at param ${pno})"); + if (isset($wanted)) { + return array(0, "Wanted ${wanted}, got ${got} at param ${pno}"); + } else { + $allowed = array(); + foreach ($sig as $val) { + end($val); + $allowed[] = key($val); + } + $allowed = array_unique($allowed); + $last = count($allowed) - 1; + if ($last > 0) { + $allowed[$last] = 'or ' . $allowed[$last]; + } + return array(0, + 'Signature permits ' . implode(', ', $allowed) + . ' parameters but the request had ' + . $in->getNumParams()); + } } /** * @return object a new XML_RPC_Response object + * + * @uses XML_RPC_Message::getEncoding(), XML_RPC_Server::$encoding */ function parseRequest($data = '') { @@ -428,13 +560,17 @@ class XML_RPC_Server $sr = $this->verifySignature($m, $dmap[$methName]['signature'] ); } - if ( (!isset($dmap[$methName]['signature'])) || $sr[0]) { + if (!isset($dmap[$methName]['signature']) || $sr[0]) { // if no signature or correct signature if ($sysCall) { $r = call_user_func($dmap[$methName]['function'], $this, $m); } else { $r = call_user_func($dmap[$methName]['function'], $m); } + if (!is_a($r, 'XML_RPC_Response')) { + $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'], + $XML_RPC_str['not_response_object']); + } } else { $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'], $XML_RPC_str['incorrect_params'] @@ -456,7 +592,8 @@ class XML_RPC_Server * * Useful for debugging. */ - function echoInput() { + function echoInput() + { global $HTTP_RAW_POST_DATA; $r = new XML_RPC_Response(0); -- cgit v1.1