Open Monograph Press  3.3.0
generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php
1 <?php
2 
3 namespace PhpXmlRpc;
4 
7 
8 class Server
9 {
13  protected $dmap = array();
19  public $functions_parameters_type = 'xmlrpcvals';
26  public $phpvals_encoding_options = array('auto_dates');
31  public $debug = 1;
38  public $exception_handling = 0;
44  public $compress_response = false;
49  public $accepted_compression = array();
51  public $allow_system_funcs = true;
57  public $accepted_charset_encodings = array();
67  public $response_charset_encoding = '';
71  protected $debug_info = '';
75  public $user_data = null;
76 
77  protected static $_xmlrpc_debuginfo = '';
78  protected static $_xmlrpcs_occurred_errors = '';
79  protected static $_xmlrpcs_prev_ehandler = '';
80 
85  public function __construct($dispatchMap = null, $serviceNow = true)
86  {
87  // if ZLIB is enabled, let the server by default accept compressed requests,
88  // and compress responses sent to clients that support them
89  if (function_exists('gzinflate')) {
90  $this->accepted_compression = array('gzip', 'deflate');
91  $this->compress_response = true;
92  }
93 
94  // by default the xml parser can support these 3 charset encodings
95  $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
96 
97  // dispMap is a dispatch array of methods mapped to function names and signatures.
98  // If a method doesn't appear in the map then an unknown method error is generated
99  /* milosch - changed to make passing dispMap optional.
100  * instead, you can use the class add_to_map() function
101  * to add functions manually (borrowed from SOAPX4)
102  */
103  if ($dispatchMap) {
104  $this->dmap = $dispatchMap;
105  if ($serviceNow) {
106  $this->service();
107  }
108  }
109  }
110 
125  public function setDebug($level)
126  {
127  $this->debug = $level;
128  }
129 
139  public static function xmlrpc_debugmsg($msg)
140  {
141  static::$_xmlrpc_debuginfo .= $msg . "\n";
142  }
143 
144  public static function error_occurred($msg)
145  {
146  static::$_xmlrpcs_occurred_errors .= $msg . "\n";
147  }
148 
156  public function serializeDebug($charsetEncoding = '')
157  {
158  // Tough encoding problem: which internal charset should we assume for debug info?
159  // It might contain a copy of raw data received from client, ie with unknown encoding,
160  // intermixed with php generated data and user generated data...
161  // so we split it: system debug is base 64 encoded,
162  // user debug info should be encoded by the end user using the INTERNAL_ENCODING
163  $out = '';
164  if ($this->debug_info != '') {
165  $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n" . base64_encode($this->debug_info) . "\n-->\n";
166  }
167  if (static::$_xmlrpc_debuginfo != '') {
168  $out .= "<!-- DEBUG INFO:\n" . Charset::instance()->encodeEntities(str_replace('--', '_-', static::$_xmlrpc_debuginfo), PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "\n-->\n";
169  // NB: a better solution MIGHT be to use CDATA, but we need to insert it
170  // into return payload AFTER the beginning tag
171  //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', static::$_xmlrpc_debuginfo) . "\n]]>\n";
172  }
173 
174  return $out;
175  }
176 
187  public function service($data = null, $returnPayload = false)
188  {
189  if ($data === null) {
190  $data = file_get_contents('php://input');
191  }
192  $rawData = $data;
193 
194  // reset internal debug info
195  $this->debug_info = '';
196 
197  // Save what we received, before parsing it
198  if ($this->debug > 1) {
199  $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
200  }
201 
202  $r = $this->parseRequestHeaders($data, $reqCharset, $respCharset, $respEncoding);
203  if (!$r) {
204  // this actually executes the request
205  $r = $this->parseRequest($data, $reqCharset);
206  }
207 
208  // save full body of request into response, for more debugging usages
209  $r->raw_data = $rawData;
210 
211  if ($this->debug > 2 && static::$_xmlrpcs_occurred_errors) {
212  $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
213  static::$_xmlrpcs_occurred_errors . "+++END+++");
214  }
215 
216  $payload = $this->xml_header($respCharset);
217  if ($this->debug > 0) {
218  $payload = $payload . $this->serializeDebug($respCharset);
219  }
220 
221  // G. Giunta 2006-01-27: do not create response serialization if it has
222  // already happened. Helps building json magic
223  if (empty($r->payload)) {
224  $r->serialize($respCharset);
225  }
226  $payload = $payload . $r->payload;
227 
228  if ($returnPayload) {
229  return $payload;
230  }
231 
232  // if we get a warning/error that has output some text before here, then we cannot
233  // add a new header. We cannot say we are sending xml, either...
234  if (!headers_sent()) {
235  header('Content-Type: ' . $r->content_type);
236  // we do not know if client actually told us an accepted charset, but if he did
237  // we have to tell him what we did
238  header("Vary: Accept-Charset");
239 
240  // http compression of output: only
241  // if we can do it, and we want to do it, and client asked us to,
242  // and php ini settings do not force it already
243  $phpNoSelfCompress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
244  if ($this->compress_response && function_exists('gzencode') && $respEncoding != ''
245  && $phpNoSelfCompress
246  ) {
247  if (strpos($respEncoding, 'gzip') !== false) {
248  $payload = gzencode($payload);
249  header("Content-Encoding: gzip");
250  header("Vary: Accept-Encoding");
251  } elseif (strpos($respEncoding, 'deflate') !== false) {
252  $payload = gzcompress($payload);
253  header("Content-Encoding: deflate");
254  header("Vary: Accept-Encoding");
255  }
256  }
257 
258  // do not output content-length header if php is compressing output for us:
259  // it will mess up measurements
260  if ($phpNoSelfCompress) {
261  header('Content-Length: ' . (int)strlen($payload));
262  }
263  } else {
264  error_log('XML-RPC: ' . __METHOD__ . ': http headers already sent before response is fully generated. Check for php warning or error messages');
265  }
266 
267  print $payload;
268 
269  // return request, in case subclasses want it
270  return $r;
271  }
272 
282  public function add_to_map($methodName, $function, $sig = null, $doc = false, $sigDoc = false)
283  {
284  $this->dmap[$methodName] = array(
285  'function' => $function,
286  'docstring' => $doc,
287  );
288  if ($sig) {
289  $this->dmap[$methodName]['signature'] = $sig;
290  }
291  if ($sigDoc) {
292  $this->dmap[$methodName]['signature_docs'] = $sigDoc;
293  }
294  }
295 
304  protected function verifySignature($in, $sigs)
305  {
306  // check each possible signature in turn
307  if (is_object($in)) {
308  $numParams = $in->getNumParams();
309  } else {
310  $numParams = count($in);
311  }
312  foreach ($sigs as $curSig) {
313  if (count($curSig) == $numParams + 1) {
314  $itsOK = 1;
315  for ($n = 0; $n < $numParams; $n++) {
316  if (is_object($in)) {
317  $p = $in->getParam($n);
318  if ($p->kindOf() == 'scalar') {
319  $pt = $p->scalartyp();
320  } else {
321  $pt = $p->kindOf();
322  }
323  } else {
324  $pt = $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
325  }
326 
327  // param index is $n+1, as first member of sig is return type
328  if ($pt != $curSig[$n + 1] && $curSig[$n + 1] != Value::$xmlrpcValue) {
329  $itsOK = 0;
330  $pno = $n + 1;
331  $wanted = $curSig[$n + 1];
332  $got = $pt;
333  break;
334  }
335  }
336  if ($itsOK) {
337  return array(1, '');
338  }
339  }
340  }
341  if (isset($wanted)) {
342  return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
343  } else {
344  return array(0, "No method signature matches number of parameters");
345  }
346  }
347 
353  protected function parseRequestHeaders(&$data, &$reqEncoding, &$respEncoding, &$respCompression)
354  {
355  // check if $_SERVER is populated: it might have been disabled via ini file
356  // (this is true even when in CLI mode)
357  if (count($_SERVER) == 0) {
358  error_log('XML-RPC: ' . __METHOD__ . ': cannot parse request headers as $_SERVER is not populated');
359  }
360 
361  if ($this->debug > 1) {
362  if (function_exists('getallheaders')) {
363  $this->debugmsg(''); // empty line
364  foreach (getallheaders() as $name => $val) {
365  $this->debugmsg("HEADER: $name: $val");
366  }
367  }
368  }
369 
370  if (isset($_SERVER['HTTP_CONTENT_ENCODING'])) {
371  $contentEncoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
372  } else {
373  $contentEncoding = '';
374  }
375 
376  // check if request body has been compressed and decompress it
377  if ($contentEncoding != '' && strlen($data)) {
378  if ($contentEncoding == 'deflate' || $contentEncoding == 'gzip') {
379  // if decoding works, use it. else assume data wasn't gzencoded
380  if (function_exists('gzinflate') && in_array($contentEncoding, $this->accepted_compression)) {
381  if ($contentEncoding == 'deflate' && $degzdata = @gzuncompress($data)) {
382  $data = $degzdata;
383  if ($this->debug > 1) {
384  $this->debugmsg("\n+++INFLATED REQUEST+++[" . strlen($data) . " chars]+++\n" . $data . "\n+++END+++");
385  }
386  } elseif ($contentEncoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10))) {
387  $data = $degzdata;
388  if ($this->debug > 1) {
389  $this->debugmsg("+++INFLATED REQUEST+++[" . strlen($data) . " chars]+++\n" . $data . "\n+++END+++");
390  }
391  } else {
392  $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_decompress_fail'], PhpXmlRpc::$xmlrpcstr['server_decompress_fail']);
393 
394  return $r;
395  }
396  } else {
397  $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_cannot_decompress'], PhpXmlRpc::$xmlrpcstr['server_cannot_decompress']);
398 
399  return $r;
400  }
401  }
402  }
403 
404  // check if client specified accepted charsets, and if we know how to fulfill
405  // the request
406  if ($this->response_charset_encoding == 'auto') {
407  $respEncoding = '';
408  if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
409  // here we should check if we can match the client-requested encoding
410  // with the encodings we know we can generate.
412  $clientAcceptedCharsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
413  // Give preference to internal encoding
414  $knownCharsets = array(PhpXmlRpc::$xmlrpc_internalencoding, 'UTF-8', 'ISO-8859-1', 'US-ASCII');
415  foreach ($knownCharsets as $charset) {
416  foreach ($clientAcceptedCharsets as $accepted) {
417  if (strpos($accepted, $charset) === 0) {
418  $respEncoding = $charset;
419  break;
420  }
421  }
422  if ($respEncoding) {
423  break;
424  }
425  }
426  }
427  } else {
428  $respEncoding = $this->response_charset_encoding;
429  }
430 
431  if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
432  $respCompression = $_SERVER['HTTP_ACCEPT_ENCODING'];
433  } else {
434  $respCompression = '';
435  }
436 
437  // 'guestimate' request encoding
439  $reqEncoding = XMLParser::guessEncoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
440  $data);
441 
442  return;
443  }
444 
456  public function parseRequest($data, $reqEncoding = '')
457  {
458  // decompose incoming XML into request structure
459 
460  if ($reqEncoding != '') {
461  // Since parsing will fail if charset is not specified in the xml prologue,
462  // the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that...
463  // The following code might be better for mb_string enabled installs, but
464  // makes the lib about 200% slower...
465  //if (!is_valid_charset($reqEncoding, array('UTF-8')))
466  if (!in_array($reqEncoding, array('UTF-8', 'US-ASCII')) && !XMLParser::hasEncoding($data)) {
467  if ($reqEncoding == 'ISO-8859-1') {
468  $data = utf8_encode($data);
469  } else {
470  if (extension_loaded('mbstring')) {
471  $data = mb_convert_encoding($data, 'UTF-8', $reqEncoding);
472  } else {
473  error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $reqEncoding);
474  }
475  }
476  }
477  }
478 
479  $parser = xml_parser_create();
480  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
481  // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
482  // the xml parser to give us back data in the expected charset
483  // What if internal encoding is not in one of the 3 allowed?
484  // we use the broadest one, ie. utf8
485  // This allows to send data which is native in various charset,
486  // by extending xmlrpc_encode_entities() and setting xmlrpc_internalencoding
487  if (!in_array(PhpXmlRpc::$xmlrpc_internalencoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) {
488  xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
489  } else {
490  xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, PhpXmlRpc::$xmlrpc_internalencoding);
491  }
492 
493  $xmlRpcParser = new XMLParser();
494  xml_set_object($parser, $xmlRpcParser);
495 
496  if ($this->functions_parameters_type != 'xmlrpcvals') {
497  xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
498  } else {
499  xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
500  }
501  xml_set_character_data_handler($parser, 'xmlrpc_cd');
502  xml_set_default_handler($parser, 'xmlrpc_dh');
503  if (!xml_parse($parser, $data, 1)) {
504  // return XML error as a faultCode
505  $r = new Response(0,
506  PhpXmlRpc::$xmlrpcerrxml + xml_get_error_code($parser),
507  sprintf('XML error: %s at line %d, column %d',
508  xml_error_string(xml_get_error_code($parser)),
509  xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
510  xml_parser_free($parser);
511  } elseif ($xmlRpcParser->_xh['isf']) {
512  xml_parser_free($parser);
513  $r = new Response(0,
514  PhpXmlRpc::$xmlrpcerr['invalid_request'],
515  PhpXmlRpc::$xmlrpcstr['invalid_request'] . ' ' . $xmlRpcParser->_xh['isf_reason']);
516  } else {
517  xml_parser_free($parser);
518  // small layering violation in favor of speed and memory usage:
519  // we should allow the 'execute' method handle this, but in the
520  // most common scenario (xmlrpc values type server with some methods
521  // registered as phpvals) that would mean a useless encode+decode pass
522  if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$xmlRpcParser->_xh['method']]['parameters_type']) && ($this->dmap[$xmlRpcParser->_xh['method']]['parameters_type'] == 'phpvals'))) {
523  if ($this->debug > 1) {
524  $this->debugmsg("\n+++PARSED+++\n" . var_export($xmlRpcParser->_xh['params'], true) . "\n+++END+++");
525  }
526  $r = $this->execute($xmlRpcParser->_xh['method'], $xmlRpcParser->_xh['params'], $xmlRpcParser->_xh['pt']);
527  } else {
528  // build a Request object with data parsed from xml
529  $req = new Request($xmlRpcParser->_xh['method']);
530  // now add parameters in
531  for ($i = 0; $i < count($xmlRpcParser->_xh['params']); $i++) {
532  $req->addParam($xmlRpcParser->_xh['params'][$i]);
533  }
534 
535  if ($this->debug > 1) {
536  $this->debugmsg("\n+++PARSED+++\n" . var_export($req, true) . "\n+++END+++");
537  }
538  $r = $this->execute($req);
539  }
540  }
541 
542  return $r;
543  }
544 
556  protected function execute($req, $params = null, $paramTypes = null)
557  {
558  static::$_xmlrpcs_occurred_errors = '';
559  static::$_xmlrpc_debuginfo = '';
560 
561  if (is_object($req)) {
562  $methName = $req->method();
563  } else {
564  $methName = $req;
565  }
566  $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
567  $dmap = $sysCall ? $this->getSystemDispatchMap() : $this->dmap;
568 
569  if (!isset($dmap[$methName]['function'])) {
570  // No such method
571  return new Response(0,
572  PhpXmlRpc::$xmlrpcerr['unknown_method'],
573  PhpXmlRpc::$xmlrpcstr['unknown_method']);
574  }
575 
576  // Check signature
577  if (isset($dmap[$methName]['signature'])) {
578  $sig = $dmap[$methName]['signature'];
579  if (is_object($req)) {
580  list($ok, $errStr) = $this->verifySignature($req, $sig);
581  } else {
582  list($ok, $errStr) = $this->verifySignature($paramTypes, $sig);
583  }
584  if (!$ok) {
585  // Didn't match.
586  return new Response(
587  0,
588  PhpXmlRpc::$xmlrpcerr['incorrect_params'],
589  PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": ${errStr}"
590  );
591  }
592  }
593 
594  $func = $dmap[$methName]['function'];
595  // let the 'class::function' syntax be accepted in dispatch maps
596  if (is_string($func) && strpos($func, '::')) {
597  $func = explode('::', $func);
598  }
599 
600  if (is_array($func)) {
601  if (is_object($func[0])) {
602  $funcName = get_class($func[0]) . '->' . $func[1];
603  } else {
604  $funcName = implode('::', $func);
605  }
606  } else if ($func instanceof \Closure) {
607  $funcName = 'Closure';
608  } else {
609  $funcName = $func;
610  }
611 
612  // verify that function to be invoked is in fact callable
613  if (!is_callable($func)) {
614  error_log("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler is not callable");
615  return new Response(
616  0,
617  PhpXmlRpc::$xmlrpcerr['server_error'],
618  PhpXmlRpc::$xmlrpcstr['server_error'] . ": no function matches method"
619  );
620  }
621 
622  // If debug level is 3, we should catch all errors generated during
623  // processing of user function, and log them as part of response
624  if ($this->debug > 2) {
625  self::$_xmlrpcs_prev_ehandler = set_error_handler(array('\PhpXmlRpc\Server', '_xmlrpcs_errorHandler'));
626  }
627 
628  try {
629  // Allow mixed-convention servers
630  if (is_object($req)) {
631  if ($sysCall) {
632  $r = call_user_func($func, $this, $req);
633  } else {
634  $r = call_user_func($func, $req);
635  }
636  if (!is_a($r, 'PhpXmlRpc\Response')) {
637  error_log("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler does not return an xmlrpc response object but a " . gettype($r));
638  if (is_a($r, 'PhpXmlRpc\Value')) {
639  $r = new Response($r);
640  } else {
641  $r = new Response(
642  0,
643  PhpXmlRpc::$xmlrpcerr['server_error'],
644  PhpXmlRpc::$xmlrpcstr['server_error'] . ": function does not return xmlrpc response object"
645  );
646  }
647  }
648  } else {
649  // call a 'plain php' function
650  if ($sysCall) {
651  array_unshift($params, $this);
652  $r = call_user_func_array($func, $params);
653  } else {
654  // 3rd API convention for method-handling functions: EPI-style
655  if ($this->functions_parameters_type == 'epivals') {
656  $r = call_user_func_array($func, array($methName, $params, $this->user_data));
657  // mimic EPI behaviour: if we get an array that looks like an error, make it
658  // an eror response
659  if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r)) {
660  $r = new Response(0, (integer)$r['faultCode'], (string)$r['faultString']);
661  } else {
662  // functions using EPI api should NOT return resp objects,
663  // so make sure we encode the return type correctly
664  $r = new Response(php_xmlrpc_encode($r, array('extension_api')));
665  }
666  } else {
667  $r = call_user_func_array($func, $params);
668  }
669  }
670  // the return type can be either a Response object or a plain php value...
671  if (!is_a($r, '\PhpXmlRpc\Response')) {
672  // what should we assume here about automatic encoding of datetimes
673  // and php classes instances???
674  $r = new Response(php_xmlrpc_encode($r, $this->phpvals_encoding_options));
675  }
676  }
677  } catch (\Exception $e) {
678  // (barring errors in the lib) an uncatched exception happened
679  // in the called function, we wrap it in a proper error-response
680  switch ($this->exception_handling) {
681  case 2:
682  if ($this->debug > 2) {
683  if (self::$_xmlrpcs_prev_ehandler) {
684  set_error_handler(self::$_xmlrpcs_prev_ehandler);
685  } else {
686  restore_error_handler();
687  }
688  }
689  throw $e;
690  break;
691  case 1:
692  $r = new Response(0, $e->getCode(), $e->getMessage());
693  break;
694  default:
695  $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_error'], PhpXmlRpc::$xmlrpcstr['server_error']);
696  }
697  }
698  if ($this->debug > 2) {
699  // note: restore the error handler we found before calling the
700  // user func, even if it has been changed inside the func itself
701  if (self::$_xmlrpcs_prev_ehandler) {
702  set_error_handler(self::$_xmlrpcs_prev_ehandler);
703  } else {
704  restore_error_handler();
705  }
706  }
707 
708  return $r;
709  }
710 
716  protected function debugmsg($string)
717  {
718  $this->debug_info .= $string . "\n";
719  }
720 
725  protected function xml_header($charsetEncoding = '')
726  {
727  if ($charsetEncoding != '') {
728  return "<?xml version=\"1.0\" encoding=\"$charsetEncoding\"?" . ">\n";
729  } else {
730  return "<?xml version=\"1.0\"?" . ">\n";
731  }
732  }
733 
734  /* Functions that implement system.XXX methods of xmlrpc servers */
735 
739  public function getSystemDispatchMap()
740  {
741  return array(
742  'system.listMethods' => array(
743  'function' => 'PhpXmlRpc\Server::_xmlrpcs_listMethods',
744  // listMethods: signature was either a string, or nothing.
745  // The useless string variant has been removed
746  'signature' => array(array(Value::$xmlrpcArray)),
747  'docstring' => 'This method lists all the methods that the XML-RPC server knows how to dispatch',
748  'signature_docs' => array(array('list of method names')),
749  ),
750  'system.methodHelp' => array(
751  'function' => 'PhpXmlRpc\Server::_xmlrpcs_methodHelp',
752  'signature' => array(array(Value::$xmlrpcString, Value::$xmlrpcString)),
753  'docstring' => 'Returns help text if defined for the method passed, otherwise returns an empty string',
754  'signature_docs' => array(array('method description', 'name of the method to be described')),
755  ),
756  'system.methodSignature' => array(
757  'function' => 'PhpXmlRpc\Server::_xmlrpcs_methodSignature',
758  'signature' => array(array(Value::$xmlrpcArray, Value::$xmlrpcString)),
759  'docstring' => '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)',
760  'signature_docs' => array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described')),
761  ),
762  'system.multicall' => array(
763  'function' => 'PhpXmlRpc\Server::_xmlrpcs_multicall',
764  'signature' => array(array(Value::$xmlrpcArray, Value::$xmlrpcArray)),
765  'docstring' => 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details',
766  'signature_docs' => 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"')),
767  ),
768  'system.getCapabilities' => array(
769  'function' => 'PhpXmlRpc\Server::_xmlrpcs_getCapabilities',
770  'signature' => array(array(Value::$xmlrpcStruct)),
771  'docstring' => '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',
772  'signature_docs' => array(array('list of capabilities, described as structs with a version number and url for the spec')),
773  ),
774  );
775  }
776 
780  public function getCapabilities()
781  {
782  $outAr = array(
783  // xmlrpc spec: always supported
784  'xmlrpc' => array(
785  'specUrl' => 'http://www.xmlrpc.com/spec',
786  'specVersion' => 1
787  ),
788  // if we support system.xxx functions, we always support multicall, too...
789  // Note that, as of 2006/09/17, the following URL does not respond anymore
790  'system.multicall' => array(
791  'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
792  'specVersion' => 1
793  ),
794  // introspection: version 2! we support 'mixed', too
795  'introspection' => array(
796  'specUrl' => 'http://phpxmlrpc.sourceforge.net/doc-2/ch10.html',
797  'specVersion' => 2,
798  ),
799  );
800 
801  // NIL extension
802  if (PhpXmlRpc::$xmlrpc_null_extension) {
803  $outAr['nil'] = array(
804  'specUrl' => 'http://www.ontosys.com/xml-rpc/extensions.php',
805  'specVersion' => 1
806  );
807  }
808 
809  return $outAr;
810  }
811 
812  public static function _xmlrpcs_getCapabilities($server, $req = null)
813  {
814  $encoder = new Encoder();
815  return new Response($encoder->encode($server->getCapabilities()));
816  }
817 
818  public static function _xmlrpcs_listMethods($server, $req = null) // if called in plain php values mode, second param is missing
819  {
820  $outAr = array();
821  foreach ($server->dmap as $key => $val) {
822  $outAr[] = new Value($key, 'string');
823  }
824  if ($server->allow_system_funcs) {
825  foreach ($server->getSystemDispatchMap() as $key => $val) {
826  $outAr[] = new Value($key, 'string');
827  }
828  }
829 
830  return new Response(new Value($outAr, 'array'));
831  }
832 
833  public static function _xmlrpcs_methodSignature($server, $req)
834  {
835  // let accept as parameter both an xmlrpc value or string
836  if (is_object($req)) {
837  $methName = $req->getParam(0);
838  $methName = $methName->scalarval();
839  } else {
840  $methName = $req;
841  }
842  if (strpos($methName, "system.") === 0) {
843  $dmap = $server->getSystemDispatchMap();
844  } else {
845  $dmap = $server->dmap;
846  }
847  if (isset($dmap[$methName])) {
848  if (isset($dmap[$methName]['signature'])) {
849  $sigs = array();
850  foreach ($dmap[$methName]['signature'] as $inSig) {
851  $curSig = array();
852  foreach ($inSig as $sig) {
853  $curSig[] = new Value($sig, 'string');
854  }
855  $sigs[] = new Value($curSig, 'array');
856  }
857  $r = new Response(new Value($sigs, 'array'));
858  } else {
859  // NB: according to the official docs, we should be returning a
860  // "none-array" here, which means not-an-array
861  $r = new Response(new Value('undef', 'string'));
862  }
863  } else {
864  $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
865  }
866 
867  return $r;
868  }
869 
870  public static function _xmlrpcs_methodHelp($server, $req)
871  {
872  // let accept as parameter both an xmlrpc value or string
873  if (is_object($req)) {
874  $methName = $req->getParam(0);
875  $methName = $methName->scalarval();
876  } else {
877  $methName = $req;
878  }
879  if (strpos($methName, "system.") === 0) {
880  $dmap = $server->getSystemDispatchMap();
881  } else {
882  $dmap = $server->dmap;
883  }
884  if (isset($dmap[$methName])) {
885  if (isset($dmap[$methName]['docstring'])) {
886  $r = new Response(new Value($dmap[$methName]['docstring']), 'string');
887  } else {
888  $r = new Response(new Value('', 'string'));
889  }
890  } else {
891  $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
892  }
893 
894  return $r;
895  }
896 
897  public static function _xmlrpcs_multicall_error($err)
898  {
899  if (is_string($err)) {
900  $str = PhpXmlRpc::$xmlrpcstr["multicall_${err}"];
901  $code = PhpXmlRpc::$xmlrpcerr["multicall_${err}"];
902  } else {
903  $code = $err->faultCode();
904  $str = $err->faultString();
905  }
906  $struct = array();
907  $struct['faultCode'] = new Value($code, 'int');
908  $struct['faultString'] = new Value($str, 'string');
909 
910  return new Value($struct, 'struct');
911  }
912 
913  public static function _xmlrpcs_multicall_do_call($server, $call)
914  {
915  if ($call->kindOf() != 'struct') {
916  return static::_xmlrpcs_multicall_error('notstruct');
917  }
918  $methName = @$call['methodName'];
919  if (!$methName) {
920  return static::_xmlrpcs_multicall_error('nomethod');
921  }
922  if ($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string') {
923  return static::_xmlrpcs_multicall_error('notstring');
924  }
925  if ($methName->scalarval() == 'system.multicall') {
926  return static::_xmlrpcs_multicall_error('recursion');
927  }
928 
929  $params = @$call['params'];
930  if (!$params) {
931  return static::_xmlrpcs_multicall_error('noparams');
932  }
933  if ($params->kindOf() != 'array') {
934  return static::_xmlrpcs_multicall_error('notarray');
935  }
936 
937  $req = new Request($methName->scalarval());
938  foreach($params as $i => $param) {
939  if (!$req->addParam($param)) {
940  $i++; // for error message, we count params from 1
941  return static::_xmlrpcs_multicall_error(new Response(0,
942  PhpXmlRpc::$xmlrpcerr['incorrect_params'],
943  PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": probable xml error in param " . $i));
944  }
945  }
946 
947  $result = $server->execute($req);
948 
949  if ($result->faultCode() != 0) {
950  return static::_xmlrpcs_multicall_error($result); // Method returned fault.
951  }
952 
953  return new Value(array($result->value()), 'array');
954  }
955 
956  public static function _xmlrpcs_multicall_do_call_phpvals($server, $call)
957  {
958  if (!is_array($call)) {
959  return static::_xmlrpcs_multicall_error('notstruct');
960  }
961  if (!array_key_exists('methodName', $call)) {
962  return static::_xmlrpcs_multicall_error('nomethod');
963  }
964  if (!is_string($call['methodName'])) {
965  return static::_xmlrpcs_multicall_error('notstring');
966  }
967  if ($call['methodName'] == 'system.multicall') {
968  return static::_xmlrpcs_multicall_error('recursion');
969  }
970  if (!array_key_exists('params', $call)) {
971  return static::_xmlrpcs_multicall_error('noparams');
972  }
973  if (!is_array($call['params'])) {
974  return static::_xmlrpcs_multicall_error('notarray');
975  }
976 
977  // this is a real dirty and simplistic hack, since we might have received a
978  // base64 or datetime values, but they will be listed as strings here...
979  $numParams = count($call['params']);
980  $pt = array();
981  $wrapper = new Wrapper();
982  foreach ($call['params'] as $val) {
983  $pt[] = $wrapper->php2XmlrpcType(gettype($val));
984  }
985 
986  $result = $server->execute($call['methodName'], $call['params'], $pt);
987 
988  if ($result->faultCode() != 0) {
989  return static::_xmlrpcs_multicall_error($result); // Method returned fault.
990  }
991 
992  return new Value(array($result->value()), 'array');
993  }
994 
995  public static function _xmlrpcs_multicall($server, $req)
996  {
997  $result = array();
998  // let accept a plain list of php parameters, beside a single xmlrpc msg object
999  if (is_object($req)) {
1000  $calls = $req->getParam(0);
1001  foreach($calls as $call) {
1002  $result[] = static::_xmlrpcs_multicall_do_call($server, $call);
1003  }
1004  } else {
1005  $numCalls = count($req);
1006  for ($i = 0; $i < $numCalls; $i++) {
1007  $result[$i] = static::_xmlrpcs_multicall_do_call_phpvals($server, $req[$i]);
1008  }
1009  }
1010 
1011  return new Response(new Value($result, 'array'));
1012  }
1013 
1022  public static function _xmlrpcs_errorHandler($errCode, $errString, $filename = null, $lineNo = null, $context = null)
1023  {
1024  // obey the @ protocol
1025  if (error_reporting() == 0) {
1026  return;
1027  }
1028 
1029  //if($errCode != E_NOTICE && $errCode != E_WARNING && $errCode != E_USER_NOTICE && $errCode != E_USER_WARNING)
1030  if ($errCode != E_STRICT) {
1032  }
1033  // Try to avoid as much as possible disruption to the previous error handling
1034  // mechanism in place
1035  if (self::$_xmlrpcs_prev_ehandler == '') {
1036  // The previous error handler was the default: all we should do is log error
1037  // to the default error log (if level high enough)
1038  if (ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errCode)) {
1039  error_log($errString);
1040  }
1041  } else {
1042  // Pass control on to previous error handler, trying to avoid loops...
1043  if (self::$_xmlrpcs_prev_ehandler != array('\PhpXmlRpc\Server', '_xmlrpcs_errorHandler')) {
1044  if (is_array(self::$_xmlrpcs_prev_ehandler)) {
1045  // the following works both with static class methods and plain object methods as error handler
1046  call_user_func_array(self::$_xmlrpcs_prev_ehandler, array($errCode, $errString, $filename, $lineNo, $context));
1047  } else {
1048  $method = self::$_xmlrpcs_prev_ehandler;
1049  $method($errCode, $errString, $filename, $lineNo, $context);
1050  }
1051  }
1052  }
1053  }
1054 }
PhpXmlRpc\Helper\Charset
Definition: Charset.php:7
PhpXmlRpc\Response
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Response.php:7
PhpXmlRpc\Wrapper
Definition: Wrapper.php:19
PhpXmlRpc\Server\add_to_map
add_to_map($methodName, $function, $sig=null, $doc=false, $sigDoc=false)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:282
PhpXmlRpc\Server\error_occurred
static error_occurred($msg)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:144
PhpXmlRpc\Server\_xmlrpcs_multicall_error
static _xmlrpcs_multicall_error($err)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:897
PhpXmlRpc\Server
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:8
PhpXmlRpc\Server\serializeDebug
serializeDebug($charsetEncoding='')
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:156
PhpXmlRpc\Server\execute
execute($req, $params=null, $paramTypes=null)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:556
PhpXmlRpc\Server\_xmlrpcs_listMethods
static _xmlrpcs_listMethods($server, $req=null)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:818
PhpXmlRpc\Server\_xmlrpcs_multicall_do_call
static _xmlrpcs_multicall_do_call($server, $call)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:913
PhpXmlRpc\Server\parseRequest
parseRequest($data, $reqEncoding='')
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:456
PhpXmlRpc\Server\_xmlrpcs_methodHelp
static _xmlrpcs_methodHelp($server, $req)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:870
PhpXmlRpc\Server\_xmlrpcs_multicall
static _xmlrpcs_multicall($server, $req)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:995
PhpXmlRpc\Server\_xmlrpcs_methodSignature
static _xmlrpcs_methodSignature($server, $req)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:833
PhpXmlRpc\Server\xmlrpc_debugmsg
static xmlrpc_debugmsg($msg)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:139
PhpXmlRpc\Server\_xmlrpcs_errorHandler
static _xmlrpcs_errorHandler($errCode, $errString, $filename=null, $lineNo=null, $context=null)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:1022
PhpXmlRpc\Server\verifySignature
verifySignature($in, $sigs)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:304
PhpXmlRpc\Value
Definition: Value.php:7
PhpXmlRpc\Server\_xmlrpcs_getCapabilities
static _xmlrpcs_getCapabilities($server, $req=null)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:812
PhpXmlRpc\Server\getSystemDispatchMap
getSystemDispatchMap()
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:739
PhpXmlRpc\Helper\XMLParser
Definition: XMLParser.php:11
PhpXmlRpc\Server\__construct
__construct($dispatchMap=null, $serviceNow=true)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:85
PhpXmlRpc\Server\_xmlrpcs_multicall_do_call_phpvals
static _xmlrpcs_multicall_do_call_phpvals($server, $call)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:956
PhpXmlRpc\Server\getCapabilities
getCapabilities()
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:780
PhpXmlRpc\Server\service
service($data=null, $returnPayload=false)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:187
PhpXmlRpc\Server\xml_header
xml_header($charsetEncoding='')
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:725
PhpXmlRpc\Server\parseRequestHeaders
parseRequestHeaders(&$data, &$reqEncoding, &$respEncoding, &$respCompression)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:353
PhpXmlRpc\Encoder
Definition: Encoder.php:7
PhpXmlRpc\Server\setDebug
setDebug($level)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:125
PhpXmlRpc\Server\debugmsg
debugmsg($string)
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Server.php:716
PhpXmlRpc
Definition: Autoloader.php:3
PhpXmlRpc\Request
Definition: generic/plagiarism/vendor/phpxmlrpc/phpxmlrpc/src/Request.php:10