Make WordPress Core

Ticket #37827: 37827.5.diff

File 37827.5.diff, 359.8 KB (added by wonderboymusic, 8 years ago)
  • src/wp-includes/IXR/class-IXR-base64.php

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

     
    11<?php
    2 /**
    3  * IXR - The Incutio XML-RPC Library
    4  *
    5  * Copyright (c) 2010, Incutio Ltd.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * 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 copyright
    14  *    notice, this list of conditions and the following disclaimer in the
    15  *    documentation and/or other materials provided with the distribution.
    16  *  - Neither the name of Incutio Ltd. nor the names of its contributors
    17  *    may be used to endorse or promote products derived from this software
    18  *    without specific prior written permission.
    19  *
    20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
    24  * 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, OR
    27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
    28  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
    30  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    31  *
    32  * @package IXR
    33  * @since 1.5.0
    34  *
    35  * @copyright  Incutio Ltd 2010 (http://www.incutio.com)
    36  * @version    1.7.4 7th September 2010
    37  * @author     Simon Willison
    38  * @link       http://scripts.incutio.com/xmlrpc/ Site/manual
    39  * @license    http://www.opensource.org/licenses/bsd-license.php BSD
    40  */
    412
    423/**
    43  * IXR_Value
    44  *
    45  * @package IXR
    46  * @since 1.5.0
    47  */
    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 objects
    64             foreach ($this->data as $key => $value) {
    65                 $this->data[$key] = new IXR_Value($value);
    66             }
    67         }
    68         if ($type == 'array') {
    69             for ($i = 0, $j = count($this->data); $i < $j; $i++) {
    70                 $this->data[$i] = new IXR_Value($this->data[$i]);
    71             }
    72         }
    73     }
    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 date
    95         if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
    96             return 'date';
    97         }
    98         if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
    99             return 'base64';
    100         }
    101 
    102         // If it is a normal PHP object convert it in to a struct
    103         if (is_object($this->data)) {
    104             $this->data = get_object_vars($this->data);
    105             return 'struct';
    106         }
    107         if (!is_array($this->data)) {
    108             return 'string';
    109         }
    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 value
    122         switch ($this->type) {
    123             case 'boolean':
    124                 return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
    125                 break;
    126             case 'int':
    127                 return '<int>'.$this->data.'</int>';
    128                 break;
    129             case 'double':
    130                 return '<double>'.$this->data.'</double>';
    131                 break;
    132             case 'string':
    133                 return '<string>'.htmlspecialchars($this->data).'</string>';
    134                 break;
    135             case 'array':
    136                 $return = '<array><data>'."\n";
    137                 foreach ($this->data as $item) {
    138                     $return .= '  <value>'.$item->getXml()."</value>\n";
    139                 }
    140                 $return .= '</data></array>';
    141                 return $return;
    142                 break;
    143             case 'struct':
    144                 $return = '<struct>'."\n";
    145                 foreach ($this->data as $name => $value) {
    146                                         $name = htmlspecialchars($name);
    147                     $return .= "  <member><name>$name</name><value>";
    148                     $return .= $value->getXml()."</value></member>\n";
    149                 }
    150                 $return .= '</struct>';
    151                 return $return;
    152                 break;
    153             case 'date':
    154             case 'base64':
    155                 return $this->data->getXml();
    156                 break;
    157         }
    158         return false;
    159     }
    160 
    161     /**
    162      * Checks whether or not the supplied array is a struct or not
    163      *
    164      * @param array $array
    165      * @return bool
    166      */
    167     function isStruct($array)
    168     {
    169         $expected = 0;
    170         foreach ($array as $key => $value) {
    171             if ((string)$key !== (string)$expected) {
    172                 return true;
    173             }
    174             $expected++;
    175         }
    176         return false;
    177     }
    178 }
    179 
    180 /**
    181  * IXR_MESSAGE
    182  *
    183  * @package IXR
    184  * @since 1.5.0
    185  *
    186  */
    187 class IXR_Message
    188 {
    189     var $message;
    190     var $messageType;  // methodCall / methodResponse / fault
    191     var $faultCode;
    192     var $faultString;
    193     var $methodName;
    194     var $params;
    195 
    196     // Current variable stacks
    197     var $_arraystructs = array();   // The stack used to keep track of the current array/struct
    198     var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
    199     var $_currentStructName = array();  // A stack as well
    200     var $_param;
    201     var $_value;
    202     var $_currentTag;
    203     var $_currentTagContents;
    204     // The XML parser
    205     var $_parser;
    206 
    207         /**
    208          * PHP5 constructor.
    209          */
    210     function __construct( $message )
    211     {
    212         $this->message =& $message;
    213     }
    214 
    215         /**
    216          * PHP4 constructor.
    217          */
    218         public function IXR_Message( $message ) {
    219                 self::__construct( $message );
    220         }
    221 
    222     function parse()
    223     {
    224         // first remove the XML declaration
    225         // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages
    226         $header = preg_replace( '/<\?xml.*?\?'.'>/s', '', substr( $this->message, 0, 100 ), 1 );
    227         $this->message = trim( substr_replace( $this->message, $header, 0, 100 ) );
    228         if ( '' == $this->message ) {
    229             return false;
    230         }
    231 
    232         // Then remove the DOCTYPE
    233         $header = preg_replace( '/^<!DOCTYPE[^>]*+>/i', '', substr( $this->message, 0, 200 ), 1 );
    234         $this->message = trim( substr_replace( $this->message, $header, 0, 200 ) );
    235         if ( '' == $this->message ) {
    236             return false;
    237         }
    238 
    239         // Check that the root tag is valid
    240         $root_tag = substr( $this->message, 0, strcspn( substr( $this->message, 0, 20 ), "> \t\r\n" ) );
    241         if ( '<!DOCTYPE' === strtoupper( $root_tag ) ) {
    242             return false;
    243         }
    244         if ( ! in_array( $root_tag, array( '<methodCall', '<methodResponse', '<fault' ) ) ) {
    245             return false;
    246         }
    247 
    248         // Bail if there are too many elements to parse
    249         $element_limit = 30000;
    250         if ( function_exists( 'apply_filters' ) ) {
    251             /**
    252              * Filters the number of elements to parse in an XML-RPC response.
    253              *
    254              * @since 4.0.0
    255              *
    256              * @param int $element_limit Default elements limit.
    257              */
    258             $element_limit = apply_filters( 'xmlrpc_element_limit', $element_limit );
    259         }
    260         if ( $element_limit && 2 * $element_limit < substr_count( $this->message, '<' ) ) {
    261             return false;
    262         }
    263 
    264         $this->_parser = xml_parser_create();
    265         // Set XML parser to take the case of tags in to account
    266         xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
    267         // Set XML parser callback functions
    268         xml_set_object($this->_parser, $this);
    269         xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
    270         xml_set_character_data_handler($this->_parser, 'cdata');
    271 
    272         // 256Kb, parse in chunks to avoid the RAM usage on very large messages
    273         $chunk_size = 262144;
    274 
    275         /**
    276          * Filters the chunk size that can be used to parse an XML-RPC reponse message.
    277          *
    278          * @since 4.4.0
    279          *
    280          * @param int $chunk_size Chunk size to parse in bytes.
    281          */
    282         $chunk_size = apply_filters( 'xmlrpc_chunk_parsing_size', $chunk_size );
    283 
    284         $final = false;
    285         do {
    286             if (strlen($this->message) <= $chunk_size) {
    287                 $final = true;
    288             }
    289             $part = substr($this->message, 0, $chunk_size);
    290             $this->message = substr($this->message, $chunk_size);
    291             if (!xml_parse($this->_parser, $part, $final)) {
    292                 return false;
    293             }
    294             if ($final) {
    295                 break;
    296             }
    297         } while (true);
    298         xml_parser_free($this->_parser);
    299 
    300         // Grab the error messages, if any
    301         if ($this->messageType == 'fault') {
    302             $this->faultCode = $this->params[0]['faultCode'];
    303             $this->faultString = $this->params[0]['faultString'];
    304         }
    305         return true;
    306     }
    307 
    308     function tag_open($parser, $tag, $attr)
    309     {
    310         $this->_currentTagContents = '';
    311         $this->currentTag = $tag;
    312         switch($tag) {
    313             case 'methodCall':
    314             case 'methodResponse':
    315             case 'fault':
    316                 $this->messageType = $tag;
    317                 break;
    318                 /* Deal with stacks of arrays and structs */
    319             case 'data':    // data is to all intents and puposes more interesting than array
    320                 $this->_arraystructstypes[] = 'array';
    321                 $this->_arraystructs[] = array();
    322                 break;
    323             case 'struct':
    324                 $this->_arraystructstypes[] = 'struct';
    325                 $this->_arraystructs[] = array();
    326                 break;
    327         }
    328     }
    329 
    330     function cdata($parser, $cdata)
    331     {
    332         $this->_currentTagContents .= $cdata;
    333     }
    334 
    335     function tag_close($parser, $tag)
    336     {
    337         $valueFlag = false;
    338         switch($tag) {
    339             case 'int':
    340             case 'i4':
    341                 $value = (int)trim($this->_currentTagContents);
    342                 $valueFlag = true;
    343                 break;
    344             case 'double':
    345                 $value = (double)trim($this->_currentTagContents);
    346                 $valueFlag = true;
    347                 break;
    348             case 'string':
    349                 $value = (string)trim($this->_currentTagContents);
    350                 $valueFlag = true;
    351                 break;
    352             case 'dateTime.iso8601':
    353                 $value = new IXR_Date(trim($this->_currentTagContents));
    354                 $valueFlag = true;
    355                 break;
    356             case 'value':
    357                 // "If no type is indicated, the type is string."
    358                 if (trim($this->_currentTagContents) != '') {
    359                     $value = (string)$this->_currentTagContents;
    360                     $valueFlag = true;
    361                 }
    362                 break;
    363             case 'boolean':
    364                 $value = (boolean)trim($this->_currentTagContents);
    365                 $valueFlag = true;
    366                 break;
    367             case 'base64':
    368                 $value = base64_decode($this->_currentTagContents);
    369                 $valueFlag = true;
    370                 break;
    371                 /* Deal with stacks of arrays and structs */
    372             case 'data':
    373             case 'struct':
    374                 $value = array_pop($this->_arraystructs);
    375                 array_pop($this->_arraystructstypes);
    376                 $valueFlag = true;
    377                 break;
    378             case 'member':
    379                 array_pop($this->_currentStructName);
    380                 break;
    381             case 'name':
    382                 $this->_currentStructName[] = trim($this->_currentTagContents);
    383                 break;
    384             case 'methodName':
    385                 $this->methodName = trim($this->_currentTagContents);
    386                 break;
    387         }
    388 
    389         if ($valueFlag) {
    390             if (count($this->_arraystructs) > 0) {
    391                 // Add value to struct or array
    392                 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
    393                     // Add to struct
    394                     $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
    395                 } else {
    396                     // Add to array
    397                     $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
    398                 }
    399             } else {
    400                 // Just add as a parameter
    401                 $this->params[] = $value;
    402             }
    403         }
    404         $this->_currentTagContents = '';
    405     }
    406 }
    407 
    408 /**
    409  * IXR_Server
    410  *
    411  * @package IXR
    412  * @since 1.5.0
    413  */
    414 class IXR_Server
    415 {
    416     var $data;
    417     var $callbacks = array();
    418     var $message;
    419     var $capabilities;
    420 
    421         /**
    422          * PHP5 constructor.
    423          */
    424     function __construct( $callbacks = false, $data = false, $wait = false )
    425     {
    426         $this->setCapabilities();
    427         if ($callbacks) {
    428             $this->callbacks = $callbacks;
    429         }
    430         $this->setCallbacks();
    431         if (!$wait) {
    432             $this->serve($data);
    433         }
    434     }
    435 
    436         /**
    437          * PHP4 constructor.
    438          */
    439         public function IXR_Server( $callbacks = false, $data = false, $wait = false ) {
    440                 self::__construct( $callbacks, $data, $wait );
    441         }
    442 
    443     function serve($data = false)
    444     {
    445         if (!$data) {
    446             if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
    447                 if ( function_exists( 'status_header' ) ) {
    448                     status_header( 405 ); // WP #20986
    449                     header( 'Allow: POST' );
    450                 }
    451                 header('Content-Type: text/plain'); // merged from WP #9093
    452                 die('XML-RPC server accepts POST requests only.');
    453             }
    454 
    455             global $HTTP_RAW_POST_DATA;
    456             if (empty($HTTP_RAW_POST_DATA)) {
    457                 // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293
    458                 $data = file_get_contents('php://input');
    459             } else {
    460                 $data =& $HTTP_RAW_POST_DATA;
    461             }
    462         }
    463         $this->message = new IXR_Message($data);
    464         if (!$this->message->parse()) {
    465             $this->error(-32700, 'parse error. not well formed');
    466         }
    467         if ($this->message->messageType != 'methodCall') {
    468             $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
    469         }
    470         $result = $this->call($this->message->methodName, $this->message->params);
    471 
    472         // Is the result an error?
    473         if (is_a($result, 'IXR_Error')) {
    474             $this->error($result);
    475         }
    476 
    477         // Encode the result
    478         $r = new IXR_Value($result);
    479         $resultxml = $r->getXml();
    480 
    481         // Create the XML
    482         $xml = <<<EOD
    483 <methodResponse>
    484   <params>
    485     <param>
    486       <value>
    487       $resultxml
    488       </value>
    489     </param>
    490   </params>
    491 </methodResponse>
    492 
    493 EOD;
    494       // Send it
    495       $this->output($xml);
    496     }
    497 
    498     function call($methodname, $args)
    499     {
    500         if (!$this->hasMethod($methodname)) {
    501             return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
    502         }
    503         $method = $this->callbacks[$methodname];
    504 
    505         // Perform the callback and send the response
    506         if (count($args) == 1) {
    507             // If only one parameter just send that instead of the whole array
    508             $args = $args[0];
    509         }
    510 
    511         // Are we dealing with a function or a method?
    512         if (is_string($method) && substr($method, 0, 5) == 'this:') {
    513             // It's a class method - check it exists
    514             $method = substr($method, 5);
    515             if (!method_exists($this, $method)) {
    516                 return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.');
    517             }
    518 
    519             //Call the method
    520             $result = $this->$method($args);
    521         } else {
    522             // It's a function - does it exist?
    523             if (is_array($method)) {
    524                 if (!is_callable(array($method[0], $method[1]))) {
    525                     return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.');
    526                 }
    527             } else if (!function_exists($method)) {
    528                 return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
    529             }
    530 
    531             // Call the function
    532             $result = call_user_func($method, $args);
    533         }
    534         return $result;
    535     }
    536 
    537     function error($error, $message = false)
    538     {
    539         // Accepts either an error object or an error code and message
    540         if ($message && !is_object($error)) {
    541             $error = new IXR_Error($error, $message);
    542         }
    543         $this->output($error->getXml());
    544     }
    545 
    546     function output($xml)
    547     {
    548         $charset = function_exists('get_option') ? get_option('blog_charset') : '';
    549         if ($charset)
    550             $xml = '<?xml version="1.0" encoding="'.$charset.'"?>'."\n".$xml;
    551         else
    552             $xml = '<?xml version="1.0"?>'."\n".$xml;
    553         $length = strlen($xml);
    554         header('Connection: close');
    555         if ($charset)
    556             header('Content-Type: text/xml; charset='.$charset);
    557         else
    558             header('Content-Type: text/xml');
    559         header('Date: '.date('r'));
    560         echo $xml;
    561         exit;
    562     }
    563 
    564     function hasMethod($method)
    565     {
    566         return in_array($method, array_keys($this->callbacks));
    567     }
    568 
    569     function setCapabilities()
    570     {
    571         // Initialises capabilities array
    572         $this->capabilities = array(
    573             'xmlrpc' => array(
    574                 'specUrl' => 'http://www.xmlrpc.com/spec',
    575                 'specVersion' => 1
    576         ),
    577             'faults_interop' => array(
    578                 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
    579                 'specVersion' => 20010516
    580         ),
    581             'system.multicall' => array(
    582                 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
    583                 'specVersion' => 1
    584         ),
    585         );
    586     }
    587 
    588     function getCapabilities($args)
    589     {
    590         return $this->capabilities;
    591     }
    592 
    593     function setCallbacks()
    594     {
    595         $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
    596         $this->callbacks['system.listMethods'] = 'this:listMethods';
    597         $this->callbacks['system.multicall'] = 'this:multiCall';
    598     }
    599 
    600     function listMethods($args)
    601     {
    602         // Returns a list of methods - uses array_reverse to ensure user defined
    603         // methods are listed before server defined methods
    604         return array_reverse(array_keys($this->callbacks));
    605     }
    606 
    607     function multiCall($methodcalls)
    608     {
    609         // See http://www.xmlrpc.com/discuss/msgReader$1208
    610         $return = array();
    611         foreach ($methodcalls as $call) {
    612             $method = $call['methodName'];
    613             $params = $call['params'];
    614             if ($method == 'system.multicall') {
    615                 $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
    616             } else {
    617                 $result = $this->call($method, $params);
    618             }
    619             if (is_a($result, 'IXR_Error')) {
    620                 $return[] = array(
    621                     'faultCode' => $result->code,
    622                     'faultString' => $result->message
    623                 );
    624             } else {
    625                 $return[] = array($result);
    626             }
    627         }
    628         return $return;
    629     }
    630 }
    631 
    632 /**
    633  * IXR_Request
    634  *
    635  * @package IXR
    636  * @since 1.5.0
    637  */
    638 class IXR_Request
    639 {
    640     var $method;
    641     var $args;
    642     var $xml;
    643 
    644         /**
    645          * PHP5 constructor.
    646          */
    647     function __construct($method, $args)
    648     {
    649         $this->method = $method;
    650         $this->args = $args;
    651         $this->xml = <<<EOD
    652 <?xml version="1.0"?>
    653 <methodCall>
    654 <methodName>{$this->method}</methodName>
    655 <params>
    656 
    657 EOD;
    658         foreach ($this->args as $arg) {
    659             $this->xml .= '<param><value>';
    660             $v = new IXR_Value($arg);
    661             $this->xml .= $v->getXml();
    662             $this->xml .= "</value></param>\n";
    663         }
    664         $this->xml .= '</params></methodCall>';
    665     }
    666 
    667         /**
    668          * PHP4 constructor.
    669          */
    670         public function IXR_Request( $method, $args ) {
    671                 self::__construct( $method, $args );
    672         }
    673 
    674     function getLength()
    675     {
    676         return strlen($this->xml);
    677     }
    678 
    679     function getXml()
    680     {
    681         return $this->xml;
    682     }
    683 }
    684 
    685 /**
    6864 * IXR_Client
    6875 *
    6886 * @package IXR
     
    846164        return $this->error->message;
    847165    }
    848166}
    849 
    850 
    851 /**
    852  * IXR_Error
    853  *
    854  * @package IXR
    855  * @since 1.5.0
    856  */
    857 class IXR_Error
    858 {
    859     var $code;
    860     var $message;
    861 
    862         /**
    863          * PHP5 constructor.
    864          */
    865     function __construct( $code, $message )
    866     {
    867         $this->code = $code;
    868         $this->message = htmlspecialchars($message);
    869     }
    870 
    871         /**
    872          * PHP4 constructor.
    873          */
    874         public function IXR_Error( $code, $message ) {
    875                 self::__construct( $code, $message );
    876         }
    877 
    878     function getXml()
    879     {
    880         $xml = <<<EOD
    881 <methodResponse>
    882   <fault>
    883     <value>
    884       <struct>
    885         <member>
    886           <name>faultCode</name>
    887           <value><int>{$this->code}</int></value>
    888         </member>
    889         <member>
    890           <name>faultString</name>
    891           <value><string>{$this->message}</string></value>
    892         </member>
    893       </struct>
    894     </value>
    895   </fault>
    896 </methodResponse>
    897 
    898 EOD;
    899         return $xml;
    900     }
    901 }
    902 
    903 /**
    904  * IXR_Date
    905  *
    906  * @package IXR
    907  * @since 1.5.0
    908  */
    909 class IXR_Date {
    910     var $year;
    911     var $month;
    912     var $day;
    913     var $hour;
    914     var $minute;
    915     var $second;
    916     var $timezone;
    917 
    918         /**
    919          * PHP5 constructor.
    920          */
    921     function __construct( $time )
    922     {
    923         // $time can be a PHP timestamp or an ISO one
    924         if (is_numeric($time)) {
    925             $this->parseTimestamp($time);
    926         } else {
    927             $this->parseIso($time);
    928         }
    929     }
    930 
    931         /**
    932          * PHP4 constructor.
    933          */
    934         public function IXR_Date( $time ) {
    935                 self::__construct( $time );
    936         }
    937 
    938     function parseTimestamp($timestamp)
    939     {
    940         $this->year = date('Y', $timestamp);
    941         $this->month = date('m', $timestamp);
    942         $this->day = date('d', $timestamp);
    943         $this->hour = date('H', $timestamp);
    944         $this->minute = date('i', $timestamp);
    945         $this->second = date('s', $timestamp);
    946         $this->timezone = '';
    947     }
    948 
    949     function parseIso($iso)
    950     {
    951         $this->year = substr($iso, 0, 4);
    952         $this->month = substr($iso, 4, 2);
    953         $this->day = substr($iso, 6, 2);
    954         $this->hour = substr($iso, 9, 2);
    955         $this->minute = substr($iso, 12, 2);
    956         $this->second = substr($iso, 15, 2);
    957         $this->timezone = substr($iso, 17);
    958     }
    959 
    960     function getIso()
    961     {
    962         return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
    963     }
    964 
    965     function getXml()
    966     {
    967         return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
    968     }
    969 
    970     function getTimestamp()
    971     {
    972         return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
    973     }
    974 }
    975 
    976 /**
    977  * IXR_Base64
    978  *
    979  * @package IXR
    980  * @since 1.5.0
    981  */
    982 class IXR_Base64
    983 {
    984     var $data;
    985 
    986         /**
    987          * PHP5 constructor.
    988          */
    989     function __construct( $data )
    990     {
    991         $this->data = $data;
    992     }
    993 
    994         /**
    995          * PHP4 constructor.
    996          */
    997         public function IXR_Base64( $data ) {
    998                 self::__construct( $data );
    999         }
    1000 
    1001     function getXml()
    1002     {
    1003         return '<base64>'.base64_encode($this->data).'</base64>';
    1004     }
    1005 }
    1006 
    1007 /**
    1008  * IXR_IntrospectionServer
    1009  *
    1010  * @package IXR
    1011  * @since 1.5.0
    1012  */
    1013 class IXR_IntrospectionServer extends IXR_Server
    1014 {
    1015     var $signatures;
    1016     var $help;
    1017 
    1018         /**
    1019          * PHP5 constructor.
    1020          */
    1021     function __construct()
    1022     {
    1023         $this->setCallbacks();
    1024         $this->setCapabilities();
    1025         $this->capabilities['introspection'] = array(
    1026             'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
    1027             'specVersion' => 1
    1028         );
    1029         $this->addCallback(
    1030             'system.methodSignature',
    1031             'this:methodSignature',
    1032             array('array', 'string'),
    1033             'Returns an array describing the return type and required parameters of a method'
    1034         );
    1035         $this->addCallback(
    1036             'system.getCapabilities',
    1037             'this:getCapabilities',
    1038             array('struct'),
    1039             'Returns a struct describing the XML-RPC specifications supported by this server'
    1040         );
    1041         $this->addCallback(
    1042             'system.listMethods',
    1043             'this:listMethods',
    1044             array('array'),
    1045             'Returns an array of available methods on this server'
    1046         );
    1047         $this->addCallback(
    1048             'system.methodHelp',
    1049             'this:methodHelp',
    1050             array('string', 'string'),
    1051             'Returns a documentation string for the specified method'
    1052         );
    1053     }
    1054 
    1055         /**
    1056          * PHP4 constructor.
    1057          */
    1058         public function IXR_IntrospectionServer() {
    1059                 self::__construct();
    1060         }
    1061 
    1062     function addCallback($method, $callback, $args, $help)
    1063     {
    1064         $this->callbacks[$method] = $callback;
    1065         $this->signatures[$method] = $args;
    1066         $this->help[$method] = $help;
    1067     }
    1068 
    1069     function call($methodname, $args)
    1070     {
    1071         // Make sure it's in an array
    1072         if ($args && !is_array($args)) {
    1073             $args = array($args);
    1074         }
    1075 
    1076         // Over-rides default call method, adds signature check
    1077         if (!$this->hasMethod($methodname)) {
    1078             return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
    1079         }
    1080         $method = $this->callbacks[$methodname];
    1081         $signature = $this->signatures[$methodname];
    1082         $returnType = array_shift($signature);
    1083 
    1084         // Check the number of arguments
    1085         if (count($args) != count($signature)) {
    1086             return new IXR_Error(-32602, 'server error. wrong number of method parameters');
    1087         }
    1088 
    1089         // Check the argument types
    1090         $ok = true;
    1091         $argsbackup = $args;
    1092         for ($i = 0, $j = count($args); $i < $j; $i++) {
    1093             $arg = array_shift($args);
    1094             $type = array_shift($signature);
    1095             switch ($type) {
    1096                 case 'int':
    1097                 case 'i4':
    1098                     if (is_array($arg) || !is_int($arg)) {
    1099                         $ok = false;
    1100                     }
    1101                     break;
    1102                 case 'base64':
    1103                 case 'string':
    1104                     if (!is_string($arg)) {
    1105                         $ok = false;
    1106                     }
    1107                     break;
    1108                 case 'boolean':
    1109                     if ($arg !== false && $arg !== true) {
    1110                         $ok = false;
    1111                     }
    1112                     break;
    1113                 case 'float':
    1114                 case 'double':
    1115                     if (!is_float($arg)) {
    1116                         $ok = false;
    1117                     }
    1118                     break;
    1119                 case 'date':
    1120                 case 'dateTime.iso8601':
    1121                     if (!is_a($arg, 'IXR_Date')) {
    1122                         $ok = false;
    1123                     }
    1124                     break;
    1125             }
    1126             if (!$ok) {
    1127                 return new IXR_Error(-32602, 'server error. invalid method parameters');
    1128             }
    1129         }
    1130         // It passed the test - run the "real" method call
    1131         return parent::call($methodname, $argsbackup);
    1132     }
    1133 
    1134     function methodSignature($method)
    1135     {
    1136         if (!$this->hasMethod($method)) {
    1137             return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
    1138         }
    1139         // We should be returning an array of types
    1140         $types = $this->signatures[$method];
    1141         $return = array();
    1142         foreach ($types as $type) {
    1143             switch ($type) {
    1144                 case 'string':
    1145                     $return[] = 'string';
    1146                     break;
    1147                 case 'int':
    1148                 case 'i4':
    1149                     $return[] = 42;
    1150                     break;
    1151                 case 'double':
    1152                     $return[] = 3.1415;
    1153                     break;
    1154                 case 'dateTime.iso8601':
    1155                     $return[] = new IXR_Date(time());
    1156                     break;
    1157                 case 'boolean':
    1158                     $return[] = true;
    1159                     break;
    1160                 case 'base64':
    1161                     $return[] = new IXR_Base64('base64');
    1162                     break;
    1163                 case 'array':
    1164                     $return[] = array('array');
    1165                     break;
    1166                 case 'struct':
    1167                     $return[] = array('struct' => 'struct');
    1168                     break;
    1169             }
    1170         }
    1171         return $return;
    1172     }
    1173 
    1174     function methodHelp($method)
    1175     {
    1176         return $this->help[$method];
    1177     }
    1178 }
    1179 
    1180 /**
    1181  * IXR_ClientMulticall
    1182  *
    1183  * @package IXR
    1184  * @since 1.5.0
    1185  */
    1186 class IXR_ClientMulticall extends IXR_Client
    1187 {
    1188     var $calls = array();
    1189 
    1190         /**
    1191          * PHP5 constructor.
    1192          */
    1193     function __construct( $server, $path = false, $port = 80 )
    1194     {
    1195         parent::IXR_Client($server, $path, $port);
    1196         $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
    1197     }
    1198 
    1199         /**
    1200          * PHP4 constructor.
    1201          */
    1202         public function IXR_ClientMulticall( $server, $path = false, $port = 80 ) {
    1203                 self::__construct( $server, $path, $port );
    1204         }
    1205 
    1206     function addCall()
    1207     {
    1208         $args = func_get_args();
    1209         $methodName = array_shift($args);
    1210         $struct = array(
    1211             'methodName' => $methodName,
    1212             'params' => $args
    1213         );
    1214         $this->calls[] = $struct;
    1215     }
    1216 
    1217     function query()
    1218     {
    1219         // Prepare multicall, then call the parent::query() method
    1220         return parent::query('system.multicall', $this->calls);
    1221     }
    1222 }
  • src/wp-includes/IXR/class-IXR-clientmulticall.php

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

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

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

     
    11<?php
    2 /**
    3  * IXR - The Incutio XML-RPC Library
    4  *
    5  * Copyright (c) 2010, Incutio Ltd.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * 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 copyright
    14  *    notice, this list of conditions and the following disclaimer in the
    15  *    documentation and/or other materials provided with the distribution.
    16  *  - Neither the name of Incutio Ltd. nor the names of its contributors
    17  *    may be used to endorse or promote products derived from this software
    18  *    without specific prior written permission.
    19  *
    20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
    24  * 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, OR
    27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
    28  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
    30  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    31  *
    32  * @package IXR
    33  * @since 1.5.0
    34  *
    35  * @copyright  Incutio Ltd 2010 (http://www.incutio.com)
    36  * @version    1.7.4 7th September 2010
    37  * @author     Simon Willison
    38  * @link       http://scripts.incutio.com/xmlrpc/ Site/manual
    39  * @license    http://www.opensource.org/licenses/bsd-license.php BSD
    40  */
    412
    423/**
    43  * IXR_Value
    44  *
    45  * @package IXR
    46  * @since 1.5.0
    47  */
    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 objects
    64             foreach ($this->data as $key => $value) {
    65                 $this->data[$key] = new IXR_Value($value);
    66             }
    67         }
    68         if ($type == 'array') {
    69             for ($i = 0, $j = count($this->data); $i < $j; $i++) {
    70                 $this->data[$i] = new IXR_Value($this->data[$i]);
    71             }
    72         }
    73     }
    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 date
    95         if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
    96             return 'date';
    97         }
    98         if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
    99             return 'base64';
    100         }
    101 
    102         // If it is a normal PHP object convert it in to a struct
    103         if (is_object($this->data)) {
    104             $this->data = get_object_vars($this->data);
    105             return 'struct';
    106         }
    107         if (!is_array($this->data)) {
    108             return 'string';
    109         }
    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 value
    122         switch ($this->type) {
    123             case 'boolean':
    124                 return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
    125                 break;
    126             case 'int':
    127                 return '<int>'.$this->data.'</int>';
    128                 break;
    129             case 'double':
    130                 return '<double>'.$this->data.'</double>';
    131                 break;
    132             case 'string':
    133                 return '<string>'.htmlspecialchars($this->data).'</string>';
    134                 break;
    135             case 'array':
    136                 $return = '<array><data>'."\n";
    137                 foreach ($this->data as $item) {
    138                     $return .= '  <value>'.$item->getXml()."</value>\n";
    139                 }
    140                 $return .= '</data></array>';
    141                 return $return;
    142                 break;
    143             case 'struct':
    144                 $return = '<struct>'."\n";
    145                 foreach ($this->data as $name => $value) {
    146                                         $name = htmlspecialchars($name);
    147                     $return .= "  <member><name>$name</name><value>";
    148                     $return .= $value->getXml()."</value></member>\n";
    149                 }
    150                 $return .= '</struct>';
    151                 return $return;
    152                 break;
    153             case 'date':
    154             case 'base64':
    155                 return $this->data->getXml();
    156                 break;
    157         }
    158         return false;
    159     }
    160 
    161     /**
    162      * Checks whether or not the supplied array is a struct or not
    163      *
    164      * @param array $array
    165      * @return bool
    166      */
    167     function isStruct($array)
    168     {
    169         $expected = 0;
    170         foreach ($array as $key => $value) {
    171             if ((string)$key !== (string)$expected) {
    172                 return true;
    173             }
    174             $expected++;
    175         }
    176         return false;
    177     }
    178 }
    179 
    180 /**
    181  * IXR_MESSAGE
    182  *
    183  * @package IXR
    184  * @since 1.5.0
    185  *
    186  */
    187 class IXR_Message
    188 {
    189     var $message;
    190     var $messageType;  // methodCall / methodResponse / fault
    191     var $faultCode;
    192     var $faultString;