Ticket #37827: 37827.5.diff
File 37827.5.diff, 359.8 KB (added by , 8 years ago) |
---|
-
src/wp-includes/IXR/class-IXR-base64.php
1 1 <?php 2 /**3 * IXR - The Incutio XML-RPC Library4 *5 * Copyright (c) 2010, Incutio Ltd.6 * All rights reserved.7 *8 * Redistribution and use in source and binary forms, with or without9 * modification, are permitted provided that the following conditions are met:10 *11 * - Redistributions of source code must retain the above copyright notice,12 * this list of conditions and the following disclaimer.13 * - Redistributions in binary form must reproduce the above copyright14 * notice, this list of conditions and the following disclaimer in the15 * documentation and/or other materials provided with the distribution.16 * - Neither the name of Incutio Ltd. nor the names of its contributors17 * may be used to endorse or promote products derived from this software18 * without specific prior written permission.19 *20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31 *32 * @package IXR33 * @since 1.5.034 *35 * @copyright Incutio Ltd 2010 (http://www.incutio.com)36 * @version 1.7.4 7th September 201037 * @author Simon Willison38 * @link http://scripts.incutio.com/xmlrpc/ Site/manual39 * @license http://www.opensource.org/licenses/bsd-license.php BSD40 */41 2 42 3 /** 43 * IXR_Value44 *45 * @package IXR46 * @since 1.5.047 */48 class IXR_Value {49 var $data;50 var $type;51 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 objects64 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 }74 75 /**76 * PHP4 constructor.77 */78 public function IXR_Value( $data, $type = false ) {79 self::__construct( $data, $type );80 }81 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 }93 94 // Deal with IXR object types base64 and date95 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 }101 102 // If it is a normal PHP object convert it in to a struct103 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 }110 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 }118 119 function getXml()120 {121 // Return XML for this value122 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 }160 161 /**162 * Checks whether or not the supplied array is a struct or not163 *164 * @param array $array165 * @return bool166 */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 }179 180 /**181 * IXR_MESSAGE182 *183 * @package IXR184 * @since 1.5.0185 *186 */187 class IXR_Message188 {189 var $message;190 var $messageType; // methodCall / methodResponse / fault191 var $faultCode;192 var $faultString;193 var $methodName;194 var $params;195 196 // Current variable stacks197 var $_arraystructs = array(); // The stack used to keep track of the current array/struct198 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array199 var $_currentStructName = array(); // A stack as well200 var $_param;201 var $_value;202 var $_currentTag;203 var $_currentTagContents;204 // The XML parser205 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 declaration225 // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages226 $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 DOCTYPE233 $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 valid240 $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 parse249 $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.0255 *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 account266 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);267 // Set XML parser callback functions268 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 messages273 $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.0279 *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 any301 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 array320 $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 array392 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {393 // Add to struct394 $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;395 } else {396 // Add to array397 $this->_arraystructs[count($this->_arraystructs)-1][] = $value;398 }399 } else {400 // Just add as a parameter401 $this->params[] = $value;402 }403 }404 $this->_currentTagContents = '';405 }406 }407 408 /**409 * IXR_Server410 *411 * @package IXR412 * @since 1.5.0413 */414 class IXR_Server415 {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 #20986449 header( 'Allow: POST' );450 }451 header('Content-Type: text/plain'); // merged from WP #9093452 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=41293458 $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 result478 $r = new IXR_Value($result);479 $resultxml = $r->getXml();480 481 // Create the XML482 $xml = <<<EOD483 <methodResponse>484 <params>485 <param>486 <value>487 $resultxml488 </value>489 </param>490 </params>491 </methodResponse>492 493 EOD;494 // Send it495 $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 response506 if (count($args) == 1) {507 // If only one parameter just send that instead of the whole array508 $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 exists514 $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 method520 $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 function532 $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 message540 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 else552 $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 else558 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 array572 $this->capabilities = array(573 'xmlrpc' => array(574 'specUrl' => 'http://www.xmlrpc.com/spec',575 'specVersion' => 1576 ),577 'faults_interop' => array(578 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',579 'specVersion' => 20010516580 ),581 'system.multicall' => array(582 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',583 'specVersion' => 1584 ),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 defined603 // methods are listed before server defined methods604 return array_reverse(array_keys($this->callbacks));605 }606 607 function multiCall($methodcalls)608 {609 // See http://www.xmlrpc.com/discuss/msgReader$1208610 $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->message623 );624 } else {625 $return[] = array($result);626 }627 }628 return $return;629 }630 }631 632 /**633 * IXR_Request634 *635 * @package IXR636 * @since 1.5.0637 */638 class IXR_Request639 {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 = <<<EOD652 <?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_Client687 *688 * @package IXR689 * @since 1.5.0690 *691 */692 class IXR_Client693 {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 message705 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 instead714 $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 path720 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 headers754 $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 request767 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 trim800 $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 back811 $this->message = new IXR_Message($contents);812 if (!$this->message->parse()) {813 // XML error814 $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 OK825 return true;826 }827 828 function getResponse()829 {830 // methodResponses can only have one param - return that831 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_Error853 *854 * @package IXR855 * @since 1.5.0856 */857 class IXR_Error858 {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 = <<<EOD881 <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_Date905 *906 * @package IXR907 * @since 1.5.0908 */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 one924 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 4 * IXR_Base64 978 5 * 979 6 * @package IXR … … 1003 30 return '<base64>'.base64_encode($this->data).'</base64>'; 1004 31 } 1005 32 } 1006 1007 /**1008 * IXR_IntrospectionServer1009 *1010 * @package IXR1011 * @since 1.5.01012 */1013 class IXR_IntrospectionServer extends IXR_Server1014 {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' => 11028 );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 array1072 if ($args && !is_array($args)) {1073 $args = array($args);1074 }1075 1076 // Over-rides default call method, adds signature check1077 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 arguments1085 if (count($args) != count($signature)) {1086 return new IXR_Error(-32602, 'server error. wrong number of method parameters');1087 }1088 1089 // Check the argument types1090 $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 call1131 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 types1140 $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_ClientMulticall1182 *1183 * @package IXR1184 * @since 1.5.01185 */1186 class IXR_ClientMulticall extends IXR_Client1187 {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' => $args1213 );1214 $this->calls[] = $struct;1215 }1216 1217 function query()1218 {1219 // Prepare multicall, then call the parent::query() method1220 return parent::query('system.multicall', $this->calls);1221 }1222 } -
src/wp-includes/IXR/class-IXR-client.php
1 1 <?php 2 /**3 * IXR - The Incutio XML-RPC Library4 *5 * Copyright (c) 2010, Incutio Ltd.6 * All rights reserved.7 *8 * Redistribution and use in source and binary forms, with or without9 * modification, are permitted provided that the following conditions are met:10 *11 * - Redistributions of source code must retain the above copyright notice,12 * this list of conditions and the following disclaimer.13 * - Redistributions in binary form must reproduce the above copyright14 * notice, this list of conditions and the following disclaimer in the15 * documentation and/or other materials provided with the distribution.16 * - Neither the name of Incutio Ltd. nor the names of its contributors17 * may be used to endorse or promote products derived from this software18 * without specific prior written permission.19 *20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31 *32 * @package IXR33 * @since 1.5.034 *35 * @copyright Incutio Ltd 2010 (http://www.incutio.com)36 * @version 1.7.4 7th September 201037 * @author Simon Willison38 * @link http://scripts.incutio.com/xmlrpc/ Site/manual39 * @license http://www.opensource.org/licenses/bsd-license.php BSD40 */41 2 42 3 /** 43 * IXR_Value44 *45 * @package IXR46 * @since 1.5.047 */48 class IXR_Value {49 var $data;50 var $type;51 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 objects64 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 }74 75 /**76 * PHP4 constructor.77 */78 public function IXR_Value( $data, $type = false ) {79 self::__construct( $data, $type );80 }81 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 }93 94 // Deal with IXR object types base64 and date95 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 }101 102 // If it is a normal PHP object convert it in to a struct103 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 }110 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 }118 119 function getXml()120 {121 // Return XML for this value122 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 }160 161 /**162 * Checks whether or not the supplied array is a struct or not163 *164 * @param array $array165 * @return bool166 */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 }179 180 /**181 * IXR_MESSAGE182 *183 * @package IXR184 * @since 1.5.0185 *186 */187 class IXR_Message188 {189 var $message;190 var $messageType; // methodCall / methodResponse / fault191 var $faultCode;192 var $faultString;193 var $methodName;194 var $params;195 196 // Current variable stacks197 var $_arraystructs = array(); // The stack used to keep track of the current array/struct198 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array199 var $_currentStructName = array(); // A stack as well200 var $_param;201 var $_value;202 var $_currentTag;203 var $_currentTagContents;204 // The XML parser205 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 declaration225 // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages226 $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 DOCTYPE233 $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 valid240 $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 parse249 $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.0255 *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 account266 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);267 // Set XML parser callback functions268 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 messages273 $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.0279 *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 any301 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 array320 $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 array392 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {393 // Add to struct394 $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;395 } else {396 // Add to array397 $this->_arraystructs[count($this->_arraystructs)-1][] = $value;398 }399 } else {400 // Just add as a parameter401 $this->params[] = $value;402 }403 }404 $this->_currentTagContents = '';405 }406 }407 408 /**409 * IXR_Server410 *411 * @package IXR412 * @since 1.5.0413 */414 class IXR_Server415 {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 #20986449 header( 'Allow: POST' );450 }451 header('Content-Type: text/plain'); // merged from WP #9093452 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=41293458 $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 result478 $r = new IXR_Value($result);479 $resultxml = $r->getXml();480 481 // Create the XML482 $xml = <<<EOD483 <methodResponse>484 <params>485 <param>486 <value>487 $resultxml488 </value>489 </param>490 </params>491 </methodResponse>492 493 EOD;494 // Send it495 $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 response506 if (count($args) == 1) {507 // If only one parameter just send that instead of the whole array508 $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 exists514 $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 method520 $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 function532 $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 message540 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 else552 $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 else558 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 array572 $this->capabilities = array(573 'xmlrpc' => array(574 'specUrl' => 'http://www.xmlrpc.com/spec',575 'specVersion' => 1576 ),577 'faults_interop' => array(578 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',579 'specVersion' => 20010516580 ),581 'system.multicall' => array(582 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',583 'specVersion' => 1584 ),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 defined603 // methods are listed before server defined methods604 return array_reverse(array_keys($this->callbacks));605 }606 607 function multiCall($methodcalls)608 {609 // See http://www.xmlrpc.com/discuss/msgReader$1208610 $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->message623 );624 } else {625 $return[] = array($result);626 }627 }628 return $return;629 }630 }631 632 /**633 * IXR_Request634 *635 * @package IXR636 * @since 1.5.0637 */638 class IXR_Request639 {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 = <<<EOD652 <?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 4 * IXR_Client 687 5 * 688 6 * @package IXR … … 846 164 return $this->error->message; 847 165 } 848 166 } 849 850 851 /**852 * IXR_Error853 *854 * @package IXR855 * @since 1.5.0856 */857 class IXR_Error858 {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 = <<<EOD881 <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_Date905 *906 * @package IXR907 * @since 1.5.0908 */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 one924 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_Base64978 *979 * @package IXR980 * @since 1.5.0981 */982 class IXR_Base64983 {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_IntrospectionServer1009 *1010 * @package IXR1011 * @since 1.5.01012 */1013 class IXR_IntrospectionServer extends IXR_Server1014 {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' => 11028 );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 array1072 if ($args && !is_array($args)) {1073 $args = array($args);1074 }1075 1076 // Over-rides default call method, adds signature check1077 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 arguments1085 if (count($args) != count($signature)) {1086 return new IXR_Error(-32602, 'server error. wrong number of method parameters');1087 }1088 1089 // Check the argument types1090 $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 call1131 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 types1140 $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_ClientMulticall1182 *1183 * @package IXR1184 * @since 1.5.01185 */1186 class IXR_ClientMulticall extends IXR_Client1187 {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' => $args1213 );1214 $this->calls[] = $struct;1215 }1216 1217 function query()1218 {1219 // Prepare multicall, then call the parent::query() method1220 return parent::query('system.multicall', $this->calls);1221 }1222 } -
src/wp-includes/IXR/class-IXR-clientmulticall.php
1 1 <?php 2 2 /** 3 * IXR - The Incutio XML-RPC Library4 *5 * Copyright (c) 2010, Incutio Ltd.6 * All rights reserved.7 *8 * Redistribution and use in source and binary forms, with or without9 * modification, are permitted provided that the following conditions are met:10 *11 * - Redistributions of source code must retain the above copyright notice,12 * this list of conditions and the following disclaimer.13 * - Redistributions in binary form must reproduce the above copyright14 * notice, this list of conditions and the following disclaimer in the15 * documentation and/or other materials provided with the distribution.16 * - Neither the name of Incutio Ltd. nor the names of its contributors17 * may be used to endorse or promote products derived from this software18 * without specific prior written permission.19 *20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31 *32 * @package IXR33 * @since 1.5.034 *35 * @copyright Incutio Ltd 2010 (http://www.incutio.com)36 * @version 1.7.4 7th September 201037 * @author Simon Willison38 * @link http://scripts.incutio.com/xmlrpc/ Site/manual39 * @license http://www.opensource.org/licenses/bsd-license.php BSD40 */41 42 /**43 * IXR_Value44 *45 * @package IXR46 * @since 1.5.047 */48 class IXR_Value {49 var $data;50 var $type;51 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 objects64 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 }74 75 /**76 * PHP4 constructor.77 */78 public function IXR_Value( $data, $type = false ) {79 self::__construct( $data, $type );80 }81 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 }93 94 // Deal with IXR object types base64 and date95 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 }101 102 // If it is a normal PHP object convert it in to a struct103 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 }110 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 }118 119 function getXml()120 {121 // Return XML for this value122 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 }160 161 /**162 * Checks whether or not the supplied array is a struct or not163 *164 * @param array $array165 * @return bool166 */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 }179 180 /**181 * IXR_MESSAGE182 *183 * @package IXR184 * @since 1.5.0185 *186 */187 class IXR_Message188 {189 var $message;190 var $messageType; // methodCall / methodResponse / fault191 var $faultCode;192 var $faultString;193 var $methodName;194 var $params;195 196 // Current variable stacks197 var $_arraystructs = array(); // The stack used to keep track of the current array/struct198 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array199 var $_currentStructName = array(); // A stack as well200 var $_param;201 var $_value;202 var $_currentTag;203 var $_currentTagContents;204 // The XML parser205 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 declaration225 // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages226 $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 DOCTYPE233 $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 valid240 $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 parse249 $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.0255 *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 account266 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);267 // Set XML parser callback functions268 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 messages273 $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.0279 *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 any301 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 array320 $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 array392 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {393 // Add to struct394 $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;395 } else {396 // Add to array397 $this->_arraystructs[count($this->_arraystructs)-1][] = $value;398 }399 } else {400 // Just add as a parameter401 $this->params[] = $value;402 }403 }404 $this->_currentTagContents = '';405 }406 }407 408 /**409 * IXR_Server410 *411 * @package IXR412 * @since 1.5.0413 */414 class IXR_Server415 {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 #20986449 header( 'Allow: POST' );450 }451 header('Content-Type: text/plain'); // merged from WP #9093452 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=41293458 $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 result478 $r = new IXR_Value($result);479 $resultxml = $r->getXml();480 481 // Create the XML482 $xml = <<<EOD483 <methodResponse>484 <params>485 <param>486 <value>487 $resultxml488 </value>489 </param>490 </params>491 </methodResponse>492 493 EOD;494 // Send it495 $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 response506 if (count($args) == 1) {507 // If only one parameter just send that instead of the whole array508 $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 exists514 $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 method520 $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 function532 $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 message540 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 else552 $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 else558 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 array572 $this->capabilities = array(573 'xmlrpc' => array(574 'specUrl' => 'http://www.xmlrpc.com/spec',575 'specVersion' => 1576 ),577 'faults_interop' => array(578 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',579 'specVersion' => 20010516580 ),581 'system.multicall' => array(582 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',583 'specVersion' => 1584 ),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 defined603 // methods are listed before server defined methods604 return array_reverse(array_keys($this->callbacks));605 }606 607 function multiCall($methodcalls)608 {609 // See http://www.xmlrpc.com/discuss/msgReader$1208610 $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->message623 );624 } else {625 $return[] = array($result);626 }627 }628 return $return;629 }630 }631 632 /**633 * IXR_Request634 *635 * @package IXR636 * @since 1.5.0637 */638 class IXR_Request639 {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 = <<<EOD652 <?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_Client687 *688 * @package IXR689 * @since 1.5.0690 *691 */692 class IXR_Client693 {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 message705 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 instead714 $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 path720 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 headers754 $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 request767 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 trim800 $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 back811 $this->message = new IXR_Message($contents);812 if (!$this->message->parse()) {813 // XML error814 $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 OK825 return true;826 }827 828 function getResponse()829 {830 // methodResponses can only have one param - return that831 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_Error853 *854 * @package IXR855 * @since 1.5.0856 */857 class IXR_Error858 {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 = <<<EOD881 <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_Date905 *906 * @package IXR907 * @since 1.5.0908 */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 one924 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_Base64978 *979 * @package IXR980 * @since 1.5.0981 */982 class IXR_Base64983 {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_IntrospectionServer1009 *1010 * @package IXR1011 * @since 1.5.01012 */1013 class IXR_IntrospectionServer extends IXR_Server1014 {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' => 11028 );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 array1072 if ($args && !is_array($args)) {1073 $args = array($args);1074 }1075 1076 // Over-rides default call method, adds signature check1077 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 arguments1085 if (count($args) != count($signature)) {1086 return new IXR_Error(-32602, 'server error. wrong number of method parameters');1087 }1088 1089 // Check the argument types1090 $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 call1131 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 types1140 $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 3 * IXR_ClientMulticall 1182 4 * 1183 5 * @package IXR -
src/wp-includes/IXR/class-IXR-date.php
1 1 <?php 2 /**3 * IXR - The Incutio XML-RPC Library4 *5 * Copyright (c) 2010, Incutio Ltd.6 * All rights reserved.7 *8 * Redistribution and use in source and binary forms, with or without9 * modification, are permitted provided that the following conditions are met:10 *11 * - Redistributions of source code must retain the above copyright notice,12 * this list of conditions and the following disclaimer.13 * - Redistributions in binary form must reproduce the above copyright14 * notice, this list of conditions and the following disclaimer in the15 * documentation and/or other materials provided with the distribution.16 * - Neither the name of Incutio Ltd. nor the names of its contributors17 * may be used to endorse or promote products derived from this software18 * without specific prior written permission.19 *20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31 *32 * @package IXR33 * @since 1.5.034 *35 * @copyright Incutio Ltd 2010 (http://www.incutio.com)36 * @version 1.7.4 7th September 201037 * @author Simon Willison38 * @link http://scripts.incutio.com/xmlrpc/ Site/manual39 * @license http://www.opensource.org/licenses/bsd-license.php BSD40 */41 2 42 3 /** 43 * IXR_Value44 *45 * @package IXR46 * @since 1.5.047 */48 class IXR_Value {49 var $data;50 var $type;51 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 objects64 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 }74 75 /**76 * PHP4 constructor.77 */78 public function IXR_Value( $data, $type = false ) {79 self::__construct( $data, $type );80 }81 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 }93 94 // Deal with IXR object types base64 and date95 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 }101 102 // If it is a normal PHP object convert it in to a struct103 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 }110 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 }118 119 function getXml()120 {121 // Return XML for this value122 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 }160 161 /**162 * Checks whether or not the supplied array is a struct or not163 *164 * @param array $array165 * @return bool166 */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 }179 180 /**181 * IXR_MESSAGE182 *183 * @package IXR184 * @since 1.5.0185 *186 */187 class IXR_Message188 {189 var $message;190 var $messageType; // methodCall / methodResponse / fault191 var $faultCode;192 var $faultString;193 var $methodName;194 var $params;195 196 // Current variable stacks197 var $_arraystructs = array(); // The stack used to keep track of the current array/struct198 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array199 var $_currentStructName = array(); // A stack as well200 var $_param;201 var $_value;202 var $_currentTag;203 var $_currentTagContents;204 // The XML parser205 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 declaration225 // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages226 $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 DOCTYPE233 $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 valid240 $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 parse249 $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.0255 *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 account266 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);267 // Set XML parser callback functions268 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 messages273 $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.0279 *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 any301 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 array320 $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 array392 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {393 // Add to struct394 $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;395 } else {396 // Add to array397 $this->_arraystructs[count($this->_arraystructs)-1][] = $value;398 }399 } else {400 // Just add as a parameter401 $this->params[] = $value;402 }403 }404 $this->_currentTagContents = '';405 }406 }407 408 /**409 * IXR_Server410 *411 * @package IXR412 * @since 1.5.0413 */414 class IXR_Server415 {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 #20986449 header( 'Allow: POST' );450 }451 header('Content-Type: text/plain'); // merged from WP #9093452 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=41293458 $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 result478 $r = new IXR_Value($result);479 $resultxml = $r->getXml();480 481 // Create the XML482 $xml = <<<EOD483 <methodResponse>484 <params>485 <param>486 <value>487 $resultxml488 </value>489 </param>490 </params>491 </methodResponse>492 493 EOD;494 // Send it495 $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 response506 if (count($args) == 1) {507 // If only one parameter just send that instead of the whole array508 $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 exists514 $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 method520 $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 function532 $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 message540 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 else552 $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 else558 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 array572 $this->capabilities = array(573 'xmlrpc' => array(574 'specUrl' => 'http://www.xmlrpc.com/spec',575 'specVersion' => 1576 ),577 'faults_interop' => array(578 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',579 'specVersion' => 20010516580 ),581 'system.multicall' => array(582 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',583 'specVersion' => 1584 ),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 defined603 // methods are listed before server defined methods604 return array_reverse(array_keys($this->callbacks));605 }606 607 function multiCall($methodcalls)608 {609 // See http://www.xmlrpc.com/discuss/msgReader$1208610 $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->message623 );624 } else {625 $return[] = array($result);626 }627 }628 return $return;629 }630 }631 632 /**633 * IXR_Request634 *635 * @package IXR636 * @since 1.5.0637 */638 class IXR_Request639 {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 = <<<EOD652 <?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_Client687 *688 * @package IXR689 * @since 1.5.0690 *691 */692 class IXR_Client693 {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 message705 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 instead714 $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 path720 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 headers754 $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 request767 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 trim800 $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 back811 $this->message = new IXR_Message($contents);812 if (!$this->message->parse()) {813 // XML error814 $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 OK825 return true;826 }827 828 function getResponse()829 {830 // methodResponses can only have one param - return that831 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_Error853 *854 * @package IXR855 * @since 1.5.0856 */857 class IXR_Error858 {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 = <<<EOD881 <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 4 * IXR_Date 905 5 * 906 6 * @package IXR … … 972 72 return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); 973 73 } 974 74 } 975 976 /**977 * IXR_Base64978 *979 * @package IXR980 * @since 1.5.0981 */982 class IXR_Base64983 {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_IntrospectionServer1009 *1010 * @package IXR1011 * @since 1.5.01012 */1013 class IXR_IntrospectionServer extends IXR_Server1014 {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' => 11028 );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 array1072 if ($args && !is_array($args)) {1073 $args = array($args);1074 }1075 1076 // Over-rides default call method, adds signature check1077 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 arguments1085 if (count($args) != count($signature)) {1086 return new IXR_Error(-32602, 'server error. wrong number of method parameters');1087 }1088 1089 // Check the argument types1090 $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 call1131 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 types1140 $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_ClientMulticall1182 *1183 * @package IXR1184 * @since 1.5.01185 */1186 class IXR_ClientMulticall extends IXR_Client1187 {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' => $args1213 );1214 $this->calls[] = $struct;1215 }1216 1217 function query()1218 {1219 // Prepare multicall, then call the parent::query() method1220 return parent::query('system.multicall', $this->calls);1221 }1222 } -
src/wp-includes/IXR/class-IXR-error.php
1 1 <?php 2 /**3 * IXR - The Incutio XML-RPC Library4 *5 * Copyright (c) 2010, Incutio Ltd.6 * All rights reserved.7 *8 * Redistribution and use in source and binary forms, with or without9 * modification, are permitted provided that the following conditions are met:10 *11 * - Redistributions of source code must retain the above copyright notice,12 * this list of conditions and the following disclaimer.13 * - Redistributions in binary form must reproduce the above copyright14 * notice, this list of conditions and the following disclaimer in the15 * documentation and/or other materials provided with the distribution.16 * - Neither the name of Incutio Ltd. nor the names of its contributors17 * may be used to endorse or promote products derived from this software18 * without specific prior written permission.19 *20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31 *32 * @package IXR33 * @since 1.5.034 *35 * @copyright Incutio Ltd 2010 (http://www.incutio.com)36 * @version 1.7.4 7th September 201037 * @author Simon Willison38 * @link http://scripts.incutio.com/xmlrpc/ Site/manual39 * @license http://www.opensource.org/licenses/bsd-license.php BSD40 */41 2 42 3 /** 43 * IXR_Value44 *45 * @package IXR46 * @since 1.5.047 */48 class IXR_Value {49 var $data;50 var $type;51 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 objects64 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 }74 75 /**76 * PHP4 constructor.77 */78 public function IXR_Value( $data, $type = false ) {79 self::__construct( $data, $type );80 }81 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 }93 94 // Deal with IXR object types base64 and date95 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 }101 102 // If it is a normal PHP object convert it in to a struct103 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 }110 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 }118 119 function getXml()120 {121 // Return XML for this value122 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 }160 161 /**162 * Checks whether or not the supplied array is a struct or not163 *164 * @param array $array165 * @return bool166 */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 }179 180 /**181 * IXR_MESSAGE182 *183 * @package IXR184 * @since 1.5.0185 *186 */187 class IXR_Message188 {189 var $message;190 var $messageType; // methodCall / methodResponse / fault191 var $faultCode;192 var $faultString;193 var $methodName;194 var $params;195 196 // Current variable stacks197 var $_arraystructs = array(); // The stack used to keep track of the current array/struct198 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array199 var $_currentStructName = array(); // A stack as well200 var $_param;201 var $_value;202 var $_currentTag;203 var $_currentTagContents;204 // The XML parser205 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 declaration225 // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages226 $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 DOCTYPE233 $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 valid240 $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 parse249 $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.0255 *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 account266 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);267 // Set XML parser callback functions268 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 messages273 $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.0279 *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 any301 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 array320 $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 array392 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {393 // Add to struct394 $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;395 } else {396 // Add to array397 $this->_arraystructs[count($this->_arraystructs)-1][] = $value;398 }399 } else {400 // Just add as a parameter401 $this->params[] = $value;402 }403 }404 $this->_currentTagContents = '';405 }406 }407 408 /**409 * IXR_Server410 *411 * @package IXR412 * @since 1.5.0413 */414 class IXR_Server415 {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 #20986449 header( 'Allow: POST' );450 }451 header('Content-Type: text/plain'); // merged from WP #9093452 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=41293458 $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 result478 $r = new IXR_Value($result);479 $resultxml = $r->getXml();480 481 // Create the XML482 $xml = <<<EOD483 <methodResponse>484 <params>485 <param>486 <value>487 $resultxml488 </value>489 </param>490 </params>491 </methodResponse>492 493 EOD;494 // Send it495 $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 response506 if (count($args) == 1) {507 // If only one parameter just send that instead of the whole array508 $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 exists514 $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 method520 $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 function532 $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 message540 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 else552 $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 else558 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 array572 $this->capabilities = array(573 'xmlrpc' => array(574 'specUrl' => 'http://www.xmlrpc.com/spec',575 'specVersion' => 1576 ),577 'faults_interop' => array(578 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',579 'specVersion' => 20010516580 ),581 'system.multicall' => array(582 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',583 'specVersion' => 1584 ),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 defined603 // methods are listed before server defined methods604 return array_reverse(array_keys($this->callbacks));605 }606 607 function multiCall($methodcalls)608 {609 // See http://www.xmlrpc.com/discuss/msgReader$1208610 $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->message623 );624 } else {625 $return[] = array($result);626 }627 }628 return $return;629 }630 }631 632 /**633 * IXR_Request634 *635 * @package IXR636 * @since 1.5.0637 */638 class IXR_Request639 {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 = <<<EOD652 <?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_Client687 *688 * @package IXR689 * @since 1.5.0690 *691 */692 class IXR_Client693 {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 message705 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 instead714 $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 path720 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 headers754 $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 request767 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 trim800 $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 back811 $this->message = new IXR_Message($contents);812 if (!$this->message->parse()) {813 // XML error814 $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 OK825 return true;826 }827 828 function getResponse()829 {830 // methodResponses can only have one param - return that831 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 4 * IXR_Error 853 5 * 854 6 * @package IXR … … 899 51 return $xml; 900 52 } 901 53 } 902 903 /**904 * IXR_Date905 *906 * @package IXR907 * @since 1.5.0908 */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 one924 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_Base64978 *979 * @package IXR980 * @since 1.5.0981 */982 class IXR_Base64983 {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_IntrospectionServer1009 *1010 * @package IXR1011 * @since 1.5.01012 */1013 class IXR_IntrospectionServer extends IXR_Server1014 {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' => 11028 );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 array1072 if ($args && !is_array($args)) {1073 $args = array($args);1074 }1075 1076 // Over-rides default call method, adds signature check1077 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 arguments1085 if (count($args) != count($signature)) {1086 return new IXR_Error(-32602, 'server error. wrong number of method parameters');1087 }1088 1089 // Check the argument types1090 $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 call1131 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 types1140 $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_ClientMulticall1182 *1183 * @package IXR1184 * @since 1.5.01185 */1186 class IXR_ClientMulticall extends IXR_Client1187 {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' => $args1213 );1214 $this->calls[] = $struct;1215 }1216 1217 function query()1218 {1219 // Prepare multicall, then call the parent::query() method1220 return parent::query('system.multicall', $this->calls);1221 }1222 } -
src/wp-includes/IXR/class-IXR-introspectionserver.php
1 1 <?php 2 /**3 * IXR - The Incutio XML-RPC Library4 *5 * Copyright (c) 2010, Incutio Ltd.6 * All rights reserved.7 *8 * Redistribution and use in source and binary forms, with or without9 * modification, are permitted provided that the following conditions are met:10 *11 * - Redistributions of source code must retain the above copyright notice,12 * this list of conditions and the following disclaimer.13 * - Redistributions in binary form must reproduce the above copyright14 * notice, this list of conditions and the following disclaimer in the15 * documentation and/or other materials provided with the distribution.16 * - Neither the name of Incutio Ltd. nor the names of its contributors17 * may be used to endorse or promote products derived from this software18 * without specific prior written permission.19 *20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31 *32 * @package IXR33 * @since 1.5.034 *35 * @copyright Incutio Ltd 2010 (http://www.incutio.com)36 * @version 1.7.4 7th September 201037 * @author Simon Willison38 * @link http://scripts.incutio.com/xmlrpc/ Site/manual39 * @license http://www.opensource.org/licenses/bsd-license.php BSD40 */41 2 42 3 /** 43 * IXR_Value44 *45 * @package IXR46 * @since 1.5.047 */48 class IXR_Value {49 var $data;50 var $type;51 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 objects64 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 }74 75 /**76 * PHP4 constructor.77 */78 public function IXR_Value( $data, $type = false ) {79 self::__construct( $data, $type );80 }81 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 }93 94 // Deal with IXR object types base64 and date95 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 }101 102 // If it is a normal PHP object convert it in to a struct103 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 }110 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 }118 119 function getXml()120 {121 // Return XML for this value122 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 }160 161 /**162 * Checks whether or not the supplied array is a struct or not163 *164 * @param array $array165 * @return bool166 */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 }179 180 /**181 * IXR_MESSAGE182 *183 * @package IXR184 * @since 1.5.0185 *186 */187 class IXR_Message188 {189 var $message;190 var $messageType; // methodCall / methodResponse / fault191 var $faultCode;192 var $faultString;