PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/class-IXR.php

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