cms/drupal/includes/xmlrpc.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     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 
       
   182   // Strip XML declaration.
       
   183   $header = preg_replace('/<\?xml.*?\?'.'>/s', '', substr($xmlrpc_message->message, 0, 100), 1);
       
   184   $xml = trim(substr_replace($xmlrpc_message->message, $header, 0, 100));
       
   185   if ($xml == '') {
       
   186     return FALSE;
       
   187   }
       
   188   // Strip DTD.
       
   189   $header = preg_replace('/^<!DOCTYPE[^>]*+>/i', '', substr($xml, 0, 200), 1);
       
   190   $xml = trim(substr_replace($xml, $header, 0, 200));
       
   191   if ($xml == '') {
       
   192     return FALSE;
       
   193   }
       
   194   // Confirm the XML now starts with a valid root tag. A root tag can end in [> \t\r\n]
       
   195   $root_tag = substr($xml, 0, strcspn(substr($xml, 0, 20), "> \t\r\n"));
       
   196   // Reject a second DTD.
       
   197   if (strtoupper($root_tag) == '<!DOCTYPE') {
       
   198     return FALSE;
       
   199   }
       
   200   if (!in_array($root_tag, array('<methodCall', '<methodResponse', '<fault'))) {
       
   201     return FALSE;
       
   202   }
       
   203   // Skip parsing if there is an unreasonably large number of tags.
       
   204   try {
       
   205     $dom = new DOMDocument();
       
   206     @$dom->loadXML($xml);
       
   207     if ($dom->getElementsByTagName('*')->length > variable_get('xmlrpc_message_maximum_tag_count', 30000)) {
       
   208       return FALSE;
       
   209     }
       
   210   }
       
   211   catch (Exception $e) {
       
   212     return FALSE;
       
   213   }
       
   214 
       
   215   if (!xml_parse($xmlrpc_message->_parser, $xml)) {
       
   216     return FALSE;
       
   217   }
       
   218   xml_parser_free($xmlrpc_message->_parser);
       
   219 
       
   220   // Grab the error messages, if any.
       
   221   $xmlrpc_message = xmlrpc_message_get();
       
   222   if (!isset($xmlrpc_message->messagetype)) {
       
   223     return FALSE;
       
   224   }
       
   225   elseif ($xmlrpc_message->messagetype == 'fault') {
       
   226     $xmlrpc_message->fault_code = $xmlrpc_message->params[0]['faultCode'];
       
   227     $xmlrpc_message->fault_string = $xmlrpc_message->params[0]['faultString'];
       
   228   }
       
   229   return TRUE;
       
   230 }
       
   231 
       
   232 /**
       
   233  * Stores a copy of the most recent XML-RPC message object temporarily.
       
   234  *
       
   235  * @param $value
       
   236  *   An XML-RPC message to store, or NULL to keep the last message.
       
   237  *
       
   238  * @return object
       
   239  *   The most recently stored message.
       
   240  *
       
   241  * @see xmlrpc_message_get()
       
   242  */
       
   243 function xmlrpc_message_set($value = NULL) {
       
   244   static $xmlrpc_message;
       
   245   if ($value) {
       
   246     $xmlrpc_message = $value;
       
   247   }
       
   248   return $xmlrpc_message;
       
   249 }
       
   250 
       
   251 /**
       
   252  * Returns the most recently stored XML-RPC message object.
       
   253  *
       
   254  * @return object
       
   255  *   The most recently stored message.
       
   256  *
       
   257  * @see xmlrpc_message_set()
       
   258  */
       
   259 function xmlrpc_message_get() {
       
   260   return xmlrpc_message_set();
       
   261 }
       
   262 
       
   263 /**
       
   264  * Handles opening tags for XML parsing in xmlrpc_message_parse().
       
   265  */
       
   266 function xmlrpc_message_tag_open($parser, $tag, $attr) {
       
   267   $xmlrpc_message = xmlrpc_message_get();
       
   268   $xmlrpc_message->current_tag_contents = '';
       
   269   $xmlrpc_message->last_open = $tag;
       
   270   switch ($tag) {
       
   271     case 'methodCall':
       
   272     case 'methodResponse':
       
   273     case 'fault':
       
   274       $xmlrpc_message->messagetype = $tag;
       
   275       break;
       
   276 
       
   277     // Deal with stacks of arrays and structs
       
   278     case 'data':
       
   279       $xmlrpc_message->array_structs_types[] = 'array';
       
   280       $xmlrpc_message->array_structs[] = array();
       
   281       break;
       
   282 
       
   283     case 'struct':
       
   284       $xmlrpc_message->array_structs_types[] = 'struct';
       
   285       $xmlrpc_message->array_structs[] = array();
       
   286       break;
       
   287   }
       
   288   xmlrpc_message_set($xmlrpc_message);
       
   289 }
       
   290 
       
   291 /**
       
   292  * Handles character data for XML parsing in xmlrpc_message_parse().
       
   293  */
       
   294 function xmlrpc_message_cdata($parser, $cdata) {
       
   295   $xmlrpc_message = xmlrpc_message_get();
       
   296   $xmlrpc_message->current_tag_contents .= $cdata;
       
   297   xmlrpc_message_set($xmlrpc_message);
       
   298 }
       
   299 
       
   300 /**
       
   301  * Handles closing tags for XML parsing in xmlrpc_message_parse().
       
   302  */
       
   303 function xmlrpc_message_tag_close($parser, $tag) {
       
   304   $xmlrpc_message = xmlrpc_message_get();
       
   305   $value_flag = FALSE;
       
   306   switch ($tag) {
       
   307     case 'int':
       
   308     case 'i4':
       
   309       $value = (int)trim($xmlrpc_message->current_tag_contents);
       
   310       $value_flag = TRUE;
       
   311       break;
       
   312 
       
   313     case 'double':
       
   314       $value = (double)trim($xmlrpc_message->current_tag_contents);
       
   315       $value_flag = TRUE;
       
   316       break;
       
   317 
       
   318     case 'string':
       
   319       $value = $xmlrpc_message->current_tag_contents;
       
   320       $value_flag = TRUE;
       
   321       break;
       
   322 
       
   323     case 'dateTime.iso8601':
       
   324       $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents));
       
   325       // $value = $iso->getTimestamp();
       
   326       $value_flag = TRUE;
       
   327       break;
       
   328 
       
   329     case 'value':
       
   330       // If no type is indicated, the type is string
       
   331       // We take special care for empty values
       
   332       if (trim($xmlrpc_message->current_tag_contents) != '' || (isset($xmlrpc_message->last_open) && ($xmlrpc_message->last_open == 'value'))) {
       
   333         $value = (string) $xmlrpc_message->current_tag_contents;
       
   334         $value_flag = TRUE;
       
   335       }
       
   336       unset($xmlrpc_message->last_open);
       
   337       break;
       
   338 
       
   339     case 'boolean':
       
   340       $value = (boolean)trim($xmlrpc_message->current_tag_contents);
       
   341       $value_flag = TRUE;
       
   342       break;
       
   343 
       
   344     case 'base64':
       
   345       $value = base64_decode(trim($xmlrpc_message->current_tag_contents));
       
   346       $value_flag = TRUE;
       
   347       break;
       
   348 
       
   349     // Deal with stacks of arrays and structs
       
   350     case 'data':
       
   351     case 'struct':
       
   352       $value = array_pop($xmlrpc_message->array_structs);
       
   353       array_pop($xmlrpc_message->array_structs_types);
       
   354       $value_flag = TRUE;
       
   355       break;
       
   356 
       
   357     case 'member':
       
   358       array_pop($xmlrpc_message->current_struct_name);
       
   359       break;
       
   360 
       
   361     case 'name':
       
   362       $xmlrpc_message->current_struct_name[] = trim($xmlrpc_message->current_tag_contents);
       
   363       break;
       
   364 
       
   365     case 'methodName':
       
   366       $xmlrpc_message->methodname = trim($xmlrpc_message->current_tag_contents);
       
   367       break;
       
   368   }
       
   369   if ($value_flag) {
       
   370     if (count($xmlrpc_message->array_structs) > 0) {
       
   371       // Add value to struct or array
       
   372       if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types) - 1] == 'struct') {
       
   373         // Add to struct
       
   374         $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name) - 1]] = $value;
       
   375       }
       
   376       else {
       
   377         // Add to array
       
   378         $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][] = $value;
       
   379       }
       
   380     }
       
   381     else {
       
   382       // Just add as a parameter
       
   383       $xmlrpc_message->params[] = $value;
       
   384     }
       
   385   }
       
   386   if (!in_array($tag, array("data", "struct", "member"))) {
       
   387     $xmlrpc_message->current_tag_contents = '';
       
   388   }
       
   389   xmlrpc_message_set($xmlrpc_message);
       
   390 }
       
   391 
       
   392 /**
       
   393  * Constructs an object representing an XML-RPC request.
       
   394  *
       
   395  * @param $method
       
   396  *   The name of the method to be called.
       
   397  * @param $args
       
   398  *   An array of parameters to send with the method.
       
   399  *
       
   400  * @return object
       
   401  *   An XML-RPC object representing the request.
       
   402  */
       
   403 function xmlrpc_request($method, $args) {
       
   404   $xmlrpc_request = new stdClass();
       
   405   $xmlrpc_request->method = $method;
       
   406   $xmlrpc_request->args = $args;
       
   407   $xmlrpc_request->xml = <<<EOD
       
   408 <?xml version="1.0"?>
       
   409 <methodCall>
       
   410 <methodName>{$xmlrpc_request->method}</methodName>
       
   411 <params>
       
   412 
       
   413 EOD;
       
   414   foreach ($xmlrpc_request->args as $arg) {
       
   415     $xmlrpc_request->xml .= '<param><value>';
       
   416     $v = xmlrpc_value($arg);
       
   417     $xmlrpc_request->xml .= xmlrpc_value_get_xml($v);
       
   418     $xmlrpc_request->xml .= "</value></param>\n";
       
   419   }
       
   420   $xmlrpc_request->xml .= '</params></methodCall>';
       
   421   return $xmlrpc_request;
       
   422 }
       
   423 
       
   424 /**
       
   425  * Generates, temporarily saves, and returns an XML-RPC error object.
       
   426  *
       
   427  * @param $code
       
   428  *   The error code.
       
   429  * @param $message
       
   430  *   The error message.
       
   431  * @param $reset
       
   432  *   TRUE to empty the temporary error storage. Ignored if $code is supplied.
       
   433  *
       
   434  * @return object
       
   435  *   An XML-RPC error object representing $code and $message, or the most
       
   436  *   recently stored error object if omitted.
       
   437  */
       
   438 function xmlrpc_error($code = NULL, $message = NULL, $reset = FALSE) {
       
   439   static $xmlrpc_error;
       
   440   if (isset($code)) {
       
   441     $xmlrpc_error = new stdClass();
       
   442     $xmlrpc_error->is_error = TRUE;
       
   443     $xmlrpc_error->code = $code;
       
   444     $xmlrpc_error->message = $message;
       
   445   }
       
   446   elseif ($reset) {
       
   447     $xmlrpc_error = NULL;
       
   448   }
       
   449   return $xmlrpc_error;
       
   450 }
       
   451 
       
   452 /**
       
   453  * Converts an XML-RPC error object into XML.
       
   454  *
       
   455  * @param $xmlrpc_error
       
   456  *   The XML-RPC error object.
       
   457  *
       
   458  * @return string
       
   459  *   An XML representation of the error as an XML methodResponse.
       
   460  */
       
   461 function xmlrpc_error_get_xml($xmlrpc_error) {
       
   462   return <<<EOD
       
   463 <methodResponse>
       
   464   <fault>
       
   465   <value>
       
   466     <struct>
       
   467     <member>
       
   468       <name>faultCode</name>
       
   469       <value><int>{$xmlrpc_error->code}</int></value>
       
   470     </member>
       
   471     <member>
       
   472       <name>faultString</name>
       
   473       <value><string>{$xmlrpc_error->message}</string></value>
       
   474     </member>
       
   475     </struct>
       
   476   </value>
       
   477   </fault>
       
   478 </methodResponse>
       
   479 
       
   480 EOD;
       
   481 }
       
   482 
       
   483 /**
       
   484  * Converts a PHP or ISO date/time to an XML-RPC object.
       
   485  *
       
   486  * @param $time
       
   487  *   A PHP timestamp or an ISO date-time string.
       
   488  *
       
   489  * @return object
       
   490  *   An XML-RPC time/date object.
       
   491  */
       
   492 function xmlrpc_date($time) {
       
   493   $xmlrpc_date = new stdClass();
       
   494   $xmlrpc_date->is_date = TRUE;
       
   495   // $time can be a PHP timestamp or an ISO one
       
   496   if (is_numeric($time)) {
       
   497     $xmlrpc_date->year = gmdate('Y', $time);
       
   498     $xmlrpc_date->month = gmdate('m', $time);
       
   499     $xmlrpc_date->day = gmdate('d', $time);
       
   500     $xmlrpc_date->hour = gmdate('H', $time);
       
   501     $xmlrpc_date->minute = gmdate('i', $time);
       
   502     $xmlrpc_date->second = gmdate('s', $time);
       
   503     $xmlrpc_date->iso8601 = gmdate('Ymd\TH:i:s', $time);
       
   504   }
       
   505   else {
       
   506     $xmlrpc_date->iso8601 = $time;
       
   507     $time = str_replace(array('-', ':'), '', $time);
       
   508     $xmlrpc_date->year = substr($time, 0, 4);
       
   509     $xmlrpc_date->month = substr($time, 4, 2);
       
   510     $xmlrpc_date->day = substr($time, 6, 2);
       
   511     $xmlrpc_date->hour = substr($time, 9, 2);
       
   512     $xmlrpc_date->minute = substr($time, 11, 2);
       
   513     $xmlrpc_date->second = substr($time, 13, 2);
       
   514   }
       
   515   return $xmlrpc_date;
       
   516 }
       
   517 
       
   518 /**
       
   519  * Converts an XML-RPC date-time object into XML.
       
   520  *
       
   521  * @param $xmlrpc_date
       
   522  *   The XML-RPC date-time object.
       
   523  *
       
   524  * @return string
       
   525  *   An XML representation of the date/time as XML.
       
   526  */
       
   527 function xmlrpc_date_get_xml($xmlrpc_date) {
       
   528   return '<dateTime.iso8601>' . $xmlrpc_date->year . $xmlrpc_date->month . $xmlrpc_date->day . 'T' . $xmlrpc_date->hour . ':' . $xmlrpc_date->minute . ':' . $xmlrpc_date->second . '</dateTime.iso8601>';
       
   529 }
       
   530 
       
   531 /**
       
   532  * Returns an XML-RPC base 64 object.
       
   533  *
       
   534  * @param $data
       
   535  *   Base 64 data to store in returned object.
       
   536  *
       
   537  * @return object
       
   538  *   An XML-RPC base 64 object.
       
   539  */
       
   540 function xmlrpc_base64($data) {
       
   541   $xmlrpc_base64 = new stdClass();
       
   542   $xmlrpc_base64->is_base64 = TRUE;
       
   543   $xmlrpc_base64->data = $data;
       
   544   return $xmlrpc_base64;
       
   545 }
       
   546 
       
   547 /**
       
   548  * Converts an XML-RPC base 64 object into XML.
       
   549  *
       
   550  * @param $xmlrpc_base64
       
   551  *   The XML-RPC base 64 object.
       
   552  *
       
   553  * @return string
       
   554  *   An XML representation of the base 64 data as XML.
       
   555  */
       
   556 function xmlrpc_base64_get_xml($xmlrpc_base64) {
       
   557   return '<base64>' . base64_encode($xmlrpc_base64->data) . '</base64>';
       
   558 }
       
   559 
       
   560 /**
       
   561  * Performs one or more XML-RPC requests.
       
   562  *
       
   563  * @param $url
       
   564  *   An absolute URL of the XML-RPC endpoint, e.g.,
       
   565  *   http://example.com/xmlrpc.php
       
   566  * @param $args
       
   567  *   An associative array whose keys are the methods to call and whose values
       
   568  *   are the arguments to pass to the respective method. If multiple methods
       
   569  *   are specified, a system.multicall is performed.
       
   570  * @param $options
       
   571  *   (optional) An array of options to pass along to drupal_http_request().
       
   572  *
       
   573  * @return
       
   574  *   A single response (single request) or an array of responses (multicall
       
   575  *   request). Each response is the return value of the method, just as if it
       
   576  *   has been a local function call, on success, or FALSE on failure. If FALSE
       
   577  *   is returned, see xmlrpc_errno() and xmlrpc_error_msg() to get more
       
   578  *   information.
       
   579  */
       
   580 function _xmlrpc($url, $args, $options = array()) {
       
   581   xmlrpc_clear_error();
       
   582   if (count($args) > 1) {
       
   583     $multicall_args = array();
       
   584     foreach ($args as $method => $call) {
       
   585       $multicall_args[] = array('methodName' => $method, 'params' => $call);
       
   586     }
       
   587     $method = 'system.multicall';
       
   588     $args = array($multicall_args);
       
   589   }
       
   590   else {
       
   591     $method = key($args);
       
   592     $args = $args[$method];
       
   593   }
       
   594   $xmlrpc_request = xmlrpc_request($method, $args);
       
   595   // Required options which will replace any that are passed in.
       
   596   $options['method'] = 'POST';
       
   597   $options['headers']['Content-Type'] = 'text/xml';
       
   598   $options['data'] = $xmlrpc_request->xml;
       
   599   $result = drupal_http_request($url, $options);
       
   600   if ($result->code != 200) {
       
   601     xmlrpc_error($result->code, $result->error);
       
   602     return FALSE;
       
   603   }
       
   604   $message = xmlrpc_message($result->data);
       
   605   // Now parse what we've got back
       
   606   if (!xmlrpc_message_parse($message)) {
       
   607     // XML error
       
   608     xmlrpc_error(-32700, t('Parse error. Not well formed'));
       
   609     return FALSE;
       
   610   }
       
   611   // Is the message a fault?
       
   612   if ($message->messagetype == 'fault') {
       
   613     xmlrpc_error($message->fault_code, $message->fault_string);
       
   614     return FALSE;
       
   615   }
       
   616   // We now know that the message is well-formed and a non-fault result.
       
   617   if ($method == 'system.multicall') {
       
   618     // Return per-method results or error objects.
       
   619     $return = array();
       
   620     foreach ($message->params[0] as $result) {
       
   621       if (array_keys($result) == array(0)) {
       
   622         $return[] = $result[0];
       
   623       }
       
   624       else {
       
   625         $return[] = xmlrpc_error($result['faultCode'], $result['faultString']);
       
   626       }
       
   627     }
       
   628   }
       
   629   else {
       
   630     $return = $message->params[0];
       
   631   }
       
   632   return $return;
       
   633 }
       
   634 
       
   635 /**
       
   636  * Returns the last XML-RPC client error number.
       
   637  */
       
   638 function xmlrpc_errno() {
       
   639   $error = xmlrpc_error();
       
   640   return ($error != NULL ? $error->code : NULL);
       
   641 }
       
   642 
       
   643 /**
       
   644  * Returns the last XML-RPC client error message.
       
   645  */
       
   646 function xmlrpc_error_msg() {
       
   647   $error = xmlrpc_error();
       
   648   return ($error != NULL ? $error->message : NULL);
       
   649 }
       
   650 
       
   651 /**
       
   652  * Clears any previously-saved errors.
       
   653  *
       
   654  * @see xmlrpc_error()
       
   655  */
       
   656 function xmlrpc_clear_error() {
       
   657   xmlrpc_error(NULL, NULL, TRUE);
       
   658 }