web/wp-includes/class-IXR.php
branchwordpress
changeset 109 03b0d1493584
child 132 4d4862461b8d
equal deleted inserted replaced
-1:000000000000 109:03b0d1493584
       
     1 <?php
       
     2 /**
       
     3  * IXR - The Inutio XML-RPC Library
       
     4  *
       
     5  * @package IXR
       
     6  * @since 1.5
       
     7  *
       
     8  * @copyright Incutio Ltd 2002-2005
       
     9  * @version 1.7 (beta) 23rd May 2005
       
    10  * @author Simon Willison
       
    11  * @link http://scripts.incutio.com/xmlrpc/ Site
       
    12  * @link http://scripts.incutio.com/xmlrpc/manual.php Manual
       
    13  * @license BSD License http://www.opensource.org/licenses/bsd-license.php
       
    14  */
       
    15 
       
    16 /**
       
    17  * IXR_Value
       
    18  *
       
    19  * @package IXR
       
    20  * @since 1.5
       
    21  */
       
    22 class IXR_Value {
       
    23     var $data;
       
    24     var $type;
       
    25 
       
    26     function IXR_Value ($data, $type = false) {
       
    27         $this->data = $data;
       
    28         if (!$type) {
       
    29             $type = $this->calculateType();
       
    30         }
       
    31         $this->type = $type;
       
    32         if ($type == 'struct') {
       
    33             /* Turn all the values in the array in to new IXR_Value objects */
       
    34             foreach ($this->data as $key => $value) {
       
    35                 $this->data[$key] = new IXR_Value($value);
       
    36             }
       
    37         }
       
    38         if ($type == 'array') {
       
    39             for ($i = 0, $j = count($this->data); $i < $j; $i++) {
       
    40                 $this->data[$i] = new IXR_Value($this->data[$i]);
       
    41             }
       
    42         }
       
    43     }
       
    44 
       
    45     function calculateType() {
       
    46         if ($this->data === true || $this->data === false) {
       
    47             return 'boolean';
       
    48         }
       
    49         if (is_integer($this->data)) {
       
    50             return 'int';
       
    51         }
       
    52         if (is_double($this->data)) {
       
    53             return 'double';
       
    54         }
       
    55         // Deal with IXR object types base64 and date
       
    56         if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
       
    57             return 'date';
       
    58         }
       
    59         if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
       
    60             return 'base64';
       
    61         }
       
    62         // If it is a normal PHP object convert it in to a struct
       
    63         if (is_object($this->data)) {
       
    64 
       
    65             $this->data = get_object_vars($this->data);
       
    66             return 'struct';
       
    67         }
       
    68         if (!is_array($this->data)) {
       
    69             return 'string';
       
    70         }
       
    71         /* We have an array - is it an array or a struct ? */
       
    72         if ($this->isStruct($this->data)) {
       
    73             return 'struct';
       
    74         } else {
       
    75             return 'array';
       
    76         }
       
    77     }
       
    78 
       
    79     function getXml() {
       
    80         /* Return XML for this value */
       
    81         switch ($this->type) {
       
    82             case 'boolean':
       
    83                 return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
       
    84                 break;
       
    85             case 'int':
       
    86                 return '<int>'.$this->data.'</int>';
       
    87                 break;
       
    88             case 'double':
       
    89                 return '<double>'.$this->data.'</double>';
       
    90                 break;
       
    91             case 'string':
       
    92                 return '<string>'.htmlspecialchars($this->data).'</string>';
       
    93                 break;
       
    94             case 'array':
       
    95                 $return = '<array><data>'."\n";
       
    96                 foreach ($this->data as $item) {
       
    97                     $return .= '  <value>'.$item->getXml()."</value>\n";
       
    98                 }
       
    99                 $return .= '</data></array>';
       
   100                 return $return;
       
   101                 break;
       
   102             case 'struct':
       
   103                 $return = '<struct>'."\n";
       
   104                 foreach ($this->data as $name => $value) {
       
   105 					$name = htmlspecialchars($name);
       
   106                     $return .= "  <member><name>$name</name><value>";
       
   107                     $return .= $value->getXml()."</value></member>\n";
       
   108                 }
       
   109                 $return .= '</struct>';
       
   110                 return $return;
       
   111                 break;
       
   112             case 'date':
       
   113             case 'base64':
       
   114                 return $this->data->getXml();
       
   115                 break;
       
   116         }
       
   117         return false;
       
   118     }
       
   119 
       
   120     function isStruct($array) {
       
   121         /* Nasty function to check if an array is a struct or not */
       
   122         $expected = 0;
       
   123         foreach ($array as $key => $value) {
       
   124             if ((string)$key != (string)$expected) {
       
   125                 return true;
       
   126             }
       
   127             $expected++;
       
   128         }
       
   129         return false;
       
   130     }
       
   131 }
       
   132 
       
   133 /**
       
   134  * IXR_Message
       
   135  *
       
   136  * @package IXR
       
   137  * @since 1.5
       
   138  */
       
   139 class IXR_Message {
       
   140     var $message;
       
   141     var $messageType;  // methodCall / methodResponse / fault
       
   142     var $faultCode;
       
   143     var $faultString;
       
   144     var $methodName;
       
   145     var $params;
       
   146     // Current variable stacks
       
   147     var $_arraystructs = array();   // The stack used to keep track of the current array/struct
       
   148     var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
       
   149     var $_currentStructName = array();  // A stack as well
       
   150     var $_param;
       
   151     var $_value;
       
   152     var $_currentTag;
       
   153     var $_currentTagContents;
       
   154     // The XML parser
       
   155     var $_parser;
       
   156     function IXR_Message ($message) {
       
   157         $this->message = $message;
       
   158     }
       
   159     function parse() {
       
   160         // first remove the XML declaration
       
   161         $this->message = preg_replace('/<\?xml.*?\?'.'>/', '', $this->message);
       
   162         if (trim($this->message) == '') {
       
   163             return false;
       
   164         }
       
   165         $this->_parser = xml_parser_create();
       
   166         // Set XML parser to take the case of tags in to account
       
   167         xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
       
   168         // Set XML parser callback functions
       
   169         xml_set_object($this->_parser, $this);
       
   170         xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
       
   171         xml_set_character_data_handler($this->_parser, 'cdata');
       
   172         if (!xml_parse($this->_parser, $this->message)) {
       
   173             /* die(sprintf('XML error: %s at line %d',
       
   174                 xml_error_string(xml_get_error_code($this->_parser)),
       
   175                 xml_get_current_line_number($this->_parser))); */
       
   176             return false;
       
   177         }
       
   178         xml_parser_free($this->_parser);
       
   179         // Grab the error messages, if any
       
   180         if ($this->messageType == 'fault') {
       
   181             $this->faultCode = $this->params[0]['faultCode'];
       
   182             $this->faultString = $this->params[0]['faultString'];
       
   183         }
       
   184         return true;
       
   185     }
       
   186     function tag_open($parser, $tag, $attr) {
       
   187         $this->_currentTagContents = '';
       
   188         $this->currentTag = $tag;
       
   189         switch($tag) {
       
   190             case 'methodCall':
       
   191             case 'methodResponse':
       
   192             case 'fault':
       
   193                 $this->messageType = $tag;
       
   194                 break;
       
   195             /* Deal with stacks of arrays and structs */
       
   196             case 'data':    // data is to all intents and puposes more interesting than array
       
   197                 $this->_arraystructstypes[] = 'array';
       
   198                 $this->_arraystructs[] = array();
       
   199                 break;
       
   200             case 'struct':
       
   201                 $this->_arraystructstypes[] = 'struct';
       
   202                 $this->_arraystructs[] = array();
       
   203                 break;
       
   204         }
       
   205     }
       
   206     function cdata($parser, $cdata) {
       
   207         $this->_currentTagContents .= $cdata;
       
   208     }
       
   209     function tag_close($parser, $tag) {
       
   210         $valueFlag = false;
       
   211         switch($tag) {
       
   212             case 'int':
       
   213             case 'i4':
       
   214                 $value = (int) trim($this->_currentTagContents);
       
   215                 $valueFlag = true;
       
   216                 break;
       
   217             case 'double':
       
   218                 $value = (double) trim($this->_currentTagContents);
       
   219                 $valueFlag = true;
       
   220                 break;
       
   221             case 'string':
       
   222                 $value = $this->_currentTagContents;
       
   223                 $valueFlag = true;
       
   224                 break;
       
   225             case 'dateTime.iso8601':
       
   226                 $value = new IXR_Date(trim($this->_currentTagContents));
       
   227                 // $value = $iso->getTimestamp();
       
   228                 $valueFlag = true;
       
   229                 break;
       
   230             case 'value':
       
   231                 // "If no type is indicated, the type is string."
       
   232                 if (trim($this->_currentTagContents) != '') {
       
   233                     $value = (string)$this->_currentTagContents;
       
   234                     $valueFlag = true;
       
   235                 }
       
   236                 break;
       
   237             case 'boolean':
       
   238                 $value = (boolean) trim($this->_currentTagContents);
       
   239                 $valueFlag = true;
       
   240                 break;
       
   241             case 'base64':
       
   242                 $value = base64_decode( trim( $this->_currentTagContents ) );
       
   243                 $valueFlag = true;
       
   244                 break;
       
   245             /* Deal with stacks of arrays and structs */
       
   246             case 'data':
       
   247             case 'struct':
       
   248                 $value = array_pop($this->_arraystructs);
       
   249                 array_pop($this->_arraystructstypes);
       
   250                 $valueFlag = true;
       
   251                 break;
       
   252             case 'member':
       
   253                 array_pop($this->_currentStructName);
       
   254                 break;
       
   255             case 'name':
       
   256                 $this->_currentStructName[] = trim($this->_currentTagContents);
       
   257                 break;
       
   258             case 'methodName':
       
   259                 $this->methodName = trim($this->_currentTagContents);
       
   260                 break;
       
   261         }
       
   262         if ($valueFlag) {
       
   263             if (count($this->_arraystructs) > 0) {
       
   264                 // Add value to struct or array
       
   265                 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
       
   266                     // Add to struct
       
   267                     $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
       
   268                 } else {
       
   269                     // Add to array
       
   270                     $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
       
   271                 }
       
   272             } else {
       
   273                 // Just add as a paramater
       
   274                 $this->params[] = $value;
       
   275             }
       
   276         }
       
   277         $this->_currentTagContents = '';
       
   278     }
       
   279 }
       
   280 
       
   281 /**
       
   282  * IXR_Server
       
   283  *
       
   284  * @package IXR
       
   285  * @since 1.5
       
   286  */
       
   287 class IXR_Server {
       
   288     var $data;
       
   289     var $callbacks = array();
       
   290     var $message;
       
   291     var $capabilities;
       
   292     function IXR_Server($callbacks = false, $data = false) {
       
   293         $this->setCapabilities();
       
   294         if ($callbacks) {
       
   295             $this->callbacks = $callbacks;
       
   296         }
       
   297         $this->setCallbacks();
       
   298         $this->serve($data);
       
   299     }
       
   300     function serve($data = false) {
       
   301         if (!$data) {
       
   302             global $HTTP_RAW_POST_DATA;
       
   303             if (!$HTTP_RAW_POST_DATA) {
       
   304                header( 'Content-Type: text/plain' );
       
   305                die('XML-RPC server accepts POST requests only.');
       
   306             }
       
   307             $data = $HTTP_RAW_POST_DATA;
       
   308         }
       
   309         $this->message = new IXR_Message($data);
       
   310         if (!$this->message->parse()) {
       
   311             $this->error(-32700, 'parse error. not well formed');
       
   312         }
       
   313         if ($this->message->messageType != 'methodCall') {
       
   314             $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
       
   315         }
       
   316         $result = $this->call($this->message->methodName, $this->message->params);
       
   317         // Is the result an error?
       
   318         if (is_a($result, 'IXR_Error')) {
       
   319             $this->error($result);
       
   320         }
       
   321         // Encode the result
       
   322         $r = new IXR_Value($result);
       
   323         $resultxml = $r->getXml();
       
   324         // Create the XML
       
   325         $xml = <<<EOD
       
   326 <methodResponse>
       
   327   <params>
       
   328     <param>
       
   329       <value>
       
   330         $resultxml
       
   331       </value>
       
   332     </param>
       
   333   </params>
       
   334 </methodResponse>
       
   335 
       
   336 EOD;
       
   337         // Send it
       
   338         $this->output($xml);
       
   339     }
       
   340     function call($methodname, $args) {
       
   341         if (!$this->hasMethod($methodname)) {
       
   342             return new IXR_Error(-32601, 'server error. requested method '.
       
   343                 $methodname.' does not exist.');
       
   344         }
       
   345         $method = $this->callbacks[$methodname];
       
   346         // Perform the callback and send the response
       
   347         if (count($args) == 1) {
       
   348             // If only one paramater just send that instead of the whole array
       
   349             $args = $args[0];
       
   350         }
       
   351         // Are we dealing with a function or a method?
       
   352         if (substr($method, 0, 5) == 'this:') {
       
   353             // It's a class method - check it exists
       
   354             $method = substr($method, 5);
       
   355             if (!method_exists($this, $method)) {
       
   356                 return new IXR_Error(-32601, 'server error. requested class method "'.
       
   357                     $method.'" does not exist.');
       
   358             }
       
   359             // Call the method
       
   360             $result = $this->$method($args);
       
   361         } else {
       
   362             // It's a function - does it exist?
       
   363             if (is_array($method)) {
       
   364                 if (!method_exists($method[0], $method[1])) {
       
   365                     return new IXR_Error(-32601, 'server error. requested object method "'.
       
   366                         $method[1].'" does not exist.');
       
   367                 }
       
   368             } else if (!function_exists($method)) {
       
   369                 return new IXR_Error(-32601, 'server error. requested function "'.
       
   370                     $method.'" does not exist.');
       
   371             }
       
   372             // Call the function
       
   373             $result = call_user_func($method, $args);
       
   374         }
       
   375         return $result;
       
   376     }
       
   377 
       
   378     function error($error, $message = false) {
       
   379         // Accepts either an error object or an error code and message
       
   380         if ($message && !is_object($error)) {
       
   381             $error = new IXR_Error($error, $message);
       
   382         }
       
   383         $this->output($error->getXml());
       
   384     }
       
   385     function output($xml) {
       
   386         $xml = '<?xml version="1.0"?>'."\n".$xml;
       
   387         $length = strlen($xml);
       
   388         header('Connection: close');
       
   389         header('Content-Length: '.$length);
       
   390         header('Content-Type: text/xml');
       
   391         header('Date: '.date('r'));
       
   392         echo $xml;
       
   393         exit;
       
   394     }
       
   395     function hasMethod($method) {
       
   396         return in_array($method, array_keys($this->callbacks));
       
   397     }
       
   398     function setCapabilities() {
       
   399         // Initialises capabilities array
       
   400         $this->capabilities = array(
       
   401             'xmlrpc' => array(
       
   402                 'specUrl' => 'http://www.xmlrpc.com/spec',
       
   403                 'specVersion' => 1
       
   404             ),
       
   405             'faults_interop' => array(
       
   406                 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
       
   407                 'specVersion' => 20010516
       
   408             ),
       
   409             'system.multicall' => array(
       
   410                 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
       
   411                 'specVersion' => 1
       
   412             ),
       
   413         );
       
   414     }
       
   415     function getCapabilities($args) {
       
   416         return $this->capabilities;
       
   417     }
       
   418     function setCallbacks() {
       
   419         $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
       
   420         $this->callbacks['system.listMethods'] = 'this:listMethods';
       
   421         $this->callbacks['system.multicall'] = 'this:multiCall';
       
   422     }
       
   423     function listMethods($args) {
       
   424         // Returns a list of methods - uses array_reverse to ensure user defined
       
   425         // methods are listed before server defined methods
       
   426         return array_reverse(array_keys($this->callbacks));
       
   427     }
       
   428     function multiCall($methodcalls) {
       
   429         // See http://www.xmlrpc.com/discuss/msgReader$1208
       
   430         $return = array();
       
   431         foreach ($methodcalls as $call) {
       
   432             $method = $call['methodName'];
       
   433             $params = $call['params'];
       
   434             if ($method == 'system.multicall') {
       
   435                 $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
       
   436             } else {
       
   437                 $result = $this->call($method, $params);
       
   438             }
       
   439             if (is_a($result, 'IXR_Error')) {
       
   440                 $return[] = array(
       
   441                     'faultCode' => $result->code,
       
   442                     'faultString' => $result->message
       
   443                 );
       
   444             } else {
       
   445                 $return[] = array($result);
       
   446             }
       
   447         }
       
   448         return $return;
       
   449     }
       
   450 }
       
   451 
       
   452 /**
       
   453  * IXR_Request
       
   454  *
       
   455  * @package IXR
       
   456  * @since 1.5
       
   457  */
       
   458 class IXR_Request {
       
   459     var $method;
       
   460     var $args;
       
   461     var $xml;
       
   462     function IXR_Request($method, $args) {
       
   463         $this->method = $method;
       
   464         $this->args = $args;
       
   465         $this->xml = <<<EOD
       
   466 <?xml version="1.0"?>
       
   467 <methodCall>
       
   468 <methodName>{$this->method}</methodName>
       
   469 <params>
       
   470 
       
   471 EOD;
       
   472         foreach ($this->args as $arg) {
       
   473             $this->xml .= '<param><value>';
       
   474             $v = new IXR_Value($arg);
       
   475             $this->xml .= $v->getXml();
       
   476             $this->xml .= "</value></param>\n";
       
   477         }
       
   478         $this->xml .= '</params></methodCall>';
       
   479     }
       
   480     function getLength() {
       
   481         return strlen($this->xml);
       
   482     }
       
   483     function getXml() {
       
   484         return $this->xml;
       
   485     }
       
   486 }
       
   487 
       
   488 /**
       
   489  * IXR_Client
       
   490  *
       
   491  * @package IXR
       
   492  * @since 1.5
       
   493  */
       
   494 class IXR_Client {
       
   495     var $server;
       
   496     var $port;
       
   497     var $path;
       
   498     var $useragent;
       
   499 	var $headers;
       
   500     var $response;
       
   501     var $message = false;
       
   502     var $debug = false;
       
   503     var $timeout;
       
   504     // Storage place for an error message
       
   505     var $error = false;
       
   506     function IXR_Client($server, $path = false, $port = 80, $timeout = false) {
       
   507         if (!$path) {
       
   508             // Assume we have been given a URL instead
       
   509             $bits = parse_url($server);
       
   510             $this->server = $bits['host'];
       
   511             $this->port = isset($bits['port']) ? $bits['port'] : 80;
       
   512             $this->path = isset($bits['path']) ? $bits['path'] : '/';
       
   513             // Make absolutely sure we have a path
       
   514             if (!$this->path) {
       
   515                 $this->path = '/';
       
   516             }
       
   517         } else {
       
   518             $this->server = $server;
       
   519             $this->path = $path;
       
   520             $this->port = $port;
       
   521         }
       
   522         $this->useragent = 'The Incutio XML-RPC PHP Library';
       
   523         $this->timeout = $timeout;
       
   524     }
       
   525     function query() {
       
   526         $args = func_get_args();
       
   527         $method = array_shift($args);
       
   528         $request = new IXR_Request($method, $args);
       
   529         $length = $request->getLength();
       
   530         $xml = $request->getXml();
       
   531         $r = "\r\n";
       
   532         $request  = "POST {$this->path} HTTP/1.0$r";
       
   533 
       
   534 		$this->headers['Host']			= $this->server;
       
   535 		$this->headers['Content-Type']	= 'text/xml';
       
   536 		$this->headers['User-Agent']	= $this->useragent;
       
   537 		$this->headers['Content-Length']= $length;
       
   538 
       
   539 		foreach( $this->headers as $header => $value ) {
       
   540 			$request .= "{$header}: {$value}{$r}";
       
   541 		}
       
   542 		$request .= $r;
       
   543 
       
   544         $request .= $xml;
       
   545         // Now send the request
       
   546         if ($this->debug) {
       
   547             echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n";
       
   548         }
       
   549         if ($this->timeout) {
       
   550             $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
       
   551         } else {
       
   552             $fp = @fsockopen($this->server, $this->port, $errno, $errstr);
       
   553         }
       
   554         if (!$fp) {
       
   555             $this->error = new IXR_Error(-32300, "transport error - could not open socket: $errno $errstr");
       
   556             return false;
       
   557         }
       
   558         fputs($fp, $request);
       
   559         $contents = '';
       
   560         $debug_contents = '';
       
   561         $gotFirstLine = false;
       
   562         $gettingHeaders = true;
       
   563         while (!feof($fp)) {
       
   564             $line = fgets($fp, 4096);
       
   565             if (!$gotFirstLine) {
       
   566                 // Check line for '200'
       
   567                 if (strstr($line, '200') === false) {
       
   568                     $this->error = new IXR_Error(-32301, 'transport error - HTTP status code was not 200');
       
   569                     return false;
       
   570                 }
       
   571                 $gotFirstLine = true;
       
   572             }
       
   573             if (trim($line) == '') {
       
   574                 $gettingHeaders = false;
       
   575             }
       
   576             if (!$gettingHeaders) {
       
   577                 $contents .= trim($line);
       
   578             }
       
   579             if ($this->debug) {
       
   580                 $debug_contents .= $line;
       
   581             }
       
   582         }
       
   583         if ($this->debug) {
       
   584             echo '<pre class="ixr_response">'.htmlspecialchars($debug_contents)."\n</pre>\n\n";
       
   585         }
       
   586         // Now parse what we've got back
       
   587         $this->message = new IXR_Message($contents);
       
   588         if (!$this->message->parse()) {
       
   589             // XML error
       
   590             $this->error = new IXR_Error(-32700, 'parse error. not well formed');
       
   591             return false;
       
   592         }
       
   593         // Is the message a fault?
       
   594         if ($this->message->messageType == 'fault') {
       
   595             $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
       
   596             return false;
       
   597         }
       
   598         // Message must be OK
       
   599         return true;
       
   600     }
       
   601     function getResponse() {
       
   602         // methodResponses can only have one param - return that
       
   603         return $this->message->params[0];
       
   604     }
       
   605     function isError() {
       
   606         return (is_object($this->error));
       
   607     }
       
   608     function getErrorCode() {
       
   609         return $this->error->code;
       
   610     }
       
   611     function getErrorMessage() {
       
   612         return $this->error->message;
       
   613     }
       
   614 }
       
   615 
       
   616 /**
       
   617  * IXR_Error
       
   618  *
       
   619  * @package IXR
       
   620  * @since 1.5
       
   621  */
       
   622 class IXR_Error {
       
   623     var $code;
       
   624     var $message;
       
   625     function IXR_Error($code, $message) {
       
   626         $this->code = $code;
       
   627         // WP adds htmlspecialchars(). See #5666
       
   628         $this->message = htmlspecialchars($message);
       
   629     }
       
   630     function getXml() {
       
   631         $xml = <<<EOD
       
   632 <methodResponse>
       
   633   <fault>
       
   634     <value>
       
   635       <struct>
       
   636         <member>
       
   637           <name>faultCode</name>
       
   638           <value><int>{$this->code}</int></value>
       
   639         </member>
       
   640         <member>
       
   641           <name>faultString</name>
       
   642           <value><string>{$this->message}</string></value>
       
   643         </member>
       
   644       </struct>
       
   645     </value>
       
   646   </fault>
       
   647 </methodResponse>
       
   648 
       
   649 EOD;
       
   650         return $xml;
       
   651     }
       
   652 }
       
   653 
       
   654 /**
       
   655  * IXR_Date
       
   656  *
       
   657  * @package IXR
       
   658  * @since 1.5
       
   659  */
       
   660 class IXR_Date {
       
   661     var $year;
       
   662     var $month;
       
   663     var $day;
       
   664     var $hour;
       
   665     var $minute;
       
   666     var $second;
       
   667     var $timezone;
       
   668     function IXR_Date($time) {
       
   669         // $time can be a PHP timestamp or an ISO one
       
   670         if (is_numeric($time)) {
       
   671             $this->parseTimestamp($time);
       
   672         } else {
       
   673             $this->parseIso($time);
       
   674         }
       
   675     }
       
   676     function parseTimestamp($timestamp) {
       
   677         $this->year = date('Y', $timestamp);
       
   678         $this->month = date('m', $timestamp);
       
   679         $this->day = date('d', $timestamp);
       
   680         $this->hour = date('H', $timestamp);
       
   681         $this->minute = date('i', $timestamp);
       
   682         $this->second = date('s', $timestamp);
       
   683         // WP adds timezone. See #2036
       
   684         $this->timezone = '';
       
   685     }
       
   686     function parseIso($iso) {
       
   687         $this->year = substr($iso, 0, 4);
       
   688         $this->month = substr($iso, 4, 2);
       
   689         $this->day = substr($iso, 6, 2);
       
   690         $this->hour = substr($iso, 9, 2);
       
   691         $this->minute = substr($iso, 12, 2);
       
   692         $this->second = substr($iso, 15, 2);
       
   693         // WP adds timezone. See #2036
       
   694         $this->timezone = substr($iso, 17);
       
   695     }
       
   696     function getIso() {
       
   697     	// WP adds timezone. See #2036
       
   698         return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
       
   699     }
       
   700     function getXml() {
       
   701         return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
       
   702     }
       
   703     function getTimestamp() {
       
   704         return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
       
   705     }
       
   706 }
       
   707 
       
   708 /**
       
   709  * IXR_Base64
       
   710  *
       
   711  * @package IXR
       
   712  * @since 1.5
       
   713  */
       
   714 class IXR_Base64 {
       
   715     var $data;
       
   716     function IXR_Base64($data) {
       
   717         $this->data = $data;
       
   718     }
       
   719     function getXml() {
       
   720         return '<base64>'.base64_encode($this->data).'</base64>';
       
   721     }
       
   722 }
       
   723 
       
   724 /**
       
   725  * IXR_IntrospectionServer
       
   726  *
       
   727  * @package IXR
       
   728  * @since 1.5
       
   729  */
       
   730 class IXR_IntrospectionServer extends IXR_Server {
       
   731     var $signatures;
       
   732     var $help;
       
   733     function IXR_IntrospectionServer() {
       
   734         $this->setCallbacks();
       
   735         $this->setCapabilities();
       
   736         $this->capabilities['introspection'] = array(
       
   737             'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
       
   738             'specVersion' => 1
       
   739         );
       
   740         $this->addCallback(
       
   741             'system.methodSignature',
       
   742             'this:methodSignature',
       
   743             array('array', 'string'),
       
   744             'Returns an array describing the return type and required parameters of a method'
       
   745         );
       
   746         $this->addCallback(
       
   747             'system.getCapabilities',
       
   748             'this:getCapabilities',
       
   749             array('struct'),
       
   750             'Returns a struct describing the XML-RPC specifications supported by this server'
       
   751         );
       
   752         $this->addCallback(
       
   753             'system.listMethods',
       
   754             'this:listMethods',
       
   755             array('array'),
       
   756             'Returns an array of available methods on this server'
       
   757         );
       
   758         $this->addCallback(
       
   759             'system.methodHelp',
       
   760             'this:methodHelp',
       
   761             array('string', 'string'),
       
   762             'Returns a documentation string for the specified method'
       
   763         );
       
   764     }
       
   765     function addCallback($method, $callback, $args, $help) {
       
   766         $this->callbacks[$method] = $callback;
       
   767         $this->signatures[$method] = $args;
       
   768         $this->help[$method] = $help;
       
   769     }
       
   770     function call($methodname, $args) {
       
   771         // Make sure it's in an array
       
   772         if ($args && !is_array($args)) {
       
   773             $args = array($args);
       
   774         }
       
   775         // Over-rides default call method, adds signature check
       
   776         if (!$this->hasMethod($methodname)) {
       
   777             return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
       
   778         }
       
   779         $method = $this->callbacks[$methodname];
       
   780         $signature = $this->signatures[$methodname];
       
   781         $returnType = array_shift($signature);
       
   782         // Check the number of arguments
       
   783         if (count($args) != count($signature)) {
       
   784             return new IXR_Error(-32602, 'server error. wrong number of method parameters');
       
   785         }
       
   786         // Check the argument types
       
   787         $ok = true;
       
   788         $argsbackup = $args;
       
   789         for ($i = 0, $j = count($args); $i < $j; $i++) {
       
   790             $arg = array_shift($args);
       
   791             $type = array_shift($signature);
       
   792             switch ($type) {
       
   793                 case 'int':
       
   794                 case 'i4':
       
   795                     if (is_array($arg) || !is_int($arg)) {
       
   796                         $ok = false;
       
   797                     }
       
   798                     break;
       
   799                 case 'base64':
       
   800                 case 'string':
       
   801                     if (!is_string($arg)) {
       
   802                         $ok = false;
       
   803                     }
       
   804                     break;
       
   805                 case 'boolean':
       
   806                     if ($arg !== false && $arg !== true) {
       
   807                         $ok = false;
       
   808                     }
       
   809                     break;
       
   810                 case 'float':
       
   811                 case 'double':
       
   812                     if (!is_float($arg)) {
       
   813                         $ok = false;
       
   814                     }
       
   815                     break;
       
   816                 case 'date':
       
   817                 case 'dateTime.iso8601':
       
   818                     if (!is_a($arg, 'IXR_Date')) {
       
   819                         $ok = false;
       
   820                     }
       
   821                     break;
       
   822             }
       
   823             if (!$ok) {
       
   824                 return new IXR_Error(-32602, 'server error. invalid method parameters');
       
   825             }
       
   826         }
       
   827         // It passed the test - run the "real" method call
       
   828         return parent::call($methodname, $argsbackup);
       
   829     }
       
   830     function methodSignature($method) {
       
   831         if (!$this->hasMethod($method)) {
       
   832             return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
       
   833         }
       
   834         // We should be returning an array of types
       
   835         $types = $this->signatures[$method];
       
   836         $return = array();
       
   837         foreach ($types as $type) {
       
   838             switch ($type) {
       
   839                 case 'string':
       
   840                     $return[] = 'string';
       
   841                     break;
       
   842                 case 'int':
       
   843                 case 'i4':
       
   844                     $return[] = 42;
       
   845                     break;
       
   846                 case 'double':
       
   847                     $return[] = 3.1415;
       
   848                     break;
       
   849                 case 'dateTime.iso8601':
       
   850                     $return[] = new IXR_Date(time());
       
   851                     break;
       
   852                 case 'boolean':
       
   853                     $return[] = true;
       
   854                     break;
       
   855                 case 'base64':
       
   856                     $return[] = new IXR_Base64('base64');
       
   857                     break;
       
   858                 case 'array':
       
   859                     $return[] = array('array');
       
   860                     break;
       
   861                 case 'struct':
       
   862                     $return[] = array('struct' => 'struct');
       
   863                     break;
       
   864             }
       
   865         }
       
   866         return $return;
       
   867     }
       
   868     function methodHelp($method) {
       
   869         return $this->help[$method];
       
   870     }
       
   871 }
       
   872 
       
   873 /**
       
   874  * IXR_ClientMulticall
       
   875  *
       
   876  * @package IXR
       
   877  * @since 1.5
       
   878  */
       
   879 class IXR_ClientMulticall extends IXR_Client {
       
   880     var $calls = array();
       
   881     function IXR_ClientMulticall($server, $path = false, $port = 80) {
       
   882         parent::IXR_Client($server, $path, $port);
       
   883         $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
       
   884     }
       
   885     function addCall() {
       
   886         $args = func_get_args();
       
   887         $methodName = array_shift($args);
       
   888         $struct = array(
       
   889             'methodName' => $methodName,
       
   890             'params' => $args
       
   891         );
       
   892         $this->calls[] = $struct;
       
   893     }
       
   894     function query() {
       
   895         // Prepare multicall, then call the parent::query() method
       
   896         return parent::query('system.multicall', $this->calls);
       
   897     }
       
   898 }
       
   899 
       
   900 ?>