Drupal PHP Cross Reference Content Management Systems

Source: /includes/xmlrpc.inc - 624 lines - 17738 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4   * @file
   5   * Drupal XML-RPC library.
   6   *
   7   * Based on the IXR - The Incutio XML-RPC Library - (c) Incutio Ltd 2002-2005
   8   * Version 1.7 (beta) - Simon Willison, 23rd May 2005
   9   * Site:   http://scripts.incutio.com/xmlrpc/
  10   * Manual: http://scripts.incutio.com/xmlrpc/manual.php
  11   * This version is made available under the GNU GPL License
  12   */
  13  
  14  /**
  15   * Turns a data structure into objects with 'data' and 'type' attributes.
  16   *
  17   * @param $data
  18   *   The data structure.
  19   * @param $type
  20   *   Optional type to assign to $data.
  21   *
  22   * @return object
  23   *   An XML-RPC data object containing the input $data.
  24   */
  25  function xmlrpc_value($data, $type = FALSE) {
  26    $xmlrpc_value = new stdClass();
  27    $xmlrpc_value->data = $data;
  28    if (!$type) {
  29      $type = xmlrpc_value_calculate_type($xmlrpc_value);
  30    }
  31    $xmlrpc_value->type = $type;
  32    if ($type == 'struct') {
  33      // Turn all the values in the array into new xmlrpc_values
  34      foreach ($xmlrpc_value->data as $key => $value) {
  35        $xmlrpc_value->data[$key] = xmlrpc_value($value);
  36      }
  37    }
  38    if ($type == 'array') {
  39      for ($i = 0, $j = count($xmlrpc_value->data); $i < $j; $i++) {
  40        $xmlrpc_value->data[$i] = xmlrpc_value($xmlrpc_value->data[$i]);
  41      }
  42    }
  43    return $xmlrpc_value;
  44  }
  45  
  46  /**
  47   * Maps a PHP type to an XML-RPC type.
  48   *
  49   * @param $xmlrpc_value
  50   *   Variable whose type should be mapped.
  51   *
  52   * @return string
  53   *   The corresponding XML-RPC type.
  54   *
  55   * @see http://www.xmlrpc.com/spec#scalars
  56   */
  57  function xmlrpc_value_calculate_type($xmlrpc_value) {
  58    // http://www.php.net/gettype: Never use gettype() to test for a certain type
  59    // [...] Instead, use the is_* functions.
  60    if (is_bool($xmlrpc_value->data)) {
  61      return 'boolean';
  62    }
  63    if (is_double($xmlrpc_value->data)) {
  64      return 'double';
  65    }
  66    if (is_int($xmlrpc_value->data)) {
  67      return 'int';
  68    }
  69    if (is_array($xmlrpc_value->data)) {
  70      // empty or integer-indexed arrays are 'array', string-indexed arrays 'struct'
  71      return empty($xmlrpc_value->data) || range(0, count($xmlrpc_value->data) - 1) === array_keys($xmlrpc_value->data) ? 'array' : 'struct';
  72    }
  73    if (is_object($xmlrpc_value->data)) {
  74      if (isset($xmlrpc_value->data->is_date)) {
  75        return 'date';
  76      }
  77      if (isset($xmlrpc_value->data->is_base64)) {
  78        return 'base64';
  79      }
  80      $xmlrpc_value->data = get_object_vars($xmlrpc_value->data);
  81      return 'struct';
  82    }
  83    // default
  84    return 'string';
  85  }
  86  
  87  /**
  88   * Generates XML representing the given value.
  89   *
  90   * @param $xmlrpc_value
  91   *   A value to be represented in XML.
  92   *
  93   * @return
  94   *   XML representation of $xmlrpc_value.
  95   */
  96  function xmlrpc_value_get_xml($xmlrpc_value) {
  97    switch ($xmlrpc_value->type) {
  98      case 'boolean':
  99        return '<boolean>' . (($xmlrpc_value->data) ? '1' : '0') . '</boolean>';
 100  
 101      case 'int':
 102        return '<int>' . $xmlrpc_value->data . '</int>';
 103  
 104      case 'double':
 105        return '<double>' . $xmlrpc_value->data . '</double>';
 106  
 107      case 'string':
 108        // Note: we don't escape apostrophes because of the many blogging clients
 109        // that don't support numerical entities (and XML in general) properly.
 110        return '<string>' . htmlspecialchars($xmlrpc_value->data) . '</string>';
 111  
 112      case 'array':
 113        $return = '<array><data>' . "\n";
 114        foreach ($xmlrpc_value->data as $item) {
 115          $return .= '  <value>' . xmlrpc_value_get_xml($item) . "</value>\n";
 116        }
 117        $return .= '</data></array>';
 118        return $return;
 119  
 120      case 'struct':
 121        $return = '<struct>' . "\n";
 122        foreach ($xmlrpc_value->data as $name => $value) {
 123          $return .= "  <member><name>" . check_plain($name) . "</name><value>";
 124          $return .= xmlrpc_value_get_xml($value) . "</value></member>\n";
 125        }
 126        $return .= '</struct>';
 127        return $return;
 128  
 129      case 'date':
 130        return xmlrpc_date_get_xml($xmlrpc_value->data);
 131  
 132      case 'base64':
 133        return xmlrpc_base64_get_xml($xmlrpc_value->data);
 134    }
 135    return FALSE;
 136  }
 137  
 138  /**
 139   * Constructs an object representing an XML-RPC message.
 140   *
 141   * @param $message
 142   *   A string containing an XML message.
 143   *
 144   * @return object
 145   *   An XML-RPC object containing the message.
 146   *
 147   * @see http://www.xmlrpc.com/spec
 148   */
 149  function xmlrpc_message($message) {
 150    $xmlrpc_message = new stdClass();
 151    // The stack used to keep track of the current array/struct
 152    $xmlrpc_message->array_structs = array();
 153    // The stack used to keep track of if things are structs or array
 154    $xmlrpc_message->array_structs_types = array();
 155    // A stack as well
 156    $xmlrpc_message->current_struct_name = array();
 157    $xmlrpc_message->message = $message;
 158    return $xmlrpc_message;
 159  }
 160  
 161  /**
 162   * Parses an XML-RPC message.
 163   *
 164   * If parsing fails, the faultCode and faultString will be added to the message
 165   * object.
 166   *
 167   * @param $xmlrpc_message
 168   *   An object generated by xmlrpc_message().
 169   *
 170   * @return
 171   *   TRUE if parsing succeeded; FALSE otherwise.
 172   */
 173  function xmlrpc_message_parse($xmlrpc_message) {
 174    $xmlrpc_message->_parser = xml_parser_create();
 175    // Set XML parser to take the case of tags into account.
 176    xml_parser_set_option($xmlrpc_message->_parser, XML_OPTION_CASE_FOLDING, FALSE);
 177    // Set XML parser callback functions
 178    xml_set_element_handler($xmlrpc_message->_parser, 'xmlrpc_message_tag_open', 'xmlrpc_message_tag_close');
 179    xml_set_character_data_handler($xmlrpc_message->_parser, 'xmlrpc_message_cdata');
 180    xmlrpc_message_set($xmlrpc_message);
 181    if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) {
 182      return FALSE;
 183    }
 184    xml_parser_free($xmlrpc_message->_parser);
 185  
 186    // Grab the error messages, if any.
 187    $xmlrpc_message = xmlrpc_message_get();
 188    if (!isset($xmlrpc_message->messagetype)) {
 189      return FALSE;
 190    }
 191    elseif ($xmlrpc_message->messagetype == 'fault') {
 192      $xmlrpc_message->fault_code = $xmlrpc_message->params[0]['faultCode'];
 193      $xmlrpc_message->fault_string = $xmlrpc_message->params[0]['faultString'];
 194    }
 195    return TRUE;
 196  }
 197  
 198  /**
 199   * Stores a copy of the most recent XML-RPC message object temporarily.
 200   *
 201   * @param $value
 202   *   An XML-RPC message to store, or NULL to keep the last message.
 203   *
 204   * @return object
 205   *   The most recently stored message.
 206   *
 207   * @see xmlrpc_message_get()
 208   */
 209  function xmlrpc_message_set($value = NULL) {
 210    static $xmlrpc_message;
 211    if ($value) {
 212      $xmlrpc_message = $value;
 213    }
 214    return $xmlrpc_message;
 215  }
 216  
 217  /**
 218   * Returns the most recently stored XML-RPC message object.
 219   *
 220   * @return object
 221   *   The most recently stored message.
 222   *
 223   * @see xmlrpc_message_set()
 224   */
 225  function xmlrpc_message_get() {
 226    return xmlrpc_message_set();
 227  }
 228  
 229  /**
 230   * Handles opening tags for XML parsing in xmlrpc_message_parse().
 231   */
 232  function xmlrpc_message_tag_open($parser, $tag, $attr) {
 233    $xmlrpc_message = xmlrpc_message_get();
 234    $xmlrpc_message->current_tag_contents = '';
 235    $xmlrpc_message->last_open = $tag;
 236    switch ($tag) {
 237      case 'methodCall':
 238      case 'methodResponse':
 239      case 'fault':
 240        $xmlrpc_message->messagetype = $tag;
 241        break;
 242  
 243      // Deal with stacks of arrays and structs
 244      case 'data':
 245        $xmlrpc_message->array_structs_types[] = 'array';
 246        $xmlrpc_message->array_structs[] = array();
 247        break;
 248  
 249      case 'struct':
 250        $xmlrpc_message->array_structs_types[] = 'struct';
 251        $xmlrpc_message->array_structs[] = array();
 252        break;
 253    }
 254    xmlrpc_message_set($xmlrpc_message);
 255  }
 256  
 257  /**
 258   * Handles character data for XML parsing in xmlrpc_message_parse().
 259   */
 260  function xmlrpc_message_cdata($parser, $cdata) {
 261    $xmlrpc_message = xmlrpc_message_get();
 262    $xmlrpc_message->current_tag_contents .= $cdata;
 263    xmlrpc_message_set($xmlrpc_message);
 264  }
 265  
 266  /**
 267   * Handles closing tags for XML parsing in xmlrpc_message_parse().
 268   */
 269  function xmlrpc_message_tag_close($parser, $tag) {
 270    $xmlrpc_message = xmlrpc_message_get();
 271    $value_flag = FALSE;
 272    switch ($tag) {
 273      case 'int':
 274      case 'i4':
 275        $value = (int)trim($xmlrpc_message->current_tag_contents);
 276        $value_flag = TRUE;
 277        break;
 278  
 279      case 'double':
 280        $value = (double)trim($xmlrpc_message->current_tag_contents);
 281        $value_flag = TRUE;
 282        break;
 283  
 284      case 'string':
 285        $value = $xmlrpc_message->current_tag_contents;
 286        $value_flag = TRUE;
 287        break;
 288  
 289      case 'dateTime.iso8601':
 290        $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents));
 291        // $value = $iso->getTimestamp();
 292        $value_flag = TRUE;
 293        break;
 294  
 295      case 'value':
 296        // If no type is indicated, the type is string
 297        // We take special care for empty values
 298        if (trim($xmlrpc_message->current_tag_contents) != '' || (isset($xmlrpc_message->last_open) && ($xmlrpc_message->last_open == 'value'))) {
 299          $value = (string) $xmlrpc_message->current_tag_contents;
 300          $value_flag = TRUE;
 301        }
 302        unset($xmlrpc_message->last_open);
 303        break;
 304  
 305      case 'boolean':
 306        $value = (boolean)trim($xmlrpc_message->current_tag_contents);
 307        $value_flag = TRUE;
 308        break;
 309  
 310      case 'base64':
 311        $value = base64_decode(trim($xmlrpc_message->current_tag_contents));
 312        $value_flag = TRUE;
 313        break;
 314  
 315      // Deal with stacks of arrays and structs
 316      case 'data':
 317      case 'struct':
 318        $value = array_pop($xmlrpc_message->array_structs);
 319        array_pop($xmlrpc_message->array_structs_types);
 320        $value_flag = TRUE;
 321        break;
 322  
 323      case 'member':
 324        array_pop($xmlrpc_message->current_struct_name);
 325        break;
 326  
 327      case 'name':
 328        $xmlrpc_message->current_struct_name[] = trim($xmlrpc_message->current_tag_contents);
 329        break;
 330  
 331      case 'methodName':
 332        $xmlrpc_message->methodname = trim($xmlrpc_message->current_tag_contents);
 333        break;
 334    }
 335    if ($value_flag) {
 336      if (count($xmlrpc_message->array_structs) > 0) {
 337        // Add value to struct or array
 338        if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types) - 1] == 'struct') {
 339          // Add to struct
 340          $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name) - 1]] = $value;
 341        }
 342        else {
 343          // Add to array
 344          $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][] = $value;
 345        }
 346      }
 347      else {
 348        // Just add as a parameter
 349        $xmlrpc_message->params[] = $value;
 350      }
 351    }
 352    if (!in_array($tag, array("data", "struct", "member"))) {
 353      $xmlrpc_message->current_tag_contents = '';
 354    }
 355    xmlrpc_message_set($xmlrpc_message);
 356  }
 357  
 358  /**
 359   * Constructs an object representing an XML-RPC request.
 360   *
 361   * @param $method
 362   *   The name of the method to be called.
 363   * @param $args
 364   *   An array of parameters to send with the method.
 365   *
 366   * @return object
 367   *   An XML-RPC object representing the request.
 368   */
 369  function xmlrpc_request($method, $args) {
 370    $xmlrpc_request = new stdClass();
 371    $xmlrpc_request->method = $method;
 372    $xmlrpc_request->args = $args;
 373    $xmlrpc_request->xml = <<<EOD
 374  <?xml version="1.0"?>
 375  <methodCall>
 376  <methodName>{$xmlrpc_request->method}</methodName>
 377  <params>
 378  
 379  EOD;
 380    foreach ($xmlrpc_request->args as $arg) {
 381      $xmlrpc_request->xml .= '<param><value>';
 382      $v = xmlrpc_value($arg);
 383      $xmlrpc_request->xml .= xmlrpc_value_get_xml($v);
 384      $xmlrpc_request->xml .= "</value></param>\n";
 385    }
 386    $xmlrpc_request->xml .= '</params></methodCall>';
 387    return $xmlrpc_request;
 388  }
 389  
 390  /**
 391   * Generates, temporarily saves, and returns an XML-RPC error object.
 392   *
 393   * @param $code
 394   *   The error code.
 395   * @param $message
 396   *   The error message.
 397   * @param $reset
 398   *   TRUE to empty the temporary error storage. Ignored if $code is supplied.
 399   *
 400   * @return object
 401   *   An XML-RPC error object representing $code and $message, or the most
 402   *   recently stored error object if omitted.
 403   */
 404  function xmlrpc_error($code = NULL, $message = NULL, $reset = FALSE) {
 405    static $xmlrpc_error;
 406    if (isset($code)) {
 407      $xmlrpc_error = new stdClass();
 408      $xmlrpc_error->is_error = TRUE;
 409      $xmlrpc_error->code = $code;
 410      $xmlrpc_error->message = $message;
 411    }
 412    elseif ($reset) {
 413      $xmlrpc_error = NULL;
 414    }
 415    return $xmlrpc_error;
 416  }
 417  
 418  /**
 419   * Converts an XML-RPC error object into XML.
 420   *
 421   * @param $xmlrpc_error
 422   *   The XML-RPC error object.
 423   *
 424   * @return string
 425   *   An XML representation of the error as an XML methodResponse.
 426   */
 427  function xmlrpc_error_get_xml($xmlrpc_error) {
 428    return <<<EOD
 429  <methodResponse>
 430    <fault>
 431    <value>
 432      <struct>
 433      <member>
 434        <name>faultCode</name>
 435        <value><int>{$xmlrpc_error->code}</int></value>
 436      </member>
 437      <member>
 438        <name>faultString</name>
 439        <value><string>{$xmlrpc_error->message}</string></value>
 440      </member>
 441      </struct>
 442    </value>
 443    </fault>
 444  </methodResponse>
 445  
 446  EOD;
 447  }
 448  
 449  /**
 450   * Converts a PHP or ISO date/time to an XML-RPC object.
 451   *
 452   * @param $time
 453   *   A PHP timestamp or an ISO date-time string.
 454   *
 455   * @return object
 456   *   An XML-RPC time/date object.
 457   */
 458  function xmlrpc_date($time) {
 459    $xmlrpc_date = new stdClass();
 460    $xmlrpc_date->is_date = TRUE;
 461    // $time can be a PHP timestamp or an ISO one
 462    if (is_numeric($time)) {
 463      $xmlrpc_date->year = gmdate('Y', $time);
 464      $xmlrpc_date->month = gmdate('m', $time);
 465      $xmlrpc_date->day = gmdate('d', $time);
 466      $xmlrpc_date->hour = gmdate('H', $time);
 467      $xmlrpc_date->minute = gmdate('i', $time);
 468      $xmlrpc_date->second = gmdate('s', $time);
 469      $xmlrpc_date->iso8601 = gmdate('Ymd\TH:i:s', $time);
 470    }
 471    else {
 472      $xmlrpc_date->iso8601 = $time;
 473      $time = str_replace(array('-', ':'), '', $time);
 474      $xmlrpc_date->year = substr($time, 0, 4);
 475      $xmlrpc_date->month = substr($time, 4, 2);
 476      $xmlrpc_date->day = substr($time, 6, 2);
 477      $xmlrpc_date->hour = substr($time, 9, 2);
 478      $xmlrpc_date->minute = substr($time, 11, 2);
 479      $xmlrpc_date->second = substr($time, 13, 2);
 480    }
 481    return $xmlrpc_date;
 482  }
 483  
 484  /**
 485   * Converts an XML-RPC date-time object into XML.
 486   *
 487   * @param $xmlrpc_date
 488   *   The XML-RPC date-time object.
 489   *
 490   * @return string
 491   *   An XML representation of the date/time as XML.
 492   */
 493  function xmlrpc_date_get_xml($xmlrpc_date) {
 494    return '<dateTime.iso8601>' . $xmlrpc_date->year . $xmlrpc_date->month . $xmlrpc_date->day . 'T' . $xmlrpc_date->hour . ':' . $xmlrpc_date->minute . ':' . $xmlrpc_date->second . '</dateTime.iso8601>';
 495  }
 496  
 497  /**
 498   * Returns an XML-RPC base 64 object.
 499   *
 500   * @param $data
 501   *   Base 64 data to store in returned object.
 502   *
 503   * @return object
 504   *   An XML-RPC base 64 object.
 505   */
 506  function xmlrpc_base64($data) {
 507    $xmlrpc_base64 = new stdClass();
 508    $xmlrpc_base64->is_base64 = TRUE;
 509    $xmlrpc_base64->data = $data;
 510    return $xmlrpc_base64;
 511  }
 512  
 513  /**
 514   * Converts an XML-RPC base 64 object into XML.
 515   *
 516   * @param $xmlrpc_base64
 517   *   The XML-RPC base 64 object.
 518   *
 519   * @return string
 520   *   An XML representation of the base 64 data as XML.
 521   */
 522  function xmlrpc_base64_get_xml($xmlrpc_base64) {
 523    return '<base64>' . base64_encode($xmlrpc_base64->data) . '</base64>';
 524  }
 525  
 526  /**
 527   * Performs one or more XML-RPC requests.
 528   *
 529   * @param $url
 530   *   An absolute URL of the XML-RPC endpoint, e.g.,
 531   *   http://example.com/xmlrpc.php
 532   * @param $args
 533   *   An associative array whose keys are the methods to call and whose values
 534   *   are the arguments to pass to the respective method. If multiple methods
 535   *   are specified, a system.multicall is performed.
 536   * @param $options
 537   *   (optional) An array of options to pass along to drupal_http_request().
 538   *
 539   * @return
 540   *   A single response (single request) or an array of responses (multicall
 541   *   request). Each response is the return value of the method, just as if it
 542   *   has been a local function call, on success, or FALSE on failure. If FALSE
 543   *   is returned, see xmlrpc_errno() and xmlrpc_error_msg() to get more
 544   *   information.
 545   */
 546  function _xmlrpc($url, $args, $options = array()) {
 547    xmlrpc_clear_error();
 548    if (count($args) > 1) {
 549      $multicall_args = array();
 550      foreach ($args as $method => $call) {
 551        $multicall_args[] = array('methodName' => $method, 'params' => $call);
 552      }
 553      $method = 'system.multicall';
 554      $args = array($multicall_args);
 555    }
 556    else {
 557      $method = key($args);
 558      $args = $args[$method];
 559    }
 560    $xmlrpc_request = xmlrpc_request($method, $args);
 561    // Required options which will replace any that are passed in.
 562    $options['method'] = 'POST';
 563    $options['headers']['Content-Type'] = 'text/xml';
 564    $options['data'] = $xmlrpc_request->xml;
 565    $result = drupal_http_request($url, $options);
 566    if ($result->code != 200) {
 567      xmlrpc_error($result->code, $result->error);
 568      return FALSE;
 569    }
 570    $message = xmlrpc_message($result->data);
 571    // Now parse what we've got back
 572    if (!xmlrpc_message_parse($message)) {
 573      // XML error
 574      xmlrpc_error(-32700, t('Parse error. Not well formed'));
 575      return FALSE;
 576    }
 577    // Is the message a fault?
 578    if ($message->messagetype == 'fault') {
 579      xmlrpc_error($message->fault_code, $message->fault_string);
 580      return FALSE;
 581    }
 582    // We now know that the message is well-formed and a non-fault result.
 583    if ($method == 'system.multicall') {
 584      // Return per-method results or error objects.
 585      $return = array();
 586      foreach ($message->params[0] as $result) {
 587        if (array_keys($result) == array(0)) {
 588          $return[] = $result[0];
 589        }
 590        else {
 591          $return[] = xmlrpc_error($result['faultCode'], $result['faultString']);
 592        }
 593      }
 594    }
 595    else {
 596      $return = $message->params[0];
 597    }
 598    return $return;
 599  }
 600  
 601  /**
 602   * Returns the last XML-RPC client error number.
 603   */
 604  function xmlrpc_errno() {
 605    $error = xmlrpc_error();
 606    return ($error != NULL ? $error->code : NULL);
 607  }
 608  
 609  /**
 610   * Returns the last XML-RPC client error message.
 611   */
 612  function xmlrpc_error_msg() {
 613    $error = xmlrpc_error();
 614    return ($error != NULL ? $error->message : NULL);
 615  }
 616  
 617  /**
 618   * Clears any previously-saved errors.
 619   *
 620   * @see xmlrpc_error()
 621   */
 622  function xmlrpc_clear_error() {
 623    xmlrpc_error(NULL, NULL, TRUE);
 624  }

title

Description

title

Description

title

Description

title

title

Body