[ Index ]

WordPress Source Cross Reference

title

Body

[close]

/wp-includes/ -> class-IXR.php (source)

   1  <?php
   2  /* 
   3     IXR - The Inutio XML-RPC Library - (c) Incutio Ltd 2002-2005
   4     Version 1.7 (beta) - Simon Willison, 23rd May 2005
   5     Site:   http://scripts.incutio.com/xmlrpc/
   6     Manual: http://scripts.incutio.com/xmlrpc/manual.php
   7     Made available under the BSD License: http://www.opensource.org/licenses/bsd-license.php
   8  */
   9  
  10  class IXR_Value {
  11      var $data;
  12      var $type;
  13      function IXR_Value ($data, $type = false) {
  14          $this->data = $data;
  15          if (!$type) {
  16              $type = $this->calculateType();
  17          }
  18          $this->type = $type;
  19          if ($type == 'struct') {
  20              /* Turn all the values in the array in to new IXR_Value objects */
  21              foreach ($this->data as $key => $value) {
  22                  $this->data[$key] = new IXR_Value($value);
  23              }
  24          }
  25          if ($type == 'array') {
  26              for ($i = 0, $j = count($this->data); $i < $j; $i++) {
  27                  $this->data[$i] = new IXR_Value($this->data[$i]);
  28              }
  29          }
  30      }
  31      function calculateType() {
  32          if ($this->data === true || $this->data === false) {
  33              return 'boolean';
  34          }
  35          if (is_integer($this->data)) {
  36              return 'int';
  37          }
  38          if (is_double($this->data)) {
  39              return 'double';
  40          }
  41          // Deal with IXR object types base64 and date
  42          if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
  43              return 'date';
  44          }
  45          if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
  46              return 'base64';
  47          }
  48          // If it is a normal PHP object convert it in to a struct
  49          if (is_object($this->data)) {
  50              
  51              $this->data = get_object_vars($this->data);
  52              return 'struct';
  53          }
  54          if (!is_array($this->data)) {
  55              return 'string';
  56          }
  57          /* We have an array - is it an array or a struct ? */
  58          if ($this->isStruct($this->data)) {
  59              return 'struct';
  60          } else {
  61              return 'array';
  62          }
  63      }
  64      function getXml() {
  65          /* Return XML for this value */
  66          switch ($this->type) {
  67              case 'boolean':
  68                  return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
  69                  break;
  70              case 'int':
  71                  return '<int>'.$this->data.'</int>';
  72                  break;
  73              case 'double':
  74                  return '<double>'.$this->data.'</double>';
  75                  break;
  76              case 'string':
  77                  return '<string>'.htmlspecialchars($this->data).'</string>';
  78                  break;
  79              case 'array':
  80                  $return = '<array><data>'."\n";
  81                  foreach ($this->data as $item) {
  82                      $return .= '  <value>'.$item->getXml()."</value>\n";
  83                  }
  84                  $return .= '</data></array>';
  85                  return $return;
  86                  break;
  87              case 'struct':
  88                  $return = '<struct>'."\n";
  89                  foreach ($this->data as $name => $value) {
  90                      $name = htmlspecialchars($name);
  91                      $return .= "  <member><name>$name</name><value>";
  92                      $return .= $value->getXml()."</value></member>\n";
  93                  }
  94                  $return .= '</struct>';
  95                  return $return;
  96                  break;
  97              case 'date':
  98              case 'base64':
  99                  return $this->data->getXml();
 100                  break;
 101          }
 102          return false;
 103      }
 104      function isStruct($array) {
 105          /* Nasty function to check if an array is a struct or not */
 106          $expected = 0;
 107          foreach ($array as $key => $value) {
 108              if ((string)$key != (string)$expected) {
 109                  return true;
 110              }
 111              $expected++;
 112          }
 113          return false;
 114      }
 115  }
 116  
 117  
 118  class IXR_Message {
 119      var $message;
 120      var $messageType;  // methodCall / methodResponse / fault
 121      var $faultCode;
 122      var $faultString;
 123      var $methodName;
 124      var $params;
 125      // Current variable stacks
 126      var $_arraystructs = array();   // The stack used to keep track of the current array/struct
 127      var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
 128      var $_currentStructName = array();  // A stack as well
 129      var $_param;
 130      var $_value;
 131      var $_currentTag;
 132      var $_currentTagContents;
 133      // The XML parser
 134      var $_parser;
 135      function IXR_Message ($message) {
 136          $this->message = $message;
 137      }
 138      function parse() {
 139          // first remove the XML declaration
 140          $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message);
 141          if (trim($this->message) == '') {
 142              return false;
 143          }
 144          $this->_parser = xml_parser_create();
 145          // Set XML parser to take the case of tags in to account
 146          xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
 147          // Set XML parser callback functions
 148          xml_set_object($this->_parser, $this);
 149          xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
 150          xml_set_character_data_handler($this->_parser, 'cdata');
 151          if (!xml_parse($this->_parser, $this->message)) {
 152              /* die(sprintf('XML error: %s at line %d',
 153                  xml_error_string(xml_get_error_code($this->_parser)),
 154                  xml_get_current_line_number($this->_parser))); */
 155              return false;
 156          }
 157          xml_parser_free($this->_parser);
 158          // Grab the error messages, if any
 159          if ($this->messageType == 'fault') {
 160              $this->faultCode = $this->params[0]['faultCode'];
 161              $this->faultString = $this->params[0]['faultString'];
 162          }
 163          return true;
 164      }
 165      function tag_open($parser, $tag, $attr) {
 166          $this->_currentTagContents = '';
 167          $this->currentTag = $tag;
 168          switch($tag) {
 169              case 'methodCall':
 170              case 'methodResponse':
 171              case 'fault':
 172                  $this->messageType = $tag;
 173                  break;
 174              /* Deal with stacks of arrays and structs */
 175              case 'data':    // data is to all intents and puposes more interesting than array
 176                  $this->_arraystructstypes[] = 'array';
 177                  $this->_arraystructs[] = array();
 178                  break;
 179              case 'struct':
 180                  $this->_arraystructstypes[] = 'struct';
 181                  $this->_arraystructs[] = array();
 182                  break;
 183          }
 184      }
 185      function cdata($parser, $cdata) {
 186          $this->_currentTagContents .= $cdata;
 187      }
 188      function tag_close($parser, $tag) {
 189          $valueFlag = false;
 190          switch($tag) {
 191              case 'int':
 192              case 'i4':
 193                  $value = (int) trim($this->_currentTagContents);
 194                  $valueFlag = true;
 195                  break;
 196              case 'double':
 197                  $value = (double) trim($this->_currentTagContents);
 198                  $valueFlag = true;
 199                  break;
 200              case 'string':
 201                  $value = $this->_currentTagContents;
 202                  $valueFlag = true;
 203                  break;
 204              case 'dateTime.iso8601':
 205                  $value = new IXR_Date(trim($this->_currentTagContents));
 206                  // $value = $iso->getTimestamp();
 207                  $valueFlag = true;
 208                  break;
 209              case 'value':
 210                  // "If no type is indicated, the type is string."
 211                  if (trim($this->_currentTagContents) != '') {
 212                      $value = (string)$this->_currentTagContents;
 213                      $valueFlag = true;
 214                  }
 215                  break;
 216              case 'boolean':
 217                  $value = (boolean) trim($this->_currentTagContents);
 218                  $valueFlag = true;
 219                  break;
 220              case 'base64':
 221                  $value = base64_decode( trim( $this->_currentTagContents ) );
 222                  $valueFlag = true;
 223                  break;
 224              /* Deal with stacks of arrays and structs */
 225              case 'data':
 226              case 'struct':
 227                  $value = array_pop($this->_arraystructs);
 228                  array_pop($this->_arraystructstypes);
 229                  $valueFlag = true;
 230                  break;
 231              case 'member':
 232                  array_pop($this->_currentStructName);
 233                  break;
 234              case 'name':
 235                  $this->_currentStructName[] = trim($this->_currentTagContents);
 236                  break;
 237              case 'methodName':
 238                  $this->methodName = trim($this->_currentTagContents);
 239                  break;
 240          }
 241          if ($valueFlag) {
 242              if (count($this->_arraystructs) > 0) {
 243                  // Add value to struct or array
 244                  if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
 245                      // Add to struct
 246                      $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
 247                  } else {
 248                      // Add to array
 249                      $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
 250                  }
 251              } else {
 252                  // Just add as a paramater
 253                  $this->params[] = $value;
 254              }
 255          }
 256          $this->_currentTagContents = '';
 257      }       
 258  }
 259  
 260  
 261  class IXR_Server {
 262      var $data;
 263      var $callbacks = array();
 264      var $message;
 265      var $capabilities;
 266      function IXR_Server($callbacks = false, $data = false) {
 267          $this->setCapabilities();
 268          if ($callbacks) {
 269              $this->callbacks = $callbacks;
 270          }
 271          $this->setCallbacks();
 272          $this->serve($data);
 273      }
 274      function serve($data = false) {
 275          if (!$data) {
 276              global $HTTP_RAW_POST_DATA;
 277              if (!$HTTP_RAW_POST_DATA) {
 278                 die('XML-RPC server accepts POST requests only.');
 279              }
 280              $data = $HTTP_RAW_POST_DATA;
 281          }
 282          $this->message = new IXR_Message($data);
 283          if (!$this->message->parse()) {
 284              $this->error(-32700, 'parse error. not well formed');
 285          }
 286          if ($this->message->messageType != 'methodCall') {
 287              $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
 288          }
 289          $result = $this->call($this->message->methodName, $this->message->params);
 290          // Is the result an error?
 291          if (is_a($result, 'IXR_Error')) {
 292              $this->error($result);
 293          }
 294          // Encode the result
 295          $r = new IXR_Value($result);
 296          $resultxml = $r->getXml();
 297          // Create the XML
 298          $xml = <<<EOD
 299  <methodResponse>
 300    <params>
 301      <param>
 302        <value>
 303          $resultxml
 304        </value>
 305      </param>
 306    </params>
 307  </methodResponse>
 308  
 309  EOD;
 310          // Send it
 311          $this->output($xml);
 312      }
 313      function call($methodname, $args) {
 314          if (!$this->hasMethod($methodname)) {
 315              return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
 316          }
 317          $method = $this->callbacks[$methodname];
 318          // Perform the callback and send the response
 319          if (count($args) == 1) {
 320              // If only one paramater just send that instead of the whole array
 321              $args = $args[0];
 322          }
 323          // Are we dealing with a function or a method?
 324          if (substr($method, 0, 5) == 'this:') {
 325              // It's a class method - check it exists
 326              $method = substr($method, 5);
 327              if (!method_exists($this, $method)) {
 328                  return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.');
 329              }
 330              // Call the method
 331              $result = $this->$method($args);
 332          } else {
 333              // It's a function - does it exist?
 334              if (is_array($method)) {
 335                  if (!method_exists($method[0], $method[1])) {
 336                  return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.');
 337                  }
 338              } else if (!function_exists($method)) {
 339                  return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
 340              }
 341              // Call the function
 342              $result = call_user_func($method, $args);
 343          }
 344          return $result;
 345      }
 346  
 347      function error($error, $message = false) {
 348          // Accepts either an error object or an error code and message
 349          if ($message && !is_object($error)) {
 350              $error = new IXR_Error($error, $message);
 351          }
 352          $this->output($error->getXml());
 353      }
 354      function output($xml) {
 355          $xml = '<?xml version="1.0"?>'."\n".$xml;
 356          $length = strlen($xml);
 357          header('Connection: close');
 358          header('Content-Length: '.$length);
 359          header('Content-Type: text/xml');
 360          header('Date: '.date('r'));
 361          echo $xml;
 362          exit;
 363      }
 364      function hasMethod($method) {
 365          return in_array($method, array_keys($this->callbacks));
 366      }
 367      function setCapabilities() {
 368          // Initialises capabilities array
 369          $this->capabilities = array(
 370              'xmlrpc' => array(
 371                  'specUrl' => 'http://www.xmlrpc.com/spec',
 372                  'specVersion' => 1
 373              ),
 374              'faults_interop' => array(
 375                  'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
 376                  'specVersion' => 20010516
 377              ),
 378              'system.multicall' => array(
 379                  'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
 380                  'specVersion' => 1
 381              ),
 382          );   
 383      }
 384      function getCapabilities($args) {
 385          return $this->capabilities;
 386      }
 387      function setCallbacks() {
 388          $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
 389          $this->callbacks['system.listMethods'] = 'this:listMethods';
 390          $this->callbacks['system.multicall'] = 'this:multiCall';
 391      }
 392      function listMethods($args) {
 393          // Returns a list of methods - uses array_reverse to ensure user defined
 394          // methods are listed before server defined methods
 395          return array_reverse(array_keys($this->callbacks));
 396      }
 397      function multiCall($methodcalls) {
 398          // See http://www.xmlrpc.com/discuss/msgReader$1208
 399          $return = array();
 400          foreach ($methodcalls as $call) {
 401              $method = $call['methodName'];
 402              $params = $call['params'];
 403              if ($method == 'system.multicall') {
 404                  $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
 405              } else {
 406                  $result = $this->call($method, $params);
 407              }
 408              if (is_a($result, 'IXR_Error')) {
 409                  $return[] = array(
 410                      'faultCode' => $result->code,
 411                      'faultString' => $result->message
 412                  );
 413              } else {
 414                  $return[] = array($result);
 415              }
 416          }
 417          return $return;
 418      }
 419  }
 420  
 421  class IXR_Request {
 422      var $method;
 423      var $args;
 424      var $xml;
 425      function IXR_Request($method, $args) {
 426          $this->method = $method;
 427          $this->args = $args;
 428          $this->xml = <<<EOD
 429  <?xml version="1.0"?>
 430  <methodCall>
 431  <methodName>{$this->method}</methodName>
 432  <params>
 433  
 434  EOD;
 435          foreach ($this->args as $arg) {
 436              $this->xml .= '<param><value>';
 437              $v = new IXR_Value($arg);
 438              $this->xml .= $v->getXml();
 439              $this->xml .= "</value></param>\n";
 440          }
 441          $this->xml .= '</params></methodCall>';
 442      }
 443      function getLength() {
 444          return strlen($this->xml);
 445      }
 446      function getXml() {
 447          return $this->xml;
 448      }
 449  }
 450  
 451  
 452  class IXR_Client {
 453      var $server;
 454      var $port;
 455      var $path;
 456      var $useragent;
 457      var $response;
 458      var $message = false;
 459      var $debug = false;
 460      var $timeout;
 461      // Storage place for an error message
 462      var $error = false;
 463      function IXR_Client($server, $path = false, $port = 80, $timeout = false) {
 464          if (!$path) {
 465              // Assume we have been given a URL instead
 466              $bits = parse_url($server);
 467              $this->server = $bits['host'];
 468              $this->port = isset($bits['port']) ? $bits['port'] : 80;
 469              $this->path = isset($bits['path']) ? $bits['path'] : '/';
 470              // Make absolutely sure we have a path
 471              if (!$this->path) {
 472                  $this->path = '/';
 473              }
 474          } else {
 475              $this->server = $server;
 476              $this->path = $path;
 477              $this->port = $port;
 478          }
 479          $this->useragent = 'Incutio XML-RPC';