X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fxmlrpcs.inc;h=6dd64a51fef3bcb4f2d9562c6ba7f619bfc98543;hb=f45909a92b268970729c121269af62d71eb7cd47;hp=9e504b8e09de3eb36c5757162a9139f5cef07e48;hpb=fe0fb0e92d6bcd85d153059f593d387c286f9f76;p=plcapi.git diff --git a/lib/xmlrpcs.inc b/lib/xmlrpcs.inc index 9e504b8..6dd64a5 100644 --- a/lib/xmlrpcs.inc +++ b/lib/xmlrpcs.inc @@ -1,7 +1,6 @@ -// $Id: xmlrpcs.inc,v 1.71 2008/10/29 23:41:28 ggiunta Exp $ // Copyright (c) 1999,2000,2002 Edd Dumbill. // All rights reserved. @@ -365,6 +364,7 @@ $GLOBALS['_xmlrpcs_occurred_errors'] = ''; $GLOBALS['_xmlrpcs_prev_ehandler'] = ''; + /** * Error handler used to track errors that occur during server-side execution of PHP code. * This allows to report back to the client whether an internal error has occurred or not @@ -381,7 +381,7 @@ return; //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING) - if($errcode != 2048) // do not use E_STRICT by name, since on PHP 4 it will not be defined + if($errcode != E_STRICT) { $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n"; } @@ -420,7 +420,7 @@ /** * Add a string to the debug info that can be later seralized by the server * as part of the response message. - * Note that for best compatbility, the debug string should be encoded using + * Note that for best compatibility, the debug string should be encoded using * the $GLOBALS['xmlrpc_internalencoding'] character set. * @param string $m * @access public @@ -432,17 +432,34 @@ class xmlrpc_server { - /// array defining php functions exposed as xmlrpc methods by this server + /** + * Array defining php functions exposed as xmlrpc methods by this server + * @access private + */ var $dmap=array(); /** - * Defines how functions in dmap will be invokde: either using an xmlrpc msg object + * Defines how functions in dmap will be invoked: either using an xmlrpc msg object * or plain php values. * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals' */ var $functions_parameters_type='xmlrpcvals'; - /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3 + /** + * Option used for fine-tuning the encoding the php values returned from + * functions registered in the dispatch map when the functions_parameters_types + * member is set to 'phpvals' + * @see php_xmlrpc_encode for a list of values + */ + var $phpvals_encoding_options = array( 'auto_dates' ); + /// controls whether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3 var $debug = 1; /** + * Controls behaviour of server when invoked user function throws an exception: + * 0 = catch it and return an 'internal error' xmlrpc response (default) + * 1 = catch it and return an xmlrpc response with the error corresponding to the exception + * 2 = allow the exception to float to the upper layers + */ + var $exception_handling = 0; + /** * When set to true, it will enable HTTP compression of the response, in case * the client has declared its support for compression in the request. */ @@ -466,14 +483,19 @@ * NB: pretty dangerous if you accept every charset and do not have mbstring enabled) */ var $response_charset_encoding = ''; - /// storage for internal debug info + /** + * Storage for internal debug info + * @access private + */ var $debug_info = ''; - /// extra data passed at runtime to method handling functions. Used only by EPI layer + /** + * Extra data passed at runtime to method handling functions. Used only by EPI layer + */ var $user_data = null; /** - * @param array $dispmap the dispatch map withd efinition of exposed services - * @param boolean $servicenow set to false to prevent the server from runnung upon construction + * @param array $dispmap the dispatch map with definition of exposed services + * @param boolean $servicenow set to false to prevent the server from running upon construction */ function xmlrpc_server($dispMap=null, $serviceNow=true) { @@ -518,7 +540,7 @@ * with the standard processing of the php function exposed as method. In * particular, triggering an USER_ERROR level error will not halt script * execution anymore, but just end up logged in the xmlrpc response) - * Note that info added at elevel 2 and 3 will be base64 encoded + * Note that info added at level 2 and 3 will be base64 encoded * @access public */ function setDebug($in) @@ -565,15 +587,7 @@ if ($data === null) { // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA - $ver = phpversion(); - if ($ver[0] >= 5) - { - $data = file_get_contents('php://input'); - } - else - { - $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; - } + $data = file_get_contents('php://input'); } $raw_data = $data; @@ -659,7 +673,7 @@ } else { - error_log('XML-RPC: xmlrpc_server::service: http headers already sent before response is fully generated. Check for php warning or error messages'); + error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages'); } print $payload; @@ -689,7 +703,7 @@ } if ($sigdoc) { - $this->dmap[$methodname]['signature_docs'] = $sigdoc; + $this->dmap[$methodname]['signature_docs'] = $sigdoc; } } @@ -697,6 +711,7 @@ * Verify type and number of parameters received against a list of known signatures * @param array $in array of either xmlrpcval objects or xmlrpc type definitions * @param array $sig array of known signatures to match against + * @return array * @access private */ function verifySignature($in, $sig) @@ -762,15 +777,16 @@ /** * Parse http headers received along with xmlrpc request. If needed, inflate request - * @return null on success or an xmlrpcresp + * @return mixed null on success or an xmlrpcresp * @access private */ function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression) { - // Play nice to PHP 4.0.x: superglobals were not yet invented... - if(!isset($_SERVER)) + // check if $_SERVER is populated: it might have been disabled via ini file + // (this is true even when in CLI mode) + if (count($_SERVER) == 0) { - $_SERVER = $GLOBALS['HTTP_SERVER_VARS']; + error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated'); } if($this->debug > 1) @@ -912,26 +928,28 @@ $GLOBALS['_xh']['rt']=''; // decompose incoming XML into request structure + if ($req_encoding != '') { - if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) - // the following code might be better for mb_string enabled installs, but - // makes the lib about 200% slower... - //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) - { - error_log('XML-RPC: xmlrpc_server::parseRequest: invalid charset encoding of received request: '.$req_encoding); - $req_encoding = $GLOBALS['xmlrpc_defencoding']; - } - /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue, - // the encoding is not UTF8 and there are non-ascii chars in the text... - /// @todo use an ampty string for php 5 ??? - $parser = xml_parser_create($req_encoding); - } - else - { - $parser = xml_parser_create(); + // Since parsing will fail if charset is not specified in the xml prologue, + // the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that... + // The following code might be better for mb_string enabled installs, but + // makes the lib about 200% slower... + //if (!is_valid_charset($req_encoding, array('UTF-8'))) + if (!in_array($req_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($data)) { + if ($req_encoding == 'ISO-8859-1') { + $data = utf8_encode($data); + } else { + if (extension_loaded('mbstring')) { + $data = mb_convert_encoding($data, 'UTF-8', $req_encoding); + } else { + error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $req_encoding); + } + } + } } + $parser = xml_parser_create(); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell // the xml parser to give us back data in the expected charset @@ -974,7 +992,11 @@ else { xml_parser_free($parser); - if ($this->functions_parameters_type != 'xmlrpcvals') + // small layering violation in favor of speed and memory usage: + // we should allow the 'execute' method handle this, but in the + // most common scenario (xmlrpcvals type server with some methods + // registered as phpvals) that would mean a useless encode+decode pass + if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals'))) { if($this->debug > 1) { @@ -1063,7 +1085,7 @@ // verify that function to be invoked is in fact callable if(!is_callable($func)) { - error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler is not callable"); + error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable"); return new xmlrpcresp( 0, $GLOBALS['xmlrpcerr']['server_error'], @@ -1077,71 +1099,91 @@ { $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler'); } - if (is_object($m)) + try { - if($sysCall) - { - $r = call_user_func($func, $this, $m); - } - else - { - $r = call_user_func($func, $m); - } - if (!is_a($r, 'xmlrpcresp')) + // Allow mixed-convention servers + if (is_object($m)) { - error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object"); - if (is_a($r, 'xmlrpcval')) + if($sysCall) { - $r = new xmlrpcresp($r); + $r = call_user_func($func, $this, $m); } else { - $r = new xmlrpcresp( - 0, - $GLOBALS['xmlrpcerr']['server_error'], - $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object" - ); + $r = call_user_func($func, $m); + } + if (!is_a($r, 'xmlrpcresp')) + { + error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object"); + if (is_a($r, 'xmlrpcval')) + { + $r = new xmlrpcresp($r); + } + else + { + $r = new xmlrpcresp( + 0, + $GLOBALS['xmlrpcerr']['server_error'], + $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object" + ); + } } - } - } - else - { - // call a 'plain php' function - if($sysCall) - { - array_unshift($params, $this); - $r = call_user_func_array($func, $params); } else { - // 3rd API convention for method-handling functions: EPI-style - if ($this->functions_parameters_type == 'epivals') + // call a 'plain php' function + if($sysCall) { - $r = call_user_func_array($func, array($methName, $params, $this->user_data)); - // mimic EPI behaviour: if we get an array that looks like an error, make it - // an eror response - if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r)) + array_unshift($params, $this); + $r = call_user_func_array($func, $params); + } + else + { + // 3rd API convention for method-handling functions: EPI-style + if ($this->functions_parameters_type == 'epivals') { - $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']); + $r = call_user_func_array($func, array($methName, $params, $this->user_data)); + // mimic EPI behaviour: if we get an array that looks like an error, make it + // an eror response + if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r)) + { + $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']); + } + else + { + // functions using EPI api should NOT return resp objects, + // so make sure we encode the return type correctly + $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api'))); + } } else { - // functions using EPI api should NOT return resp objects, - // so make sure we encode the return type correctly - $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api'))); + $r = call_user_func_array($func, $params); } } - else + // the return type can be either an xmlrpcresp object or a plain php value... + if (!is_a($r, 'xmlrpcresp')) { - $r = call_user_func_array($func, $params); + // what should we assume here about automatic encoding of datetimes + // and php classes instances??? + $r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options)); } } - // the return type can be either an xmlrpcresp object or a plain php value... - if (!is_a($r, 'xmlrpcresp')) + } + catch(Exception $e) + { + // (barring errors in the lib) an uncatched exception happened + // in the called function, we wrap it in a proper error-response + switch($this->exception_handling) { - // what should we assume here about automatic encoding of datetimes - // and php classes instances??? - $r = new xmlrpcresp(php_xmlrpc_encode($r, array('auto_dates'))); + case 2: + throw $e; + break; + case 1: + $r = new xmlrpcresp(0, $e->getCode(), $e->getMessage()); + break; + default: + $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']); } } if($this->debug > 2) @@ -1162,7 +1204,7 @@ /** * add a string to the 'internal debug message' (separate from 'user debug message') - * @param string $strings + * @param string $string * @access private */ function debugmsg($string)