Guest User

Untitled

a guest
Jan 22nd, 2017
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 41.42 KB | None | 0 0
  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. */
  40.  
  41.  
  42. class IXR_Value
  43. {
  44. var $data;
  45. var $type;
  46.  
  47. function IXR_Value($data, $type = false)
  48. {
  49. $this->data = $data;
  50. if (!$type) {
  51. $type = $this->calculateType();
  52. }
  53. $this->type = $type;
  54. if ($type == 'struct') {
  55. // Turn all the values in the array in to new IXR_Value objects
  56. foreach ($this->data as $key => $value) {
  57. $this->data[$key] = new IXR_Value($value);
  58. }
  59. }
  60. if ($type == 'array') {
  61. for ($i = 0, $j = count($this->data); $i < $j; $i++) {
  62. $this->data[$i] = new IXR_Value($this->data[$i]);
  63. }
  64. }
  65. }
  66.  
  67. function calculateType()
  68. {
  69. if ($this->data === true || $this->data === false) {
  70. return 'boolean';
  71. }
  72. if (is_integer($this->data)) {
  73. return 'int';
  74. }
  75. if (is_double($this->data)) {
  76. return 'double';
  77. }
  78.  
  79. // Deal with IXR object types base64 and date
  80. if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
  81. return 'date';
  82. }
  83. if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
  84. return 'base64';
  85. }
  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.  
  96. // We have an array - is it an array or a struct?
  97. if ($this->isStruct($this->data)) {
  98. return 'struct';
  99. } else {
  100. return 'array';
  101. }
  102. }
  103.  
  104. function getXml()
  105. {
  106. // Return XML for this value
  107. switch ($this->type) {
  108. case 'boolean':
  109. return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
  110. break;
  111. case 'int':
  112. return '<int>'.$this->data.'</int>';
  113. break;
  114. case 'double':
  115. return '<double>'.$this->data.'</double>';
  116. break;
  117. case 'string':
  118. return '<string>'.htmlspecialchars($this->data).'</string>';
  119. break;
  120. case 'array':
  121. $return = '<array><data>'."\n";
  122. foreach ($this->data as $item) {
  123. $return .= ' <value>'.$item->getXml()."</value>\n";
  124. }
  125. $return .= '</data></array>';
  126. return $return;
  127. break;
  128. case 'struct':
  129. $return = '<struct>'."\n";
  130. foreach ($this->data as $name => $value) {
  131. $return .= " <member><name>$name</name><value>";
  132. $return .= $value->getXml()."</value></member>\n";
  133. }
  134. $return .= '</struct>';
  135. return $return;
  136. break;
  137. case 'date':
  138. case 'base64':
  139. return $this->data->getXml();
  140. break;
  141. }
  142. return false;
  143. }
  144.  
  145. /**
  146. * Checks whether or not the supplied array is a struct or not
  147. *
  148. * @param unknown_type $array
  149. * @return boolean
  150. */
  151. function isStruct($array)
  152. {
  153. $expected = 0;
  154. foreach ($array as $key => $value) {
  155. if ((string)$key != (string)$expected) {
  156. return true;
  157. }
  158. $expected++;
  159. }
  160. return false;
  161. }
  162. }
  163.  
  164. /**
  165. * IXR_MESSAGE
  166. *
  167. * @package IXR
  168. * @since 1.5
  169. *
  170. */
  171. class IXR_Message
  172. {
  173. var $message;
  174. var $messageType; // methodCall / methodResponse / fault
  175. var $faultCode;
  176. var $faultString;
  177. var $methodName;
  178. var $params;
  179.  
  180. // Current variable stacks
  181. var $_arraystructs = array(); // The stack used to keep track of the current array/struct
  182. var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
  183. var $_currentStructName = array(); // A stack as well
  184. var $_param;
  185. var $_value;
  186. var $_currentTag;
  187. var $_currentTagContents;
  188. // The XML parser
  189. var $_parser;
  190.  
  191. function IXR_Message($message)
  192. {
  193. $this->message =& $message;
  194. }
  195.  
  196. function parse()
  197. {
  198. // first remove the XML declaration
  199. // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages
  200. $header = preg_replace( '/<\?xml.*?\?'.'>/', '', substr($this->message, 0, 100), 1);
  201. $this->message = substr_replace($this->message, $header, 0, 100);
  202. if (trim($this->message) == '') {
  203. return false;
  204. }
  205. $this->_parser = xml_parser_create();
  206. // Set XML parser to take the case of tags in to account
  207. xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
  208. // Set XML parser callback functions
  209. xml_set_object($this->_parser, $this);
  210. xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
  211. xml_set_character_data_handler($this->_parser, 'cdata');
  212. $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages
  213. do {
  214. if (strlen($this->message) <= $chunk_size) {
  215. $final = true;
  216. }
  217. $part = substr($this->message, 0, $chunk_size);
  218. $this->message = substr($this->message, $chunk_size);
  219. if (!xml_parse($this->_parser, $part, $final)) {
  220. return false;
  221. }
  222. if ($final) {
  223. break;
  224. }
  225. } while (true);
  226. xml_parser_free($this->_parser);
  227.  
  228. // Grab the error messages, if any
  229. if ($this->messageType == 'fault') {
  230. $this->faultCode = $this->params[0]['faultCode'];
  231. $this->faultString = $this->params[0]['faultString'];
  232. }
  233. return true;
  234. }
  235.  
  236. function tag_open($parser, $tag, $attr)
  237. {
  238. $this->_currentTagContents = '';
  239. $this->currentTag = $tag;
  240. switch($tag) {
  241. case 'methodCall':
  242. case 'methodResponse':
  243. case 'fault':
  244. $this->messageType = $tag;
  245. break;
  246. /* Deal with stacks of arrays and structs */
  247. case 'data': // data is to all intents and puposes more interesting than array
  248. $this->_arraystructstypes[] = 'array';
  249. $this->_arraystructs[] = array();
  250. break;
  251. case 'struct':
  252. $this->_arraystructstypes[] = 'struct';
  253. $this->_arraystructs[] = array();
  254. break;
  255. }
  256. }
  257.  
  258. function cdata($parser, $cdata)
  259. {
  260. $this->_currentTagContents .= $cdata;
  261. }
  262.  
  263. function tag_close($parser, $tag)
  264. {
  265. $valueFlag = false;
  266. switch($tag) {
  267. case 'int':
  268. case 'i4':
  269. $value = (int)trim($this->_currentTagContents);
  270. $valueFlag = true;
  271. break;
  272. case 'double':
  273. $value = (double)trim($this->_currentTagContents);
  274. $valueFlag = true;
  275. break;
  276. case 'string':
  277. $value = (string)trim($this->_currentTagContents);
  278. $valueFlag = true;
  279. break;
  280. case 'dateTime.iso8601':
  281. $value = new IXR_Date(trim($this->_currentTagContents));
  282. $valueFlag = true;
  283. break;
  284. case 'value':
  285. // "If no type is indicated, the type is string."
  286. if (trim($this->_currentTagContents) != '') {
  287. $value = (string)$this->_currentTagContents;
  288. $valueFlag = true;
  289. }
  290. break;
  291. case 'boolean':
  292. $value = (boolean)trim($this->_currentTagContents);
  293. $valueFlag = true;
  294. break;
  295. case 'base64':
  296. $value = base64_decode($this->_currentTagContents);
  297. $valueFlag = true;
  298. break;
  299. /* Deal with stacks of arrays and structs */
  300. case 'data':
  301. case 'struct':
  302. $value = array_pop($this->_arraystructs);
  303. array_pop($this->_arraystructstypes);
  304. $valueFlag = true;
  305. break;
  306. case 'member':
  307. array_pop($this->_currentStructName);
  308. break;
  309. case 'name':
  310. $this->_currentStructName[] = trim($this->_currentTagContents);
  311. break;
  312. case 'methodName':
  313. $this->methodName = trim($this->_currentTagContents);
  314. break;
  315. }
  316.  
  317. if ($valueFlag) {
  318. if (count($this->_arraystructs) > 0) {
  319. // Add value to struct or array
  320. if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
  321. // Add to struct
  322. $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
  323. } else {
  324. // Add to array
  325. $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
  326. }
  327. } else {
  328. // Just add as a paramater
  329. $this->params[] = $value;
  330. }
  331. }
  332. $this->_currentTagContents = '';
  333. }
  334. }
  335.  
  336. /**
  337. * IXR_Server
  338. *
  339. * @package IXR
  340. * @since 1.5
  341. */
  342. class IXR_Server
  343. {
  344. var $data;
  345. var $callbacks = array();
  346. var $message;
  347. var $capabilities;
  348.  
  349. function IXR_Server($callbacks = false, $data = false, $wait = false)
  350. {
  351. $this->setCapabilities();
  352. if ($callbacks) {
  353. $this->callbacks = $callbacks;
  354. }
  355. $this->setCallbacks();
  356. if (!$wait) {
  357. $this->serve($data);
  358. }
  359. }
  360.  
  361. function serve($data = false)
  362. {
  363. if (!$data) {
  364. if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
  365. header('Content-Type: text/plain'); // merged from WP #9093
  366. die('XML-RPC server accepts POST requests only.');
  367. }
  368.  
  369. global $HTTP_RAW_POST_DATA;
  370. if (empty($HTTP_RAW_POST_DATA)) {
  371. // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293
  372. $data = file_get_contents('php://input');
  373. } else {
  374. $data =& $HTTP_RAW_POST_DATA;
  375. }
  376. }
  377. $this->message = new IXR_Message($data);
  378. if (!$this->message->parse()) {
  379. $this->error(-32700, 'parse error. not well formed');
  380. }
  381. if ($this->message->messageType != 'methodCall') {
  382. $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
  383. }
  384. $result = $this->call($this->message->methodName, $this->message->params);
  385.  
  386. // Is the result an error?
  387. if (is_a($result, 'IXR_Error')) {
  388. $this->error($result);
  389. }
  390.  
  391. // Encode the result
  392. $r = new IXR_Value($result);
  393. $resultxml = $r->getXml();
  394.  
  395. // Create the XML
  396. $xml = <<<EOD
  397. <methodResponse>
  398. <params>
  399. <param>
  400. <value>
  401. $resultxml
  402. </value>
  403. </param>
  404. </params>
  405. </methodResponse>
  406.  
  407. EOD;
  408. // Send it
  409. $this->output($xml);
  410. }
  411.  
  412. function call($methodname, $args)
  413. {
  414. if (!$this->hasMethod($methodname)) {
  415. return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
  416. }
  417. $method = $this->callbacks[$methodname];
  418.  
  419. // Perform the callback and send the response
  420. if (count($args) == 1) {
  421. // If only one paramater just send that instead of the whole array
  422. $args = $args[0];
  423. }
  424.  
  425. // Are we dealing with a function or a method?
  426. if (is_string($method) && substr($method, 0, 5) == 'this:') {
  427. // It's a class method - check it exists
  428. $method = substr($method, 5);
  429. if (!method_exists($this, $method)) {
  430. return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.');
  431. }
  432.  
  433. //Call the method
  434. $result = $this->$method($args);
  435. } else {
  436. // It's a function - does it exist?
  437. if (is_array($method)) {
  438. if (!method_exists($method[0], $method[1])) {
  439. return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.');
  440. }
  441. } else if (!function_exists($method)) {
  442. return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
  443. }
  444.  
  445. // Call the function
  446. $result = call_user_func($method, $args);
  447. }
  448. return $result;
  449. }
  450.  
  451. function error($error, $message = false)
  452. {
  453. // Accepts either an error object or an error code and message
  454. if ($message && !is_object($error)) {
  455. $error = new IXR_Error($error, $message);
  456. }
  457. $this->output($error->getXml());
  458. }
  459.  
  460. function output($xml)
  461. {
  462. $xml = '<?xml version="1.0"?>'."\n".$xml;
  463. $length = strlen($xml);
  464. header('Connection: close');
  465. header('Content-Length: '.$length);
  466. header('Content-Type: text/xml');
  467. header('Date: '.date('r'));
  468. echo $xml;
  469. exit;
  470. }
  471.  
  472. function hasMethod($method)
  473. {
  474. return in_array($method, array_keys($this->callbacks));
  475. }
  476.  
  477. function setCapabilities()
  478. {
  479. // Initialises capabilities array
  480. $this->capabilities = array(
  481. 'xmlrpc' => array(
  482. 'specUrl' => 'http://www.xmlrpc.com/spec',
  483. 'specVersion' => 1
  484. ),
  485. 'faults_interop' => array(
  486. 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
  487. 'specVersion' => 20010516
  488. ),
  489. 'system.multicall' => array(
  490. 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
  491. 'specVersion' => 1
  492. ),
  493. );
  494. }
  495.  
  496. function getCapabilities($args)
  497. {
  498. return $this->capabilities;
  499. }
  500.  
  501. function setCallbacks()
  502. {
  503. $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
  504. $this->callbacks['system.listMethods'] = 'this:listMethods';
  505. $this->callbacks['system.multicall'] = 'this:multiCall';
  506. }
  507.  
  508. function listMethods($args)
  509. {
  510. // Returns a list of methods - uses array_reverse to ensure user defined
  511. // methods are listed before server defined methods
  512. return array_reverse(array_keys($this->callbacks));
  513. }
  514.  
  515. function multiCall($methodcalls)
  516. {
  517. // See http://www.xmlrpc.com/discuss/msgReader$1208
  518. $return = array();
  519. foreach ($methodcalls as $call) {
  520. $method = $call['methodName'];
  521. $params = $call['params'];
  522. if ($method == 'system.multicall') {
  523. $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
  524. } else {
  525. $result = $this->call($method, $params);
  526. }
  527. if (is_a($result, 'IXR_Error')) {
  528. $return[] = array(
  529. 'faultCode' => $result->code,
  530. 'faultString' => $result->message
  531. );
  532. } else {
  533. $return[] = array($result);
  534. }
  535. }
  536. return $return;
  537. }
  538. }
  539.  
  540. /**
  541. * IXR_Request
  542. *
  543. * @package IXR
  544. * @since 1.5
  545. */
  546. class IXR_Request
  547. {
  548. var $method;
  549. var $args;
  550. var $xml;
  551.  
  552. function IXR_Request($method, $args)
  553. {
  554. $this->method = $method;
  555. $this->args = $args;
  556. $this->xml = <<<EOD
  557. <?xml version="1.0"?>
  558. <methodCall>
  559. <methodName>{$this->method}</methodName>
  560. <params>
  561.  
  562. EOD;
  563. foreach ($this->args as $arg) {
  564. $this->xml .= '<param><value>';
  565. $v = new IXR_Value($arg);
  566. $this->xml .= $v->getXml();
  567. $this->xml .= "</value></param>\n";
  568. }
  569. $this->xml .= '</params></methodCall>';
  570. }
  571.  
  572. function getLength()
  573. {
  574. return strlen($this->xml);
  575. }
  576.  
  577. function getXml()
  578. {
  579. return $this->xml;
  580. }
  581. }
  582.  
  583. /**
  584. * IXR_Client
  585. *
  586. * @package IXR
  587. * @since 1.5
  588. *
  589. */
  590. class IXR_Client
  591. {
  592. var $server;
  593. var $port;
  594. var $path;
  595. var $useragent;
  596. var $response;
  597. var $message = false;
  598. var $debug = false;
  599. var $timeout;
  600.  
  601. // Storage place for an error message
  602. var $error = false;
  603.  
  604. function IXR_Client($server, $path = false, $port = 80, $timeout = 15)
  605. {
  606. if (!$path) {
  607. // Assume we have been given a URL instead
  608. $bits = parse_url($server);
  609. $this->server = $bits['host'];
  610. $this->port = isset($bits['port']) ? $bits['port'] : 80;
  611. $this->path = isset($bits['path']) ? $bits['path'] : '/';
  612.  
  613. // Make absolutely sure we have a path
  614. if (!$this->path) {
  615. $this->path = '/';
  616. }
  617. } else {
  618. $this->server = $server;
  619. $this->path = $path;
  620. $this->port = $port;
  621. }
  622. $this->useragent = 'The Incutio XML-RPC PHP Library';
  623. $this->timeout = $timeout;
  624. }
  625.  
  626. function query()
  627. {
  628. $args = func_get_args();
  629. $method = array_shift($args);
  630. $request = new IXR_Request($method, $args);
  631. $length = $request->getLength();
  632. $xml = $request->getXml();
  633. $r = "\r\n";
  634. $request = "POST {$this->path} HTTP/1.0$r";
  635.  
  636. // Merged from WP #8145 - allow custom headers
  637. $this->headers['Host'] = $this->server;
  638. $this->headers['Content-Type'] = 'text/xml';
  639. $this->headers['User-Agent'] = $this->useragent;
  640. $this->headers['Content-Length']= $length;
  641.  
  642. foreach( $this->headers as $header => $value ) {
  643. $request .= "{$header}: {$value}{$r}";
  644. }
  645. $request .= $r;
  646.  
  647. $request .= $xml;
  648.  
  649. // Now send the request
  650. if ($this->debug) {
  651. echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n";
  652. }
  653.  
  654. if ($this->timeout) {
  655. $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
  656. } else {
  657. $fp = @fsockopen($this->server, $this->port, $errno, $errstr);
  658. }
  659. if (!$fp) {
  660. $this->error = new IXR_Error(-32300, 'transport error - could not open socket');
  661. return false;
  662. }
  663. fputs($fp, $request);
  664. $contents = '';
  665. $debugContents = '';
  666. $gotFirstLine = false;
  667. $gettingHeaders = true;
  668. while (!feof($fp)) {
  669. $line = fgets($fp, 4096);
  670. if (!$gotFirstLine) {
  671. // Check line for '200'
  672. if (strstr($line, '200') === false) {
  673. $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
  674. return false;
  675. }
  676. $gotFirstLine = true;
  677. }
  678. if (trim($line) == '') {
  679. $gettingHeaders = false;
  680. }
  681. if (!$gettingHeaders) {
  682. // merged from WP #12559 - remove trim
  683. $contents .= $line;
  684. }
  685. if ($this->debug) {
  686. $debugContents .= $line;
  687. }
  688. }
  689. if ($this->debug) {
  690. echo '<pre class="ixr_response">'.htmlspecialchars($debugContents)."\n</pre>\n\n";
  691. }
  692.  
  693. // Now parse what we've got back
  694. $this->message = new IXR_Message($contents);
  695. if (!$this->message->parse()) {
  696. // XML error
  697. $this->error = new IXR_Error(-32700, 'parse error. not well formed');
  698. return false;
  699. }
  700.  
  701. // Is the message a fault?
  702. if ($this->message->messageType == 'fault') {
  703. $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
  704. return false;
  705. }
  706.  
  707. // Message must be OK
  708. return true;
  709. }
  710.  
  711. function getResponse()
  712. {
  713. // methodResponses can only have one param - return that
  714. return $this->message->params[0];
  715. }
  716.  
  717. function isError()
  718. {
  719. return (is_object($this->error));
  720. }
  721.  
  722. function getErrorCode()
  723. {
  724. return $this->error->code;
  725. }
  726.  
  727. function getErrorMessage()
  728. {
  729. return $this->error->message;
  730. }
  731. }
  732.  
  733.  
  734. /**
  735. * IXR_Error
  736. *
  737. * @package IXR
  738. * @since 1.5
  739. */
  740. class IXR_Error
  741. {
  742. var $code;
  743. var $message;
  744.  
  745. function IXR_Error($code, $message)
  746. {
  747. $this->code = $code;
  748. $this->message = htmlspecialchars($message);
  749. }
  750.  
  751. function getXml()
  752. {
  753. $xml = <<<EOD
  754. <methodResponse>
  755. <fault>
  756. <value>
  757. <struct>
  758. <member>
  759. <name>faultCode</name>
  760. <value><int>{$this->code}</int></value>
  761. </member>
  762. <member>
  763. <name>faultString</name>
  764. <value><string>{$this->message}</string></value>
  765. </member>
  766. </struct>
  767. </value>
  768. </fault>
  769. </methodResponse>
  770.  
  771. EOD;
  772. return $xml;
  773. }
  774. }
  775.  
  776. /**
  777. * IXR_Date
  778. *
  779. * @package IXR
  780. * @since 1.5
  781. */
  782. class IXR_Date {
  783. var $year;
  784. var $month;
  785. var $day;
  786. var $hour;
  787. var $minute;
  788. var $second;
  789. var $timezone;
  790.  
  791. function IXR_Date($time)
  792. {
  793. // $time can be a PHP timestamp or an ISO one
  794. if (is_numeric($time)) {
  795. $this->parseTimestamp($time);
  796. } else {
  797. $this->parseIso($time);
  798. }
  799. }
  800.  
  801. function parseTimestamp($timestamp)
  802. {
  803. $this->year = date('Y', $timestamp);
  804. $this->month = date('m', $timestamp);
  805. $this->day = date('d', $timestamp);
  806. $this->hour = date('H', $timestamp);
  807. $this->minute = date('i', $timestamp);
  808. $this->second = date('s', $timestamp);
  809. $this->timezone = '';
  810. }
  811.  
  812. function parseIso($iso)
  813. {
  814. $this->year = substr($iso, 0, 4);
  815. $this->month = substr($iso, 4, 2);
  816. $this->day = substr($iso, 6, 2);
  817. $this->hour = substr($iso, 9, 2);
  818. $this->minute = substr($iso, 12, 2);
  819. $this->second = substr($iso, 15, 2);
  820. $this->timezone = substr($iso, 17);
  821. }
  822.  
  823. function getIso()
  824. {
  825. return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
  826. }
  827.  
  828. function getXml()
  829. {
  830. return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
  831. }
  832.  
  833. function getTimestamp()
  834. {
  835. return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
  836. }
  837. }
  838.  
  839. /**
  840. * IXR_Base64
  841. *
  842. * @package IXR
  843. * @since 1.5
  844. */
  845. class IXR_Base64
  846. {
  847. var $data;
  848.  
  849. function IXR_Base64($data)
  850. {
  851. $this->data = $data;
  852. }
  853.  
  854. function getXml()
  855. {
  856. return '<base64>'.base64_encode($this->data).'</base64>';
  857. }
  858. }
  859.  
  860. /**
  861. * IXR_IntrospectionServer
  862. *
  863. * @package IXR
  864. * @since 1.5
  865. */
  866. class IXR_IntrospectionServer extends IXR_Server
  867. {
  868. var $signatures;
  869. var $help;
  870.  
  871. function IXR_IntrospectionServer()
  872. {
  873. $this->setCallbacks();
  874. $this->setCapabilities();
  875. $this->capabilities['introspection'] = array(
  876. 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
  877. 'specVersion' => 1
  878. );
  879. $this->addCallback(
  880. 'system.methodSignature',
  881. 'this:methodSignature',
  882. array('array', 'string'),
  883. 'Returns an array describing the return type and required parameters of a method'
  884. );
  885. $this->addCallback(
  886. 'system.getCapabilities',
  887. 'this:getCapabilities',
  888. array('struct'),
  889. 'Returns a struct describing the XML-RPC specifications supported by this server'
  890. );
  891. $this->addCallback(
  892. 'system.listMethods',
  893. 'this:listMethods',
  894. array('array'),
  895. 'Returns an array of available methods on this server'
  896. );
  897. $this->addCallback(
  898. 'system.methodHelp',
  899. 'this:methodHelp',
  900. array('string', 'string'),
  901. 'Returns a documentation string for the specified method'
  902. );
  903. }
  904.  
  905. function addCallback($method, $callback, $args, $help)
  906. {
  907. $this->callbacks[$method] = $callback;
  908. $this->signatures[$method] = $args;
  909. $this->help[$method] = $help;
  910. }
  911.  
  912. function call($methodname, $args)
  913. {
  914. // Make sure it's in an array
  915. if ($args && !is_array($args)) {
  916. $args = array($args);
  917. }
  918.  
  919. // Over-rides default call method, adds signature check
  920. if (!$this->hasMethod($methodname)) {
  921. return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
  922. }
  923. $method = $this->callbacks[$methodname];
  924. $signature = $this->signatures[$methodname];
  925. $returnType = array_shift($signature);
  926.  
  927. // Check the number of arguments
  928. if (count($args) != count($signature)) {
  929. return new IXR_Error(-32602, 'server error. wrong number of method parameters');
  930. }
  931.  
  932. // Check the argument types
  933. $ok = true;
  934. $argsbackup = $args;
  935. for ($i = 0, $j = count($args); $i < $j; $i++) {
  936. $arg = array_shift($args);
  937. $type = array_shift($signature);
  938. switch ($type) {
  939. case 'int':
  940. case 'i4':
  941. if (is_array($arg) || !is_int($arg)) {
  942. $ok = false;
  943. }
  944. break;
  945. case 'base64':
  946. case 'string':
  947. if (!is_string($arg)) {
  948. $ok = false;
  949. }
  950. break;
  951. case 'boolean':
  952. if ($arg !== false && $arg !== true) {
  953. $ok = false;
  954. }
  955. break;
  956. case 'float':
  957. case 'double':
  958. if (!is_float($arg)) {
  959. $ok = false;
  960. }
  961. break;
  962. case 'date':
  963. case 'dateTime.iso8601':
  964. if (!is_a($arg, 'IXR_Date')) {
  965. $ok = false;
  966. }
  967. break;
  968. }
  969. if (!$ok) {
  970. return new IXR_Error(-32602, 'server error. invalid method parameters');
  971. }
  972. }
  973. // It passed the test - run the "real" method call
  974. return parent::call($methodname, $argsbackup);
  975. }
  976.  
  977. function methodSignature($method)
  978. {
  979. if (!$this->hasMethod($method)) {
  980. return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
  981. }
  982. // We should be returning an array of types
  983. $types = $this->signatures[$method];
  984. $return = array();
  985. foreach ($types as $type) {
  986. switch ($type) {
  987. case 'string':
  988. $return[] = 'string';
  989. break;
  990. case 'int':
  991. case 'i4':
  992. $return[] = 42;
  993. break;
  994. case 'double':
  995. $return[] = 3.1415;
  996. break;
  997. case 'dateTime.iso8601':
  998. $return[] = new IXR_Date(time());
  999. break;
  1000. case 'boolean':
  1001. $return[] = true;
  1002. break;
  1003. case 'base64':
  1004. $return[] = new IXR_Base64('base64');
  1005. break;
  1006. case 'array':
  1007. $return[] = array('array');
  1008. break;
  1009. case 'struct':
  1010. $return[] = array('struct' => 'struct');
  1011. break;
  1012. }
  1013. }
  1014. return $return;
  1015. }
  1016.  
  1017. function methodHelp($method)
  1018. {
  1019. return $this->help[$method];
  1020. }
  1021. }
  1022.  
  1023. /**
  1024. * IXR_ClientMulticall
  1025. *
  1026. * @package IXR
  1027. * @since 1.5
  1028. */
  1029. class IXR_ClientMulticall extends IXR_Client
  1030. {
  1031. var $calls = array();
  1032.  
  1033. function IXR_ClientMulticall($server, $path = false, $port = 80)
  1034. {
  1035. parent::IXR_Client($server, $path, $port);
  1036. $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
  1037. }
  1038.  
  1039. function addCall()
  1040. {
  1041. $args = func_get_args();
  1042. $methodName = array_shift($args);
  1043. $struct = array(
  1044. 'methodName' => $methodName,
  1045. 'params' => $args
  1046. );
  1047. $this->calls[] = $struct;
  1048. }
  1049.  
  1050. function query()
  1051. {
  1052. // Prepare multicall, then call the parent::query() method
  1053. return parent::query('system.multicall', $this->calls);
  1054. }
  1055. }
  1056.  
  1057. /**
  1058. * Client for communicating with a XML-RPC Server over HTTPS.
  1059. *
  1060. * @author Jason Stirk <jstirk@gmm.com.au> (@link http://blog.griffin.homelinux.org/projects/xmlrpc/)
  1061. * @version 0.2.0 26May2005 08:34 +0800
  1062. * @copyright (c) 2004-2005 Jason Stirk
  1063. * @package IXR
  1064. */
  1065. class IXR_ClientSSL extends IXR_Client
  1066. {
  1067. /**
  1068. * Filename of the SSL Client Certificate
  1069. * @access private
  1070. * @since 0.1.0
  1071. * @var string
  1072. */
  1073. var $_certFile;
  1074.  
  1075. /**
  1076. * Filename of the SSL CA Certificate
  1077. * @access private
  1078. * @since 0.1.0
  1079. * @var string
  1080. */
  1081. var $_caFile;
  1082.  
  1083. /**
  1084. * Filename of the SSL Client Private Key
  1085. * @access private
  1086. * @since 0.1.0
  1087. * @var string
  1088. */
  1089. var $_keyFile;
  1090.  
  1091. /**
  1092. * Passphrase to unlock the private key
  1093. * @access private
  1094. * @since 0.1.0
  1095. * @var string
  1096. */
  1097. var $_passphrase;
  1098.  
  1099. /**
  1100. * Constructor
  1101. * @param string $server URL of the Server to connect to
  1102. * @since 0.1.0
  1103. */
  1104. function IXR_ClientSSL($server, $path = false, $port = 443, $timeout = false)
  1105. {
  1106. parent::IXR_Client($server, $path, $port, $timeout);
  1107. $this->useragent = 'The Incutio XML-RPC PHP Library for SSL';
  1108.  
  1109. // Set class fields
  1110. $this->_certFile=false;
  1111. $this->_caFile=false;
  1112. $this->_keyFile=false;
  1113. $this->_passphrase='';
  1114. }
  1115.  
  1116. /**
  1117. * Set the client side certificates to communicate with the server.
  1118. *
  1119. * @since 0.1.0
  1120. * @param string $certificateFile Filename of the client side certificate to use
  1121. * @param string $keyFile Filename of the client side certificate's private key
  1122. * @param string $keyPhrase Passphrase to unlock the private key
  1123. */
  1124. function setCertificate($certificateFile, $keyFile, $keyPhrase='')
  1125. {
  1126. // Check the files all exist
  1127. if (is_file($certificateFile)) {
  1128. $this->_certFile = $certificateFile;
  1129. } else {
  1130. die('Could not open certificate: ' . $certificateFile);
  1131. }
  1132.  
  1133. if (is_file($keyFile)) {
  1134. $this->_keyFile = $keyFile;
  1135. } else {
  1136. die('Could not open private key: ' . $keyFile);
  1137. }
  1138.  
  1139. $this->_passphrase=(string)$keyPhrase;
  1140. }
  1141.  
  1142. function setCACertificate($caFile)
  1143. {
  1144. if (is_file($caFile)) {
  1145. $this->_caFile = $caFile;
  1146. } else {
  1147. die('Could not open CA certificate: ' . $caFile);
  1148. }
  1149. }
  1150.  
  1151. /**
  1152. * Sets the connection timeout (in seconds)
  1153. * @param int $newTimeOut Timeout in seconds
  1154. * @returns void
  1155. * @since 0.1.2
  1156. */
  1157. function setTimeOut($newTimeOut)
  1158. {
  1159. $this->timeout = (int)$newTimeOut;
  1160. }
  1161.  
  1162. /**
  1163. * Returns the connection timeout (in seconds)
  1164. * @returns int
  1165. * @since 0.1.2
  1166. */
  1167. function getTimeOut()
  1168. {
  1169. return $this->timeout;
  1170. }
  1171.  
  1172. /**
  1173. * Set the query to send to the XML-RPC Server
  1174. * @since 0.1.0
  1175. */
  1176. function query()
  1177. {
  1178. $args = func_get_args();
  1179. $method = array_shift($args);
  1180. $request = new IXR_Request($method, $args);
  1181. $length = $request->getLength();
  1182. $xml = $request->getXml();
  1183.  
  1184. if ($this->debug) {
  1185. echo '<pre>'.htmlspecialchars($xml)."\n</pre>\n\n";
  1186. }
  1187.  
  1188. //This is where we deviate from the normal query()
  1189. //Rather than open a normal sock, we will actually use the cURL
  1190. //extensions to make the calls, and handle the SSL stuff.
  1191.  
  1192. //Since 04Aug2004 (0.1.3) - Need to include the port (duh...)
  1193. //Since 06Oct2004 (0.1.4) - Need to include the colon!!!
  1194. // (I swear I've fixed this before... ESP in live... But anyhu...)
  1195. $curl=curl_init('https://' . $this->server . ':' . $this->port . $this->path);
  1196. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  1197.  
  1198. //Since 23Jun2004 (0.1.2) - Made timeout a class field
  1199. curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout);
  1200.  
  1201. if ($this->debug) {
  1202. curl_setopt($curl, CURLOPT_VERBOSE, 1);
  1203. }
  1204.  
  1205. curl_setopt($curl, CURLOPT_HEADER, 1);
  1206. curl_setopt($curl, CURLOPT_POST, 1);
  1207. curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
  1208. curl_setopt($curl, CURLOPT_PORT, $this->port);
  1209. curl_setopt($curl, CURLOPT_HTTPHEADER, array(
  1210. "Content-Type: text/xml",
  1211. "Content-length: {$length}"));
  1212.  
  1213. // Process the SSL certificates, etc. to use
  1214. if (!($this->_certFile === false)) {
  1215. // We have a certificate file set, so add these to the cURL handler
  1216. curl_setopt($curl, CURLOPT_SSLCERT, $this->_certFile);
  1217. curl_setopt($curl, CURLOPT_SSLKEY, $this->_keyFile);
  1218.  
  1219. if ($this->debug) {
  1220. echo "SSL Cert at : " . $this->_certFile . "\n";
  1221. echo "SSL Key at : " . $this->_keyFile . "\n";
  1222. }
  1223.  
  1224. // See if we need to give a passphrase
  1225. if (!($this->_passphrase === '')) {
  1226. curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $this->_passphrase);
  1227. }
  1228.  
  1229. if ($this->_caFile === false) {
  1230. // Don't verify their certificate, as we don't have a CA to verify against
  1231. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
  1232. } else {
  1233. // Verify against a CA
  1234. curl_setopt($curl, CURLOPT_CAINFO, $this->_caFile);
  1235. }
  1236. }
  1237.  
  1238. // Call cURL to do it's stuff and return us the content
  1239. $contents = curl_exec($curl);
  1240. curl_close($curl);
  1241.  
  1242. // Check for 200 Code in $contents
  1243. if (!strstr($contents, '200 OK')) {
  1244. //There was no "200 OK" returned - we failed
  1245. $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
  1246. return false;
  1247. }
  1248.  
  1249. if ($this->debug) {
  1250. echo '<pre>'.htmlspecialchars($contents)."\n</pre>\n\n";
  1251. }
  1252. // Now parse what we've got back
  1253. // Since 20Jun2004 (0.1.1) - We need to remove the headers first
  1254. // Why I have only just found this, I will never know...
  1255. // So, remove everything before the first <
  1256. $contents = substr($contents,strpos($contents, '<'));
  1257.  
  1258. $this->message = new IXR_Message($contents);
  1259. if (!$this->message->parse()) {
  1260. // XML error
  1261. $this->error = new IXR_Error(-32700, 'parse error. not well formed');
  1262. return false;
  1263. }
  1264. // Is the message a fault?
  1265. if ($this->message->messageType == 'fault') {
  1266. $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
  1267. return false;
  1268. }
  1269.  
  1270. // Message must be OK
  1271. return true;
  1272. }
  1273. }
  1274.  
  1275. /**
  1276. * Extension of the {@link IXR_Server} class to easily wrap objects.
  1277. *
  1278. * Class is designed to extend the existing XML-RPC server to allow the
  1279. * presentation of methods from a variety of different objects via an
  1280. * XML-RPC server.
  1281. * It is intended to assist in organization of your XML-RPC methods by allowing
  1282. * you to "write once" in your existing model classes and present them.
  1283. *
  1284. * @author Jason Stirk <jstirk@gmm.com.au>
  1285. * @version 1.0.1 19Apr2005 17:40 +0800
  1286. * @copyright Copyright (c) 2005 Jason Stirk
  1287. * @package IXR
  1288. */
  1289. class IXR_ClassServer extends IXR_Server
  1290. {
  1291. var $_objects;
  1292. var $_delim;
  1293.  
  1294. function IXR_ClassServer($delim = '.', $wait = false)
  1295. {
  1296. $this->IXR_Server(array(), false, $wait);
  1297. $this->_delimiter = $delim;
  1298. $this->_objects = array();
  1299. }
  1300.  
  1301. function addMethod($rpcName, $functionName)
  1302. {
  1303. $this->callbacks[$rpcName] = $functionName;
  1304. }
  1305.  
  1306. function registerObject($object, $methods, $prefix=null)
  1307. {
  1308. if (is_null($prefix))
  1309. {
  1310. $prefix = get_class($object);
  1311. }
  1312. $this->_objects[$prefix] = $object;
  1313.  
  1314. // Add to our callbacks array
  1315. foreach($methods as $method)
  1316. {
  1317. if (is_array($method))
  1318. {
  1319. $targetMethod = $method[0];
  1320. $method = $method[1];
  1321. }
  1322. else
  1323. {
  1324. $targetMethod = $method;
  1325. }
  1326. $this->callbacks[$prefix . $this->_delimiter . $method]=array($prefix, $targetMethod);
  1327. }
  1328. }
  1329.  
  1330. function call($methodname, $args)
  1331. {
  1332. if (!$this->hasMethod($methodname)) {
  1333. return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
  1334. }
  1335. $method = $this->callbacks[$methodname];
  1336.  
  1337. // Perform the callback and send the response
  1338. if (count($args) == 1) {
  1339. // If only one paramater just send that instead of the whole array
  1340. $args = $args[0];
  1341. }
  1342.  
  1343. // See if this method comes from one of our objects or maybe self
  1344. if (is_array($method) || (substr($method, 0, 5) == 'this:')) {
  1345. if (is_array($method)) {
  1346. $object=$this->_objects[$method[0]];
  1347. $method=$method[1];
  1348. } else {
  1349. $object=$this;
  1350. $method = substr($method, 5);
  1351. }
  1352.  
  1353. // It's a class method - check it exists
  1354. if (!method_exists($object, $method)) {
  1355. return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.');
  1356. }
  1357.  
  1358. // Call the method
  1359. $result = $object->$method($args);
  1360. } else {
  1361. // It's a function - does it exist?
  1362. if (!function_exists($method)) {
  1363. return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
  1364. }
  1365.  
  1366. // Call the function
  1367. $result = $method($args);
  1368. }
  1369. return $result;
  1370. }
  1371. }
Add Comment
Please, Sign In to add comment