[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> IXR_Library.php (source)

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