web/drupal/includes/xmlrpc.inc
branchdrupal
changeset 74 0ff3ba646492
equal deleted inserted replaced
73:fcf75e232c5b 74:0ff3ba646492
       
     1 <?php
       
     2 // $Id: xmlrpc.inc,v 1.47.2.5 2009/01/14 21:36:16 goba Exp $
       
     3 
       
     4 /**
       
     5  * @file
       
     6  * Drupal XML-RPC library. Based on the IXR - The Incutio XML-RPC Library - (c) Incutio Ltd 2002-2005
       
     7  * Version 1.7 (beta) - Simon Willison, 23rd May 2005
       
     8  * Site:   http://scripts.incutio.com/xmlrpc/
       
     9  * Manual: http://scripts.incutio.com/xmlrpc/manual.php
       
    10  * This version is made available under the GNU GPL License
       
    11  */
       
    12 
       
    13 /**
       
    14  * Recursively turn a data structure into objects with 'data' and 'type' attributes.
       
    15  *
       
    16  * @param $data
       
    17  *   The data structure.
       
    18  * @param  $type
       
    19  *   Optional type assign to $data.
       
    20  * @return
       
    21  *   Object.
       
    22  */
       
    23 function xmlrpc_value($data, $type = FALSE) {
       
    24   $xmlrpc_value = new stdClass();
       
    25   $xmlrpc_value->data = $data;
       
    26   if (!$type) {
       
    27     $type = xmlrpc_value_calculate_type($xmlrpc_value);
       
    28   }
       
    29   $xmlrpc_value->type = $type;
       
    30   if ($type == 'struct') {
       
    31     // Turn all the values in the array into new xmlrpc_values
       
    32     foreach ($xmlrpc_value->data as $key => $value) {
       
    33       $xmlrpc_value->data[$key] = xmlrpc_value($value);
       
    34     }
       
    35   }
       
    36   if ($type == 'array') {
       
    37     for ($i = 0, $j = count($xmlrpc_value->data); $i < $j; $i++) {
       
    38       $xmlrpc_value->data[$i] = xmlrpc_value($xmlrpc_value->data[$i]);
       
    39     }
       
    40   }
       
    41   return $xmlrpc_value;
       
    42 }
       
    43 
       
    44 /**
       
    45  * Map PHP type to XML-RPC type.
       
    46  *
       
    47  * @param $xmlrpc_value
       
    48  *   Variable whose type should be mapped.
       
    49  * @return
       
    50  *   XML-RPC type as string.
       
    51  * @see
       
    52  *   http://www.xmlrpc.com/spec#scalars
       
    53  */
       
    54 function xmlrpc_value_calculate_type(&$xmlrpc_value) {
       
    55   // http://www.php.net/gettype: Never use gettype() to test for a certain type [...] Instead, use the is_* functions.
       
    56   if (is_bool($xmlrpc_value->data)) {
       
    57     return 'boolean';
       
    58   }
       
    59   if (is_double($xmlrpc_value->data)) {
       
    60     return 'double';
       
    61   }
       
    62   if (is_int($xmlrpc_value->data)) {
       
    63       return 'int';
       
    64   }
       
    65   if (is_array($xmlrpc_value->data)) {
       
    66     // empty or integer-indexed arrays are 'array', string-indexed arrays 'struct'
       
    67     return empty($xmlrpc_value->data) || range(0, count($xmlrpc_value->data) - 1) === array_keys($xmlrpc_value->data) ? 'array' : 'struct';
       
    68   }
       
    69   if (is_object($xmlrpc_value->data)) {
       
    70     if ($xmlrpc_value->data->is_date) {
       
    71       return 'date';
       
    72     }
       
    73     if ($xmlrpc_value->data->is_base64) {
       
    74       return 'base64';
       
    75     }
       
    76     $xmlrpc_value->data = get_object_vars($xmlrpc_value->data);
       
    77     return 'struct';
       
    78   }
       
    79   // default
       
    80   return 'string';
       
    81 }
       
    82 
       
    83 /**
       
    84  * Generate XML representing the given value.
       
    85  *
       
    86  * @param $xmlrpc_value
       
    87  * @return
       
    88  *   XML representation of value.
       
    89  */
       
    90 function xmlrpc_value_get_xml($xmlrpc_value) {
       
    91   switch ($xmlrpc_value->type) {
       
    92     case 'boolean':
       
    93       return '<boolean>'. (($xmlrpc_value->data) ? '1' : '0') .'</boolean>';
       
    94       break;
       
    95     case 'int':
       
    96       return '<int>'. $xmlrpc_value->data .'</int>';
       
    97       break;
       
    98     case 'double':
       
    99       return '<double>'. $xmlrpc_value->data .'</double>';
       
   100       break;
       
   101     case 'string':
       
   102       // Note: we don't escape apostrophes because of the many blogging clients
       
   103       // that don't support numerical entities (and XML in general) properly.
       
   104       return '<string>'. htmlspecialchars($xmlrpc_value->data) .'</string>';
       
   105       break;
       
   106     case 'array':
       
   107       $return = '<array><data>'."\n";
       
   108       foreach ($xmlrpc_value->data as $item) {
       
   109         $return .= '  <value>'. xmlrpc_value_get_xml($item) ."</value>\n";
       
   110       }
       
   111       $return .= '</data></array>';
       
   112       return $return;
       
   113       break;
       
   114     case 'struct':
       
   115       $return = '<struct>'."\n";
       
   116       foreach ($xmlrpc_value->data as $name => $value) {
       
   117         $return .= "  <member><name>". check_plain($name) ."</name><value>";
       
   118         $return .= xmlrpc_value_get_xml($value) ."</value></member>\n";
       
   119       }
       
   120       $return .= '</struct>';
       
   121       return $return;
       
   122       break;
       
   123     case 'date':
       
   124       return xmlrpc_date_get_xml($xmlrpc_value->data);
       
   125       break;
       
   126     case 'base64':
       
   127       return xmlrpc_base64_get_xml($xmlrpc_value->data);
       
   128       break;
       
   129   }
       
   130   return FALSE;
       
   131 }
       
   132 
       
   133 /**
       
   134  * Construct an object representing an XML-RPC message.
       
   135  *
       
   136  * @param $message
       
   137  *   String containing XML as defined at http://www.xmlrpc.com/spec
       
   138  * @return
       
   139  *   Object
       
   140  */
       
   141 function xmlrpc_message($message) {
       
   142   $xmlrpc_message = new stdClass();
       
   143   $xmlrpc_message->array_structs = array();   // The stack used to keep track of the current array/struct
       
   144   $xmlrpc_message->array_structs_types = array(); // The stack used to keep track of if things are structs or array
       
   145   $xmlrpc_message->current_struct_name = array();  // A stack as well
       
   146   $xmlrpc_message->message = $message;
       
   147   return $xmlrpc_message;
       
   148 }
       
   149 
       
   150 /**
       
   151  * Parse an XML-RPC message. If parsing fails, the faultCode and faultString
       
   152  * will be added to the message object.
       
   153  *
       
   154  * @param $xmlrpc_message
       
   155  *   Object generated by xmlrpc_message()
       
   156  * @return
       
   157  *   TRUE if parsing succeeded; FALSE otherwise
       
   158  */
       
   159 function xmlrpc_message_parse(&$xmlrpc_message) {
       
   160   // First remove the XML declaration
       
   161   $xmlrpc_message->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $xmlrpc_message->message);
       
   162   if (trim($xmlrpc_message->message) == '') {
       
   163     return FALSE;
       
   164   }
       
   165   $xmlrpc_message->_parser = xml_parser_create();
       
   166   // Set XML parser to take the case of tags into account.
       
   167   xml_parser_set_option($xmlrpc_message->_parser, XML_OPTION_CASE_FOLDING, FALSE);
       
   168   // Set XML parser callback functions
       
   169   xml_set_element_handler($xmlrpc_message->_parser, 'xmlrpc_message_tag_open', 'xmlrpc_message_tag_close');
       
   170   xml_set_character_data_handler($xmlrpc_message->_parser, 'xmlrpc_message_cdata');
       
   171   xmlrpc_message_set($xmlrpc_message);
       
   172   if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) {
       
   173     return FALSE;
       
   174   }
       
   175   xml_parser_free($xmlrpc_message->_parser);
       
   176   // Grab the error messages, if any
       
   177   $xmlrpc_message = xmlrpc_message_get();
       
   178   if ($xmlrpc_message->messagetype == 'fault') {
       
   179     $xmlrpc_message->fault_code = $xmlrpc_message->params[0]['faultCode'];
       
   180     $xmlrpc_message->fault_string = $xmlrpc_message->params[0]['faultString'];
       
   181   }
       
   182   return TRUE;
       
   183 }
       
   184 
       
   185 /**
       
   186  * Store a copy of the $xmlrpc_message object temporarily.
       
   187  *
       
   188  * @param $value
       
   189  *   Object
       
   190  * @return
       
   191  *   The most recently stored $xmlrpc_message
       
   192  */
       
   193 function xmlrpc_message_set($value = NULL) {
       
   194   static $xmlrpc_message;
       
   195   if ($value) {
       
   196     $xmlrpc_message = $value;
       
   197   }
       
   198   return $xmlrpc_message;
       
   199 }
       
   200 
       
   201 function xmlrpc_message_get() {
       
   202   return xmlrpc_message_set();
       
   203 }
       
   204 
       
   205 function xmlrpc_message_tag_open($parser, $tag, $attr) {
       
   206   $xmlrpc_message = xmlrpc_message_get();
       
   207   $xmlrpc_message->current_tag_contents = '';
       
   208   $xmlrpc_message->last_open = $tag;
       
   209   switch ($tag) {
       
   210     case 'methodCall':
       
   211     case 'methodResponse':
       
   212     case 'fault':
       
   213       $xmlrpc_message->messagetype = $tag;
       
   214       break;
       
   215     // Deal with stacks of arrays and structs
       
   216     case 'data':
       
   217       $xmlrpc_message->array_structs_types[] = 'array';
       
   218       $xmlrpc_message->array_structs[] = array();
       
   219       break;
       
   220     case 'struct':
       
   221       $xmlrpc_message->array_structs_types[] = 'struct';
       
   222       $xmlrpc_message->array_structs[] = array();
       
   223       break;
       
   224   }
       
   225   xmlrpc_message_set($xmlrpc_message);
       
   226 }
       
   227 
       
   228 function xmlrpc_message_cdata($parser, $cdata) {
       
   229   $xmlrpc_message = xmlrpc_message_get();
       
   230   $xmlrpc_message->current_tag_contents .= $cdata;
       
   231   xmlrpc_message_set($xmlrpc_message);
       
   232 }
       
   233 
       
   234 function xmlrpc_message_tag_close($parser, $tag) {
       
   235   $xmlrpc_message = xmlrpc_message_get();
       
   236   $value_flag = FALSE;
       
   237   switch ($tag) {
       
   238     case 'int':
       
   239     case 'i4':
       
   240       $value = (int)trim($xmlrpc_message->current_tag_contents);
       
   241       $value_flag = TRUE;
       
   242       break;
       
   243     case 'double':
       
   244       $value = (double)trim($xmlrpc_message->current_tag_contents);
       
   245       $value_flag = TRUE;
       
   246       break;
       
   247     case 'string':
       
   248       $value = $xmlrpc_message->current_tag_contents;
       
   249       $value_flag = TRUE;
       
   250       break;
       
   251     case 'dateTime.iso8601':
       
   252       $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents));
       
   253       // $value = $iso->getTimestamp();
       
   254       $value_flag = TRUE;
       
   255       break;
       
   256     case 'value':
       
   257       // If no type is indicated, the type is string
       
   258       // We take special care for empty values
       
   259       if (trim($xmlrpc_message->current_tag_contents) != '' || (isset($xmlrpc_message->last_open) && ($xmlrpc_message->last_open == 'value'))) {
       
   260         $value = (string)$xmlrpc_message->current_tag_contents;
       
   261         $value_flag = TRUE;
       
   262       }
       
   263       unset($xmlrpc_message->last_open);
       
   264       break;
       
   265     case 'boolean':
       
   266       $value = (boolean)trim($xmlrpc_message->current_tag_contents);
       
   267       $value_flag = TRUE;
       
   268       break;
       
   269     case 'base64':
       
   270       $value = base64_decode(trim($xmlrpc_message->current_tag_contents));
       
   271       $value_flag = TRUE;
       
   272       break;
       
   273     // Deal with stacks of arrays and structs
       
   274     case 'data':
       
   275     case 'struct':
       
   276       $value = array_pop($xmlrpc_message->array_structs );
       
   277       array_pop($xmlrpc_message->array_structs_types);
       
   278       $value_flag = TRUE;
       
   279       break;
       
   280     case 'member':
       
   281       array_pop($xmlrpc_message->current_struct_name);
       
   282       break;
       
   283     case 'name':
       
   284       $xmlrpc_message->current_struct_name[] = trim($xmlrpc_message->current_tag_contents);
       
   285       break;
       
   286     case 'methodName':
       
   287       $xmlrpc_message->methodname = trim($xmlrpc_message->current_tag_contents);
       
   288       break;
       
   289   }
       
   290   if ($value_flag) {
       
   291     if (count($xmlrpc_message->array_structs ) > 0) {
       
   292       // Add value to struct or array
       
   293       if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types)-1] == 'struct') {
       
   294         // Add to struct
       
   295         $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name)-1]] = $value;
       
   296       }
       
   297       else {
       
   298         // Add to array
       
   299         $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][] = $value;
       
   300       }
       
   301     }
       
   302     else {
       
   303       // Just add as a parameter
       
   304       $xmlrpc_message->params[] = $value;
       
   305     }
       
   306   }
       
   307   if (!in_array($tag, array("data", "struct", "member"))) {
       
   308     $xmlrpc_message->current_tag_contents = '';
       
   309   }
       
   310   xmlrpc_message_set($xmlrpc_message);
       
   311 }
       
   312 
       
   313 /**
       
   314  * Construct an object representing an XML-RPC request
       
   315  *
       
   316  * @param $method
       
   317  *   The name of the method to be called
       
   318  * @param $args
       
   319  *   An array of parameters to send with the method.
       
   320  * @return
       
   321  *   Object
       
   322  */
       
   323 function xmlrpc_request($method, $args) {
       
   324   $xmlrpc_request = new stdClass();
       
   325   $xmlrpc_request->method = $method;
       
   326   $xmlrpc_request->args = $args;
       
   327   $xmlrpc_request->xml = <<<EOD
       
   328 <?xml version="1.0"?>
       
   329 <methodCall>
       
   330 <methodName>{$xmlrpc_request->method}</methodName>
       
   331 <params>
       
   332 
       
   333 EOD;
       
   334   foreach ($xmlrpc_request->args as $arg) {
       
   335     $xmlrpc_request->xml .= '<param><value>';
       
   336     $v = xmlrpc_value($arg);
       
   337     $xmlrpc_request->xml .= xmlrpc_value_get_xml($v);
       
   338     $xmlrpc_request->xml .= "</value></param>\n";
       
   339   }
       
   340   $xmlrpc_request->xml .= '</params></methodCall>';
       
   341   return $xmlrpc_request;
       
   342 }
       
   343 
       
   344 
       
   345 function xmlrpc_error($code = NULL, $message = NULL, $reset = FALSE) {
       
   346   static $xmlrpc_error;
       
   347   if (isset($code)) {
       
   348     $xmlrpc_error = new stdClass();
       
   349     $xmlrpc_error->is_error = TRUE;
       
   350     $xmlrpc_error->code = $code;
       
   351     $xmlrpc_error->message = $message;
       
   352   }
       
   353   elseif ($reset) {
       
   354     $xmlrpc_error = NULL;
       
   355   }
       
   356   return $xmlrpc_error;
       
   357 }
       
   358 
       
   359 function xmlrpc_error_get_xml($xmlrpc_error) {
       
   360   return <<<EOD
       
   361 <methodResponse>
       
   362   <fault>
       
   363   <value>
       
   364     <struct>
       
   365     <member>
       
   366       <name>faultCode</name>
       
   367       <value><int>{$xmlrpc_error->code}</int></value>
       
   368     </member>
       
   369     <member>
       
   370       <name>faultString</name>
       
   371       <value><string>{$xmlrpc_error->message}</string></value>
       
   372     </member>
       
   373     </struct>
       
   374   </value>
       
   375   </fault>
       
   376 </methodResponse>
       
   377 
       
   378 EOD;
       
   379 }
       
   380 
       
   381 function xmlrpc_date($time) {
       
   382   $xmlrpc_date = new stdClass();
       
   383   $xmlrpc_date->is_date = TRUE;
       
   384   // $time can be a PHP timestamp or an ISO one
       
   385   if (is_numeric($time)) {
       
   386     $xmlrpc_date->year = gmdate('Y', $time);
       
   387     $xmlrpc_date->month = gmdate('m', $time);
       
   388     $xmlrpc_date->day = gmdate('d', $time);
       
   389     $xmlrpc_date->hour = gmdate('H', $time);
       
   390     $xmlrpc_date->minute = gmdate('i', $time);
       
   391     $xmlrpc_date->second = gmdate('s', $time);
       
   392     $xmlrpc_date->iso8601 = gmdate('Ymd\TH:i:s', $time);
       
   393   }
       
   394   else {
       
   395     $xmlrpc_date->iso8601 = $time;
       
   396     $time = str_replace(array('-', ':'), '', $time);
       
   397     $xmlrpc_date->year = substr($time, 0, 4);
       
   398     $xmlrpc_date->month = substr($time, 4, 2);
       
   399     $xmlrpc_date->day = substr($time, 6, 2);
       
   400     $xmlrpc_date->hour = substr($time, 9, 2);
       
   401     $xmlrpc_date->minute = substr($time, 11, 2);
       
   402     $xmlrpc_date->second = substr($time, 13, 2);
       
   403   }
       
   404   return $xmlrpc_date;
       
   405 }
       
   406 
       
   407 function xmlrpc_date_get_xml($xmlrpc_date) {
       
   408   return '<dateTime.iso8601>'. $xmlrpc_date->year . $xmlrpc_date->month . $xmlrpc_date->day .'T'. $xmlrpc_date->hour .':'. $xmlrpc_date->minute .':'. $xmlrpc_date->second .'</dateTime.iso8601>';
       
   409 }
       
   410 
       
   411 function xmlrpc_base64($data) {
       
   412   $xmlrpc_base64 = new stdClass();
       
   413   $xmlrpc_base64->is_base64 = TRUE;
       
   414   $xmlrpc_base64->data = $data;
       
   415   return $xmlrpc_base64;
       
   416 }
       
   417 
       
   418 function xmlrpc_base64_get_xml($xmlrpc_base64) {
       
   419   return '<base64>'. base64_encode($xmlrpc_base64->data) .'</base64>';
       
   420 }
       
   421 
       
   422 /**
       
   423  * Execute an XML remote procedural call. This is private function; call xmlrpc()
       
   424  * in common.inc instead of this function.
       
   425  *
       
   426  * @return
       
   427  *   A $xmlrpc_message object if the call succeeded; FALSE if the call failed
       
   428  */
       
   429 function _xmlrpc() {
       
   430   $args = func_get_args();
       
   431   $url = array_shift($args);
       
   432   xmlrpc_clear_error();
       
   433   if (is_array($args[0])) {
       
   434     $method = 'system.multicall';
       
   435     $multicall_args = array();
       
   436     foreach ($args[0] as $call) {
       
   437       $multicall_args[] = array('methodName' => array_shift($call), 'params' => $call);
       
   438     }
       
   439     $args = array($multicall_args);
       
   440   }
       
   441   else {
       
   442     $method = array_shift($args);
       
   443   }
       
   444   $xmlrpc_request = xmlrpc_request($method, $args);
       
   445   $result = drupal_http_request($url, array("Content-Type" => "text/xml"), 'POST', $xmlrpc_request->xml);
       
   446   if ($result->code != 200) {
       
   447     xmlrpc_error($result->code, $result->error);
       
   448     return FALSE;
       
   449   }
       
   450   $message = xmlrpc_message($result->data);
       
   451   // Now parse what we've got back
       
   452   if (!xmlrpc_message_parse($message)) {
       
   453     // XML error
       
   454     xmlrpc_error(-32700, t('Parse error. Not well formed'));
       
   455     return FALSE;
       
   456   }
       
   457   // Is the message a fault?
       
   458   if ($message->messagetype == 'fault') {
       
   459     xmlrpc_error($message->fault_code, $message->fault_string);
       
   460     return FALSE;
       
   461   }
       
   462   // Message must be OK
       
   463   return $message->params[0];
       
   464 }
       
   465 
       
   466 /**
       
   467  * Returns the last XML-RPC client error number
       
   468  */
       
   469 function xmlrpc_errno() {
       
   470   $error = xmlrpc_error();
       
   471   return ($error != NULL ? $error->code : NULL);
       
   472 }
       
   473 
       
   474 /**
       
   475  * Returns the last XML-RPC client error message
       
   476  */
       
   477 function xmlrpc_error_msg() {
       
   478   $error = xmlrpc_error();
       
   479   return ($error != NULL ? $error->message : NULL);
       
   480 }
       
   481 
       
   482 /**
       
   483  * Clears any previous error.
       
   484  */
       
   485 function xmlrpc_clear_error() {
       
   486   xmlrpc_error(NULL, NULL, TRUE);
       
   487 }