[ Index ]

PHP Cross Reference of BackPress

title

Body

[close]

/includes/ -> class.ixr.php (source)

   1  <?php
   2  // Last sync [WP16063]
   3  /**
   4   * IXR - The Incutio XML-RPC Library
   5   *
   6   * Copyright (c) 2010, Incutio Ltd.
   7   * All rights reserved.
   8   *
   9   * Redistribution and use in source and binary forms, with or without
  10   * modification, are permitted provided that the following conditions are met:
  11   *
  12   *  - Redistributions of source code must retain the above copyright notice,
  13   *    this list of conditions and the following disclaimer.
  14   *  - Redistributions in binary form must reproduce the above copyright
  15   *    notice, this list of conditions and the following disclaimer in the
  16   *    documentation and/or other materials provided with the distribution.
  17   *  - Neither the name of Incutio Ltd. nor the names of its contributors
  18   *    may be used to endorse or promote products derived from this software
  19   *    without specific prior written permission.
  20   *
  21   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  22   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  23   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  24   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  26   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  29   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  31   * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32   *
  33   * @package IXR
  34   * @since 1.5
  35   *
  36   * @copyright  Incutio Ltd 2010 (http://www.incutio.com)
  37   * @version    1.7.4 7th September 2010
  38   * @author     Simon Willison
  39   * @link       http://scripts.incutio.com/xmlrpc/ Site/manual
  40   * @license    http://www.opensource.org/licenses/bsd-license.php BSD
  41   */
  42  
  43  /**
  44   * IXR_Value
  45   *
  46   * @package IXR
  47   * @since 1.5
  48   */
  49  class IXR_Value {
  50      var $data;
  51      var $type;
  52  
  53      function __construct($data, $type = false)
  54      {
  55          $this->data = $data;
  56          if (!$type) {
  57              $type = $this->calculateType();
  58          }
  59          $this->type = $type;
  60          if ($type == 'struct') {
  61              // Turn all the values in the array in to new IXR_Value objects
  62              foreach ($this->data as $key => $value) {
  63                  $this->data[$key] = new IXR_Value($value);
  64              }
  65          }
  66          if ($type == 'array') {
  67              for ($i = 0, $j = count($this->data); $i < $j; $i++) {
  68                  $this->data[$i] = new IXR_Value($this->data[$i]);
  69              }
  70          }
  71      }
  72  
  73      function IXR_Value($data, $type = false)
  74      {
  75          self::__construct($data, $type);
  76      }
  77  
  78      function calculateType()
  79      {
  80          if ($this->data === true || $this->data === false) {
  81              return 'boolean';
  82          }
  83          if (is_integer($this->data)) {
  84              return 'int';
  85          }
  86          if (is_double($this->data)) {
  87              return 'double';
  88          }
  89  
  90          // Deal with IXR object types base64 and date
  91          if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
  92              return 'date';
  93          }
  94          if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
  95              return 'base64';
  96          }
  97  
  98          // If it is a normal PHP object convert it in to a struct
  99          if (is_object($this->data)) {
 100              $this->data = get_object_vars($this->data);
 101              return 'struct';
 102          }
 103          if (!is_array($this->data)) {
 104              return 'string';
 105          }
 106  
 107          // We have an array - is it an array or a struct?
 108          if ($this->isStruct($this->data)) {
 109              return 'struct';
 110          } else {
 111              return 'array';
 112          }
 113      }
 114  
 115      function getXml()
 116      {
 117          // Return XML for this value
 118          switch ($this->type) {
 119              case 'boolean':
 120                  return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
 121                  break;
 122              case 'int':
 123                  return '<int>'.$this->data.'</int>';
 124                  break;
 125              case 'double':
 126                  return '<double>'.$this->data.'</double>';
 127                  break;
 128              case 'string':
 129                  return '<string>'.htmlspecialchars($this->data).'</string>';
 130                  break;
 131              case 'array':
 132                  $return = '<array><data>'."\n";
 133                  foreach ($this->data as $item) {
 134                      $return .= '  <value>'.$item->getXml()."</value>\n";
 135                  }
 136                  $return .= '</data></array>';
 137                  return $return;
 138                  break;
 139              case 'struct':
 140                  $return = '<struct>'."\n";
 141                  foreach ($this->data as $name => $value) {
 142                      $name = htmlspecialchars($name);
 143                      $return .= "  <member><name>$name</name><value>";
 144                      $return .= $value->getXml()."</value></member>\n";
 145                  }
 146                  $return .= '</struct>';
 147                  return $return;
 148                  break;
 149              case 'date':
 150              case 'base64':
 151                  return $this->data->getXml();
 152                  break;
 153          }
 154          return false;
 155      }
 156  
 157      /**
 158       * Checks whether or not the supplied array is a struct or not
 159       *
 160       * @param unknown_type $array
 161       * @return boolean
 162       */
 163      function isStruct($array)
 164      {
 165          $expected = 0;
 166          foreach ($array as $key => $value) {
 167              if ((string)$key != (string)$expected) {
 168                  return true;
 169              }
 170              $expected++;
 171          }
 172          return false;
 173      }
 174  }
 175  
 176  /**
 177   * IXR_MESSAGE
 178   *
 179   * @package IXR
 180   * @since 1.5
 181   *
 182   */
 183  class IXR_Message
 184  {
 185      var $message;
 186      var $messageType;  // methodCall / methodResponse / fault
 187      var $faultCode;
 188      var $faultString;
 189      var $methodName;
 190      var $params;
 191  
 192      // Current variable stacks
 193      var $_arraystructs = array();   // The stack used to keep track of the current array/struct
 194      var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
 195      var $_currentStructName = array();  // A stack as well
 196      var $_param;
 197      var $_value;
 198      var $_currentTag;
 199      var $_currentTagContents;
 200      // The XML parser
 201      var $_parser;
 202  
 203      function __construct($message)
 204      {
 205          $this->message =& $message;
 206      }
 207  
 208      function IXR_Message($message)
 209      {
 210          self::__construct($message);
 211      }
 212  
 213      function parse()
 214      {
 215          // first remove the XML declaration
 216          // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages
 217          $header = preg_replace( '/<\?xml.*?\?'.'>/', '', substr($this->message, 0, 100), 1);
 218          $this->message = substr_replace($this->message, $header, 0, 100);
 219          if (trim($this->message) == '') {
 220              return false;
 221          }
 222          $this->_parser = xml_parser_create();
 223          // Set XML parser to take the case of tags in to account
 224          xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
 225          // Set XML parser callback functions
 226          xml_set_object($this->_parser, $this);
 227          xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
 228          xml_set_character_data_handler($this->_parser, 'cdata');
 229          $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages
 230          do {
 231              if (strlen($this->message) <= $chunk_size) {
 232                  $final = true;
 233              }
 234              $part = substr($this->message, 0, $chunk_size);
 235              $this->message = substr($this->message, $chunk_size);
 236              if (!xml_parse($this->_parser, $part, $final)) {
 237                  return false;
 238              }
 239              if ($final) {
 240                  break;
 241              }
 242          } while (true);
 243          xml_parser_free($this->_parser);
 244  
 245          // Grab the error messages, if any
 246          if ($this->messageType == 'fault') {
 247              $this->faultCode = $this->params[0]['faultCode'];
 248              $this->faultString = $this->params[0]['faultString'];
 249          }
 250          return true;
 251      }
 252  
 253      function tag_open($parser, $tag, $attr)
 254      {
 255          $this->_currentTagContents = '';
 256          $this->currentTag = $tag;
 257          switch($tag) {
 258              case 'methodCall':
 259              case 'methodResponse':
 260              case 'fault':
 261                  $this->messageType = $tag;
 262                  break;
 263                  /* Deal with stacks of arrays and structs */
 264              case 'data':    // data is to all intents and puposes more interesting than array
 265                  $this->_arraystructstypes[] = 'array';
 266                  $this->_arraystructs[] = array();
 267                  break;
 268              case 'struct':
 269                  $this->_arraystructstypes[] = 'struct';
 270                  $this->_arraystructs[] = array();
 271                  break;
 272          }
 273      }
 274  
 275      function cdata($parser, $cdata)
 276      {
 277          $this->_currentTagContents .= $cdata;
 278      }
 279  
 280      function tag_close($parser, $tag)
 281      {
 282          $valueFlag = false;
 283          switch($tag) {
 284              case 'int':
 285              case 'i4':
 286                  $value = (int)trim($this->_currentTagContents);
 287                  $valueFlag = true;
 288                  break;
 289              case 'double':
 290                  $value = (double)trim($this->_currentTagContents);
 291                  $valueFlag = true;
 292                  break;
 293              case 'string':
 294                  $value = (string)trim($this->_currentTagContents);
 295                  $valueFlag = true;
 296                  break;
 297              case 'dateTime.iso8601':
 298                  $value = new IXR_Date(trim($this->_currentTagContents));
 299                  $valueFlag = true;
 300                  break;
 301              case 'value':
 302                  // "If no type is indicated, the type is string."
 303                  if (trim($this->_currentTagContents) != '') {
 304                      $value = (string)$this->_currentTagContents;
 305                      $valueFlag = true;
 306                  }
 307                  break;
 308              case 'boolean':
 309                  $value = (boolean)trim($this->_currentTagContents);
 310                  $valueFlag = true;
 311                  break;
 312              case 'base64':
 313                  $value = base64_decode($this->_currentTagContents);
 314                  $valueFlag = true;
 315                  break;
 316                  /* Deal with stacks of arrays and structs */
 317              case 'data':
 318              case 'struct':
 319                  $value = array_pop($this->_arraystructs);
 320                  array_pop($this->_arraystructstypes);
 321                  $valueFlag = true;
 322                  break;
 323              case 'member':
 324                  array_pop($this->_currentStructName);
 325                  break;
 326              case 'name':
 327                  $this->_currentStructName[] = trim($this->_currentTagContents);
 328                  break;
 329              case 'methodName':
 330                  $this->methodName = trim($this->_currentTagContents);
 331                  break;
 332          }
 333  
 334          if ($valueFlag) {
 335              if (count($this->_arraystructs) > 0) {
 336                  // Add value to struct or array
 337                  if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
 338                      // Add to struct
 339                      $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
 340                  } else {
 341                      // Add to array
 342                      $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
 343                  }
 344              } else {
 345                  // Just add as a paramater
 346                  $this->params[] = $value;
 347              }
 348          }
 349          $this->_currentTagContents = '';
 350      }
 351  }
 352  
 353  /**
 354   * IXR_Server
 355   *
 356   * @package IXR
 357   * @since 1.5
 358   */
 359  class IXR_Server
 360  {
 361      var $data;
 362      var $callbacks = array();
 363      var $message;
 364      var $capabilities;
 365  
 366      function __construct($callbacks = false, $data = false, $wait = false)
 367      {
 368          $this->setCapabilities();
 369          if ($callbacks) {
 370              $this->callbacks = $callbacks;
 371          }
 372          $this->setCallbacks();
 373          if (!$wait) {
 374              $this->serve($data);
 375          }
 376      }
 377  
 378      function IXR_Server($callbacks = false, $data = false, $wait = false)
 379      {
 380          self::__construct($callbacks, $data, $wait);
 381      }
 382  
 383      function serve($data = false)
 384      {
 385          if (!$data) {
 386              if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
 387                  header('Content-Type: text/plain'); // merged from WP #9093
 388                  die('XML-RPC server accepts POST requests only.');
 389              }
 390  
 391              global $HTTP_RAW_POST_DATA;
 392              if (empty($HTTP_RAW_POST_DATA)) {
 393                  // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293
 394                  $data = file_get_contents('php://input');
 395              } else {
 396                  $data =& $HTTP_RAW_POST_DATA;
 397              }
 398          }
 399          $this->message = new IXR_Message($data);
 400          if (!$this->message->parse()) {
 401              $this->error(-32700, 'parse error. not well formed');
 402          }
 403          if ($this->message->messageType != 'methodCall') {
 404              $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
 405          }
 406          $result = $this->call($this->message->methodName, $this->message->params);
 407  
 408          // Is the result an error?
 409          if (is_a($result, 'IXR_Error')) {
 410              $this->error($result);
 411          }
 412  
 413          // Encode the result
 414          $r = new IXR_Value($result);
 415          $resultxml = $r->getXml();
 416  
 417          // Create the XML
 418          $xml = <<<EOD
 419  <methodResponse>
 420    <params>
 421      <param>
 422        <value>
 423        $resultxml
 424        </value>
 425      </param>
 426    </params>
 427  </methodResponse>
 428  
 429  EOD;
 430        // Send it
 431        $this->output($xml);
 432      }
 433  
 434      function call($methodname, $args)
 435      {
 436          if (!$this->hasMethod($methodname)) {
 437              return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
 438          }
 439          $method = $this->callbacks[$methodname];
 440  
 441          // Perform the callback and send the response
 442          if (count($args) == 1) {
 443              // If only one paramater just send that instead of the whole array
 444              $args = $args[0];
 445          }
 446  
 447          // Are we dealing with a function or a method?
 448          if (is_string($method) && substr($method, 0, 5) == 'this:') {
 449              // It's a class method - check it exists
 450              $method = substr($method, 5);
 451              if (!method_exists($this, $method)) {
 452                  return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.');
 453              }
 454  
 455              //Call the method
 456              $result = $this->$method($args);
 457          } else {
 458              // It's a function - does it exist?
 459              if (is_array($method)) {
 460                  if (!method_exists($method[0], $method[1])) {
 461                      return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.');
 462                  }
 463              } else if (!function_exists($method)) {
 464                  return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
 465              }
 466  
 467              // Call the function
 468              $result = call_user_func($method, $args);
 469          }
 470          return $result;
 471      }
 472  
 473      function error($error, $message = false)
 474      {
 475          // Accepts either an error object or an error code and message
 476          if ($message && !is_object($error)) {
 477              $error = new IXR_Error($error, $message);
 478          }
 479          $this->output($error->getXml());
 480      }
 481  
 482      function output($xml)
 483      {
 484          $xml = '<?xml version="1.0"?>'."\n".$xml;
 485          $length = strlen($xml);
 486          header('Connection: close');
 487          header('Content-Length: '.$length);
 488          header('Content-Type: text/xml');
 489          header('Date: '.date('r'));
 490          echo $xml;
 491          exit;
 492      }
 493  
 494      function hasMethod($method)
 495      {
 496          return in_array($method, array_keys($this->callbacks));
 497      }
 498  
 499      function setCapabilities()
 500      {
 501          // Initialises capabilities array
 502          $this->capabilities = array(
 503              'xmlrpc' => array(
 504                  'specUrl' => 'http://www.xmlrpc.com/spec',
 505                  'specVersion' => 1
 506          ),
 507              'faults_interop' => array(
 508                  'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
 509                  'specVersion' => 20010516
 510          ),
 511              'system.multicall' => array(
 512                  'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
 513                  'specVersion' => 1
 514          ),
 515          );
 516      }
 517  
 518      function getCapabilities($args)
 519      {
 520          return $this->capabilities;
 521      }
 522  
 523      function setCallbacks()
 524      {
 525          $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
 526          $this->callbacks['system.listMethods'] = 'this:listMethods';
 527          $this->callbacks['system.multicall'] = 'this:multiCall';
 528      }
 529  
 530      function listMethods($args)
 531      {
 532          // Returns a list of methods - uses array_reverse to ensure user defined
 533          // methods are listed before server defined methods
 534          return array_reverse(array_keys($this->callbacks));
 535      }
 536  
 537      function multiCall($methodcalls)
 538      {
 539          // See http://www.xmlrpc.com/discuss/msgReader$1208
 540          $return = array();
 541          foreach ($methodcalls as $call) {
 542              $method = $call['methodName'];
 543              $params = $call['params'];
 544              if ($method == 'system.multicall') {
 545                  $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
 546              } else {
 547                  $result = $this->call($method, $params);
 548              }
 549              if (is_a($result, 'IXR_Error')) {
 550                  $return[] = array(
 551                      'faultCode' => $result->code,
 552                      'faultString' => $result->message
 553                  );
 554              } else {
 555                  $return[] = array($result);
 556              }
 557          }
 558          return $return;
 559      }
 560  }
 561  
 562  /**
 563   * IXR_Request
 564   *
 565   * @package IXR
 566   * @since 1.5
 567   */
 568  class IXR_Request
 569  {
 570      var $method;
 571      var $args;
 572      var $xml;
 573  
 574      function __construct($method, $args)
 575      {
 576          $this->method = $method;
 577          $this->args = $args;
 578          $this->xml = <<<EOD
 579  <?xml version="1.0"?>
 580  <methodCall>
 581  <methodName>{$this->method}</methodName>
 582  <params>
 583  
 584  EOD;
 585          foreach ($this->args as $arg) {
 586              $this->xml .= '<param><value>';
 587              $v = new IXR_Value($arg);
 588              $this->xml .= $v->getXml();
 589              $this->xml .= "</value></param>\n";
 590          }
 591          $this->xml .= '</params></methodCall>';
 592      }
 593  
 594      function IXR_Request($method, $args)
 595      {
 596          self::__construct($method, $args);
 597      }
 598  
 599      function getLength()
 600      {
 601          return strlen($this->xml);
 602      }
 603  
 604      function getXml()
 605      {
 606          return $this->xml;
 607      }
 608  }
 609  
 610  /**
 611   * IXR_Client
 612   *
 613   * @package IXR
 614   * @since 1.5
 615   *
 616   */
 617  class IXR_Client
 618  {
 619      var $server;
 620      var $port;
 621      var $path;
 622      var $useragent;
 623      var $response;
 624      var $message = false;
 625      var $debug = false;
 626      var $timeout;
 627      var $headers = array();
 628  
 629      // Storage place for an error message
 630      var $error = false;
 631  
 632      function __construct($server, $path = false, $port = 80, $timeout = 15)
 633      {
 634          if (!$path) {
 635              // Assume we have been given a URL instead
 636              $bits = parse_url($server);
 637              $this->server = $bits['host'];
 638              $this->port = isset($bits['port']) ? $bits['port'] : 80;
 639              $this->path = isset($bits['path']) ? $bits['path'] : '/';
 640  
 641              // Make absolutely sure we have a path
 642              if (!$this->path) {
 643                  $this->path = '/';
 644              }
 645          } else {
 646              $this->server = $server;
 647              $this->path = $path;
 648              $this->port = $port;
 649          }
 650          $this->useragent = 'The Incutio XML-RPC PHP Library';
 651          $this->timeout = $timeout;
 652      }
 653  
 654      function IXR_Client($server, $path = false, $port = 80, $timeout = 15)
 655      {
 656          self::__construct($server, $path, $port, $timeout);
 657      }
 658  
 659      function query()
 660      {
 661          $args = func_get_args();
 662          $method = array_shift($args);
 663          $request = new IXR_Request($method, $args);
 664          $length = $request->getLength();
 665          $xml = $request->getXml();
 666          $r = "\r\n";
 667          $request  = "POST {$this->path} HTTP/1.0$r";
 668  
 669          // Merged from WP #8145 - allow custom headers
 670          $this->headers['Host']          = $this->server;
 671          $this->headers['Content-Type']  = 'text/xml';
 672          $this->headers['User-Agent']    = $this->useragent;
 673          $this->headers['Content-Length']= $length;
 674  
 675          foreach( $this->headers as $header => $value ) {
 676              $request .= "{$header}: {$value}{$r}";
 677          }
 678          $request .= $r;
 679  
 680          $request .= $xml;
 681  
 682          // Now send the request
 683          if ($this->debug) {
 684              echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n";
 685          }
 686  
 687          if ($this->timeout) {
 688              $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
 689          } else {
 690              $fp = @fsockopen($this->server, $this->port, $errno, $errstr);
 691          }
 692          if (!$fp) {
 693              $this->error = new IXR_Error(-32300, 'transport error - could not open socket');
 694              return false;
 695          }
 696          fputs($fp, $request);
 697          $contents = '';
 698          $debugContents = '';
 699          $gotFirstLine = false;
 700          $gettingHeaders = true;
 701          while (!feof($fp)) {
 702              $line = fgets($fp, 4096);
 703              if (!$gotFirstLine) {
 704                  // Check line for '200'
 705                  if (strstr($line, '200') === false) {
 706                      $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
 707                      return false;
 708                  }
 709                  $gotFirstLine = true;
 710              }
 711              if (trim($line) == '') {
 712                  $gettingHeaders = false;
 713              }
 714              if (!$gettingHeaders) {
 715                  // merged from WP #12559 - remove trim
 716                  $contents .= $line;
 717              }
 718              if ($this->debug) {
 719                  $debugContents .= $line;
 720              }
 721          }
 722          if ($this->debug) {
 723              echo '<pre class="ixr_response">'.htmlspecialchars($debugContents)."\n</pre>\n\n";
 724          }
 725  
 726          // Now parse what we've got back
 727          $this->message = new IXR_Message($contents);
 728          if (!$this->message->parse()) {
 729              // XML error
 730              $this->error = new IXR_Error(-32700, 'parse error. not well formed');
 731              return false;
 732          }
 733  
 734          // Is the message a fault?
 735          if ($this->message->messageType == 'fault') {
 736              $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
 737              return false;
 738          }
 739  
 740          // Message must be OK
 741          return true;
 742      }
 743  
 744      function getResponse()
 745      {
 746          // methodResponses can only have one param - return that
 747          return $this->message->params[0];
 748      }
 749  
 750      function isError()
 751      {
 752          return (is_object($this->error));
 753      }
 754  
 755      function getErrorCode()
 756      {
 757          return $this->error->code;
 758      }
 759  
 760      function getErrorMessage()
 761      {
 762          return $this->error->message;
 763      }
 764  }
 765  
 766  
 767  /**
 768   * IXR_Error
 769   *
 770   * @package IXR
 771   * @since 1.5
 772   */
 773  class IXR_Error
 774  {
 775      var $code;
 776      var $message;
 777  
 778      function __construct($code, $message)
 779      {
 780          $this->code = $code;
 781          $this->message = htmlspecialchars($message);
 782      }
 783  
 784      function IXR_Error($code, $message)
 785      {
 786          self::__construct($code, $message);
 787      }
 788  
 789      function getXml()
 790      {
 791          $xml = <<<EOD
 792  <methodResponse>
 793    <fault>
 794      <value>
 795        <struct>
 796          <member>
 797            <name>faultCode</name>
 798            <value><int>{$this->code}</int></value>
 799          </member>
 800          <member>
 801            <name>faultString</name>
 802            <value><string>{$this->message}</string></value>
 803          </member>
 804        </struct>
 805      </value>
 806    </fault>
 807  </methodResponse>
 808  
 809  EOD;
 810          return $xml;
 811      }
 812  }
 813  
 814  /**
 815   * IXR_Date
 816   *
 817   * @package IXR
 818   * @since 1.5
 819   */
 820  class IXR_Date {
 821      var $year;
 822      var $month;
 823      var $day;
 824      var $hour;
 825      var $minute;
 826      var $second;
 827      var $timezone;
 828  
 829      function __construct($time)
 830      {
 831          // $time can be a PHP timestamp or an ISO one
 832          if (is_numeric($time)) {
 833              $this->parseTimestamp($time);
 834          } else {
 835              $this->parseIso($time);
 836          }
 837      }
 838  
 839      function IXR_Date($time)
 840      {
 841          self::__construct($time);
 842      }
 843  
 844      function parseTimestamp($timestamp)
 845      {
 846          $this->year = date('Y', $timestamp);
 847          $this->month = date('m', $timestamp);
 848          $this->day = date('d', $timestamp);
 849          $this->hour = date('H', $timestamp);
 850          $this->minute = date('i', $timestamp);
 851          $this->second = date('s', $timestamp);
 852          $this->timezone = '';
 853      }
 854  
 855      function parseIso($iso)
 856      {
 857          $this->year = substr($iso, 0, 4);
 858          $this->month = substr($iso, 4, 2);
 859          $this->day = substr($iso, 6, 2);
 860          $this->hour = substr($iso, 9, 2);
 861          $this->minute = substr($iso, 12, 2);
 862          $this->second = substr($iso, 15, 2);
 863          $this->timezone = substr($iso, 17);
 864      }
 865  
 866      function getIso()
 867      {
 868          return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
 869      }
 870  
 871      function getXml()
 872      {
 873          return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
 874      }
 875  
 876      function getTimestamp()
 877      {
 878          return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
 879      }
 880  }
 881  
 882  /**
 883   * IXR_Base64
 884   *
 885   * @package IXR
 886   * @since 1.5
 887   */
 888  class IXR_Base64
 889  {
 890      var $data;
 891  
 892      function __construct($data)
 893      {
 894          $this->data = $data;
 895      }
 896  
 897      function IXR_Base64($data)
 898      {
 899          self::__construct($data);
 900      }
 901  
 902      function getXml()
 903      {
 904          return '<base64>'.base64_encode($this->data).'</base64>';
 905      }
 906  }
 907  
 908  /**
 909   * IXR_IntrospectionServer
 910   *
 911   * @package IXR
 912   * @since 1.5
 913   */
 914  class IXR_IntrospectionServer extends IXR_Server
 915  {
 916      var $signatures;
 917      var $help;
 918  
 919      function __construct()
 920      {
 921          $this->setCallbacks();
 922          $this->setCapabilities();
 923          $this->capabilities['introspection'] = array(
 924              'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
 925              'specVersion' => 1
 926          );
 927          $this->addCallback(
 928              'system.methodSignature',
 929              'this:methodSignature',
 930              array('array', 'string'),
 931              'Returns an array describing the return type and required parameters of a method'
 932          );
 933          $this->addCallback(
 934              'system.getCapabilities',
 935              'this:getCapabilities',
 936              array('struct'),
 937              'Returns a struct describing the XML-RPC specifications supported by this server'
 938          );
 939          $this->addCallback(
 940              'system.listMethods',
 941              'this:listMethods',
 942              array('array'),
 943              'Returns an array of available methods on this server'
 944          );
 945          $this->addCallback(
 946              'system.methodHelp',
 947              'this:methodHelp',
 948              array('string', 'string'),
 949              'Returns a documentation string for the specified method'
 950          );
 951      }
 952  
 953      function IXR_IntrospectionServer()
 954      {
 955          self::__construct();
 956      }
 957  
 958      function addCallback($method, $callback, $args, $help)
 959      {
 960          $this->callbacks[$method] = $callback;
 961          $this->signatures[$method] = $args;
 962          $this->help[$method] = $help;
 963      }
 964  
 965      function call($methodname, $args)
 966      {
 967          // Make sure it's in an array
 968          if ($args && !is_array($args)) {
 969              $args = array($args);
 970          }
 971  
 972          // Over-rides default call method, adds signature check
 973          if (!$this->hasMethod($methodname)) {
 974              return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
 975          }
 976          $method = $this->callbacks[$methodname];
 977          $signature = $this->signatures[$methodname];
 978          $returnType = array_shift($signature);
 979  
 980          // Check the number of arguments
 981          if (count($args) != count($signature)) {
 982              return new IXR_Error(-32602, 'server error. wrong number of method parameters');
 983          }
 984  
 985          // Check the argument types
 986          $ok = true;
 987          $argsbackup = $args;
 988          for ($i = 0, $j = count($args); $i < $j; $i++) {
 989              $arg = array_shift($args);
 990              $type = array_shift($signature);
 991              switch ($type) {
 992                  case 'int':
 993                  case 'i4':
 994                      if (is_array($arg) || !is_int($arg)) {
 995                          $ok = false;
 996                      }
 997                      break;
 998                  case 'base64':
 999                  case 'string':
1000                      if (!is_string($arg)) {
1001                          $ok = false;
1002                      }
1003                      break;
1004                  case 'boolean':
1005                      if ($arg !== false && $arg !== true) {
1006                          $ok = false;
1007                      }
1008                      break;
1009                  case 'float':
1010                  case 'double':
1011                      if (!is_float($arg)) {
1012                          $ok = false;
1013                      }
1014                      break;
1015                  case 'date':
1016                  case 'dateTime.iso8601':
1017                      if (!is_a($arg, 'IXR_Date')) {
1018                          $ok = false;
1019                      }
1020                      break;
1021              }
1022              if (!$ok) {
1023                  return new IXR_Error(-32602, 'server error. invalid method parameters');
1024              }
1025          }
1026          // It passed the test - run the "real" method call
1027          return parent::call($methodname, $argsbackup);
1028      }
1029  
1030      function methodSignature($method)
1031      {
1032          if (!$this->hasMethod($method)) {
1033              return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
1034          }
1035          // We should be returning an array of types
1036          $types = $this->signatures[$method];
1037          $return = array();
1038          foreach ($types as $type) {
1039              switch ($type) {
1040                  case 'string':
1041                      $return[] = 'string';
1042                      break;
1043                  case 'int':
1044                  case 'i4':
1045                      $return[] = 42;
1046                      break;
1047                  case 'double':
1048                      $return[] = 3.1415;
1049                      break;
1050                  case 'dateTime.iso8601':
1051                      $return[] = new IXR_Date(time());
1052                      break;
1053                  case 'boolean':
1054                      $return[] = true;
1055                      break;
1056                  case 'base64':
1057                      $return[] = new IXR_Base64('base64');
1058                      break;
1059                  case 'array':
1060                      $return[] = array('array');
1061                      break;
1062                  case 'struct':
1063                      $return[] = array('struct' => 'struct');
1064                      break;
1065              }
1066          }
1067          return $return;
1068      }
1069  
1070      function methodHelp($method)
1071      {
1072          return $this->help[$method];
1073      }
1074  }
1075  
1076  /**
1077   * IXR_ClientMulticall
1078   *
1079   * @package IXR
1080   * @since 1.5
1081   */
1082  class IXR_ClientMulticall extends IXR_Client
1083  {
1084      var $calls = array();
1085  
1086      function __construct($server, $path = false, $port = 80)
1087      {
1088          parent::__construct($server, $path, $port);
1089          $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
1090      }
1091  
1092      function IXR_ClientMulticall($server, $path = false, $port = 80)
1093      {
1094          self::__construct($server, $path, $port);
1095      }
1096  
1097      function addCall()
1098      {
1099          $args = func_get_args();
1100          $methodName = array_shift($args);
1101          $struct = array(
1102              'methodName' => $methodName,
1103              'params' => $args
1104          );
1105          $this->calls[] = $struct;
1106      }
1107  
1108      function query()
1109      {
1110          // Prepare multicall, then call the parent::query() method
1111          return parent::query('system.multicall', $this->calls);
1112      }
1113  }
1114  
1115  ?>


Generated: Thu Dec 26 01:01:01 2024 Cross-referenced by PHPXref 0.7.1