Code coverage for /20080809/includes/xmlrpc.inc

Line #Times calledCode
1
<?php
2
// $Id: xmlrpc.inc,v 1.52 2008/08/08 20:00:09 dries 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
 */
2319
function xmlrpc_value($data, $type = FALSE) {
2419
  $xmlrpc_value = new stdClass();
2519
  $xmlrpc_value->data = $data;
2619
  if (!$type) {
2719
    $type = xmlrpc_value_calculate_type($xmlrpc_value);
2819
  }
2919
  $xmlrpc_value->type = $type;
3019
  if ($type == 'struct') {
31
    // Turn all the values in the array into new xmlrpc_values
3210
    foreach ($xmlrpc_value->data as $key => $value) {
3310
      $xmlrpc_value->data[$key] = xmlrpc_value($value);
3410
    }
3510
  }
3619
  if ($type == 'array') {
377
    for ($i = 0, $j = count($xmlrpc_value->data); $i < $j; $i++) {
387
      $xmlrpc_value->data[$i] = xmlrpc_value($xmlrpc_value->data[$i]);
397
    }
407
  }
4119
  return $xmlrpc_value;
420
}
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
 */
5419
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.
5619
  if (is_bool($xmlrpc_value->data)) {
578
    return 'boolean';
580
  }
5916
  if (is_double($xmlrpc_value->data)) {
603
    return 'double';
610
  }
6216
  if (is_int($xmlrpc_value->data)) {
6311
      return 'int';
640
  }
6513
  if (is_array($xmlrpc_value->data)) {
66
    // empty or integer-indexed arrays are 'array', string-indexed arrays
'struct'
6711
    return empty($xmlrpc_value->data) || range(0,
count($xmlrpc_value->data) - 1) === array_keys($xmlrpc_value->data) ?
'array' : 'struct';
680
  }
6911
  if (is_object($xmlrpc_value->data)) {
705
    if (isset($xmlrpc_value->data->is_date)) {
714
      return 'date';
720
    }
734
    if (isset($xmlrpc_value->data->is_base64)) {
744
      return 'base64';
750
    }
760
    $xmlrpc_value->data = get_object_vars($xmlrpc_value->data);
770
    return 'struct';
780
  }
79
  // default
8011
  return 'string';
810
}
82
83
/**
84
 * Generate XML representing the given value.
85
 *
86
 * @param $xmlrpc_value
87
 * @return
88
 *   XML representation of value.
89
 */
9019
function xmlrpc_value_get_xml($xmlrpc_value) {
9119
  switch ($xmlrpc_value->type) {
9219
    case 'boolean':
938
      return '<boolean>' . (($xmlrpc_value->data) ? '1' : '0') .
'</boolean>';
940
      break;
9516
    case 'int':
9611
      return '<int>' . $xmlrpc_value->data . '</int>';
970
      break;
9813
    case 'double':
993
      return '<double>' . $xmlrpc_value->data . '</double>';
1000
      break;
10113
    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.
10411
      return '<string>' . htmlspecialchars($xmlrpc_value->data) .
'</string>';
1050
      break;
10611
    case 'array':
1077
      $return = '<array><data>' . "\n";
1087
      foreach ($xmlrpc_value->data as $item) {
1097
        $return .= '  <value>' . xmlrpc_value_get_xml($item) .
"</value>\n";
1107
      }
1117
      $return .= '</data></array>';
1127
      return $return;
1130
      break;
11411
    case 'struct':
11510
      $return = '<struct>' . "\n";
11610
      foreach ($xmlrpc_value->data as $name => $value) {
11710
        $return .= "  <member><name>" . check_plain($name) .
"</name><value>";
11810
        $return .= xmlrpc_value_get_xml($value) . "</value></member>\n";
11910
      }
12010
      $return .= '</struct>';
12110
      return $return;
1220
      break;
1235
    case 'date':
1244
      return xmlrpc_date_get_xml($xmlrpc_value->data);
1250
      break;
1264
    case 'base64':
1274
      return xmlrpc_base64_get_xml($xmlrpc_value->data);
1280
      break;
1290
  }
1300
  return FALSE;
1310
}
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
 */
14119
function xmlrpc_message($message) {
14219
  $xmlrpc_message = new stdClass();
14319
  $xmlrpc_message->array_structs = array();   // The stack used to keep
track of the current array/struct
14419
  $xmlrpc_message->array_structs_types = array(); // The stack used to keep
track of if things are structs or array
14519
  $xmlrpc_message->current_struct_name = array();  // A stack as well
14619
  $xmlrpc_message->message = $message;
14719
  return $xmlrpc_message;
1480
}
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
 */
15919
function xmlrpc_message_parse(&$xmlrpc_message) {
160
  // First remove the XML declaration
16119
  $xmlrpc_message->message = preg_replace('/<\?xml(.*)?\?' . '>/', '',
$xmlrpc_message->message);
16219
  if (trim($xmlrpc_message->message) == '') {
1630
    return FALSE;
1640
  }
16519
  $xmlrpc_message->_parser = xml_parser_create();
166
  // Set XML parser to take the case of tags into account.
16719
  xml_parser_set_option($xmlrpc_message->_parser, XML_OPTION_CASE_FOLDING,
FALSE);
168
  // Set XML parser callback functions
16919
  xml_set_element_handler($xmlrpc_message->_parser,
'xmlrpc_message_tag_open', 'xmlrpc_message_tag_close');
17019
  xml_set_character_data_handler($xmlrpc_message->_parser,
'xmlrpc_message_cdata');
17119
  xmlrpc_message_set($xmlrpc_message);
17219
  if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) {
1730
    return FALSE;
1740
  }
17519
  xml_parser_free($xmlrpc_message->_parser);
176
  // Grab the error messages, if any
17719
  $xmlrpc_message = xmlrpc_message_get();
17819
  if ($xmlrpc_message->messagetype == 'fault') {
1790
    $xmlrpc_message->fault_code = $xmlrpc_message->params[0]['faultCode'];
1800
    $xmlrpc_message->fault_string =
$xmlrpc_message->params[0]['faultString'];
1810
  }
18219
  return TRUE;
1830
}
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
 */
19319
function xmlrpc_message_set($value = NULL) {
19419
  static $xmlrpc_message;
19519
  if ($value) {
19619
    $xmlrpc_message = $value;
19719
  }
19819
  return $xmlrpc_message;
1990
}
200
20119
function xmlrpc_message_get() {
20219
  return xmlrpc_message_set();
2030
}
204
20519
function xmlrpc_message_tag_open($parser, $tag, $attr) {
20619
  $xmlrpc_message = xmlrpc_message_get();
20719
  $xmlrpc_message->current_tag_contents = '';
20819
  $xmlrpc_message->last_open = $tag;
209
  switch ($tag) {
21019
    case 'methodCall':
21119
    case 'methodResponse':
21219
    case 'fault':
21319
      $xmlrpc_message->messagetype = $tag;
21419
      break;
215
    // Deal with stacks of arrays and structs
21619
    case 'data':
2176
      $xmlrpc_message->array_structs_types[] = 'array';
2186
      $xmlrpc_message->array_structs[] = array();
2196
      break;
22019
    case 'struct':
2219
      $xmlrpc_message->array_structs_types[] = 'struct';
2229
      $xmlrpc_message->array_structs[] = array();
2239
      break;
2240
  }
22519
  xmlrpc_message_set($xmlrpc_message);
22619
}
227
22819
function xmlrpc_message_cdata($parser, $cdata) {
22919
  $xmlrpc_message = xmlrpc_message_get();
23019
  $xmlrpc_message->current_tag_contents .= $cdata;
23119
  xmlrpc_message_set($xmlrpc_message);
23219
}
233
23419
function xmlrpc_message_tag_close($parser, $tag) {
23519
  $xmlrpc_message = xmlrpc_message_get();
23619
  $value_flag = FALSE;
237
  switch ($tag) {
23819
    case 'int':
23919
    case 'i4':
24010
      $value = (int)trim($xmlrpc_message->current_tag_contents);
24110
      $value_flag = TRUE;
24210
      break;
24319
    case 'double':
2443
      $value = (double)trim($xmlrpc_message->current_tag_contents);
2453
      $value_flag = TRUE;
2463
      break;
24719
    case 'string':
24815
      $value = $xmlrpc_message->current_tag_contents;
24915
      $value_flag = TRUE;
25015
      break;
25119
    case 'dateTime.iso8601':
2524
      $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents));
253
      // $value = $iso->getTimestamp();
2544
      $value_flag = TRUE;
2554
      break;
25619
    case 'value':
257
      // If no type is indicated, the type is string
258
      // We take special care for empty values
25919
      if (trim($xmlrpc_message->current_tag_contents) != '' ||
(isset($xmlrpc_message->last_open) && ($xmlrpc_message->last_open ==
'value'))) {
2600
        $value = (string)$xmlrpc_message->current_tag_contents;
2610
        $value_flag = TRUE;
2620
      }
26319
      unset($xmlrpc_message->last_open);
26419
      break;
26519
    case 'boolean':
2667
      $value = (boolean)trim($xmlrpc_message->current_tag_contents);
2677
      $value_flag = TRUE;
2687
      break;
26919
    case 'base64':
2704
      $value = base64_decode(trim($xmlrpc_message->current_tag_contents));
2714
      $value_flag = TRUE;
2724
      break;
273
    // Deal with stacks of arrays and structs
27419
    case 'data':
27519
    case 'struct':
27610
      $value = array_pop($xmlrpc_message->array_structs );
27710
      array_pop($xmlrpc_message->array_structs_types);
27810
      $value_flag = TRUE;
27910
      break;
28019
    case 'member':
2819
      array_pop($xmlrpc_message->current_struct_name);
2829
      break;
28319
    case 'name':
2849
      $xmlrpc_message->current_struct_name[] =
trim($xmlrpc_message->current_tag_contents);
2859
      break;
28619
    case 'methodName':
28717
      $xmlrpc_message->methodname =
trim($xmlrpc_message->current_tag_contents);
28817
      break;
2890
  }
29019
  if ($value_flag) {
29119
    if (count($xmlrpc_message->array_structs ) > 0) {
292
      // Add value to struct or array
29310
      if
($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types)-1]
== 'struct') {
294
        // Add to struct
2959
        $xmlrpc_message->array_structs
[count($xmlrpc_message->array_structs
)-1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name)-1]]
= $value;
2969
      }
297
      else {
298
        // Add to array
2996
        $xmlrpc_message->array_structs
[count($xmlrpc_message->array_structs )-1][] = $value;
300
      }
30110
    }
302
    else {
303
      // Just add as a parameter
30419
      $xmlrpc_message->params[] = $value;
305
    }
30619
  }
30719
  if (!in_array($tag, array("data", "struct", "member"))) {
30819
    $xmlrpc_message->current_tag_contents = '';
30919
  }
31019
  xmlrpc_message_set($xmlrpc_message);
31119
}
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
 */
32319
function xmlrpc_request($method, $args) {
3242
  $xmlrpc_request = new stdClass();
3252
  $xmlrpc_request->method = $method;
3262
  $xmlrpc_request->args = $args;
3270
  $xmlrpc_request->xml = <<<EOD
3282
<?xml version="1.0"?>
329
<methodCall>
3302
<methodName>{$xmlrpc_request->method}</methodName>
331
<params>
3322
333
EOD;
3342
  foreach ($xmlrpc_request->args as $arg) {
3352
    $xmlrpc_request->xml .= '<param><value>';
3362
    $v = xmlrpc_value($arg);
3372
    $xmlrpc_request->xml .= xmlrpc_value_get_xml($v);
3382
    $xmlrpc_request->xml .= "</value></param>\n";
3392
  }
3402
  $xmlrpc_request->xml .= '</params></methodCall>';
3412
  return $xmlrpc_request;
3420
}
343
344
34519
function xmlrpc_error($code = NULL, $message = NULL, $reset = FALSE) {
3462
  static $xmlrpc_error;
3472
  if (isset($code)) {
3480
    $xmlrpc_error = new stdClass();
3490
    $xmlrpc_error->is_error = TRUE;
3500
    $xmlrpc_error->code = $code;
3510
    $xmlrpc_error->message = $message;
3520
    module_invoke('system', 'check_http_request');
3530
  }
3542
  elseif ($reset) {
3552
    $xmlrpc_error = NULL;
3562
  }
3572
  return $xmlrpc_error;
3580
}
359
36019
function xmlrpc_error_get_xml($xmlrpc_error) {
361
  return <<<EOD
3620
<methodResponse>
363
  <fault>
364
  <value>
365
    <struct>
366
    <member>
367
      <name>faultCode</name>
3680
      <value><int>{$xmlrpc_error->code}</int></value>
369
    </member>
370
    <member>
371
      <name>faultString</name>
3720
      <value><string>{$xmlrpc_error->message}</string></value>
373
    </member>
374
    </struct>
375
  </value>
376
  </fault>
377
</methodResponse>
3780
3790
EOD;
3800
}
381
38219
function xmlrpc_date($time) {
3835
  $xmlrpc_date = new stdClass();
3845
  $xmlrpc_date->is_date = TRUE;
385
  // $time can be a PHP timestamp or an ISO one
3865
  if (is_numeric($time)) {
3874
    $xmlrpc_date->year = date('Y', $time);
3884
    $xmlrpc_date->month = date('m', $time);
3894
    $xmlrpc_date->day = date('d', $time);
3904
    $xmlrpc_date->hour = date('H', $time);
3914
    $xmlrpc_date->minute = date('i', $time);
3924
    $xmlrpc_date->second = date('s', $time);
3934
    $xmlrpc_date->iso8601 = date('Ymd\TH:i:s', $time);
3944
  }
395
  else {
3964
    $xmlrpc_date->iso8601 = $time;
3974
    $time = str_replace(array('-', ':'), '', $time);
3984
    $xmlrpc_date->year = substr($time, 0, 4);
3994
    $xmlrpc_date->month = substr($time, 4, 2);
4004
    $xmlrpc_date->day = substr($time, 6, 2);
4014
    $xmlrpc_date->hour = substr($time, 9, 2);
4024
    $xmlrpc_date->minute = substr($time, 11, 2);
4034
    $xmlrpc_date->second = substr($time, 13, 2);
404
  }
4055
  return $xmlrpc_date;
4060
}
407
40819
function xmlrpc_date_get_xml($xmlrpc_date) {
4094
  return '<dateTime.iso8601>' . $xmlrpc_date->year . $xmlrpc_date->month .
$xmlrpc_date->day . 'T' . $xmlrpc_date->hour . ':' . $xmlrpc_date->minute .
':' . $xmlrpc_date->second . '</dateTime.iso8601>';
4100
}
411
41219
function xmlrpc_base64($data) {
4134
  $xmlrpc_base64 = new stdClass();
4144
  $xmlrpc_base64->is_base64 = TRUE;
4154
  $xmlrpc_base64->data = $data;
4164
  return $xmlrpc_base64;
4170
}
418
41919
function xmlrpc_base64_get_xml($xmlrpc_base64) {
4204
  return '<base64>' . base64_encode($xmlrpc_base64->data) . '</base64>';
4210
}
422
423
/**
424
 * Execute an XML remote procedural call. This is private function; call
xmlrpc()
425
 * in common.inc instead of this function.
426
 *
427
 * @return
428
 *   A $xmlrpc_message object if the call succeeded; FALSE if the call
failed
429
 */
43019
function _xmlrpc() {
4312
  $args = func_get_args();
4322
  $url = array_shift($args);
4332
  xmlrpc_clear_error();
4342
  if (is_array($args[0])) {
4351
    $method = 'system.multicall';
4361
    $multicall_args = array();
4371
    foreach ($args[0] as $call) {
4381
      $multicall_args[] = array('methodName' => array_shift($call),
'params' => $call);
4391
    }
4401
    $args = array($multicall_args);
4411
  }
442
  else {
4432
    $method = array_shift($args);
444
  }
4452
  $xmlrpc_request = xmlrpc_request($method, $args);
4462
  $result = drupal_http_request($url, array("Content-Type" => "text/xml"),
'POST', $xmlrpc_request->xml);
4472
  if ($result->code != 200) {
4480
    xmlrpc_error($result->code, $result->error);
4490
    return FALSE;
4500
  }
4512
  $message = xmlrpc_message($result->data);
452
  // Now parse what we've got back
4532
  if (!xmlrpc_message_parse($message)) {
454
    // XML error
4550
    xmlrpc_error(-32700, t('Parse error. Not well formed'));
4560
    return FALSE;
4570
  }
458
  // Is the message a fault?
4592
  if ($message->messagetype == 'fault') {
4600
    xmlrpc_error($message->fault_code, $message->fault_string);
4610
    return FALSE;
4620
  }
463
  // Message must be OK
4642
  return $message->params[0];
4650
}
466
467
/**
468
 * Returns the last XML-RPC client error number
469
 */
47019
function xmlrpc_errno() {
4710
  $error = xmlrpc_error();
4720
  return ($error != NULL ? $error->code : NULL);
4730
}
474
475
/**
476
 * Returns the last XML-RPC client error message
477
 */
47819
function xmlrpc_error_msg() {
4790
  $error = xmlrpc_error();
4800
  return ($error != NULL ? $error->message : NULL);
4810
}
482
483
/**
484
 * Clears any previous error.
485
 */
48619
function xmlrpc_clear_error() {
4872
  xmlrpc_error(NULL, NULL, TRUE);
48819
}