home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Blogs / wordpress2.6.exe / wordpress2.6 / wp-includes / class-IXR.php < prev    next >
Encoding:
PHP Script  |  2008-06-14  |  27.5 KB  |  874 lines

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