00001 <?php 00002 // by Edd Dumbill (C) 1999-2002 00003 // <edd@usefulinc.com> 00004 // $Original: xmlrpcs.inc,v 1.66 2006/09/17 21:25:06 ggiunta Exp $ 00005 // $Id: xmlrpcs.inc.php 1199 2007-09-07 07:03:41Z kimitake $ 00006 // $NucleusJP: xmlrpcs.inc.php,v 1.9.2.2 2007/09/07 07:04:24 kimitake Exp $ 00007 00008 // Copyright (c) 1999,2000,2002 Edd Dumbill. 00009 // All rights reserved. 00010 // 00011 // Redistribution and use in source and binary forms, with or without 00012 // modification, are permitted provided that the following conditions 00013 // are met: 00014 // 00015 // * Redistributions of source code must retain the above copyright 00016 // notice, this list of conditions and the following disclaimer. 00017 // 00018 // * Redistributions in binary form must reproduce the above 00019 // copyright notice, this list of conditions and the following 00020 // disclaimer in the documentation and/or other materials provided 00021 // with the distribution. 00022 // 00023 // * Neither the name of the "XML-RPC for PHP" nor the names of its 00024 // contributors may be used to endorse or promote products derived 00025 // from this software without specific prior written permission. 00026 // 00027 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00028 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00029 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00030 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00031 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00032 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00033 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00034 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00035 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 00036 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00037 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 00038 // OF THE POSSIBILITY OF SUCH DAMAGE. 00039 00040 // XML RPC Server class 00041 // requires: xmlrpc.inc 00042 00043 $GLOBALS['xmlrpcs_capabilities'] = array( 00044 // xmlrpc spec: always supported 00045 'xmlrpc' => new xmlrpcval(array( 00046 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'), 00047 'specVersion' => new xmlrpcval(1, 'int') 00048 ), 'struct'), 00049 // if we support system.xxx functions, we always support multicall, too... 00050 // Note that, as of 2006/09/17, the following URL does not respond anymore 00051 'system.multicall' => new xmlrpcval(array( 00052 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'), 00053 'specVersion' => new xmlrpcval(1, 'int') 00054 ), 'struct'), 00055 // introspection: version 2! we support 'mixed', too 00056 'introspection' => new xmlrpcval(array( 00057 'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'), 00058 'specVersion' => new xmlrpcval(2, 'int') 00059 ), 'struct') 00060 ); 00061 00062 /* Functions that implement system.XXX methods of xmlrpc servers */ 00063 $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct'])); 00064 $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to'; 00065 $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec')); 00066 function _xmlrpcs_getCapabilities($server, $m=null) 00067 { 00068 $outAr = $GLOBALS['xmlrpcs_capabilities']; 00069 // NIL extension 00070 if ($GLOBALS['xmlrpc_null_extension']) { 00071 $outAr['nil'] = new xmlrpcval(array( 00072 'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'), 00073 'specVersion' => new xmlrpcval(1, 'int') 00074 ), 'struct'); 00075 } 00076 return new xmlrpcresp(new xmlrpcval($outAr, 'struct')); 00077 } 00078 00079 // listMethods: signature was either a string, or nothing. 00080 // The useless string variant has been removed 00081 $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray'])); 00082 $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch'; 00083 $_xmlrpcs_listMethods_sdoc=array(array('list of method names')); 00084 function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing 00085 { 00086 00087 $outAr=array(); 00088 foreach($server->dmap as $key => $val) 00089 { 00090 $outAr[]=&new xmlrpcval($key, 'string'); 00091 } 00092 if($server->allow_system_funcs) 00093 { 00094 foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val) 00095 { 00096 $outAr[]=&new xmlrpcval($key, 'string'); 00097 } 00098 } 00099 return new xmlrpcresp(new xmlrpcval($outAr, 'array')); 00100 } 00101 00102 $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString'])); 00103 $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)'; 00104 $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described')); 00105 function _xmlrpcs_methodSignature($server, $m) 00106 { 00107 // let accept as parameter both an xmlrpcval or string 00108 if (is_object($m)) 00109 { 00110 $methName=$m->getParam(0); 00111 $methName=$methName->scalarval(); 00112 } 00113 else 00114 { 00115 $methName=$m; 00116 } 00117 if(strpos($methName, "system.") === 0) 00118 { 00119 $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1; 00120 } 00121 else 00122 { 00123 $dmap=$server->dmap; $sysCall=0; 00124 } 00125 if(isset($dmap[$methName])) 00126 { 00127 if(isset($dmap[$methName]['signature'])) 00128 { 00129 $sigs=array(); 00130 foreach($dmap[$methName]['signature'] as $inSig) 00131 { 00132 $cursig=array(); 00133 foreach($inSig as $sig) 00134 { 00135 $cursig[]=&new xmlrpcval($sig, 'string'); 00136 } 00137 $sigs[]=&new xmlrpcval($cursig, 'array'); 00138 } 00139 $r=&new xmlrpcresp(new xmlrpcval($sigs, 'array')); 00140 } 00141 else 00142 { 00143 // NB: according to the official docs, we should be returning a 00144 // "none-array" here, which means not-an-array 00145 $r=&new xmlrpcresp(new xmlrpcval('undef', 'string')); 00146 } 00147 } 00148 else 00149 { 00150 $r=&new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']); 00151 } 00152 return $r; 00153 } 00154 00155 $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString'])); 00156 $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string'; 00157 $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described')); 00158 function _xmlrpcs_methodHelp($server, $m) 00159 { 00160 // let accept as parameter both an xmlrpcval or string 00161 if (is_object($m)) 00162 { 00163 $methName=$m->getParam(0); 00164 $methName=$methName->scalarval(); 00165 } 00166 else 00167 { 00168 $methName=$m; 00169 } 00170 if(strpos($methName, "system.") === 0) 00171 { 00172 $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1; 00173 } 00174 else 00175 { 00176 $dmap=$server->dmap; $sysCall=0; 00177 } 00178 if(isset($dmap[$methName])) 00179 { 00180 if(isset($dmap[$methName]['docstring'])) 00181 { 00182 $r=&new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string'); 00183 } 00184 else 00185 { 00186 $r=&new xmlrpcresp(new xmlrpcval('', 'string')); 00187 } 00188 } 00189 else 00190 { 00191 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']); 00192 } 00193 return $r; 00194 } 00195 00196 $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray'])); 00197 $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details'; 00198 $_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"')); 00199 function _xmlrpcs_multicall_error($err) 00200 { 00201 if(is_string($err)) 00202 { 00203 $str = $GLOBALS['xmlrpcstr']["multicall_${err}"]; 00204 $code = $GLOBALS['xmlrpcerr']["multicall_${err}"]; 00205 } 00206 else 00207 { 00208 $code = $err->faultCode(); 00209 $str = $err->faultString(); 00210 } 00211 $struct = array(); 00212 $struct['faultCode'] =& new xmlrpcval($code, 'int'); 00213 $struct['faultString'] =& new xmlrpcval($str, 'string'); 00214 return new xmlrpcval($struct, 'struct'); 00215 } 00216 00217 function _xmlrpcs_multicall_do_call($server, $call) 00218 { 00219 if($call->kindOf() != 'struct') 00220 { 00221 return _xmlrpcs_multicall_error('notstruct'); 00222 } 00223 $methName = @$call->structmem('methodName'); 00224 if(!$methName) 00225 { 00226 return _xmlrpcs_multicall_error('nomethod'); 00227 } 00228 if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string') 00229 { 00230 return _xmlrpcs_multicall_error('notstring'); 00231 } 00232 if($methName->scalarval() == 'system.multicall') 00233 { 00234 return _xmlrpcs_multicall_error('recursion'); 00235 } 00236 00237 $params = @$call->structmem('params'); 00238 if(!$params) 00239 { 00240 return _xmlrpcs_multicall_error('noparams'); 00241 } 00242 if($params->kindOf() != 'array') 00243 { 00244 return _xmlrpcs_multicall_error('notarray'); 00245 } 00246 $numParams = $params->arraysize(); 00247 00248 $msg =& new xmlrpcmsg($methName->scalarval()); 00249 for($i = 0; $i < $numParams; $i++) 00250 { 00251 if(!$msg->addParam($params->arraymem($i))) 00252 { 00253 $i++; 00254 return _xmlrpcs_multicall_error(new xmlrpcresp(0, 00255 $GLOBALS['xmlrpcerr']['incorrect_params'], 00256 $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i)); 00257 } 00258 } 00259 00260 $result = $server->execute($msg); 00261 00262 if($result->faultCode() != 0) 00263 { 00264 return _xmlrpcs_multicall_error($result); // Method returned fault. 00265 } 00266 00267 return new xmlrpcval(array($result->value()), 'array'); 00268 } 00269 00270 function _xmlrpcs_multicall_do_call_phpvals($server, $call) 00271 { 00272 if(!is_array($call)) 00273 { 00274 return _xmlrpcs_multicall_error('notstruct'); 00275 } 00276 if(!array_key_exists('methodName', $call)) 00277 { 00278 return _xmlrpcs_multicall_error('nomethod'); 00279 } 00280 if (!is_string($call['methodName'])) 00281 { 00282 return _xmlrpcs_multicall_error('notstring'); 00283 } 00284 if($call['methodName'] == 'system.multicall') 00285 { 00286 return _xmlrpcs_multicall_error('recursion'); 00287 } 00288 if(!array_key_exists('params', $call)) 00289 { 00290 return _xmlrpcs_multicall_error('noparams'); 00291 } 00292 if(!is_array($call['params'])) 00293 { 00294 return _xmlrpcs_multicall_error('notarray'); 00295 } 00296 00297 // this is a real dirty and simplistic hack, since we might have received a 00298 // base64 or datetime values, but they will be listed as strings here... 00299 $numParams = count($call['params']); 00300 $pt = array(); 00301 foreach($call['params'] as $val) 00302 $pt[] = php_2_xmlrpc_type(gettype($val)); 00303 00304 $result = $server->execute($call['methodName'], $call['params'], $pt); 00305 00306 if($result->faultCode() != 0) 00307 { 00308 return _xmlrpcs_multicall_error($result); // Method returned fault. 00309 } 00310 00311 return new xmlrpcval(array($result->value()), 'array'); 00312 } 00313 00314 function _xmlrpcs_multicall($server, $m) 00315 { 00316 $result = array(); 00317 // let accept a plain list of php parameters, beside a single xmlrpc msg object 00318 if (is_object($m)) 00319 { 00320 $calls = $m->getParam(0); 00321 $numCalls = $calls->arraysize(); 00322 for($i = 0; $i < $numCalls; $i++) 00323 { 00324 $call = $calls->arraymem($i); 00325 $result[$i] = _xmlrpcs_multicall_do_call($server, $call); 00326 } 00327 } 00328 else 00329 { 00330 $numCalls=count($m); 00331 for($i = 0; $i < $numCalls; $i++) 00332 { 00333 $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]); 00334 } 00335 } 00336 00337 return new xmlrpcresp(new xmlrpcval($result, 'array')); 00338 } 00339 00340 $GLOBALS['_xmlrpcs_dmap']=array( 00341 'system.listMethods' => array( 00342 'function' => '_xmlrpcs_listMethods', 00343 'signature' => $_xmlrpcs_listMethods_sig, 00344 'docstring' => $_xmlrpcs_listMethods_doc, 00345 'signature_docs' => $_xmlrpcs_listMethods_sdoc), 00346 'system.methodHelp' => array( 00347 'function' => '_xmlrpcs_methodHelp', 00348 'signature' => $_xmlrpcs_methodHelp_sig, 00349 'docstring' => $_xmlrpcs_methodHelp_doc, 00350 'signature_docs' => $_xmlrpcs_methodHelp_sdoc), 00351 'system.methodSignature' => array( 00352 'function' => '_xmlrpcs_methodSignature', 00353 'signature' => $_xmlrpcs_methodSignature_sig, 00354 'docstring' => $_xmlrpcs_methodSignature_doc, 00355 'signature_docs' => $_xmlrpcs_methodSignature_sdoc), 00356 'system.multicall' => array( 00357 'function' => '_xmlrpcs_multicall', 00358 'signature' => $_xmlrpcs_multicall_sig, 00359 'docstring' => $_xmlrpcs_multicall_doc, 00360 'signature_docs' => $_xmlrpcs_multicall_sdoc), 00361 'system.getCapabilities' => array( 00362 'function' => '_xmlrpcs_getCapabilities', 00363 'signature' => $_xmlrpcs_getCapabilities_sig, 00364 'docstring' => $_xmlrpcs_getCapabilities_doc, 00365 'signature_docs' => $_xmlrpcs_getCapabilities_sdoc) 00366 ); 00367 00368 $GLOBALS['_xmlrpcs_occurred_errors'] = ''; 00369 $GLOBALS['_xmlrpcs_prev_ehandler'] = ''; 00379 function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null) 00380 { 00381 // obey the @ protocol 00382 if (error_reporting() == 0) 00383 return; 00384 00385 //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING) 00386 if($errcode != 2048) // do not use E_STRICT by name, since on PHP 4 it will not be defined 00387 { 00388 $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n"; 00389 } 00390 // Try to avoid as much as possible disruption to the previous error handling 00391 // mechanism in place 00392 if($GLOBALS['_xmlrpcs_prev_ehandler'] == '') 00393 { 00394 // The previous error handler was the default: all we should do is log error 00395 // to the default error log (if level high enough) 00396 if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode)) 00397 { 00398 error_log($errstring); 00399 } 00400 } 00401 else 00402 { 00403 // Pass control on to previous error handler, trying to avoid loops... 00404 if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler') 00405 { 00406 // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers 00407 if(is_array($GLOBALS['_xmlrpcs_prev_ehandler'])) 00408 { 00409 $GLOBALS['_xmlrpcs_prev_ehandler'][0]->$GLOBALS['_xmlrpcs_prev_ehandler'][1]($errcode, $errstring, $filename, $lineno, $context); 00410 } 00411 else 00412 { 00413 $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context); 00414 } 00415 } 00416 } 00417 } 00418 00419 $GLOBALS['_xmlrpc_debuginfo']=''; 00420 00429 function xmlrpc_debugmsg($m) 00430 { 00431 $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n"; 00432 } 00433 00434 class xmlrpc_server 00435 { 00437 var $dmap=array(); 00443 var $functions_parameters_type='xmlrpcvals'; 00445 var $debug = 1; 00450 var $compress_response = false; 00455 var $accepted_compression = array(); 00457 var $allow_system_funcs = true; 00459 var $accepted_charset_encodings = array(); 00469 var $response_charset_encoding = ''; 00471 var $debug_info = ''; 00473 var $user_data = null; 00474 00479 function xmlrpc_server($dispMap=null, $serviceNow=true) 00480 { 00481 // if ZLIB is enabled, let the server by default accept compressed requests, 00482 // and compress responses sent to clients that support them 00483 if(function_exists('gzinflate')) 00484 { 00485 $this->accepted_compression = array('gzip', 'deflate'); 00486 $this->compress_response = true; 00487 } 00488 00489 // by default the xml parser can support these 3 charset encodings 00490 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII'); 00491 00492 // dispMap is a dispatch array of methods 00493 // mapped to function names and signatures 00494 // if a method 00495 // doesn't appear in the map then an unknown 00496 // method error is generated 00497 /* milosch - changed to make passing dispMap optional. 00498 * instead, you can use the class add_to_map() function 00499 * to add functions manually (borrowed from SOAPX4) 00500 */ 00501 if($dispMap) 00502 { 00503 $this->dmap = $dispMap; 00504 if($serviceNow) 00505 { 00506 $this->service(); 00507 } 00508 } 00509 } 00510 00525 function setDebug($in) 00526 { 00527 $this->debug=$in; 00528 } 00529 00535 function serializeDebug($charset_encoding='') 00536 { 00537 // Tough encoding problem: which internal charset should we assume for debug info? 00538 // It might contain a copy of raw data received from client, ie with unknown encoding, 00539 // intermixed with php generated data and user generated data... 00540 // so we split it: system debug is base 64 encoded, 00541 // user debug info should be encoded by the end user using the INTERNAL_ENCODING 00542 $out = ''; 00543 if ($this->debug_info != '') 00544 { 00545 $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n"; 00546 } 00547 if($GLOBALS['_xmlrpc_debuginfo']!='') 00548 { 00549 00550 $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n"; 00551 // NB: a better solution MIGHT be to use CDATA, but we need to insert it 00552 // into return payload AFTER the beginning tag 00553 //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n"; 00554 } 00555 return $out; 00556 } 00557 00564 function service($data=null, $return_payload=false) 00565 { 00566 if ($data === null) 00567 { 00568 $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; 00569 } 00570 $raw_data = $data; 00571 00572 // reset internal debug info 00573 $this->debug_info = ''; 00574 00575 // Echo back what we received, before parsing it 00576 if($this->debug > 1) 00577 { 00578 $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++"); 00579 } 00580 00581 $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding); 00582 if (!$r) 00583 { 00584 $r=$this->parseRequest($data, $req_charset); 00585 } 00586 00587 // save full body of request into response, for more debugging usages 00588 $r->raw_data = $raw_data; 00589 00590 if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors']) 00591 { 00592 $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" . 00593 $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++"); 00594 } 00595 00596 $payload=$this->xml_header($resp_charset); 00597 if($this->debug > 0) 00598 { 00599 $payload = $payload . $this->serializeDebug($resp_charset); 00600 } 00601 00602 // G. Giunta 2006-01-27: do not create response serialization if it has 00603 // already happened. Helps building json magic 00604 if (empty($r->payload)) 00605 { 00606 $r->serialize($resp_charset); 00607 } 00608 $payload = $payload . $r->payload; 00609 00610 if ($return_payload) 00611 { 00612 return $payload; 00613 } 00614 00615 // if we get a warning/error that has output some text before here, then we cannot 00616 // add a new header. We cannot say we are sending xml, either... 00617 if(!headers_sent()) 00618 { 00619 header('Content-Type: '.$r->content_type); 00620 // we do not know if client actually told us an accepted charset, but if he did 00621 // we have to tell him what we did 00622 header("Vary: Accept-Charset"); 00623 00624 // http compression of output: only 00625 // if we can do it, and we want to do it, and client asked us to, 00626 // and php ini settings do not force it already 00627 $php_no_self_compress = ini_get('zlib.output_compression') == '' && (ini_get('output_handler') != 'ob_gzhandler'); 00628 if($this->compress_response && function_exists('gzencode') && $resp_encoding != '' 00629 && $php_no_self_compress) 00630 { 00631 if(strpos($resp_encoding, 'gzip') !== false) 00632 { 00633 $payload = gzencode($payload); 00634 header("Content-Encoding: gzip"); 00635 header("Vary: Accept-Encoding"); 00636 } 00637 elseif (strpos($resp_encoding, 'deflate') !== false) 00638 { 00639 $payload = gzcompress($payload); 00640 header("Content-Encoding: deflate"); 00641 header("Vary: Accept-Encoding"); 00642 } 00643 } 00644 00645 // do not ouput content-length header if php is compressing output for us: 00646 // it will mess up measurements 00647 if($php_no_self_compress) 00648 { 00649 header('Content-Length: ' . (int)strlen($payload)); 00650 } 00651 } 00652 else 00653 { 00654 error_log('XML-RPC: xmlrpc_server::service: http headers already sent before response is fully generated. Check for php warning or error messages'); 00655 } 00656 00657 print $payload; 00658 00659 // return request, in case subclasses want it 00660 return $r; 00661 } 00662 00671 function add_to_map($methodname,$function,$sig=null,$doc='') 00672 { 00673 $this->dmap[$methodname] = array( 00674 'function' => $function, 00675 'docstring' => $doc 00676 ); 00677 if ($sig) 00678 { 00679 $this->dmap[$methodname]['signature'] = $sig; 00680 } 00681 } 00682 00689 function verifySignature($in, $sig) 00690 { 00691 // check each possible signature in turn 00692 if (is_object($in)) 00693 { 00694 $numParams = $in->getNumParams(); 00695 } 00696 else 00697 { 00698 $numParams = count($in); 00699 } 00700 foreach($sig as $cursig) 00701 { 00702 if(count($cursig)==$numParams+1) 00703 { 00704 $itsOK=1; 00705 for($n=0; $n<$numParams; $n++) 00706 { 00707 if (is_object($in)) 00708 { 00709 $p=$in->getParam($n); 00710 if($p->kindOf() == 'scalar') 00711 { 00712 $pt=$p->scalartyp(); 00713 } 00714 else 00715 { 00716 $pt=$p->kindOf(); 00717 } 00718 } 00719 else 00720 { 00721 $pt= $in[$n] == 'i4' ? 'int' : $in[$n]; // dispatch maps never use i4... 00722 } 00723 00724 // param index is $n+1, as first member of sig is return type 00725 if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue']) 00726 { 00727 $itsOK=0; 00728 $pno=$n+1; 00729 $wanted=$cursig[$n+1]; 00730 $got=$pt; 00731 break; 00732 } 00733 } 00734 if($itsOK) 00735 { 00736 return array(1,''); 00737 } 00738 } 00739 } 00740 if(isset($wanted)) 00741 { 00742 return array(0, "Wanted ${wanted}, got ${got} at param ${pno}"); 00743 } 00744 else 00745 { 00746 return array(0, "No method signature matches number of parameters"); 00747 } 00748 } 00749 00755 function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression) 00756 { 00757 // Play nice to PHP 4.0.x: superglobals were not yet invented... 00758 if(!isset($_SERVER)) 00759 { 00760 $_SERVER = $GLOBALS['HTTP_SERVER_VARS']; 00761 } 00762 00763 if($this->debug > 1) 00764 { 00765 if(function_exists('getallheaders')) 00766 { 00767 $this->debugmsg(''); // empty line 00768 foreach(getallheaders() as $name => $val) 00769 { 00770 $this->debugmsg("HEADER: $name: $val"); 00771 } 00772 } 00773 00774 } 00775 00776 if(isset($_SERVER['HTTP_CONTENT_ENCODING'])) 00777 { 00778 $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']); 00779 } 00780 else 00781 { 00782 $content_encoding = ''; 00783 } 00784 00785 // check if request body has been compressed and decompress it 00786 if($content_encoding != '' && strlen($data)) 00787 { 00788 if($content_encoding == 'deflate' || $content_encoding == 'gzip') 00789 { 00790 // if decoding works, use it. else assume data wasn't gzencoded 00791 if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression)) 00792 { 00793 if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data)) 00794 { 00795 $data = $degzdata; 00796 if($this->debug > 1) 00797 { 00798 $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++"); 00799 } 00800 } 00801 elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10))) 00802 { 00803 $data = $degzdata; 00804 if($this->debug > 1) 00805 $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++"); 00806 } 00807 else 00808 { 00809 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']); 00810 return $r; 00811 } 00812 } 00813 else 00814 { 00815 //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.'); 00816 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']); 00817 return $r; 00818 } 00819 } 00820 } 00821 00822 // check if client specified accepted charsets, and if we know how to fulfill 00823 // the request 00824 if ($this->response_charset_encoding == 'auto') 00825 { 00826 $resp_encoding = ''; 00827 if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) 00828 { 00829 // here we should check if we can match the client-requested encoding 00830 // with the encodings we know we can generate. 00832 $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET'])); 00833 // Give preference to internal encoding 00834 $known_charsets = array($this->internal_encoding, 'UTF-8', 'ISO-8859-1', 'US-ASCII'); 00835 foreach ($known_charsets as $charset) 00836 { 00837 foreach ($client_accepted_charsets as $accepted) 00838 if (strpos($accepted, $charset) === 0) 00839 { 00840 $resp_encoding = $charset; 00841 break; 00842 } 00843 if ($resp_encoding) 00844 break; 00845 } 00846 } 00847 } 00848 else 00849 { 00850 $resp_encoding = $this->response_charset_encoding; 00851 } 00852 00853 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) 00854 { 00855 $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING']; 00856 } 00857 else 00858 { 00859 $resp_compression = ''; 00860 } 00861 00862 // 'guestimate' request encoding 00864 $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '', 00865 $data); 00866 00867 return null; 00868 } 00869 00878 function parseRequest($data, $req_encoding='') 00879 { 00880 // 2005/05/07 commented and moved into caller function code 00881 //if($data=='') 00882 //{ 00883 // $data=$GLOBALS['HTTP_RAW_POST_DATA']; 00884 //} 00885 00886 // G. Giunta 2005/02/13: we do NOT expect to receive html entities 00887 // so we do not try to convert them into xml character entities 00888 //$data = xmlrpc_html_entity_xlate($data); 00889 00890 $GLOBALS['_xh']=array(); 00891 $GLOBALS['_xh']['ac']=''; 00892 $GLOBALS['_xh']['stack']=array(); 00893 $GLOBALS['_xh']['valuestack'] = array(); 00894 $GLOBALS['_xh']['params']=array(); 00895 $GLOBALS['_xh']['pt']=array(); 00896 $GLOBALS['_xh']['isf']=0; 00897 $GLOBALS['_xh']['isf_reason']=''; 00898 $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not 00899 $GLOBALS['_xh']['rt']=''; 00900 00901 // decompose incoming XML into request structure 00902 if ($req_encoding != '') 00903 { 00904 if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) 00905 // the following code might be better for mb_string enabled installs, but 00906 // makes the lib about 200% slower... 00907 //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) 00908 { 00909 error_log('XML-RPC: xmlrpc_server::parseRequest: invalid charset encoding of received request: '.$req_encoding); 00910 $req_encoding = $GLOBALS['xmlrpc_defencoding']; 00911 } 00913 // the encoding is not UTF8 and there are non-ascii chars in the text... 00914 $parser = xml_parser_create($req_encoding); 00915 } 00916 else 00917 { 00918 $parser = xml_parser_create(); 00919 } 00920 00921 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); 00922 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell 00923 // the xml parser to give us back data in the expected charset 00924 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']); 00925 00926 if ($this->functions_parameters_type != 'xmlrpcvals') 00927 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast'); 00928 else 00929 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee'); 00930 xml_set_character_data_handler($parser, 'xmlrpc_cd'); 00931 xml_set_default_handler($parser, 'xmlrpc_dh'); 00932 if(!xml_parse($parser, $data, 1)) 00933 { 00934 // return XML error as a faultCode 00935 $r=&new xmlrpcresp(0, 00936 $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser), 00937 sprintf('XML error: %s at line %d, column %d', 00938 xml_error_string(xml_get_error_code($parser)), 00939 xml_get_current_line_number($parser), xml_get_current_column_number($parser))); 00940 xml_parser_free($parser); 00941 } 00942 elseif ($GLOBALS['_xh']['isf']) 00943 { 00944 xml_parser_free($parser); 00945 $r=&new xmlrpcresp(0, 00946 $GLOBALS['xmlrpcerr']['invalid_request'], 00947 $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']); 00948 } 00949 else 00950 { 00951 xml_parser_free($parser); 00952 if ($this->functions_parameters_type != 'xmlrpcvals') 00953 { 00954 if($this->debug > 1) 00955 { 00956 $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++"); 00957 } 00958 $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']); 00959 } 00960 else 00961 { 00962 // build an xmlrpcmsg object with data parsed from xml 00963 $m=&new xmlrpcmsg($GLOBALS['_xh']['method']); 00964 // now add parameters in 00965 for($i=0; $i<count($GLOBALS['_xh']['params']); $i++) 00966 { 00967 $m->addParam($GLOBALS['_xh']['params'][$i]); 00968 } 00969 00970 if($this->debug > 1) 00971 { 00972 $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++"); 00973 } 00974 00975 $r = $this->execute($m); 00976 } 00977 } 00978 return $r; 00979 } 00980 00989 function execute($m, $params=null, $paramtypes=null) 00990 { 00991 if (is_object($m)) 00992 { 00993 $methName = $m->method(); 00994 } 00995 else 00996 { 00997 $methName = $m; 00998 } 00999 $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0); 01000 $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap; 01001 01002 if(!isset($dmap[$methName]['function'])) 01003 { 01004 // No such method 01005 return new xmlrpcresp(0, 01006 $GLOBALS['xmlrpcerr']['unknown_method'], 01007 $GLOBALS['xmlrpcstr']['unknown_method']); 01008 } 01009 01010 // Check signature 01011 if(isset($dmap[$methName]['signature'])) 01012 { 01013 $sig = $dmap[$methName]['signature']; 01014 if (is_object($m)) 01015 { 01016 list($ok, $errstr) = $this->verifySignature($m, $sig); 01017 } 01018 else 01019 { 01020 list($ok, $errstr) = $this->verifySignature($paramtypes, $sig); 01021 } 01022 if(!$ok) 01023 { 01024 // Didn't match. 01025 return new xmlrpcresp( 01026 0, 01027 $GLOBALS['xmlrpcerr']['incorrect_params'], 01028 $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}" 01029 ); 01030 } 01031 } 01032 01033 $func = $dmap[$methName]['function']; 01034 // let the 'class::function' syntax be accepted in dispatch maps 01035 if(is_string($func) && strpos($func, '::')) 01036 { 01037 $func = explode('::', $func); 01038 } 01039 // verify that function to be invoked is in fact callable 01040 if(!is_callable($func)) 01041 { 01042 error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler is not callable"); 01043 return new xmlrpcresp( 01044 0, 01045 $GLOBALS['xmlrpcerr']['server_error'], 01046 $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method" 01047 ); 01048 } 01049 01050 // If debug level is 3, we should catch all errors generated during 01051 // processing of user function, and log them as part of response 01052 if($this->debug > 2) 01053 { 01054 $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler'); 01055 } 01056 if (is_object($m)) 01057 { 01058 if($sysCall) 01059 { 01060 $r = call_user_func($func, $this, $m); 01061 } 01062 else 01063 { 01064 $r = call_user_func($func, $m); 01065 } 01066 if (!is_a($r, 'xmlrpcresp')) 01067 { 01068 error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object"); 01069 if (is_a($r, 'xmlrpcval')) 01070 { 01071 $r =& new xmlrpcresp($r); 01072 } 01073 else 01074 { 01075 $r =& new xmlrpcresp( 01076 0, 01077 $GLOBALS['xmlrpcerr']['server_error'], 01078 $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object" 01079 ); 01080 } 01081 } 01082 } 01083 else 01084 { 01085 // call a 'plain php' function 01086 if($sysCall) 01087 { 01088 array_unshift($params, $this); 01089 $r = call_user_func_array($func, $params); 01090 } 01091 else 01092 { 01093 // 3rd API convention for method-handling functions: EPI-style 01094 if ($this->functions_parameters_type == 'epivals') 01095 { 01096 $r = call_user_func_array($func, array($methName, $params, $this->user_data)); 01097 // mimic EPI behaviour: if we get an array that looks like an error, make it 01098 // an eror response 01099 if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r)) 01100 { 01101 $r =& new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']); 01102 } 01103 else 01104 { 01105 // functions using EPI api should NOT return resp objects, 01106 // so make sure we encode the return type correctly 01107 $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api'))); 01108 } 01109 } 01110 else 01111 { 01112 $r = call_user_func_array($func, $params); 01113 } 01114 } 01115 // the return type can be either an xmlrpcresp object or a plain php value... 01116 if (!is_a($r, 'xmlrpcresp')) 01117 { 01118 // what should we assume here about automatic encoding of datetimes 01119 // and php classes instances??? 01120 $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('auto_dates'))); 01121 } 01122 } 01123 if($this->debug > 2) 01124 { 01125 // note: restore the error handler we found before calling the 01126 // user func, even if it has been changed inside the func itself 01127 if($GLOBALS['_xmlrpcs_prev_ehandler']) 01128 { 01129 set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']); 01130 } 01131 else 01132 { 01133 restore_error_handler(); 01134 } 01135 } 01136 return $r; 01137 } 01138 01144 function debugmsg($string) 01145 { 01146 $this->debug_info .= $string."\n"; 01147 } 01148 01152 function xml_header($charset_encoding='') 01153 { 01154 if ($charset_encoding != '') 01155 { 01156 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n"; 01157 } 01158 else 01159 { 01160 return "<?xml version=\"1.0\"?" . ">\n"; 01161 } 01162 } 01163 01168 function echoInput() 01169 { 01170 $r=&new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string')); 01171 print $r->serialize(); 01172 } 01173 } 01174 ?>