Changeset 38389 for trunk/src/wp-includes/class-IXR.php
- Timestamp:
- 08/26/2016 10:07:14 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-IXR.php
r37518 r38389 40 40 */ 41 41 42 /** 43 * IXR_Value 44 * 45 * @package IXR 46 * @since 1.5.0 47 */ 48 class IXR_Value { 49 var $data; 50 var $type; 42 require_once( ABSPATH . WPINC . '/IXR/class-IXR-value.php' ); 51 43 52 /** 53 * PHP5 constructor. 54 */ 55 function __construct( $data, $type = false ) 56 { 57 $this->data = $data; 58 if (!$type) { 59 $type = $this->calculateType(); 60 } 61 $this->type = $type; 62 if ($type == 'struct') { 63 // Turn all the values in the array in to new IXR_Value objects 64 foreach ($this->data as $key => $value) { 65 $this->data[$key] = new IXR_Value($value); 66 } 67 } 68 if ($type == 'array') { 69 for ($i = 0, $j = count($this->data); $i < $j; $i++) { 70 $this->data[$i] = new IXR_Value($this->data[$i]); 71 } 72 } 73 } 44 require_once( ABSPATH . WPINC . '/IXR/class-IXR-message.php' ); 74 45 75 /** 76 * PHP4 constructor. 77 */ 78 public function IXR_Value( $data, $type = false ) { 79 self::__construct( $data, $type ); 80 } 46 require_once( ABSPATH . WPINC . '/IXR/class-IXR-server.php' ); 81 47 82 function calculateType() 83 { 84 if ($this->data === true || $this->data === false) { 85 return 'boolean'; 86 } 87 if (is_integer($this->data)) { 88 return 'int'; 89 } 90 if (is_double($this->data)) { 91 return 'double'; 92 } 48 require_once( ABSPATH . WPINC . '/IXR/class-IXR-request.php' ); 93 49 94 // Deal with IXR object types base64 and date 95 if (is_object($this->data) && is_a($this->data, 'IXR_Date')) { 96 return 'date'; 97 } 98 if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) { 99 return 'base64'; 100 } 50 require_once( ABSPATH . WPINC . '/IXR/class-IXR-client.php' ); 101 51 102 // If it is a normal PHP object convert it in to a struct 103 if (is_object($this->data)) { 104 $this->data = get_object_vars($this->data); 105 return 'struct'; 106 } 107 if (!is_array($this->data)) { 108 return 'string'; 109 } 52 require_once( ABSPATH . WPINC . '/IXR/class-IXR-error.php' ); 110 53 111 // We have an array - is it an array or a struct? 112 if ($this->isStruct($this->data)) { 113 return 'struct'; 114 } else { 115 return 'array'; 116 } 117 } 54 require_once( ABSPATH . WPINC . '/IXR/class-IXR-date.php' ); 118 55 119 function getXml() 120 { 121 // Return XML for this value 122 switch ($this->type) { 123 case 'boolean': 124 return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>'; 125 break; 126 case 'int': 127 return '<int>'.$this->data.'</int>'; 128 break; 129 case 'double': 130 return '<double>'.$this->data.'</double>'; 131 break; 132 case 'string': 133 return '<string>'.htmlspecialchars($this->data).'</string>'; 134 break; 135 case 'array': 136 $return = '<array><data>'."\n"; 137 foreach ($this->data as $item) { 138 $return .= ' <value>'.$item->getXml()."</value>\n"; 139 } 140 $return .= '</data></array>'; 141 return $return; 142 break; 143 case 'struct': 144 $return = '<struct>'."\n"; 145 foreach ($this->data as $name => $value) { 146 $name = htmlspecialchars($name); 147 $return .= " <member><name>$name</name><value>"; 148 $return .= $value->getXml()."</value></member>\n"; 149 } 150 $return .= '</struct>'; 151 return $return; 152 break; 153 case 'date': 154 case 'base64': 155 return $this->data->getXml(); 156 break; 157 } 158 return false; 159 } 56 require_once( ABSPATH . WPINC . '/IXR/class-IXR-base64.php' ); 160 57 161 /** 162 * Checks whether or not the supplied array is a struct or not 163 * 164 * @param array $array 165 * @return bool 166 */ 167 function isStruct($array) 168 { 169 $expected = 0; 170 foreach ($array as $key => $value) { 171 if ((string)$key !== (string)$expected) { 172 return true; 173 } 174 $expected++; 175 } 176 return false; 177 } 178 } 58 require_once( ABSPATH . WPINC . '/IXR/class-IXR-introspectionserver.php' ); 179 59 180 /** 181 * IXR_MESSAGE 182 * 183 * @package IXR 184 * @since 1.5.0 185 * 186 */ 187 class IXR_Message 188 { 189 var $message; 190 var $messageType; // methodCall / methodResponse / fault 191 var $faultCode; 192 var $faultString; 193 var $methodName; 194 var $params; 195 196 // Current variable stacks 197 var $_arraystructs = array(); // The stack used to keep track of the current array/struct 198 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array 199 var $_currentStructName = array(); // A stack as well 200 var $_param; 201 var $_value; 202 var $_currentTag; 203 var $_currentTagContents; 204 // The XML parser 205 var $_parser; 206 207 /** 208 * PHP5 constructor. 209 */ 210 function __construct( $message ) 211 { 212 $this->message =& $message; 213 } 214 215 /** 216 * PHP4 constructor. 217 */ 218 public function IXR_Message( $message ) { 219 self::__construct( $message ); 220 } 221 222 function parse() 223 { 224 // first remove the XML declaration 225 // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages 226 $header = preg_replace( '/<\?xml.*?\?'.'>/s', '', substr( $this->message, 0, 100 ), 1 ); 227 $this->message = trim( substr_replace( $this->message, $header, 0, 100 ) ); 228 if ( '' == $this->message ) { 229 return false; 230 } 231 232 // Then remove the DOCTYPE 233 $header = preg_replace( '/^<!DOCTYPE[^>]*+>/i', '', substr( $this->message, 0, 200 ), 1 ); 234 $this->message = trim( substr_replace( $this->message, $header, 0, 200 ) ); 235 if ( '' == $this->message ) { 236 return false; 237 } 238 239 // Check that the root tag is valid 240 $root_tag = substr( $this->message, 0, strcspn( substr( $this->message, 0, 20 ), "> \t\r\n" ) ); 241 if ( '<!DOCTYPE' === strtoupper( $root_tag ) ) { 242 return false; 243 } 244 if ( ! in_array( $root_tag, array( '<methodCall', '<methodResponse', '<fault' ) ) ) { 245 return false; 246 } 247 248 // Bail if there are too many elements to parse 249 $element_limit = 30000; 250 if ( function_exists( 'apply_filters' ) ) { 251 /** 252 * Filters the number of elements to parse in an XML-RPC response. 253 * 254 * @since 4.0.0 255 * 256 * @param int $element_limit Default elements limit. 257 */ 258 $element_limit = apply_filters( 'xmlrpc_element_limit', $element_limit ); 259 } 260 if ( $element_limit && 2 * $element_limit < substr_count( $this->message, '<' ) ) { 261 return false; 262 } 263 264 $this->_parser = xml_parser_create(); 265 // Set XML parser to take the case of tags in to account 266 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); 267 // Set XML parser callback functions 268 xml_set_object($this->_parser, $this); 269 xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); 270 xml_set_character_data_handler($this->_parser, 'cdata'); 271 272 // 256Kb, parse in chunks to avoid the RAM usage on very large messages 273 $chunk_size = 262144; 274 275 /** 276 * Filters the chunk size that can be used to parse an XML-RPC reponse message. 277 * 278 * @since 4.4.0 279 * 280 * @param int $chunk_size Chunk size to parse in bytes. 281 */ 282 $chunk_size = apply_filters( 'xmlrpc_chunk_parsing_size', $chunk_size ); 283 284 $final = false; 285 do { 286 if (strlen($this->message) <= $chunk_size) { 287 $final = true; 288 } 289 $part = substr($this->message, 0, $chunk_size); 290 $this->message = substr($this->message, $chunk_size); 291 if (!xml_parse($this->_parser, $part, $final)) { 292 return false; 293 } 294 if ($final) { 295 break; 296 } 297 } while (true); 298 xml_parser_free($this->_parser); 299 300 // Grab the error messages, if any 301 if ($this->messageType == 'fault') { 302 $this->faultCode = $this->params[0]['faultCode']; 303 $this->faultString = $this->params[0]['faultString']; 304 } 305 return true; 306 } 307 308 function tag_open($parser, $tag, $attr) 309 { 310 $this->_currentTagContents = ''; 311 $this->currentTag = $tag; 312 switch($tag) { 313 case 'methodCall': 314 case 'methodResponse': 315 case 'fault': 316 $this->messageType = $tag; 317 break; 318 /* Deal with stacks of arrays and structs */ 319 case 'data': // data is to all intents and puposes more interesting than array 320 $this->_arraystructstypes[] = 'array'; 321 $this->_arraystructs[] = array(); 322 break; 323 case 'struct': 324 $this->_arraystructstypes[] = 'struct'; 325 $this->_arraystructs[] = array(); 326 break; 327 } 328 } 329 330 function cdata($parser, $cdata) 331 { 332 $this->_currentTagContents .= $cdata; 333 } 334 335 function tag_close($parser, $tag) 336 { 337 $valueFlag = false; 338 switch($tag) { 339 case 'int': 340 case 'i4': 341 $value = (int)trim($this->_currentTagContents); 342 $valueFlag = true; 343 break; 344 case 'double': 345 $value = (double)trim($this->_currentTagContents); 346 $valueFlag = true; 347 break; 348 case 'string': 349 $value = (string)trim($this->_currentTagContents); 350 $valueFlag = true; 351 break; 352 case 'dateTime.iso8601': 353 $value = new IXR_Date(trim($this->_currentTagContents)); 354 $valueFlag = true; 355 break; 356 case 'value': 357 // "If no type is indicated, the type is string." 358 if (trim($this->_currentTagContents) != '') { 359 $value = (string)$this->_currentTagContents; 360 $valueFlag = true; 361 } 362 break; 363 case 'boolean': 364 $value = (boolean)trim($this->_currentTagContents); 365 $valueFlag = true; 366 break; 367 case 'base64': 368 $value = base64_decode($this->_currentTagContents); 369 $valueFlag = true; 370 break; 371 /* Deal with stacks of arrays and structs */ 372 case 'data': 373 case 'struct': 374 $value = array_pop($this->_arraystructs); 375 array_pop($this->_arraystructstypes); 376 $valueFlag = true; 377 break; 378 case 'member': 379 array_pop($this->_currentStructName); 380 break; 381 case 'name': 382 $this->_currentStructName[] = trim($this->_currentTagContents); 383 break; 384 case 'methodName': 385 $this->methodName = trim($this->_currentTagContents); 386 break; 387 } 388 389 if ($valueFlag) { 390 if (count($this->_arraystructs) > 0) { 391 // Add value to struct or array 392 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') { 393 // Add to struct 394 $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value; 395 } else { 396 // Add to array 397 $this->_arraystructs[count($this->_arraystructs)-1][] = $value; 398 } 399 } else { 400 // Just add as a parameter 401 $this->params[] = $value; 402 } 403 } 404 $this->_currentTagContents = ''; 405 } 406 } 407 408 /** 409 * IXR_Server 410 * 411 * @package IXR 412 * @since 1.5.0 413 */ 414 class IXR_Server 415 { 416 var $data; 417 var $callbacks = array(); 418 var $message; 419 var $capabilities; 420 421 /** 422 * PHP5 constructor. 423 */ 424 function __construct( $callbacks = false, $data = false, $wait = false ) 425 { 426 $this->setCapabilities(); 427 if ($callbacks) { 428 $this->callbacks = $callbacks; 429 } 430 $this->setCallbacks(); 431 if (!$wait) { 432 $this->serve($data); 433 } 434 } 435 436 /** 437 * PHP4 constructor. 438 */ 439 public function IXR_Server( $callbacks = false, $data = false, $wait = false ) { 440 self::__construct( $callbacks, $data, $wait ); 441 } 442 443 function serve($data = false) 444 { 445 if (!$data) { 446 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') { 447 if ( function_exists( 'status_header' ) ) { 448 status_header( 405 ); // WP #20986 449 header( 'Allow: POST' ); 450 } 451 header('Content-Type: text/plain'); // merged from WP #9093 452 die('XML-RPC server accepts POST requests only.'); 453 } 454 455 global $HTTP_RAW_POST_DATA; 456 if (empty($HTTP_RAW_POST_DATA)) { 457 // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293 458 $data = file_get_contents('php://input'); 459 } else { 460 $data =& $HTTP_RAW_POST_DATA; 461 } 462 } 463 $this->message = new IXR_Message($data); 464 if (!$this->message->parse()) { 465 $this->error(-32700, 'parse error. not well formed'); 466 } 467 if ($this->message->messageType != 'methodCall') { 468 $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); 469 } 470 $result = $this->call($this->message->methodName, $this->message->params); 471 472 // Is the result an error? 473 if (is_a($result, 'IXR_Error')) { 474 $this->error($result); 475 } 476 477 // Encode the result 478 $r = new IXR_Value($result); 479 $resultxml = $r->getXml(); 480 481 // Create the XML 482 $xml = <<<EOD 483 <methodResponse> 484 <params> 485 <param> 486 <value> 487 $resultxml 488 </value> 489 </param> 490 </params> 491 </methodResponse> 492 493 EOD; 494 // Send it 495 $this->output($xml); 496 } 497 498 function call($methodname, $args) 499 { 500 if (!$this->hasMethod($methodname)) { 501 return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); 502 } 503 $method = $this->callbacks[$methodname]; 504 505 // Perform the callback and send the response 506 if (count($args) == 1) { 507 // If only one parameter just send that instead of the whole array 508 $args = $args[0]; 509 } 510 511 // Are we dealing with a function or a method? 512 if (is_string($method) && substr($method, 0, 5) == 'this:') { 513 // It's a class method - check it exists 514 $method = substr($method, 5); 515 if (!method_exists($this, $method)) { 516 return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); 517 } 518 519 //Call the method 520 $result = $this->$method($args); 521 } else { 522 // It's a function - does it exist? 523 if (is_array($method)) { 524 if (!is_callable(array($method[0], $method[1]))) { 525 return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.'); 526 } 527 } else if (!function_exists($method)) { 528 return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); 529 } 530 531 // Call the function 532 $result = call_user_func($method, $args); 533 } 534 return $result; 535 } 536 537 function error($error, $message = false) 538 { 539 // Accepts either an error object or an error code and message 540 if ($message && !is_object($error)) { 541 $error = new IXR_Error($error, $message); 542 } 543 $this->output($error->getXml()); 544 } 545 546 function output($xml) 547 { 548 $charset = function_exists('get_option') ? get_option('blog_charset') : ''; 549 if ($charset) 550 $xml = '<?xml version="1.0" encoding="'.$charset.'"?>'."\n".$xml; 551 else 552 $xml = '<?xml version="1.0"?>'."\n".$xml; 553 $length = strlen($xml); 554 header('Connection: close'); 555 if ($charset) 556 header('Content-Type: text/xml; charset='.$charset); 557 else 558 header('Content-Type: text/xml'); 559 header('Date: '.date('r')); 560 echo $xml; 561 exit; 562 } 563 564 function hasMethod($method) 565 { 566 return in_array($method, array_keys($this->callbacks)); 567 } 568 569 function setCapabilities() 570 { 571 // Initialises capabilities array 572 $this->capabilities = array( 573 'xmlrpc' => array( 574 'specUrl' => 'http://www.xmlrpc.com/spec', 575 'specVersion' => 1 576 ), 577 'faults_interop' => array( 578 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', 579 'specVersion' => 20010516 580 ), 581 'system.multicall' => array( 582 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', 583 'specVersion' => 1 584 ), 585 ); 586 } 587 588 function getCapabilities($args) 589 { 590 return $this->capabilities; 591 } 592 593 function setCallbacks() 594 { 595 $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; 596 $this->callbacks['system.listMethods'] = 'this:listMethods'; 597 $this->callbacks['system.multicall'] = 'this:multiCall'; 598 } 599 600 function listMethods($args) 601 { 602 // Returns a list of methods - uses array_reverse to ensure user defined 603 // methods are listed before server defined methods 604 return array_reverse(array_keys($this->callbacks)); 605 } 606 607 function multiCall($methodcalls) 608 { 609 // See http://www.xmlrpc.com/discuss/msgReader$1208 610 $return = array(); 611 foreach ($methodcalls as $call) { 612 $method = $call['methodName']; 613 $params = $call['params']; 614 if ($method == 'system.multicall') { 615 $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); 616 } else { 617 $result = $this->call($method, $params); 618 } 619 if (is_a($result, 'IXR_Error')) { 620 $return[] = array( 621 'faultCode' => $result->code, 622 'faultString' => $result->message 623 ); 624 } else { 625 $return[] = array($result); 626 } 627 } 628 return $return; 629 } 630 } 631 632 /** 633 * IXR_Request 634 * 635 * @package IXR 636 * @since 1.5.0 637 */ 638 class IXR_Request 639 { 640 var $method; 641 var $args; 642 var $xml; 643 644 /** 645 * PHP5 constructor. 646 */ 647 function __construct($method, $args) 648 { 649 $this->method = $method; 650 $this->args = $args; 651 $this->xml = <<<EOD 652 <?xml version="1.0"?> 653 <methodCall> 654 <methodName>{$this->method}</methodName> 655 <params> 656 657 EOD; 658 foreach ($this->args as $arg) { 659 $this->xml .= '<param><value>'; 660 $v = new IXR_Value($arg); 661 $this->xml .= $v->getXml(); 662 $this->xml .= "</value></param>\n"; 663 } 664 $this->xml .= '</params></methodCall>'; 665 } 666 667 /** 668 * PHP4 constructor. 669 */ 670 public function IXR_Request( $method, $args ) { 671 self::__construct( $method, $args ); 672 } 673 674 function getLength() 675 { 676 return strlen($this->xml); 677 } 678 679 function getXml() 680 { 681 return $this->xml; 682 } 683 } 684 685 /** 686 * IXR_Client 687 * 688 * @package IXR 689 * @since 1.5.0 690 * 691 */ 692 class IXR_Client 693 { 694 var $server; 695 var $port; 696 var $path; 697 var $useragent; 698 var $response; 699 var $message = false; 700 var $debug = false; 701 var $timeout; 702 var $headers = array(); 703 704 // Storage place for an error message 705 var $error = false; 706 707 /** 708 * PHP5 constructor. 709 */ 710 function __construct( $server, $path = false, $port = 80, $timeout = 15 ) 711 { 712 if (!$path) { 713 // Assume we have been given a URL instead 714 $bits = parse_url($server); 715 $this->server = $bits['host']; 716 $this->port = isset($bits['port']) ? $bits['port'] : 80; 717 $this->path = isset($bits['path']) ? $bits['path'] : '/'; 718 719 // Make absolutely sure we have a path 720 if (!$this->path) { 721 $this->path = '/'; 722 } 723 724 if ( ! empty( $bits['query'] ) ) { 725 $this->path .= '?' . $bits['query']; 726 } 727 } else { 728 $this->server = $server; 729 $this->path = $path; 730 $this->port = $port; 731 } 732 $this->useragent = 'The Incutio XML-RPC PHP Library'; 733 $this->timeout = $timeout; 734 } 735 736 /** 737 * PHP4 constructor. 738 */ 739 public function IXR_Client( $server, $path = false, $port = 80, $timeout = 15 ) { 740 self::__construct( $server, $path, $port, $timeout ); 741 } 742 743 function query() 744 { 745 $args = func_get_args(); 746 $method = array_shift($args); 747 $request = new IXR_Request($method, $args); 748 $length = $request->getLength(); 749 $xml = $request->getXml(); 750 $r = "\r\n"; 751 $request = "POST {$this->path} HTTP/1.0$r"; 752 753 // Merged from WP #8145 - allow custom headers 754 $this->headers['Host'] = $this->server; 755 $this->headers['Content-Type'] = 'text/xml'; 756 $this->headers['User-Agent'] = $this->useragent; 757 $this->headers['Content-Length']= $length; 758 759 foreach( $this->headers as $header => $value ) { 760 $request .= "{$header}: {$value}{$r}"; 761 } 762 $request .= $r; 763 764 $request .= $xml; 765 766 // Now send the request 767 if ($this->debug) { 768 echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n"; 769 } 770 771 if ($this->timeout) { 772 $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); 773 } else { 774 $fp = @fsockopen($this->server, $this->port, $errno, $errstr); 775 } 776 if (!$fp) { 777 $this->error = new IXR_Error(-32300, 'transport error - could not open socket'); 778 return false; 779 } 780 fputs($fp, $request); 781 $contents = ''; 782 $debugContents = ''; 783 $gotFirstLine = false; 784 $gettingHeaders = true; 785 while (!feof($fp)) { 786 $line = fgets($fp, 4096); 787 if (!$gotFirstLine) { 788 // Check line for '200' 789 if (strstr($line, '200') === false) { 790 $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); 791 return false; 792 } 793 $gotFirstLine = true; 794 } 795 if (trim($line) == '') { 796 $gettingHeaders = false; 797 } 798 if (!$gettingHeaders) { 799 // merged from WP #12559 - remove trim 800 $contents .= $line; 801 } 802 if ($this->debug) { 803 $debugContents .= $line; 804 } 805 } 806 if ($this->debug) { 807 echo '<pre class="ixr_response">'.htmlspecialchars($debugContents)."\n</pre>\n\n"; 808 } 809 810 // Now parse what we've got back 811 $this->message = new IXR_Message($contents); 812 if (!$this->message->parse()) { 813 // XML error 814 $this->error = new IXR_Error(-32700, 'parse error. not well formed'); 815 return false; 816 } 817 818 // Is the message a fault? 819 if ($this->message->messageType == 'fault') { 820 $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); 821 return false; 822 } 823 824 // Message must be OK 825 return true; 826 } 827 828 function getResponse() 829 { 830 // methodResponses can only have one param - return that 831 return $this->message->params[0]; 832 } 833 834 function isError() 835 { 836 return (is_object($this->error)); 837 } 838 839 function getErrorCode() 840 { 841 return $this->error->code; 842 } 843 844 function getErrorMessage() 845 { 846 return $this->error->message; 847 } 848 } 849 850 851 /** 852 * IXR_Error 853 * 854 * @package IXR 855 * @since 1.5.0 856 */ 857 class IXR_Error 858 { 859 var $code; 860 var $message; 861 862 /** 863 * PHP5 constructor. 864 */ 865 function __construct( $code, $message ) 866 { 867 $this->code = $code; 868 $this->message = htmlspecialchars($message); 869 } 870 871 /** 872 * PHP4 constructor. 873 */ 874 public function IXR_Error( $code, $message ) { 875 self::__construct( $code, $message ); 876 } 877 878 function getXml() 879 { 880 $xml = <<<EOD 881 <methodResponse> 882 <fault> 883 <value> 884 <struct> 885 <member> 886 <name>faultCode</name> 887 <value><int>{$this->code}</int></value> 888 </member> 889 <member> 890 <name>faultString</name> 891 <value><string>{$this->message}</string></value> 892 </member> 893 </struct> 894 </value> 895 </fault> 896 </methodResponse> 897 898 EOD; 899 return $xml; 900 } 901 } 902 903 /** 904 * IXR_Date 905 * 906 * @package IXR 907 * @since 1.5.0 908 */ 909 class IXR_Date { 910 var $year; 911 var $month; 912 var $day; 913 var $hour; 914 var $minute; 915 var $second; 916 var $timezone; 917 918 /** 919 * PHP5 constructor. 920 */ 921 function __construct( $time ) 922 { 923 // $time can be a PHP timestamp or an ISO one 924 if (is_numeric($time)) { 925 $this->parseTimestamp($time); 926 } else { 927 $this->parseIso($time); 928 } 929 } 930 931 /** 932 * PHP4 constructor. 933 */ 934 public function IXR_Date( $time ) { 935 self::__construct( $time ); 936 } 937 938 function parseTimestamp($timestamp) 939 { 940 $this->year = date('Y', $timestamp); 941 $this->month = date('m', $timestamp); 942 $this->day = date('d', $timestamp); 943 $this->hour = date('H', $timestamp); 944 $this->minute = date('i', $timestamp); 945 $this->second = date('s', $timestamp); 946 $this->timezone = ''; 947 } 948 949 function parseIso($iso) 950 { 951 $this->year = substr($iso, 0, 4); 952 $this->month = substr($iso, 4, 2); 953 $this->day = substr($iso, 6, 2); 954 $this->hour = substr($iso, 9, 2); 955 $this->minute = substr($iso, 12, 2); 956 $this->second = substr($iso, 15, 2); 957 $this->timezone = substr($iso, 17); 958 } 959 960 function getIso() 961 { 962 return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone; 963 } 964 965 function getXml() 966 { 967 return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>'; 968 } 969 970 function getTimestamp() 971 { 972 return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); 973 } 974 } 975 976 /** 977 * IXR_Base64 978 * 979 * @package IXR 980 * @since 1.5.0 981 */ 982 class IXR_Base64 983 { 984 var $data; 985 986 /** 987 * PHP5 constructor. 988 */ 989 function __construct( $data ) 990 { 991 $this->data = $data; 992 } 993 994 /** 995 * PHP4 constructor. 996 */ 997 public function IXR_Base64( $data ) { 998 self::__construct( $data ); 999 } 1000 1001 function getXml() 1002 { 1003 return '<base64>'.base64_encode($this->data).'</base64>'; 1004 } 1005 } 1006 1007 /** 1008 * IXR_IntrospectionServer 1009 * 1010 * @package IXR 1011 * @since 1.5.0 1012 */ 1013 class IXR_IntrospectionServer extends IXR_Server 1014 { 1015 var $signatures; 1016 var $help; 1017 1018 /** 1019 * PHP5 constructor. 1020 */ 1021 function __construct() 1022 { 1023 $this->setCallbacks(); 1024 $this->setCapabilities(); 1025 $this->capabilities['introspection'] = array( 1026 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', 1027 'specVersion' => 1 1028 ); 1029 $this->addCallback( 1030 'system.methodSignature', 1031 'this:methodSignature', 1032 array('array', 'string'), 1033 'Returns an array describing the return type and required parameters of a method' 1034 ); 1035 $this->addCallback( 1036 'system.getCapabilities', 1037 'this:getCapabilities', 1038 array('struct'), 1039 'Returns a struct describing the XML-RPC specifications supported by this server' 1040 ); 1041 $this->addCallback( 1042 'system.listMethods', 1043 'this:listMethods', 1044 array('array'), 1045 'Returns an array of available methods on this server' 1046 ); 1047 $this->addCallback( 1048 'system.methodHelp', 1049 'this:methodHelp', 1050 array('string', 'string'), 1051 'Returns a documentation string for the specified method' 1052 ); 1053 } 1054 1055 /** 1056 * PHP4 constructor. 1057 */ 1058 public function IXR_IntrospectionServer() { 1059 self::__construct(); 1060 } 1061 1062 function addCallback($method, $callback, $args, $help) 1063 { 1064 $this->callbacks[$method] = $callback; 1065 $this->signatures[$method] = $args; 1066 $this->help[$method] = $help; 1067 } 1068 1069 function call($methodname, $args) 1070 { 1071 // Make sure it's in an array 1072 if ($args && !is_array($args)) { 1073 $args = array($args); 1074 } 1075 1076 // Over-rides default call method, adds signature check 1077 if (!$this->hasMethod($methodname)) { 1078 return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); 1079 } 1080 $method = $this->callbacks[$methodname]; 1081 $signature = $this->signatures[$methodname]; 1082 $returnType = array_shift($signature); 1083 1084 // Check the number of arguments 1085 if (count($args) != count($signature)) { 1086 return new IXR_Error(-32602, 'server error. wrong number of method parameters'); 1087 } 1088 1089 // Check the argument types 1090 $ok = true; 1091 $argsbackup = $args; 1092 for ($i = 0, $j = count($args); $i < $j; $i++) { 1093 $arg = array_shift($args); 1094 $type = array_shift($signature); 1095 switch ($type) { 1096 case 'int': 1097 case 'i4': 1098 if (is_array($arg) || !is_int($arg)) { 1099 $ok = false; 1100 } 1101 break; 1102 case 'base64': 1103 case 'string': 1104 if (!is_string($arg)) { 1105 $ok = false; 1106 } 1107 break; 1108 case 'boolean': 1109 if ($arg !== false && $arg !== true) { 1110 $ok = false; 1111 } 1112 break; 1113 case 'float': 1114 case 'double': 1115 if (!is_float($arg)) { 1116 $ok = false; 1117 } 1118 break; 1119 case 'date': 1120 case 'dateTime.iso8601': 1121 if (!is_a($arg, 'IXR_Date')) { 1122 $ok = false; 1123 } 1124 break; 1125 } 1126 if (!$ok) { 1127 return new IXR_Error(-32602, 'server error. invalid method parameters'); 1128 } 1129 } 1130 // It passed the test - run the "real" method call 1131 return parent::call($methodname, $argsbackup); 1132 } 1133 1134 function methodSignature($method) 1135 { 1136 if (!$this->hasMethod($method)) { 1137 return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.'); 1138 } 1139 // We should be returning an array of types 1140 $types = $this->signatures[$method]; 1141 $return = array(); 1142 foreach ($types as $type) { 1143 switch ($type) { 1144 case 'string': 1145 $return[] = 'string'; 1146 break; 1147 case 'int': 1148 case 'i4': 1149 $return[] = 42; 1150 break; 1151 case 'double': 1152 $return[] = 3.1415; 1153 break; 1154 case 'dateTime.iso8601': 1155 $return[] = new IXR_Date(time()); 1156 break; 1157 case 'boolean': 1158 $return[] = true; 1159 break; 1160 case 'base64': 1161 $return[] = new IXR_Base64('base64'); 1162 break; 1163 case 'array': 1164 $return[] = array('array'); 1165 break; 1166 case 'struct': 1167 $return[] = array('struct' => 'struct'); 1168 break; 1169 } 1170 } 1171 return $return; 1172 } 1173 1174 function methodHelp($method) 1175 { 1176 return $this->help[$method]; 1177 } 1178 } 1179 1180 /** 1181 * IXR_ClientMulticall 1182 * 1183 * @package IXR 1184 * @since 1.5.0 1185 */ 1186 class IXR_ClientMulticall extends IXR_Client 1187 { 1188 var $calls = array(); 1189 1190 /** 1191 * PHP5 constructor. 1192 */ 1193 function __construct( $server, $path = false, $port = 80 ) 1194 { 1195 parent::IXR_Client($server, $path, $port); 1196 $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; 1197 } 1198 1199 /** 1200 * PHP4 constructor. 1201 */ 1202 public function IXR_ClientMulticall( $server, $path = false, $port = 80 ) { 1203 self::__construct( $server, $path, $port ); 1204 } 1205 1206 function addCall() 1207 { 1208 $args = func_get_args(); 1209 $methodName = array_shift($args); 1210 $struct = array( 1211 'methodName' => $methodName, 1212 'params' => $args 1213 ); 1214 $this->calls[] = $struct; 1215 } 1216 1217 function query() 1218 { 1219 // Prepare multicall, then call the parent::query() method 1220 return parent::query('system.multicall', $this->calls); 1221 } 1222 } 60 require_once( ABSPATH . WPINC . '/IXR/class-IXR-clientmulticall.php' );
Note: See TracChangeset
for help on using the changeset viewer.