PageRenderTime 66ms CodeModel.GetById 3ms RepoModel.GetById 0ms app.codeStats 0ms

/wordpress3.4.2/wp-includes/class-IXR.php

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