You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1920 lines
40 KiB

7 years ago
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 1.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. if ( ! function_exists('xml_parser_create'))
  40. {
  41. show_error('Your PHP installation does not support XML');
  42. }
  43. // ------------------------------------------------------------------------
  44. /**
  45. * XML-RPC request handler class
  46. *
  47. * @package CodeIgniter
  48. * @subpackage Libraries
  49. * @category XML-RPC
  50. * @author EllisLab Dev Team
  51. * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html
  52. */
  53. class CI_Xmlrpc {
  54. /**
  55. * Debug flag
  56. *
  57. * @var bool
  58. */
  59. public $debug = FALSE;
  60. /**
  61. * I4 data type
  62. *
  63. * @var string
  64. */
  65. public $xmlrpcI4 = 'i4';
  66. /**
  67. * Integer data type
  68. *
  69. * @var string
  70. */
  71. public $xmlrpcInt = 'int';
  72. /**
  73. * Boolean data type
  74. *
  75. * @var string
  76. */
  77. public $xmlrpcBoolean = 'boolean';
  78. /**
  79. * Double data type
  80. *
  81. * @var string
  82. */
  83. public $xmlrpcDouble = 'double';
  84. /**
  85. * String data type
  86. *
  87. * @var string
  88. */
  89. public $xmlrpcString = 'string';
  90. /**
  91. * DateTime format
  92. *
  93. * @var string
  94. */
  95. public $xmlrpcDateTime = 'dateTime.iso8601';
  96. /**
  97. * Base64 data type
  98. *
  99. * @var string
  100. */
  101. public $xmlrpcBase64 = 'base64';
  102. /**
  103. * Array data type
  104. *
  105. * @var string
  106. */
  107. public $xmlrpcArray = 'array';
  108. /**
  109. * Struct data type
  110. *
  111. * @var string
  112. */
  113. public $xmlrpcStruct = 'struct';
  114. /**
  115. * Data types list
  116. *
  117. * @var array
  118. */
  119. public $xmlrpcTypes = array();
  120. /**
  121. * Valid parents list
  122. *
  123. * @var array
  124. */
  125. public $valid_parents = array();
  126. /**
  127. * Response error numbers list
  128. *
  129. * @var array
  130. */
  131. public $xmlrpcerr = array();
  132. /**
  133. * Response error messages list
  134. *
  135. * @var string[]
  136. */
  137. public $xmlrpcstr = array();
  138. /**
  139. * Encoding charset
  140. *
  141. * @var string
  142. */
  143. public $xmlrpc_defencoding = 'UTF-8';
  144. /**
  145. * XML-RPC client name
  146. *
  147. * @var string
  148. */
  149. public $xmlrpcName = 'XML-RPC for CodeIgniter';
  150. /**
  151. * XML-RPC version
  152. *
  153. * @var string
  154. */
  155. public $xmlrpcVersion = '1.1';
  156. /**
  157. * Start of user errors
  158. *
  159. * @var int
  160. */
  161. public $xmlrpcerruser = 800;
  162. /**
  163. * Start of XML parse errors
  164. *
  165. * @var int
  166. */
  167. public $xmlrpcerrxml = 100;
  168. /**
  169. * Backslash replacement value
  170. *
  171. * @var string
  172. */
  173. public $xmlrpc_backslash = '';
  174. /**
  175. * XML-RPC Client object
  176. *
  177. * @var object
  178. */
  179. public $client;
  180. /**
  181. * XML-RPC Method name
  182. *
  183. * @var string
  184. */
  185. public $method;
  186. /**
  187. * XML-RPC Data
  188. *
  189. * @var array
  190. */
  191. public $data;
  192. /**
  193. * XML-RPC Message
  194. *
  195. * @var string
  196. */
  197. public $message = '';
  198. /**
  199. * Request error message
  200. *
  201. * @var string
  202. */
  203. public $error = '';
  204. /**
  205. * XML-RPC result object
  206. *
  207. * @var object
  208. */
  209. public $result;
  210. /**
  211. * XML-RPC Response
  212. *
  213. * @var array
  214. */
  215. public $response = array(); // Response from remote server
  216. /**
  217. * XSS Filter flag
  218. *
  219. * @var bool
  220. */
  221. public $xss_clean = TRUE;
  222. // --------------------------------------------------------------------
  223. /**
  224. * Constructor
  225. *
  226. * Initializes property default values
  227. *
  228. * @param array $config
  229. * @return void
  230. */
  231. public function __construct($config = array())
  232. {
  233. $this->xmlrpc_backslash = chr(92).chr(92);
  234. // Types for info sent back and forth
  235. $this->xmlrpcTypes = array(
  236. $this->xmlrpcI4 => '1',
  237. $this->xmlrpcInt => '1',
  238. $this->xmlrpcBoolean => '1',
  239. $this->xmlrpcString => '1',
  240. $this->xmlrpcDouble => '1',
  241. $this->xmlrpcDateTime => '1',
  242. $this->xmlrpcBase64 => '1',
  243. $this->xmlrpcArray => '2',
  244. $this->xmlrpcStruct => '3'
  245. );
  246. // Array of Valid Parents for Various XML-RPC elements
  247. $this->valid_parents = array('BOOLEAN' => array('VALUE'),
  248. 'I4' => array('VALUE'),
  249. 'INT' => array('VALUE'),
  250. 'STRING' => array('VALUE'),
  251. 'DOUBLE' => array('VALUE'),
  252. 'DATETIME.ISO8601' => array('VALUE'),
  253. 'BASE64' => array('VALUE'),
  254. 'ARRAY' => array('VALUE'),
  255. 'STRUCT' => array('VALUE'),
  256. 'PARAM' => array('PARAMS'),
  257. 'METHODNAME' => array('METHODCALL'),
  258. 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
  259. 'MEMBER' => array('STRUCT'),
  260. 'NAME' => array('MEMBER'),
  261. 'DATA' => array('ARRAY'),
  262. 'FAULT' => array('METHODRESPONSE'),
  263. 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
  264. );
  265. // XML-RPC Responses
  266. $this->xmlrpcerr['unknown_method'] = '1';
  267. $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
  268. $this->xmlrpcerr['invalid_return'] = '2';
  269. $this->xmlrpcstr['invalid_return'] = 'The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.';
  270. $this->xmlrpcerr['incorrect_params'] = '3';
  271. $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
  272. $this->xmlrpcerr['introspect_unknown'] = '4';
  273. $this->xmlrpcstr['introspect_unknown'] = 'Cannot inspect signature for request: method unknown';
  274. $this->xmlrpcerr['http_error'] = '5';
  275. $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
  276. $this->xmlrpcerr['no_data'] = '6';
  277. $this->xmlrpcstr['no_data'] = 'No data received from server.';
  278. $this->initialize($config);
  279. log_message('info', 'XML-RPC Class Initialized');
  280. }
  281. // --------------------------------------------------------------------
  282. /**
  283. * Initialize
  284. *
  285. * @param array $config
  286. * @return void
  287. */
  288. public function initialize($config = array())
  289. {
  290. if (count($config) > 0)
  291. {
  292. foreach ($config as $key => $val)
  293. {
  294. if (isset($this->$key))
  295. {
  296. $this->$key = $val;
  297. }
  298. }
  299. }
  300. }
  301. // --------------------------------------------------------------------
  302. /**
  303. * Parse server URL
  304. *
  305. * @param string $url
  306. * @param int $port
  307. * @param string $proxy
  308. * @param int $proxy_port
  309. * @return void
  310. */
  311. public function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080)
  312. {
  313. if (stripos($url, 'http') !== 0)
  314. {
  315. $url = 'http://'.$url;
  316. }
  317. $parts = parse_url($url);
  318. if (isset($parts['user'], $parts['pass']))
  319. {
  320. $parts['host'] = $parts['user'].':'.$parts['pass'].'@'.$parts['host'];
  321. }
  322. $path = isset($parts['path']) ? $parts['path'] : '/';
  323. if ( ! empty($parts['query']))
  324. {
  325. $path .= '?'.$parts['query'];
  326. }
  327. $this->client = new XML_RPC_Client($path, $parts['host'], $port, $proxy, $proxy_port);
  328. }
  329. // --------------------------------------------------------------------
  330. /**
  331. * Set Timeout
  332. *
  333. * @param int $seconds
  334. * @return void
  335. */
  336. public function timeout($seconds = 5)
  337. {
  338. if ($this->client !== NULL && is_int($seconds))
  339. {
  340. $this->client->timeout = $seconds;
  341. }
  342. }
  343. // --------------------------------------------------------------------
  344. /**
  345. * Set Methods
  346. *
  347. * @param string $function Method name
  348. * @return void
  349. */
  350. public function method($function)
  351. {
  352. $this->method = $function;
  353. }
  354. // --------------------------------------------------------------------
  355. /**
  356. * Take Array of Data and Create Objects
  357. *
  358. * @param array $incoming
  359. * @return void
  360. */
  361. public function request($incoming)
  362. {
  363. if ( ! is_array($incoming))
  364. {
  365. // Send Error
  366. return;
  367. }
  368. $this->data = array();
  369. foreach ($incoming as $key => $value)
  370. {
  371. $this->data[$key] = $this->values_parsing($value);
  372. }
  373. }
  374. // --------------------------------------------------------------------
  375. /**
  376. * Set Debug
  377. *
  378. * @param bool $flag
  379. * @return void
  380. */
  381. public function set_debug($flag = TRUE)
  382. {
  383. $this->debug = ($flag === TRUE);
  384. }
  385. // --------------------------------------------------------------------
  386. /**
  387. * Values Parsing
  388. *
  389. * @param mixed $value
  390. * @return object
  391. */
  392. public function values_parsing($value)
  393. {
  394. if (is_array($value) && array_key_exists(0, $value))
  395. {
  396. if ( ! isset($value[1], $this->xmlrpcTypes[$value[1]]))
  397. {
  398. $temp = new XML_RPC_Values($value[0], (is_array($value[0]) ? 'array' : 'string'));
  399. }
  400. else
  401. {
  402. if (is_array($value[0]) && ($value[1] === 'struct' OR $value[1] === 'array'))
  403. {
  404. foreach (array_keys($value[0]) as $k)
  405. {
  406. $value[0][$k] = $this->values_parsing($value[0][$k]);
  407. }
  408. }
  409. $temp = new XML_RPC_Values($value[0], $value[1]);
  410. }
  411. }
  412. else
  413. {
  414. $temp = new XML_RPC_Values($value, 'string');
  415. }
  416. return $temp;
  417. }
  418. // --------------------------------------------------------------------
  419. /**
  420. * Sends XML-RPC Request
  421. *
  422. * @return bool
  423. */
  424. public function send_request()
  425. {
  426. $this->message = new XML_RPC_Message($this->method, $this->data);
  427. $this->message->debug = $this->debug;
  428. if ( ! $this->result = $this->client->send($this->message) OR ! is_object($this->result->val))
  429. {
  430. $this->error = $this->result->errstr;
  431. return FALSE;
  432. }
  433. $this->response = $this->result->decode();
  434. return TRUE;
  435. }
  436. // --------------------------------------------------------------------
  437. /**
  438. * Returns Error
  439. *
  440. * @return string
  441. */
  442. public function display_error()
  443. {
  444. return $this->error;
  445. }
  446. // --------------------------------------------------------------------
  447. /**
  448. * Returns Remote Server Response
  449. *
  450. * @return string
  451. */
  452. public function display_response()
  453. {
  454. return $this->response;
  455. }
  456. // --------------------------------------------------------------------
  457. /**
  458. * Sends an Error Message for Server Request
  459. *
  460. * @param int $number
  461. * @param string $message
  462. * @return object
  463. */
  464. public function send_error_message($number, $message)
  465. {
  466. return new XML_RPC_Response(0, $number, $message);
  467. }
  468. // --------------------------------------------------------------------
  469. /**
  470. * Send Response for Server Request
  471. *
  472. * @param array $response
  473. * @return object
  474. */
  475. public function send_response($response)
  476. {
  477. // $response should be array of values, which will be parsed
  478. // based on their data and type into a valid group of XML-RPC values
  479. return new XML_RPC_Response($this->values_parsing($response));
  480. }
  481. } // END XML_RPC Class
  482. /**
  483. * XML-RPC Client class
  484. *
  485. * @category XML-RPC
  486. * @author EllisLab Dev Team
  487. * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html
  488. */
  489. class XML_RPC_Client extends CI_Xmlrpc
  490. {
  491. /**
  492. * Path
  493. *
  494. * @var string
  495. */
  496. public $path = '';
  497. /**
  498. * Server hostname
  499. *
  500. * @var string
  501. */
  502. public $server = '';
  503. /**
  504. * Server port
  505. *
  506. * @var int
  507. */
  508. public $port = 80;
  509. /**
  510. *
  511. * Server username
  512. *
  513. * @var string
  514. */
  515. public $username;
  516. /**
  517. * Server password
  518. *
  519. * @var string
  520. */
  521. public $password;
  522. /**
  523. * Proxy hostname
  524. *
  525. * @var string
  526. */
  527. public $proxy = FALSE;
  528. /**
  529. * Proxy port
  530. *
  531. * @var int
  532. */
  533. public $proxy_port = 8080;
  534. /**
  535. * Error number
  536. *
  537. * @var string
  538. */
  539. public $errno = '';
  540. /**
  541. * Error message
  542. *
  543. * @var string
  544. */
  545. public $errstring = '';
  546. /**
  547. * Timeout in seconds
  548. *
  549. * @var int
  550. */
  551. public $timeout = 5;
  552. /**
  553. * No Multicall flag
  554. *
  555. * @var bool
  556. */
  557. public $no_multicall = FALSE;
  558. // --------------------------------------------------------------------
  559. /**
  560. * Constructor
  561. *
  562. * @param string $path
  563. * @param object $server
  564. * @param int $port
  565. * @param string $proxy
  566. * @param int $proxy_port
  567. * @return void
  568. */
  569. public function __construct($path, $server, $port = 80, $proxy = FALSE, $proxy_port = 8080)
  570. {
  571. parent::__construct();
  572. $url = parse_url('http://'.$server);
  573. if (isset($url['user'], $url['pass']))
  574. {
  575. $this->username = $url['user'];
  576. $this->password = $url['pass'];
  577. }
  578. $this->port = $port;
  579. $this->server = $url['host'];
  580. $this->path = $path;
  581. $this->proxy = $proxy;
  582. $this->proxy_port = $proxy_port;
  583. }
  584. // --------------------------------------------------------------------
  585. /**
  586. * Send message
  587. *
  588. * @param mixed $msg
  589. * @return object
  590. */
  591. public function send($msg)
  592. {
  593. if (is_array($msg))
  594. {
  595. // Multi-call disabled
  596. return new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'], $this->xmlrpcstr['multicall_recursion']);
  597. }
  598. return $this->sendPayload($msg);
  599. }
  600. // --------------------------------------------------------------------
  601. /**
  602. * Send payload
  603. *
  604. * @param object $msg
  605. * @return object
  606. */
  607. public function sendPayload($msg)
  608. {
  609. if ($this->proxy === FALSE)
  610. {
  611. $server = $this->server;
  612. $port = $this->port;
  613. }
  614. else
  615. {
  616. $server = $this->proxy;
  617. $port = $this->proxy_port;
  618. }
  619. $fp = @fsockopen($server, $port, $this->errno, $this->errstring, $this->timeout);
  620. if ( ! is_resource($fp))
  621. {
  622. error_log($this->xmlrpcstr['http_error']);
  623. return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
  624. }
  625. if (empty($msg->payload))
  626. {
  627. // $msg = XML_RPC_Messages
  628. $msg->createPayload();
  629. }
  630. $r = "\r\n";
  631. $op = 'POST '.$this->path.' HTTP/1.0'.$r
  632. .'Host: '.$this->server.$r
  633. .'Content-Type: text/xml'.$r
  634. .(isset($this->username, $this->password) ? 'Authorization: Basic '.base64_encode($this->username.':'.$this->password).$r : '')
  635. .'User-Agent: '.$this->xmlrpcName.$r
  636. .'Content-Length: '.strlen($msg->payload).$r.$r
  637. .$msg->payload;
  638. stream_set_timeout($fp, $this->timeout); // set timeout for subsequent operations
  639. for ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result)
  640. {
  641. if (($result = fwrite($fp, substr($op, $written))) === FALSE)
  642. {
  643. break;
  644. }
  645. // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951
  646. elseif ($result === 0)
  647. {
  648. if ($timestamp === 0)
  649. {
  650. $timestamp = time();
  651. }
  652. elseif ($timestamp < (time() - $this->timeout))
  653. {
  654. $result = FALSE;
  655. break;
  656. }
  657. }
  658. else
  659. {
  660. $timestamp = 0;
  661. }
  662. }
  663. if ($result === FALSE)
  664. {
  665. error_log($this->xmlrpcstr['http_error']);
  666. return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
  667. }
  668. $resp = $msg->parseResponse($fp);
  669. fclose($fp);
  670. return $resp;
  671. }
  672. } // END XML_RPC_Client Class
  673. /**
  674. * XML-RPC Response class
  675. *
  676. * @category XML-RPC
  677. * @author EllisLab Dev Team
  678. * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html
  679. */
  680. class XML_RPC_Response
  681. {
  682. /**
  683. * Value
  684. *
  685. * @var mixed
  686. */
  687. public $val = 0;
  688. /**
  689. * Error number
  690. *
  691. * @var int
  692. */
  693. public $errno = 0;
  694. /**
  695. * Error message
  696. *
  697. * @var string
  698. */
  699. public $errstr = '';
  700. /**
  701. * Headers list
  702. *
  703. * @var array
  704. */
  705. public $headers = array();
  706. /**
  707. * XSS Filter flag
  708. *
  709. * @var bool
  710. */
  711. public $xss_clean = TRUE;
  712. // --------------------------------------------------------------------
  713. /**
  714. * Constructor
  715. *
  716. * @param mixed $val
  717. * @param int $code
  718. * @param string $fstr
  719. * @return void
  720. */
  721. public function __construct($val, $code = 0, $fstr = '')
  722. {
  723. if ($code !== 0)
  724. {
  725. // error
  726. $this->errno = $code;
  727. $this->errstr = htmlspecialchars($fstr,
  728. (is_php('5.4') ? ENT_XML1 | ENT_NOQUOTES : ENT_NOQUOTES),
  729. 'UTF-8');
  730. }
  731. elseif ( ! is_object($val))
  732. {
  733. // programmer error, not an object
  734. error_log("Invalid type '".gettype($val)."' (value: ".$val.') passed to XML_RPC_Response. Defaulting to empty value.');
  735. $this->val = new XML_RPC_Values();
  736. }
  737. else
  738. {
  739. $this->val = $val;
  740. }
  741. }
  742. // --------------------------------------------------------------------
  743. /**
  744. * Fault code
  745. *
  746. * @return int
  747. */
  748. public function faultCode()
  749. {
  750. return $this->errno;
  751. }
  752. // --------------------------------------------------------------------
  753. /**
  754. * Fault string
  755. *
  756. * @return string
  757. */
  758. public function faultString()
  759. {
  760. return $this->errstr;
  761. }
  762. // --------------------------------------------------------------------
  763. /**
  764. * Value
  765. *
  766. * @return mixed
  767. */
  768. public function value()
  769. {
  770. return $this->val;
  771. }
  772. // --------------------------------------------------------------------
  773. /**
  774. * Prepare response
  775. *
  776. * @return string xml
  777. */
  778. public function prepare_response()
  779. {
  780. return "<methodResponse>\n"
  781. .($this->errno
  782. ? '<fault>
  783. <value>
  784. <struct>
  785. <member>
  786. <name>faultCode</name>
  787. <value><int>'.$this->errno.'</int></value>
  788. </member>
  789. <member>
  790. <name>faultString</name>
  791. <value><string>'.$this->errstr.'</string></value>
  792. </member>
  793. </struct>
  794. </value>
  795. </fault>'
  796. : "<params>\n<param>\n".$this->val->serialize_class()."</param>\n</params>")
  797. ."\n</methodResponse>";
  798. }
  799. // --------------------------------------------------------------------
  800. /**
  801. * Decode
  802. *
  803. * @param mixed $array
  804. * @return array
  805. */
  806. public function decode($array = NULL)
  807. {
  808. $CI =& get_instance();
  809. if (is_array($array))
  810. {
  811. foreach ($array as $key => &$value)
  812. {
  813. if (is_array($value))
  814. {
  815. $array[$key] = $this->decode($value);
  816. }
  817. elseif ($this->xss_clean)
  818. {
  819. $array[$key] = $CI->security->xss_clean($value);
  820. }
  821. }
  822. return $array;
  823. }
  824. $result = $this->xmlrpc_decoder($this->val);
  825. if (is_array($result))
  826. {
  827. $result = $this->decode($result);
  828. }
  829. elseif ($this->xss_clean)
  830. {
  831. $result = $CI->security->xss_clean($result);
  832. }
  833. return $result;
  834. }
  835. // --------------------------------------------------------------------
  836. /**
  837. * XML-RPC Object to PHP Types
  838. *
  839. * @param object
  840. * @return array
  841. */
  842. public function xmlrpc_decoder($xmlrpc_val)
  843. {
  844. $kind = $xmlrpc_val->kindOf();
  845. if ($kind === 'scalar')
  846. {
  847. return $xmlrpc_val->scalarval();
  848. }
  849. elseif ($kind === 'array')
  850. {
  851. reset($xmlrpc_val->me);
  852. $b = current($xmlrpc_val->me);
  853. $arr = array();
  854. for ($i = 0, $size = count($b); $i < $size; $i++)
  855. {
  856. $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
  857. }
  858. return $arr;
  859. }
  860. elseif ($kind === 'struct')
  861. {
  862. reset($xmlrpc_val->me['struct']);
  863. $arr = array();
  864. foreach ($xmlrpc_val->me['struct'] as $key => &$value)
  865. {
  866. $arr[$key] = $this->xmlrpc_decoder($value);
  867. }
  868. return $arr;
  869. }
  870. }
  871. // --------------------------------------------------------------------
  872. /**
  873. * ISO-8601 time to server or UTC time
  874. *
  875. * @param string
  876. * @param bool
  877. * @return int unix timestamp
  878. */
  879. public function iso8601_decode($time, $utc = FALSE)
  880. {
  881. // Return a time in the localtime, or UTC
  882. $t = 0;
  883. if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
  884. {
  885. $fnc = ($utc === TRUE) ? 'gmmktime' : 'mktime';
  886. $t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  887. }
  888. return $t;
  889. }
  890. } // END XML_RPC_Response Class
  891. /**
  892. * XML-RPC Message class
  893. *
  894. * @category XML-RPC
  895. * @author EllisLab Dev Team
  896. * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html
  897. */
  898. class XML_RPC_Message extends CI_Xmlrpc
  899. {
  900. /**
  901. * Payload
  902. *
  903. * @var string
  904. */
  905. public $payload;
  906. /**
  907. * Method name
  908. *
  909. * @var string
  910. */
  911. public $method_name;
  912. /**
  913. * Parameter list
  914. *
  915. * @var array
  916. */
  917. public $params = array();
  918. /**
  919. * XH?
  920. *
  921. * @var array
  922. */
  923. public $xh = array();
  924. // --------------------------------------------------------------------
  925. /**
  926. * Constructor
  927. *
  928. * @param string $method
  929. * @param array $pars
  930. * @return void
  931. */
  932. public function __construct($method, $pars = FALSE)
  933. {
  934. parent::__construct();
  935. $this->method_name = $method;
  936. if (is_array($pars) && count($pars) > 0)
  937. {
  938. for ($i = 0, $c = count($pars); $i < $c; $i++)
  939. {
  940. // $pars[$i] = XML_RPC_Values
  941. $this->params[] = $pars[$i];
  942. }
  943. }
  944. }
  945. // --------------------------------------------------------------------
  946. /**
  947. * Create Payload to Send
  948. *
  949. * @return void
  950. */
  951. public function createPayload()
  952. {
  953. $this->payload = '<?xml version="1.0"?'.">\r\n<methodCall>\r\n"
  954. .'<methodName>'.$this->method_name."</methodName>\r\n"
  955. ."<params>\r\n";
  956. for ($i = 0, $c = count($this->params); $i < $c; $i++)
  957. {
  958. // $p = XML_RPC_Values
  959. $p = $this->params[$i];
  960. $this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n";
  961. }
  962. $this->payload .= "</params>\r\n</methodCall>\r\n";
  963. }
  964. // --------------------------------------------------------------------
  965. /**
  966. * Parse External XML-RPC Server's Response
  967. *
  968. * @param resource
  969. * @return object
  970. */
  971. public function parseResponse($fp)
  972. {
  973. $data = '';
  974. while ($datum = fread($fp, 4096))
  975. {
  976. $data .= $datum;
  977. }
  978. // Display HTTP content for debugging
  979. if ($this->debug === TRUE)
  980. {
  981. echo "<pre>---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n</pre>";
  982. }
  983. // Check for data
  984. if ($data === '')
  985. {
  986. error_log($this->xmlrpcstr['no_data']);
  987. return new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
  988. }
  989. // Check for HTTP 200 Response
  990. if (strpos($data, 'HTTP') === 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
  991. {
  992. $errstr = substr($data, 0, strpos($data, "\n")-1);
  993. return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error'].' ('.$errstr.')');
  994. }
  995. //-------------------------------------
  996. // Create and Set Up XML Parser
  997. //-------------------------------------
  998. $parser = xml_parser_create($this->xmlrpc_defencoding);
  999. $pname = (string) $parser;
  1000. $this->xh[$pname] = array(
  1001. 'isf' => 0,
  1002. 'ac' => '',
  1003. 'headers' => array(),
  1004. 'stack' => array(),
  1005. 'valuestack' => array(),
  1006. 'isf_reason' => 0
  1007. );
  1008. xml_set_object($parser, $this);
  1009. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE);
  1010. xml_set_element_handler($parser, 'open_tag', 'closing_tag');
  1011. xml_set_character_data_handler($parser, 'character_data');
  1012. //xml_set_default_handler($parser, 'default_handler');
  1013. // Get headers
  1014. $lines = explode("\r\n", $data);
  1015. while (($line = array_shift($lines)))
  1016. {
  1017. if (strlen($line) < 1)
  1018. {
  1019. break;
  1020. }
  1021. $this->xh[$pname]['headers'][] = $line;
  1022. }
  1023. $data = implode("\r\n", $lines);
  1024. // Parse XML data
  1025. if ( ! xml_parse($parser, $data, count($data)))
  1026. {
  1027. $errstr = sprintf('XML error: %s at line %d',
  1028. xml_error_string(xml_get_error_code($parser)),
  1029. xml_get_current_line_number($parser));
  1030. $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
  1031. xml_parser_free($parser);
  1032. return $r;
  1033. }
  1034. xml_parser_free($parser);
  1035. // Got ourselves some badness, it seems
  1036. if ($this->xh[$pname]['isf'] > 1)
  1037. {
  1038. if ($this->debug === TRUE)
  1039. {
  1040. echo "---Invalid Return---\n".$this->xh[$pname]['isf_reason']."---Invalid Return---\n\n";
  1041. }
  1042. return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);
  1043. }
  1044. elseif ( ! is_object($this->xh[$pname]['value']))
  1045. {
  1046. return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);
  1047. }
  1048. // Display XML content for debugging
  1049. if ($this->debug === TRUE)
  1050. {
  1051. echo '<pre>';
  1052. if (count($this->xh[$pname]['headers'] > 0))
  1053. {
  1054. echo "---HEADERS---\n";
  1055. foreach ($this->xh[$pname]['headers'] as $header)
  1056. {
  1057. echo $header."\n";
  1058. }
  1059. echo "---END HEADERS---\n\n";
  1060. }
  1061. echo "---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n---PARSED---\n";
  1062. var_dump($this->xh[$pname]['value']);
  1063. echo "\n---END PARSED---</pre>";
  1064. }
  1065. // Send response
  1066. $v = $this->xh[$pname]['value'];
  1067. if ($this->xh[$pname]['isf'])
  1068. {
  1069. $errno_v = $v->me['struct']['faultCode'];
  1070. $errstr_v = $v->me['struct']['faultString'];
  1071. $errno = $errno_v->scalarval();
  1072. if ($errno === 0)
  1073. {
  1074. // FAULT returned, errno needs to reflect that
  1075. $errno = -1;
  1076. }
  1077. $r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());
  1078. }
  1079. else
  1080. {
  1081. $r = new XML_RPC_Response($v);
  1082. }
  1083. $r->headers = $this->xh[$pname]['headers'];
  1084. return $r;
  1085. }
  1086. // --------------------------------------------------------------------
  1087. // ------------------------------------
  1088. // Begin Return Message Parsing section
  1089. // ------------------------------------
  1090. // quick explanation of components:
  1091. // ac - used to accumulate values
  1092. // isf - used to indicate a fault
  1093. // lv - used to indicate "looking for a value": implements
  1094. // the logic to allow values with no types to be strings
  1095. // params - used to store parameters in method calls
  1096. // method - used to store method name
  1097. // stack - array with parent tree of the xml element,
  1098. // used to validate the nesting of elements
  1099. // --------------------------------------------------------------------
  1100. /**
  1101. * Start Element Handler
  1102. *
  1103. * @param string
  1104. * @param string
  1105. * @return void
  1106. */
  1107. public function open_tag($the_parser, $name)
  1108. {
  1109. $the_parser = (string) $the_parser;
  1110. // If invalid nesting, then return
  1111. if ($this->xh[$the_parser]['isf'] > 1) return;
  1112. // Evaluate and check for correct nesting of XML elements
  1113. if (count($this->xh[$the_parser]['stack']) === 0)
  1114. {
  1115. if ($name !== 'METHODRESPONSE' && $name !== 'METHODCALL')
  1116. {
  1117. $this->xh[$the_parser]['isf'] = 2;
  1118. $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
  1119. return;
  1120. }
  1121. }
  1122. // not top level element: see if parent is OK
  1123. elseif ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
  1124. {
  1125. $this->xh[$the_parser]['isf'] = 2;
  1126. $this->xh[$the_parser]['isf_reason'] = 'XML-RPC element '.$name.' cannot be child of '.$this->xh[$the_parser]['stack'][0];
  1127. return;
  1128. }
  1129. switch ($name)
  1130. {
  1131. case 'STRUCT':
  1132. case 'ARRAY':
  1133. // Creates array for child elements
  1134. $cur_val = array('value' => array(), 'type' => $name);
  1135. array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
  1136. break;
  1137. case 'METHODNAME':
  1138. case 'NAME':
  1139. $this->xh[$the_parser]['ac'] = '';
  1140. break;
  1141. case 'FAULT':
  1142. $this->xh[$the_parser]['isf'] = 1;
  1143. break;
  1144. case 'PARAM':
  1145. $this->xh[$the_parser]['value'] = NULL;
  1146. break;
  1147. case 'VALUE':
  1148. $this->xh[$the_parser]['vt'] = 'value';
  1149. $this->xh[$the_parser]['ac'] = '';
  1150. $this->xh[$the_parser]['lv'] = 1;
  1151. break;
  1152. case 'I4':
  1153. case 'INT':
  1154. case 'STRING':
  1155. case 'BOOLEAN':
  1156. case 'DOUBLE':
  1157. case 'DATETIME.ISO8601':
  1158. case 'BASE64':
  1159. if ($this->xh[$the_parser]['vt'] !== 'value')
  1160. {
  1161. //two data elements inside a value: an error occurred!
  1162. $this->xh[$the_parser]['isf'] = 2;
  1163. $this->xh[$the_parser]['isf_reason'] = 'There is a '.$name.' element following a '
  1164. .$this->xh[$the_parser]['vt'].' element inside a single value';
  1165. return;
  1166. }
  1167. $this->xh[$the_parser]['ac'] = '';
  1168. break;
  1169. case 'MEMBER':
  1170. // Set name of <member> to nothing to prevent errors later if no <name> is found
  1171. $this->xh[$the_parser]['valuestack'][0]['name'] = '';
  1172. // Set NULL value to check to see if value passed for this param/member
  1173. $this->xh[$the_parser]['value'] = NULL;
  1174. break;
  1175. case 'DATA':
  1176. case 'METHODCALL':
  1177. case 'METHODRESPONSE':
  1178. case 'PARAMS':
  1179. // valid elements that add little to processing
  1180. break;
  1181. default:
  1182. /// An Invalid Element is Found, so we have trouble
  1183. $this->xh[$the_parser]['isf'] = 2;
  1184. $this->xh[$the_parser]['isf_reason'] = 'Invalid XML-RPC element found: '.$name;
  1185. break;
  1186. }
  1187. // Add current element name to stack, to allow validation of nesting
  1188. array_unshift($this->xh[$the_parser]['stack'], $name);
  1189. $name === 'VALUE' OR $this->xh[$the_parser]['lv'] = 0;
  1190. }
  1191. // --------------------------------------------------------------------
  1192. /**
  1193. * End Element Handler
  1194. *
  1195. * @param string
  1196. * @param string
  1197. * @return void
  1198. */
  1199. public function closing_tag($the_parser, $name)
  1200. {
  1201. $the_parser = (string) $the_parser;
  1202. if ($this->xh[$the_parser]['isf'] > 1) return;
  1203. // Remove current element from stack and set variable
  1204. // NOTE: If the XML validates, then we do not have to worry about
  1205. // the opening and closing of elements. Nesting is checked on the opening
  1206. // tag so we be safe there as well.
  1207. $curr_elem = array_shift($this->xh[$the_parser]['stack']);
  1208. switch ($name)
  1209. {
  1210. case 'STRUCT':
  1211. case 'ARRAY':
  1212. $cur_val = array_shift($this->xh[$the_parser]['valuestack']);
  1213. $this->xh[$the_parser]['value'] = isset($cur_val['values']) ? $cur_val['values'] : array();
  1214. $this->xh[$the_parser]['vt'] = strtolower($name);
  1215. break;
  1216. case 'NAME':
  1217. $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
  1218. break;
  1219. case 'BOOLEAN':
  1220. case 'I4':
  1221. case 'INT':
  1222. case 'STRING':
  1223. case 'DOUBLE':
  1224. case 'DATETIME.ISO8601':
  1225. case 'BASE64':
  1226. $this->xh[$the_parser]['vt'] = strtolower($name);
  1227. if ($name === 'STRING')
  1228. {
  1229. $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
  1230. }
  1231. elseif ($name === 'DATETIME.ISO8601')
  1232. {
  1233. $this->xh[$the_parser]['vt'] = $this->xmlrpcDateTime;
  1234. $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
  1235. }
  1236. elseif ($name === 'BASE64')
  1237. {
  1238. $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
  1239. }
  1240. elseif ($name === 'BOOLEAN')
  1241. {
  1242. // Translated BOOLEAN values to TRUE AND FALSE
  1243. $this->xh[$the_parser]['value'] = (bool) $this->xh[$the_parser]['ac'];
  1244. }
  1245. elseif ($name=='DOUBLE')
  1246. {
  1247. // we have a DOUBLE
  1248. // we must check that only 0123456789-.<space> are characters here
  1249. $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac'])
  1250. ? (float) $this->xh[$the_parser]['ac']
  1251. : 'ERROR_NON_NUMERIC_FOUND';
  1252. }
  1253. else
  1254. {
  1255. // we have an I4/INT
  1256. // we must check that only 0123456789-<space> are characters here
  1257. $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac'])
  1258. ? (int) $this->xh[$the_parser]['ac']
  1259. : 'ERROR_NON_NUMERIC_FOUND';
  1260. }
  1261. $this->xh[$the_parser]['ac'] = '';
  1262. $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
  1263. break;
  1264. case 'VALUE':
  1265. // This if() detects if no scalar was inside <VALUE></VALUE>
  1266. if ($this->xh[$the_parser]['vt'] == 'value')
  1267. {
  1268. $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
  1269. $this->xh[$the_parser]['vt'] = $this->xmlrpcString;
  1270. }
  1271. // build the XML-RPC value out of the data received, and substitute it
  1272. $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
  1273. if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] === 'ARRAY')
  1274. {
  1275. // Array
  1276. $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
  1277. }
  1278. else
  1279. {
  1280. // Struct
  1281. $this->xh[$the_parser]['value'] = $temp;
  1282. }
  1283. break;
  1284. case 'MEMBER':
  1285. $this->xh[$the_parser]['ac'] = '';
  1286. // If value add to array in the stack for the last element built
  1287. if ($this->xh[$the_parser]['value'])
  1288. {
  1289. $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
  1290. }
  1291. break;
  1292. case 'DATA':
  1293. $this->xh[$the_parser]['ac'] = '';
  1294. break;
  1295. case 'PARAM':
  1296. if ($this->xh[$the_parser]['value'])
  1297. {
  1298. $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
  1299. }
  1300. break;
  1301. case 'METHODNAME':
  1302. $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);
  1303. break;
  1304. case 'PARAMS':
  1305. case 'FAULT':
  1306. case 'METHODCALL':
  1307. case 'METHORESPONSE':
  1308. // We're all good kids with nuthin' to do
  1309. break;
  1310. default:
  1311. // End of an Invalid Element. Taken care of during the opening tag though
  1312. break;
  1313. }
  1314. }
  1315. // --------------------------------------------------------------------
  1316. /**
  1317. * Parse character data
  1318. *
  1319. * @param string
  1320. * @param string
  1321. * @return void
  1322. */
  1323. public function character_data($the_parser, $data)
  1324. {
  1325. $the_parser = (string) $the_parser;
  1326. if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
  1327. // If a value has not been found
  1328. if ($this->xh[$the_parser]['lv'] !== 3)
  1329. {
  1330. if ($this->xh[$the_parser]['lv'] === 1)
  1331. {
  1332. $this->xh[$the_parser]['lv'] = 2; // Found a value
  1333. }
  1334. if ( ! isset($this->xh[$the_parser]['ac']))
  1335. {
  1336. $this->xh[$the_parser]['ac'] = '';
  1337. }
  1338. $this->xh[$the_parser]['ac'] .= $data;
  1339. }
  1340. }
  1341. // --------------------------------------------------------------------
  1342. /**
  1343. * Add parameter
  1344. *
  1345. * @param mixed
  1346. * @return void
  1347. */
  1348. public function addParam($par)
  1349. {
  1350. $this->params[] = $par;
  1351. }
  1352. // --------------------------------------------------------------------
  1353. /**
  1354. * Output parameters
  1355. *
  1356. * @param array $array
  1357. * @return array
  1358. */
  1359. public function output_parameters(array $array = array())
  1360. {
  1361. $CI =& get_instance();
  1362. if ( ! empty($array))
  1363. {
  1364. foreach ($array as $key => &$value)
  1365. {
  1366. if (is_array($value))
  1367. {
  1368. $array[$key] = $this->output_parameters($value);
  1369. }
  1370. elseif ($key !== 'bits' && $this->xss_clean)
  1371. {
  1372. // 'bits' is for the MetaWeblog API image bits
  1373. // @todo - this needs to be made more general purpose
  1374. $array[$key] = $CI->security->xss_clean($value);
  1375. }
  1376. }
  1377. return $array;
  1378. }
  1379. $parameters = array();
  1380. for ($i = 0, $c = count($this->params); $i < $c; $i++)
  1381. {
  1382. $a_param = $this->decode_message($this->params[$i]);
  1383. if (is_array($a_param))
  1384. {
  1385. $parameters[] = $this->output_parameters($a_param);
  1386. }
  1387. else
  1388. {
  1389. $parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param;
  1390. }
  1391. }
  1392. return $parameters;
  1393. }
  1394. // --------------------------------------------------------------------
  1395. /**
  1396. * Decode message
  1397. *
  1398. * @param object
  1399. * @return mixed
  1400. */
  1401. public function decode_message($param)
  1402. {
  1403. $kind = $param->kindOf();
  1404. if ($kind === 'scalar')
  1405. {
  1406. return $param->scalarval();
  1407. }
  1408. elseif ($kind === 'array')
  1409. {
  1410. reset($param->me);
  1411. $b = current($param->me);
  1412. $arr = array();
  1413. for ($i = 0, $c = count($b); $i < $c; $i++)
  1414. {
  1415. $arr[] = $this->decode_message($param->me['array'][$i]);
  1416. }
  1417. return $arr;
  1418. }
  1419. elseif ($kind === 'struct')
  1420. {
  1421. reset($param->me['struct']);
  1422. $arr = array();
  1423. foreach ($param->me['struct'] as $key => &$value)
  1424. {
  1425. $arr[$key] = $this->decode_message($value);
  1426. }
  1427. return $arr;
  1428. }
  1429. }
  1430. } // END XML_RPC_Message Class
  1431. /**
  1432. * XML-RPC Values class
  1433. *
  1434. * @category XML-RPC
  1435. * @author EllisLab Dev Team
  1436. * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html
  1437. */
  1438. class XML_RPC_Values extends CI_Xmlrpc
  1439. {
  1440. /**
  1441. * Value data
  1442. *
  1443. * @var array
  1444. */
  1445. public $me = array();
  1446. /**
  1447. * Value type
  1448. *
  1449. * @var int
  1450. */
  1451. public $mytype = 0;
  1452. // --------------------------------------------------------------------
  1453. /**
  1454. * Constructor
  1455. *
  1456. * @param mixed $val
  1457. * @param string $type
  1458. * @return void
  1459. */
  1460. public function __construct($val = -1, $type = '')
  1461. {
  1462. parent::__construct();
  1463. if ($val !== -1 OR $type !== '')
  1464. {
  1465. $type = $type === '' ? 'string' : $type;
  1466. if ($this->xmlrpcTypes[$type] == 1)
  1467. {
  1468. $this->addScalar($val, $type);
  1469. }
  1470. elseif ($this->xmlrpcTypes[$type] == 2)
  1471. {
  1472. $this->addArray($val);
  1473. }
  1474. elseif ($this->xmlrpcTypes[$type] == 3)
  1475. {
  1476. $this->addStruct($val);
  1477. }
  1478. }
  1479. }
  1480. // --------------------------------------------------------------------
  1481. /**
  1482. * Add scalar value
  1483. *
  1484. * @param scalar
  1485. * @param string
  1486. * @return int
  1487. */
  1488. public function addScalar($val, $type = 'string')
  1489. {
  1490. $typeof = $this->xmlrpcTypes[$type];
  1491. if ($this->mytype === 1)
  1492. {
  1493. echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
  1494. return 0;
  1495. }
  1496. if ($typeof != 1)
  1497. {
  1498. echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
  1499. return 0;
  1500. }
  1501. if ($type === $this->xmlrpcBoolean)
  1502. {
  1503. $val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
  1504. }
  1505. if ($this->mytype === 2)
  1506. {
  1507. // adding to an array here
  1508. $ar = $this->me['array'];
  1509. $ar[] = new XML_RPC_Values($val, $type);
  1510. $this->me['array'] = $ar;
  1511. }
  1512. else
  1513. {
  1514. // a scalar, so set the value and remember we're scalar
  1515. $this->me[$type] = $val;
  1516. $this->mytype = $typeof;
  1517. }
  1518. return 1;
  1519. }
  1520. // --------------------------------------------------------------------
  1521. /**
  1522. * Add array value
  1523. *
  1524. * @param array
  1525. * @return int
  1526. */
  1527. public function addArray($vals)
  1528. {
  1529. if ($this->mytype !== 0)
  1530. {
  1531. echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';
  1532. return 0;
  1533. }
  1534. $this->mytype = $this->xmlrpcTypes['array'];
  1535. $this->me['array'] = $vals;
  1536. return 1;
  1537. }
  1538. // --------------------------------------------------------------------
  1539. /**
  1540. * Add struct value
  1541. *
  1542. * @param object
  1543. * @return int
  1544. */
  1545. public function addStruct($vals)
  1546. {
  1547. if ($this->mytype !== 0)
  1548. {
  1549. echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';
  1550. return 0;
  1551. }
  1552. $this->mytype = $this->xmlrpcTypes['struct'];
  1553. $this->me['struct'] = $vals;
  1554. return 1;
  1555. }
  1556. // --------------------------------------------------------------------
  1557. /**
  1558. * Get value type
  1559. *
  1560. * @return string
  1561. */
  1562. public function kindOf()
  1563. {
  1564. switch ($this->mytype)
  1565. {
  1566. case 3: return 'struct';
  1567. case 2: return 'array';
  1568. case 1: return 'scalar';
  1569. default: return 'undef';
  1570. }
  1571. }
  1572. // --------------------------------------------------------------------
  1573. /**
  1574. * Serialize data
  1575. *
  1576. * @param string
  1577. * @param mixed
  1578. * @return string
  1579. */
  1580. public function serializedata($typ, $val)
  1581. {
  1582. $rs = '';
  1583. switch ($this->xmlrpcTypes[$typ])
  1584. {
  1585. case 3:
  1586. // struct
  1587. $rs .= "<struct>\n";
  1588. reset($val);
  1589. foreach ($val as $key2 => &$val2)
  1590. {
  1591. $rs .= "<member>\n<name>{$key2}</name>\n".$this->serializeval($val2)."</member>\n";
  1592. }
  1593. $rs .= '</struct>';
  1594. break;
  1595. case 2:
  1596. // array
  1597. $rs .= "<array>\n<data>\n";
  1598. for ($i = 0, $c = count($val); $i < $c; $i++)
  1599. {
  1600. $rs .= $this->serializeval($val[$i]);
  1601. }
  1602. $rs .= "</data>\n</array>\n";
  1603. break;
  1604. case 1:
  1605. // others
  1606. switch ($typ)
  1607. {
  1608. case $this->xmlrpcBase64:
  1609. $rs .= '<'.$typ.'>'.base64_encode( (string) $val).'</'.$typ.">\n";
  1610. break;
  1611. case $this->xmlrpcBoolean:
  1612. $rs .= '<'.$typ.'>'.( (bool) $val ? '1' : '0').'</'.$typ.">\n";
  1613. break;
  1614. case $this->xmlrpcString:
  1615. $rs .= '<'.$typ.'>'.htmlspecialchars( (string) $val).'</'.$typ.">\n";
  1616. break;
  1617. default:
  1618. $rs .= '<'.$typ.'>'.$val.'</'.$typ.">\n";
  1619. break;
  1620. }
  1621. default:
  1622. break;
  1623. }
  1624. return $rs;
  1625. }
  1626. // --------------------------------------------------------------------
  1627. /**
  1628. * Serialize class
  1629. *
  1630. * @return string
  1631. */
  1632. public function serialize_class()
  1633. {
  1634. return $this->serializeval($this);
  1635. }
  1636. // --------------------------------------------------------------------
  1637. /**
  1638. * Serialize value
  1639. *
  1640. * @param object
  1641. * @return string
  1642. */
  1643. public function serializeval($o)
  1644. {
  1645. $array = $o->me;
  1646. list($value, $type) = array(reset($array), key($array));
  1647. return "<value>\n".$this->serializedata($type, $value)."</value>\n";
  1648. }
  1649. // --------------------------------------------------------------------
  1650. /**
  1651. * Scalar value
  1652. *
  1653. * @return mixed
  1654. */
  1655. public function scalarval()
  1656. {
  1657. return reset($this->me);
  1658. }
  1659. // --------------------------------------------------------------------
  1660. /**
  1661. * Encode time in ISO-8601 form.
  1662. * Useful for sending time in XML-RPC
  1663. *
  1664. * @param int unix timestamp
  1665. * @param bool
  1666. * @return string
  1667. */
  1668. public function iso8601_encode($time, $utc = FALSE)
  1669. {
  1670. return ($utc) ? strftime('%Y%m%dT%H:%i:%s', $time) : gmstrftime('%Y%m%dT%H:%i:%s', $time);
  1671. }
  1672. } // END XML_RPC_Values Class