Drupal PHP Cross Reference Content Management Systems

Source: /includes/xmlrpcs.inc - 384 lines - 11142 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4   * @file
   5   * Provides API for defining and handling XML-RPC requests.
   6   */
   7  
   8  /**
   9   * Invokes XML-RPC methods on this server.
  10   *
  11   * @param array $callbacks
  12   *   Array of external XML-RPC method names with the callbacks they map to.
  13   */
  14  function xmlrpc_server($callbacks) {
  15    $xmlrpc_server = new stdClass();
  16    // Define built-in XML-RPC method names
  17    $defaults = array(
  18      'system.multicall' => 'xmlrpc_server_multicall',
  19      array(
  20        'system.methodSignature',
  21        'xmlrpc_server_method_signature',
  22        array('array', 'string'),
  23        'Returns an array describing the return type and required parameters of a method.',
  24      ),
  25      array(
  26        'system.getCapabilities',
  27        'xmlrpc_server_get_capabilities',
  28        array('struct'),
  29        'Returns a struct describing the XML-RPC specifications supported by this server.',
  30      ),
  31      array(
  32        'system.listMethods',
  33        'xmlrpc_server_list_methods',
  34        array('array'),
  35        'Returns an array of available methods on this server.',
  36      ),
  37      array(
  38        'system.methodHelp',
  39        'xmlrpc_server_method_help',
  40        array('string', 'string'),
  41        'Returns a documentation string for the specified method.',
  42      ),
  43    );
  44    // We build an array of all method names by combining the built-ins
  45    // with those defined by modules implementing the _xmlrpc hook.
  46    // Built-in methods are overridable.
  47    $callbacks = array_merge($defaults, (array) $callbacks);
  48    drupal_alter('xmlrpc', $callbacks);
  49    foreach ($callbacks as $key => $callback) {
  50      // we could check for is_array($callback)
  51      if (is_int($key)) {
  52        $method = $callback[0];
  53        $xmlrpc_server->callbacks[$method] = $callback[1];
  54        $xmlrpc_server->signatures[$method] = $callback[2];
  55        $xmlrpc_server->help[$method] = $callback[3];
  56      }
  57      else {
  58        $xmlrpc_server->callbacks[$key] = $callback;
  59        $xmlrpc_server->signatures[$key] = '';
  60        $xmlrpc_server->help[$key] = '';
  61      }
  62    }
  63  
  64    $data = file_get_contents('php://input');
  65    if (!$data) {
  66      print 'XML-RPC server accepts POST requests only.';
  67      drupal_exit();
  68    }
  69    $xmlrpc_server->message = xmlrpc_message($data);
  70    if (!xmlrpc_message_parse($xmlrpc_server->message)) {
  71      xmlrpc_server_error(-32700, t('Parse error. Request not well formed.'));
  72    }
  73    if ($xmlrpc_server->message->messagetype != 'methodCall') {
  74      xmlrpc_server_error(-32600, t('Server error. Invalid XML-RPC. Request must be a methodCall.'));
  75    }
  76    if (!isset($xmlrpc_server->message->params)) {
  77      $xmlrpc_server->message->params = array();
  78    }
  79    xmlrpc_server_set($xmlrpc_server);
  80    $result = xmlrpc_server_call($xmlrpc_server, $xmlrpc_server->message->methodname, $xmlrpc_server->message->params);
  81  
  82    if (is_object($result) && !empty($result->is_error)) {
  83      xmlrpc_server_error($result);
  84    }
  85    // Encode the result
  86    $r = xmlrpc_value($result);
  87    // Create the XML
  88    $xml = '
  89  <methodResponse>
  90    <params>
  91    <param>
  92      <value>' . xmlrpc_value_get_xml($r) . '</value>
  93    </param>
  94    </params>
  95  </methodResponse>
  96  
  97  ';
  98    // Send it
  99    xmlrpc_server_output($xml);
 100  }
 101  
 102  /**
 103   * Throws an XML-RPC error.
 104   *
 105   * @param $error
 106   *   An error object or integer error code.
 107   * @param $message
 108   *   (optional) The description of the error. Used only if an integer error
 109   *   code was passed in.
 110   */
 111  function xmlrpc_server_error($error, $message = FALSE) {
 112    if ($message && !is_object($error)) {
 113      $error = xmlrpc_error($error, $message);
 114    }
 115    xmlrpc_server_output(xmlrpc_error_get_xml($error));
 116  }
 117  
 118  /**
 119   * Sends XML-RPC output to the browser.
 120   *
 121   * @param string $xml
 122   *   XML to send to the browser.
 123   */
 124  function xmlrpc_server_output($xml) {
 125    $xml = '<?xml version="1.0"?>' . "\n" . $xml;
 126    drupal_add_http_header('Content-Length', strlen($xml));
 127    drupal_add_http_header('Content-Type', 'text/xml');
 128    echo $xml;
 129    drupal_exit();
 130  }
 131  
 132  /**
 133   * Stores a copy of an XML-RPC request temporarily.
 134   *
 135   * @param object $xmlrpc_server
 136   *   (optional) Request object created by xmlrpc_server(). Omit to leave the
 137   *   previous server object saved.
 138   *
 139   * @return
 140   *   The latest stored request.
 141   *
 142   * @see xmlrpc_server_get()
 143   */
 144  function xmlrpc_server_set($xmlrpc_server = NULL) {
 145    static $server;
 146    if (!isset($server)) {
 147      $server = $xmlrpc_server;
 148    }
 149    return $server;
 150  }
 151  
 152  /**
 153   * Retrieves the latest stored XML-RPC request.
 154   *
 155   * @return object
 156   *   The stored request.
 157   *
 158   * @see xmlrpc_server_set()
 159   */
 160  function xmlrpc_server_get() {
 161    return xmlrpc_server_set();
 162  }
 163  
 164  /**
 165   * Dispatches an XML-RPC request and any parameters to the appropriate handler.
 166   *
 167   * @param object $xmlrpc_server
 168   *   Object containing information about this XML-RPC server, the methods it
 169   *   provides, their signatures, etc.
 170   * @param string $methodname
 171   *   The external XML-RPC method name; e.g., 'system.methodHelp'.
 172   * @param array $args
 173   *   Array containing any parameters that are to be sent along with the request.
 174   *
 175   * @return
 176   *   The results of the call.
 177   */
 178  function xmlrpc_server_call($xmlrpc_server, $methodname, $args) {
 179    // Make sure parameters are in an array
 180    if ($args && !is_array($args)) {
 181      $args = array($args);
 182    }
 183    // Has this method been mapped to a Drupal function by us or by modules?
 184    if (!isset($xmlrpc_server->callbacks[$methodname])) {
 185      return xmlrpc_error(-32601, t('Server error. Requested method @methodname not specified.', array("@methodname" => $xmlrpc_server->message->methodname)));
 186    }
 187    $method = $xmlrpc_server->callbacks[$methodname];
 188    $signature = $xmlrpc_server->signatures[$methodname];
 189  
 190    // If the method has a signature, validate the request against the signature
 191    if (is_array($signature)) {
 192      $ok = TRUE;
 193      $return_type = array_shift($signature);
 194      // Check the number of arguments
 195      if (count($args) != count($signature)) {
 196        return xmlrpc_error(-32602, t('Server error. Wrong number of method parameters.'));
 197      }
 198      // Check the argument types
 199      foreach ($signature as $key => $type) {
 200        $arg = $args[$key];
 201        switch ($type) {
 202          case 'int':
 203          case 'i4':
 204            if (is_array($arg) || !is_int($arg)) {
 205              $ok = FALSE;
 206            }
 207            break;
 208  
 209          case 'base64':
 210          case 'string':
 211            if (!is_string($arg)) {
 212              $ok = FALSE;
 213            }
 214            break;
 215  
 216          case 'boolean':
 217            if ($arg !== FALSE && $arg !== TRUE) {
 218              $ok = FALSE;
 219            }
 220            break;
 221  
 222          case 'float':
 223          case 'double':
 224            if (!is_float($arg)) {
 225              $ok = FALSE;
 226            }
 227            break;
 228  
 229          case 'date':
 230          case 'dateTime.iso8601':
 231            if (!$arg->is_date) {
 232              $ok = FALSE;
 233            }
 234            break;
 235        }
 236        if (!$ok) {
 237          return xmlrpc_error(-32602, t('Server error. Invalid method parameters.'));
 238        }
 239      }
 240    }
 241  
 242    if (!function_exists($method)) {
 243      return xmlrpc_error(-32601, t('Server error. Requested function @method does not exist.', array("@method" => $method)));
 244    }
 245    // Call the mapped function
 246    return call_user_func_array($method, $args);
 247  }
 248  
 249  /**
 250   * Dispatches multiple XML-RPC requests.
 251   *
 252   * @param array $methodcalls
 253   *   An array of XML-RPC requests to make. Each request is an array with the
 254   *   following elements:
 255   *   - methodName: Name of the method to invoke.
 256   *   - params: Parameters to pass to the method.
 257   *
 258   * @return
 259   *   An array of the results of each request.
 260   *
 261   * @see xmlrpc_server_call()
 262   */
 263  function xmlrpc_server_multicall($methodcalls) {
 264    // See http://www.xmlrpc.com/discuss/msgReader$1208
 265    $return = array();
 266    $xmlrpc_server = xmlrpc_server_get();
 267    foreach ($methodcalls as $call) {
 268      $ok = TRUE;
 269      if (!isset($call['methodName']) || !isset($call['params'])) {
 270        $result = xmlrpc_error(3, t('Invalid syntax for system.multicall.'));
 271        $ok = FALSE;
 272      }
 273      $method = $call['methodName'];
 274      $params = $call['params'];
 275      if ($method == 'system.multicall') {
 276        $result = xmlrpc_error(-32600, t('Recursive calls to system.multicall are forbidden.'));
 277      }
 278      elseif ($ok) {
 279        $result = xmlrpc_server_call($xmlrpc_server, $method, $params);
 280      }
 281      if (is_object($result) && !empty($result->is_error)) {
 282        $return[] = array(
 283          'faultCode' => $result->code,
 284          'faultString' => $result->message,
 285        );
 286      }
 287      else {
 288        $return[] = array($result);
 289      }
 290    }
 291    return $return;
 292  }
 293  
 294  /**
 295   * Lists the methods available on this XML-RPC server.
 296   *
 297   * XML-RPC method system.listMethods maps to this function.
 298   *
 299   * @return array
 300   *   Array of the names of methods available on this server.
 301   */
 302  function xmlrpc_server_list_methods() {
 303    $xmlrpc_server = xmlrpc_server_get();
 304    return array_keys($xmlrpc_server->callbacks);
 305  }
 306  
 307  /**
 308   * Returns a list of the capabilities of this server.
 309   *
 310   * XML-RPC method system.getCapabilities maps to this function.
 311   *
 312   * @return array
 313   *   Array of server capabilities.
 314   *
 315   * @see http://groups.yahoo.com/group/xml-rpc/message/2897
 316   */
 317  function xmlrpc_server_get_capabilities() {
 318    return array(
 319      'xmlrpc' => array(
 320        'specUrl' => 'http://www.xmlrpc.com/spec',
 321        'specVersion' => 1,
 322      ),
 323      'faults_interop' => array(
 324        'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
 325        'specVersion' => 20010516,
 326      ),
 327      'system.multicall' => array(
 328        'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
 329        'specVersion' => 1,
 330      ),
 331      'introspection' => array(
 332        'specUrl' => 'http://scripts.incutio.com/xmlrpc/introspection.html',
 333        'specVersion' => 1,
 334      ),
 335    );
 336  }
 337  
 338  /**
 339   * Returns one method signature for a function.
 340   *
 341   * This is the function mapped to the XML-RPC method system.methodSignature.
 342   *
 343   * A method signature is an array of the input and output types of a method. For
 344   * instance, the method signature of this function is array('array', 'string'),
 345   * because it takes an array and returns a string.
 346   *
 347   * @param string $methodname
 348   *   Name of method to return a method signature for.
 349   *
 350   * @return array
 351   *   An array of arrays of types, each of the arrays representing one method
 352   *   signature of the function that $methodname maps to.
 353   */
 354  function xmlrpc_server_method_signature($methodname) {
 355    $xmlrpc_server = xmlrpc_server_get();
 356    if (!isset($xmlrpc_server->callbacks[$methodname])) {
 357      return xmlrpc_error(-32601, t('Server error. Requested method @methodname not specified.', array("@methodname" => $methodname)));
 358    }
 359    if (!is_array($xmlrpc_server->signatures[$methodname])) {
 360      return xmlrpc_error(-32601, t('Server error. Requested method @methodname signature not specified.', array("@methodname" => $methodname)));
 361    }
 362    // We array of types
 363    $return = array();
 364    foreach ($xmlrpc_server->signatures[$methodname] as $type) {
 365      $return[] = $type;
 366    }
 367    return array($return);
 368  }
 369  
 370  /**
 371   * Returns the help for an XML-RPC method.
 372   *
 373   * XML-RPC method system.methodHelp maps to this function.
 374   *
 375   * @param string $method
 376   *   Name of method for which we return a help string.
 377   *
 378   * @return string
 379   *   Help text for $method.
 380   */
 381  function xmlrpc_server_method_help($method) {
 382    $xmlrpc_server = xmlrpc_server_get();
 383    return $xmlrpc_server->help[$method];
 384  }

title

Description

title

Description

title

Description

title

title

Body