Xmlrpc.php 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP 5.1.6 or newer
  6. *
  7. * @package CodeIgniter
  8. * @author EllisLab Dev Team
  9. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
  10. * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
  11. * @license http://codeigniter.com/user_guide/license.html
  12. * @link http://codeigniter.com
  13. * @since Version 1.0
  14. * @filesource
  15. */
  16. if ( ! function_exists('xml_parser_create'))
  17. {
  18. show_error('Your PHP installation does not support XML');
  19. }
  20. // ------------------------------------------------------------------------
  21. /**
  22. * XML-RPC request handler class
  23. *
  24. * @package CodeIgniter
  25. * @subpackage Libraries
  26. * @category XML-RPC
  27. * @author EllisLab Dev Team
  28. * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
  29. */
  30. class CI_Xmlrpc {
  31. var $debug = FALSE; // Debugging on or off
  32. var $xmlrpcI4 = 'i4';
  33. var $xmlrpcInt = 'int';
  34. var $xmlrpcBoolean = 'boolean';
  35. var $xmlrpcDouble = 'double';
  36. var $xmlrpcString = 'string';
  37. var $xmlrpcDateTime = 'dateTime.iso8601';
  38. var $xmlrpcBase64 = 'base64';
  39. var $xmlrpcArray = 'array';
  40. var $xmlrpcStruct = 'struct';
  41. var $xmlrpcTypes = array();
  42. var $valid_parents = array();
  43. var $xmlrpcerr = array(); // Response numbers
  44. var $xmlrpcstr = array(); // Response strings
  45. var $xmlrpc_defencoding = 'UTF-8';
  46. var $xmlrpcName = 'XML-RPC for CodeIgniter';
  47. var $xmlrpcVersion = '1.1';
  48. var $xmlrpcerruser = 800; // Start of user errors
  49. var $xmlrpcerrxml = 100; // Start of XML Parse errors
  50. var $xmlrpc_backslash = ''; // formulate backslashes for escaping regexp
  51. var $client;
  52. var $method;
  53. var $data;
  54. var $message = '';
  55. var $error = ''; // Error string for request
  56. var $result;
  57. var $response = array(); // Response from remote server
  58. var $xss_clean = TRUE;
  59. //-------------------------------------
  60. // VALUES THAT MULTIPLE CLASSES NEED
  61. //-------------------------------------
  62. public function __construct($config = array())
  63. {
  64. $this->xmlrpcName = $this->xmlrpcName;
  65. $this->xmlrpc_backslash = chr(92).chr(92);
  66. // Types for info sent back and forth
  67. $this->xmlrpcTypes = array(
  68. $this->xmlrpcI4 => '1',
  69. $this->xmlrpcInt => '1',
  70. $this->xmlrpcBoolean => '1',
  71. $this->xmlrpcString => '1',
  72. $this->xmlrpcDouble => '1',
  73. $this->xmlrpcDateTime => '1',
  74. $this->xmlrpcBase64 => '1',
  75. $this->xmlrpcArray => '2',
  76. $this->xmlrpcStruct => '3'
  77. );
  78. // Array of Valid Parents for Various XML-RPC elements
  79. $this->valid_parents = array('BOOLEAN' => array('VALUE'),
  80. 'I4' => array('VALUE'),
  81. 'INT' => array('VALUE'),
  82. 'STRING' => array('VALUE'),
  83. 'DOUBLE' => array('VALUE'),
  84. 'DATETIME.ISO8601' => array('VALUE'),
  85. 'BASE64' => array('VALUE'),
  86. 'ARRAY' => array('VALUE'),
  87. 'STRUCT' => array('VALUE'),
  88. 'PARAM' => array('PARAMS'),
  89. 'METHODNAME' => array('METHODCALL'),
  90. 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
  91. 'MEMBER' => array('STRUCT'),
  92. 'NAME' => array('MEMBER'),
  93. 'DATA' => array('ARRAY'),
  94. 'FAULT' => array('METHODRESPONSE'),
  95. 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
  96. );
  97. // XML-RPC Responses
  98. $this->xmlrpcerr['unknown_method'] = '1';
  99. $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
  100. $this->xmlrpcerr['invalid_return'] = '2';
  101. $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.';
  102. $this->xmlrpcerr['incorrect_params'] = '3';
  103. $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
  104. $this->xmlrpcerr['introspect_unknown'] = '4';
  105. $this->xmlrpcstr['introspect_unknown'] = "Cannot inspect signature for request: method unknown";
  106. $this->xmlrpcerr['http_error'] = '5';
  107. $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
  108. $this->xmlrpcerr['no_data'] = '6';
  109. $this->xmlrpcstr['no_data'] ='No data received from server.';
  110. $this->initialize($config);
  111. log_message('debug', "XML-RPC Class Initialized");
  112. }
  113. //-------------------------------------
  114. // Initialize Prefs
  115. //-------------------------------------
  116. function initialize($config = array())
  117. {
  118. if (count($config) > 0)
  119. {
  120. foreach ($config as $key => $val)
  121. {
  122. if (isset($this->$key))
  123. {
  124. $this->$key = $val;
  125. }
  126. }
  127. }
  128. }
  129. // END
  130. //-------------------------------------
  131. // Take URL and parse it
  132. //-------------------------------------
  133. function server($url, $port=80)
  134. {
  135. if (substr($url, 0, 4) != "http")
  136. {
  137. $url = "http://".$url;
  138. }
  139. $parts = parse_url($url);
  140. $path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
  141. if (isset($parts['query']) && $parts['query'] != '')
  142. {
  143. $path .= '?'.$parts['query'];
  144. }
  145. $this->client = new XML_RPC_Client($path, $parts['host'], $port);
  146. }
  147. // END
  148. //-------------------------------------
  149. // Set Timeout
  150. //-------------------------------------
  151. function timeout($seconds=5)
  152. {
  153. if ( ! is_null($this->client) && is_int($seconds))
  154. {
  155. $this->client->timeout = $seconds;
  156. }
  157. }
  158. // END
  159. //-------------------------------------
  160. // Set Methods
  161. //-------------------------------------
  162. function method($function)
  163. {
  164. $this->method = $function;
  165. }
  166. // END
  167. //-------------------------------------
  168. // Take Array of Data and Create Objects
  169. //-------------------------------------
  170. function request($incoming)
  171. {
  172. if ( ! is_array($incoming))
  173. {
  174. // Send Error
  175. }
  176. $this->data = array();
  177. foreach ($incoming as $key => $value)
  178. {
  179. $this->data[$key] = $this->values_parsing($value);
  180. }
  181. }
  182. // END
  183. //-------------------------------------
  184. // Set Debug
  185. //-------------------------------------
  186. function set_debug($flag = TRUE)
  187. {
  188. $this->debug = ($flag == TRUE) ? TRUE : FALSE;
  189. }
  190. //-------------------------------------
  191. // Values Parsing
  192. //-------------------------------------
  193. function values_parsing($value, $return = FALSE)
  194. {
  195. if (is_array($value) && array_key_exists(0, $value))
  196. {
  197. if ( ! isset($value['1']) OR ( ! isset($this->xmlrpcTypes[$value['1']])))
  198. {
  199. if (is_array($value[0]))
  200. {
  201. $temp = new XML_RPC_Values($value['0'], 'array');
  202. }
  203. else
  204. {
  205. $temp = new XML_RPC_Values($value['0'], 'string');
  206. }
  207. }
  208. elseif (is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array'))
  209. {
  210. while (list($k) = each($value['0']))
  211. {
  212. $value['0'][$k] = $this->values_parsing($value['0'][$k], TRUE);
  213. }
  214. $temp = new XML_RPC_Values($value['0'], $value['1']);
  215. }
  216. else
  217. {
  218. $temp = new XML_RPC_Values($value['0'], $value['1']);
  219. }
  220. }
  221. else
  222. {
  223. $temp = new XML_RPC_Values($value, 'string');
  224. }
  225. return $temp;
  226. }
  227. // END
  228. //-------------------------------------
  229. // Sends XML-RPC Request
  230. //-------------------------------------
  231. function send_request()
  232. {
  233. $this->message = new XML_RPC_Message($this->method,$this->data);
  234. $this->message->debug = $this->debug;
  235. if ( ! $this->result = $this->client->send($this->message))
  236. {
  237. $this->error = $this->result->errstr;
  238. return FALSE;
  239. }
  240. elseif ( ! is_object($this->result->val))
  241. {
  242. $this->error = $this->result->errstr;
  243. return FALSE;
  244. }
  245. $this->response = $this->result->decode();
  246. return TRUE;
  247. }
  248. // END
  249. //-------------------------------------
  250. // Returns Error
  251. //-------------------------------------
  252. function display_error()
  253. {
  254. return $this->error;
  255. }
  256. // END
  257. //-------------------------------------
  258. // Returns Remote Server Response
  259. //-------------------------------------
  260. function display_response()
  261. {
  262. return $this->response;
  263. }
  264. // END
  265. //-------------------------------------
  266. // Sends an Error Message for Server Request
  267. //-------------------------------------
  268. function send_error_message($number, $message)
  269. {
  270. return new XML_RPC_Response('0',$number, $message);
  271. }
  272. // END
  273. //-------------------------------------
  274. // Send Response for Server Request
  275. //-------------------------------------
  276. function send_response($response)
  277. {
  278. // $response should be array of values, which will be parsed
  279. // based on their data and type into a valid group of XML-RPC values
  280. $response = $this->values_parsing($response);
  281. return new XML_RPC_Response($response);
  282. }
  283. // END
  284. } // END XML_RPC Class
  285. /**
  286. * XML-RPC Client class
  287. *
  288. * @category XML-RPC
  289. * @author ExpressionEngine Dev Team
  290. * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
  291. */
  292. class XML_RPC_Client extends CI_Xmlrpc
  293. {
  294. var $path = '';
  295. var $server = '';
  296. var $port = 80;
  297. var $errno = '';
  298. var $errstring = '';
  299. var $timeout = 5;
  300. var $no_multicall = FALSE;
  301. public function __construct($path, $server, $port=80)
  302. {
  303. parent::__construct();
  304. $this->port = $port;
  305. $this->server = $server;
  306. $this->path = $path;
  307. }
  308. function send($msg)
  309. {
  310. if (is_array($msg))
  311. {
  312. // Multi-call disabled
  313. $r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']);
  314. return $r;
  315. }
  316. return $this->sendPayload($msg);
  317. }
  318. function sendPayload($msg)
  319. {
  320. $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout);
  321. if ( ! is_resource($fp))
  322. {
  323. error_log($this->xmlrpcstr['http_error']);
  324. $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']);
  325. return $r;
  326. }
  327. if (empty($msg->payload))
  328. {
  329. // $msg = XML_RPC_Messages
  330. $msg->createPayload();
  331. }
  332. $r = "\r\n";
  333. $op = "POST {$this->path} HTTP/1.0$r";
  334. $op .= "Host: {$this->server}$r";
  335. $op .= "Content-Type: text/xml$r";
  336. $op .= "User-Agent: {$this->xmlrpcName}$r";
  337. $op .= "Content-Length: ".strlen($msg->payload). "$r$r";
  338. $op .= $msg->payload;
  339. if ( ! fputs($fp, $op, strlen($op)))
  340. {
  341. error_log($this->xmlrpcstr['http_error']);
  342. $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
  343. return $r;
  344. }
  345. $resp = $msg->parseResponse($fp);
  346. fclose($fp);
  347. return $resp;
  348. }
  349. } // end class XML_RPC_Client
  350. /**
  351. * XML-RPC Response class
  352. *
  353. * @category XML-RPC
  354. * @author ExpressionEngine Dev Team
  355. * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
  356. */
  357. class XML_RPC_Response
  358. {
  359. var $val = 0;
  360. var $errno = 0;
  361. var $errstr = '';
  362. var $headers = array();
  363. var $xss_clean = TRUE;
  364. public function __construct($val, $code = 0, $fstr = '')
  365. {
  366. if ($code != 0)
  367. {
  368. // error
  369. $this->errno = $code;
  370. $this->errstr = htmlentities($fstr);
  371. }
  372. else if ( ! is_object($val))
  373. {
  374. // programmer error, not an object
  375. error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response. Defaulting to empty value.");
  376. $this->val = new XML_RPC_Values();
  377. }
  378. else
  379. {
  380. $this->val = $val;
  381. }
  382. }
  383. function faultCode()
  384. {
  385. return $this->errno;
  386. }
  387. function faultString()
  388. {
  389. return $this->errstr;
  390. }
  391. function value()
  392. {
  393. return $this->val;
  394. }
  395. function prepare_response()
  396. {
  397. $result = "<methodResponse>\n";
  398. if ($this->errno)
  399. {
  400. $result .= '<fault>
  401. <value>
  402. <struct>
  403. <member>
  404. <name>faultCode</name>
  405. <value><int>' . $this->errno . '</int></value>
  406. </member>
  407. <member>
  408. <name>faultString</name>
  409. <value><string>' . $this->errstr . '</string></value>
  410. </member>
  411. </struct>
  412. </value>
  413. </fault>';
  414. }
  415. else
  416. {
  417. $result .= "<params>\n<param>\n" .
  418. $this->val->serialize_class() .
  419. "</param>\n</params>";
  420. }
  421. $result .= "\n</methodResponse>";
  422. return $result;
  423. }
  424. function decode($array=FALSE)
  425. {
  426. $CI =& get_instance();
  427. if ($array !== FALSE && is_array($array))
  428. {
  429. while (list($key) = each($array))
  430. {
  431. if (is_array($array[$key]))
  432. {
  433. $array[$key] = $this->decode($array[$key]);
  434. }
  435. else
  436. {
  437. $array[$key] = ($this->xss_clean) ? $CI->security->xss_clean($array[$key]) : $array[$key];
  438. }
  439. }
  440. $result = $array;
  441. }
  442. else
  443. {
  444. $result = $this->xmlrpc_decoder($this->val);
  445. if (is_array($result))
  446. {
  447. $result = $this->decode($result);
  448. }
  449. else
  450. {
  451. $result = ($this->xss_clean) ? $CI->security->xss_clean($result) : $result;
  452. }
  453. }
  454. return $result;
  455. }
  456. //-------------------------------------
  457. // XML-RPC Object to PHP Types
  458. //-------------------------------------
  459. function xmlrpc_decoder($xmlrpc_val)
  460. {
  461. $kind = $xmlrpc_val->kindOf();
  462. if ($kind == 'scalar')
  463. {
  464. return $xmlrpc_val->scalarval();
  465. }
  466. elseif ($kind == 'array')
  467. {
  468. reset($xmlrpc_val->me);
  469. list($a,$b) = each($xmlrpc_val->me);
  470. $size = count($b);
  471. $arr = array();
  472. for ($i = 0; $i < $size; $i++)
  473. {
  474. $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
  475. }
  476. return $arr;
  477. }
  478. elseif ($kind == 'struct')
  479. {
  480. reset($xmlrpc_val->me['struct']);
  481. $arr = array();
  482. while (list($key,$value) = each($xmlrpc_val->me['struct']))
  483. {
  484. $arr[$key] = $this->xmlrpc_decoder($value);
  485. }
  486. return $arr;
  487. }
  488. }
  489. //-------------------------------------
  490. // ISO-8601 time to server or UTC time
  491. //-------------------------------------
  492. function iso8601_decode($time, $utc=0)
  493. {
  494. // return a timet in the localtime, or UTC
  495. $t = 0;
  496. if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
  497. {
  498. $fnc = ($utc == 1) ? 'gmmktime' : 'mktime';
  499. $t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  500. }
  501. return $t;
  502. }
  503. } // End Response Class
  504. /**
  505. * XML-RPC Message class
  506. *
  507. * @category XML-RPC
  508. * @author ExpressionEngine Dev Team
  509. * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
  510. */
  511. class XML_RPC_Message extends CI_Xmlrpc
  512. {
  513. var $payload;
  514. var $method_name;
  515. var $params = array();
  516. var $xh = array();
  517. public function __construct($method, $pars=0)
  518. {
  519. parent::__construct();
  520. $this->method_name = $method;
  521. if (is_array($pars) && count($pars) > 0)
  522. {
  523. for ($i=0; $i<count($pars); $i++)
  524. {
  525. // $pars[$i] = XML_RPC_Values
  526. $this->params[] = $pars[$i];
  527. }
  528. }
  529. }
  530. //-------------------------------------
  531. // Create Payload to Send
  532. //-------------------------------------
  533. function createPayload()
  534. {
  535. $this->payload = "<?xml version=\"1.0\"?".">\r\n<methodCall>\r\n";
  536. $this->payload .= '<methodName>' . $this->method_name . "</methodName>\r\n";
  537. $this->payload .= "<params>\r\n";
  538. for ($i=0; $i<count($this->params); $i++)
  539. {
  540. // $p = XML_RPC_Values
  541. $p = $this->params[$i];
  542. $this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n";
  543. }
  544. $this->payload .= "</params>\r\n</methodCall>\r\n";
  545. }
  546. //-------------------------------------
  547. // Parse External XML-RPC Server's Response
  548. //-------------------------------------
  549. function parseResponse($fp)
  550. {
  551. $data = '';
  552. while ($datum = fread($fp, 4096))
  553. {
  554. $data .= $datum;
  555. }
  556. //-------------------------------------
  557. // DISPLAY HTTP CONTENT for DEBUGGING
  558. //-------------------------------------
  559. if ($this->debug === TRUE)
  560. {
  561. echo "<pre>";
  562. echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
  563. echo "</pre>";
  564. }
  565. //-------------------------------------
  566. // Check for data
  567. //-------------------------------------
  568. if ($data == "")
  569. {
  570. error_log($this->xmlrpcstr['no_data']);
  571. $r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
  572. return $r;
  573. }
  574. //-------------------------------------
  575. // Check for HTTP 200 Response
  576. //-------------------------------------
  577. if (strncmp($data, 'HTTP', 4) == 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
  578. {
  579. $errstr= substr($data, 0, strpos($data, "\n")-1);
  580. $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']. ' (' . $errstr . ')');
  581. return $r;
  582. }
  583. //-------------------------------------
  584. // Create and Set Up XML Parser
  585. //-------------------------------------
  586. $parser = xml_parser_create($this->xmlrpc_defencoding);
  587. $this->xh[$parser] = array();
  588. $this->xh[$parser]['isf'] = 0;
  589. $this->xh[$parser]['ac'] = '';
  590. $this->xh[$parser]['headers'] = array();
  591. $this->xh[$parser]['stack'] = array();
  592. $this->xh[$parser]['valuestack'] = array();
  593. $this->xh[$parser]['isf_reason'] = 0;
  594. xml_set_object($parser, $this);
  595. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
  596. xml_set_element_handler($parser, 'open_tag', 'closing_tag');
  597. xml_set_character_data_handler($parser, 'character_data');
  598. //xml_set_default_handler($parser, 'default_handler');
  599. //-------------------------------------
  600. // GET HEADERS
  601. //-------------------------------------
  602. $lines = explode("\r\n", $data);
  603. while (($line = array_shift($lines)))
  604. {
  605. if (strlen($line) < 1)
  606. {
  607. break;
  608. }
  609. $this->xh[$parser]['headers'][] = $line;
  610. }
  611. $data = implode("\r\n", $lines);
  612. //-------------------------------------
  613. // PARSE XML DATA
  614. //-------------------------------------
  615. if ( ! xml_parse($parser, $data, count($data)))
  616. {
  617. $errstr = sprintf('XML error: %s at line %d',
  618. xml_error_string(xml_get_error_code($parser)),
  619. xml_get_current_line_number($parser));
  620. //error_log($errstr);
  621. $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
  622. xml_parser_free($parser);
  623. return $r;
  624. }
  625. xml_parser_free($parser);
  626. // ---------------------------------------
  627. // Got Ourselves Some Badness, It Seems
  628. // ---------------------------------------
  629. if ($this->xh[$parser]['isf'] > 1)
  630. {
  631. if ($this->debug === TRUE)
  632. {
  633. echo "---Invalid Return---\n";
  634. echo $this->xh[$parser]['isf_reason'];
  635. echo "---Invalid Return---\n\n";
  636. }
  637. $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
  638. return $r;
  639. }
  640. elseif ( ! is_object($this->xh[$parser]['value']))
  641. {
  642. $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
  643. return $r;
  644. }
  645. //-------------------------------------
  646. // DISPLAY XML CONTENT for DEBUGGING
  647. //-------------------------------------
  648. if ($this->debug === TRUE)
  649. {
  650. echo "<pre>";
  651. if (count($this->xh[$parser]['headers'] > 0))
  652. {
  653. echo "---HEADERS---\n";
  654. foreach ($this->xh[$parser]['headers'] as $header)
  655. {
  656. echo "$header\n";
  657. }
  658. echo "---END HEADERS---\n\n";
  659. }
  660. echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
  661. echo "---PARSED---\n" ;
  662. var_dump($this->xh[$parser]['value']);
  663. echo "\n---END PARSED---</pre>";
  664. }
  665. //-------------------------------------
  666. // SEND RESPONSE
  667. //-------------------------------------
  668. $v = $this->xh[$parser]['value'];
  669. if ($this->xh[$parser]['isf'])
  670. {
  671. $errno_v = $v->me['struct']['faultCode'];
  672. $errstr_v = $v->me['struct']['faultString'];
  673. $errno = $errno_v->scalarval();
  674. if ($errno == 0)
  675. {
  676. // FAULT returned, errno needs to reflect that
  677. $errno = -1;
  678. }
  679. $r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());
  680. }
  681. else
  682. {
  683. $r = new XML_RPC_Response($v);
  684. }
  685. $r->headers = $this->xh[$parser]['headers'];
  686. return $r;
  687. }
  688. // ------------------------------------
  689. // Begin Return Message Parsing section
  690. // ------------------------------------
  691. // quick explanation of components:
  692. // ac - used to accumulate values
  693. // isf - used to indicate a fault
  694. // lv - used to indicate "looking for a value": implements
  695. // the logic to allow values with no types to be strings
  696. // params - used to store parameters in method calls
  697. // method - used to store method name
  698. // stack - array with parent tree of the xml element,
  699. // used to validate the nesting of elements
  700. //-------------------------------------
  701. // Start Element Handler
  702. //-------------------------------------
  703. function open_tag($the_parser, $name, $attrs)
  704. {
  705. // If invalid nesting, then return
  706. if ($this->xh[$the_parser]['isf'] > 1) return;
  707. // Evaluate and check for correct nesting of XML elements
  708. if (count($this->xh[$the_parser]['stack']) == 0)
  709. {
  710. if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
  711. {
  712. $this->xh[$the_parser]['isf'] = 2;
  713. $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
  714. return;
  715. }
  716. }
  717. else
  718. {
  719. // not top level element: see if parent is OK
  720. if ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
  721. {
  722. $this->xh[$the_parser]['isf'] = 2;
  723. $this->xh[$the_parser]['isf_reason'] = "XML-RPC element $name cannot be child of ".$this->xh[$the_parser]['stack'][0];
  724. return;
  725. }
  726. }
  727. switch($name)
  728. {
  729. case 'STRUCT':
  730. case 'ARRAY':
  731. // Creates array for child elements
  732. $cur_val = array('value' => array(),
  733. 'type' => $name);
  734. array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
  735. break;
  736. case 'METHODNAME':
  737. case 'NAME':
  738. $this->xh[$the_parser]['ac'] = '';
  739. break;
  740. case 'FAULT':
  741. $this->xh[$the_parser]['isf'] = 1;
  742. break;
  743. case 'PARAM':
  744. $this->xh[$the_parser]['value'] = NULL;
  745. break;
  746. case 'VALUE':
  747. $this->xh[$the_parser]['vt'] = 'value';
  748. $this->xh[$the_parser]['ac'] = '';
  749. $this->xh[$the_parser]['lv'] = 1;
  750. break;
  751. case 'I4':
  752. case 'INT':
  753. case 'STRING':
  754. case 'BOOLEAN':
  755. case 'DOUBLE':
  756. case 'DATETIME.ISO8601':
  757. case 'BASE64':
  758. if ($this->xh[$the_parser]['vt'] != 'value')
  759. {
  760. //two data elements inside a value: an error occurred!
  761. $this->xh[$the_parser]['isf'] = 2;
  762. $this->xh[$the_parser]['isf_reason'] = "'Twas a $name element following a ".$this->xh[$the_parser]['vt']." element inside a single value";
  763. return;
  764. }
  765. $this->xh[$the_parser]['ac'] = '';
  766. break;
  767. case 'MEMBER':
  768. // Set name of <member> to nothing to prevent errors later if no <name> is found
  769. $this->xh[$the_parser]['valuestack'][0]['name'] = '';
  770. // Set NULL value to check to see if value passed for this param/member
  771. $this->xh[$the_parser]['value'] = NULL;
  772. break;
  773. case 'DATA':
  774. case 'METHODCALL':
  775. case 'METHODRESPONSE':
  776. case 'PARAMS':
  777. // valid elements that add little to processing
  778. break;
  779. default:
  780. /// An Invalid Element is Found, so we have trouble
  781. $this->xh[$the_parser]['isf'] = 2;
  782. $this->xh[$the_parser]['isf_reason'] = "Invalid XML-RPC element found: $name";
  783. break;
  784. }
  785. // Add current element name to stack, to allow validation of nesting
  786. array_unshift($this->xh[$the_parser]['stack'], $name);
  787. if ($name != 'VALUE') $this->xh[$the_parser]['lv'] = 0;
  788. }
  789. // END
  790. //-------------------------------------
  791. // End Element Handler
  792. //-------------------------------------
  793. function closing_tag($the_parser, $name)
  794. {
  795. if ($this->xh[$the_parser]['isf'] > 1) return;
  796. // Remove current element from stack and set variable
  797. // NOTE: If the XML validates, then we do not have to worry about
  798. // the opening and closing of elements. Nesting is checked on the opening
  799. // tag so we be safe there as well.
  800. $curr_elem = array_shift($this->xh[$the_parser]['stack']);
  801. switch($name)
  802. {
  803. case 'STRUCT':
  804. case 'ARRAY':
  805. $cur_val = array_shift($this->xh[$the_parser]['valuestack']);
  806. $this->xh[$the_parser]['value'] = ( ! isset($cur_val['values'])) ? array() : $cur_val['values'];
  807. $this->xh[$the_parser]['vt'] = strtolower($name);
  808. break;
  809. case 'NAME':
  810. $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
  811. break;
  812. case 'BOOLEAN':
  813. case 'I4':
  814. case 'INT':
  815. case 'STRING':
  816. case 'DOUBLE':
  817. case 'DATETIME.ISO8601':
  818. case 'BASE64':
  819. $this->xh[$the_parser]['vt'] = strtolower($name);
  820. if ($name == 'STRING')
  821. {
  822. $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
  823. }
  824. elseif ($name=='DATETIME.ISO8601')
  825. {
  826. $this->xh[$the_parser]['vt'] = $this->xmlrpcDateTime;
  827. $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
  828. }
  829. elseif ($name=='BASE64')
  830. {
  831. $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
  832. }
  833. elseif ($name=='BOOLEAN')
  834. {
  835. // Translated BOOLEAN values to TRUE AND FALSE
  836. if ($this->xh[$the_parser]['ac'] == '1')
  837. {
  838. $this->xh[$the_parser]['value'] = TRUE;
  839. }
  840. else
  841. {
  842. $this->xh[$the_parser]['value'] = FALSE;
  843. }
  844. }
  845. elseif ($name=='DOUBLE')
  846. {
  847. // we have a DOUBLE
  848. // we must check that only 0123456789-.<space> are characters here
  849. if ( ! preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac']))
  850. {
  851. $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
  852. }
  853. else
  854. {
  855. $this->xh[$the_parser]['value'] = (double)$this->xh[$the_parser]['ac'];
  856. }
  857. }
  858. else
  859. {
  860. // we have an I4/INT
  861. // we must check that only 0123456789-<space> are characters here
  862. if ( ! preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac']))
  863. {
  864. $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
  865. }
  866. else
  867. {
  868. $this->xh[$the_parser]['value'] = (int)$this->xh[$the_parser]['ac'];
  869. }
  870. }
  871. $this->xh[$the_parser]['ac'] = '';
  872. $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
  873. break;
  874. case 'VALUE':
  875. // This if() detects if no scalar was inside <VALUE></VALUE>
  876. if ($this->xh[$the_parser]['vt']=='value')
  877. {
  878. $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
  879. $this->xh[$the_parser]['vt'] = $this->xmlrpcString;
  880. }
  881. // build the XML-RPC value out of the data received, and substitute it
  882. $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
  883. if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY')
  884. {
  885. // Array
  886. $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
  887. }
  888. else
  889. {
  890. // Struct
  891. $this->xh[$the_parser]['value'] = $temp;
  892. }
  893. break;
  894. case 'MEMBER':
  895. $this->xh[$the_parser]['ac']='';
  896. // If value add to array in the stack for the last element built
  897. if ($this->xh[$the_parser]['value'])
  898. {
  899. $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
  900. }
  901. break;
  902. case 'DATA':
  903. $this->xh[$the_parser]['ac']='';
  904. break;
  905. case 'PARAM':
  906. if ($this->xh[$the_parser]['value'])
  907. {
  908. $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
  909. }
  910. break;
  911. case 'METHODNAME':
  912. $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);
  913. break;
  914. case 'PARAMS':
  915. case 'FAULT':
  916. case 'METHODCALL':
  917. case 'METHORESPONSE':
  918. // We're all good kids with nuthin' to do
  919. break;
  920. default:
  921. // End of an Invalid Element. Taken care of during the opening tag though
  922. break;
  923. }
  924. }
  925. //-------------------------------------
  926. // Parses Character Data
  927. //-------------------------------------
  928. function character_data($the_parser, $data)
  929. {
  930. if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
  931. // If a value has not been found
  932. if ($this->xh[$the_parser]['lv'] != 3)
  933. {
  934. if ($this->xh[$the_parser]['lv'] == 1)
  935. {
  936. $this->xh[$the_parser]['lv'] = 2; // Found a value
  937. }
  938. if ( ! @isset($this->xh[$the_parser]['ac']))
  939. {
  940. $this->xh[$the_parser]['ac'] = '';
  941. }
  942. $this->xh[$the_parser]['ac'] .= $data;
  943. }
  944. }
  945. function addParam($par) { $this->params[]=$par; }
  946. function output_parameters($array=FALSE)
  947. {
  948. $CI =& get_instance();
  949. if ($array !== FALSE && is_array($array))
  950. {
  951. while (list($key) = each($array))
  952. {
  953. if (is_array($array[$key]))
  954. {
  955. $array[$key] = $this->output_parameters($array[$key]);
  956. }
  957. else
  958. {
  959. // 'bits' is for the MetaWeblog API image bits
  960. // @todo - this needs to be made more general purpose
  961. $array[$key] = ($key == 'bits' OR $this->xss_clean == FALSE) ? $array[$key] : $CI->security->xss_clean($array[$key]);
  962. }
  963. }
  964. $parameters = $array;
  965. }
  966. else
  967. {
  968. $parameters = array();
  969. for ($i = 0; $i < count($this->params); $i++)
  970. {
  971. $a_param = $this->decode_message($this->params[$i]);
  972. if (is_array($a_param))
  973. {
  974. $parameters[] = $this->output_parameters($a_param);
  975. }
  976. else
  977. {
  978. $parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param;
  979. }
  980. }
  981. }
  982. return $parameters;
  983. }
  984. function decode_message($param)
  985. {
  986. $kind = $param->kindOf();
  987. if ($kind == 'scalar')
  988. {
  989. return $param->scalarval();
  990. }
  991. elseif ($kind == 'array')
  992. {
  993. reset($param->me);
  994. list($a,$b) = each($param->me);
  995. $arr = array();
  996. for($i = 0; $i < count($b); $i++)
  997. {
  998. $arr[] = $this->decode_message($param->me['array'][$i]);
  999. }
  1000. return $arr;
  1001. }
  1002. elseif ($kind == 'struct')
  1003. {
  1004. reset($param->me['struct']);
  1005. $arr = array();
  1006. while (list($key,$value) = each($param->me['struct']))
  1007. {
  1008. $arr[$key] = $this->decode_message($value);
  1009. }
  1010. return $arr;
  1011. }
  1012. }
  1013. } // End XML_RPC_Messages class
  1014. /**
  1015. * XML-RPC Values class
  1016. *
  1017. * @category XML-RPC
  1018. * @author ExpressionEngine Dev Team
  1019. * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
  1020. */
  1021. class XML_RPC_Values extends CI_Xmlrpc
  1022. {
  1023. var $me = array();
  1024. var $mytype = 0;
  1025. public function __construct($val=-1, $type='')
  1026. {
  1027. parent::__construct();
  1028. if ($val != -1 OR $type != '')
  1029. {
  1030. $type = $type == '' ? 'string' : $type;
  1031. if ($this->xmlrpcTypes[$type] == 1)
  1032. {
  1033. $this->addScalar($val,$type);
  1034. }
  1035. elseif ($this->xmlrpcTypes[$type] == 2)
  1036. {
  1037. $this->addArray($val);
  1038. }
  1039. elseif ($this->xmlrpcTypes[$type] == 3)
  1040. {
  1041. $this->addStruct($val);
  1042. }
  1043. }
  1044. }
  1045. function addScalar($val, $type='string')
  1046. {
  1047. $typeof = $this->xmlrpcTypes[$type];
  1048. if ($this->mytype==1)
  1049. {
  1050. echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
  1051. return 0;
  1052. }
  1053. if ($typeof != 1)
  1054. {
  1055. echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
  1056. return 0;
  1057. }
  1058. if ($type == $this->xmlrpcBoolean)
  1059. {
  1060. if (strcasecmp($val,'true')==0 OR $val==1 OR ($val==true && strcasecmp($val,'false')))
  1061. {
  1062. $val = 1;
  1063. }
  1064. else
  1065. {
  1066. $val=0;
  1067. }
  1068. }
  1069. if ($this->mytype == 2)
  1070. {
  1071. // adding to an array here
  1072. $ar = $this->me['array'];
  1073. $ar[] = new XML_RPC_Values($val, $type);
  1074. $this->me['array'] = $ar;
  1075. }
  1076. else
  1077. {
  1078. // a scalar, so set the value and remember we're scalar
  1079. $this->me[$type] = $val;
  1080. $this->mytype = $typeof;
  1081. }
  1082. return 1;
  1083. }
  1084. function addArray($vals)
  1085. {
  1086. if ($this->mytype != 0)
  1087. {
  1088. echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
  1089. return 0;
  1090. }
  1091. $this->mytype = $this->xmlrpcTypes['array'];
  1092. $this->me['array'] = $vals;
  1093. return 1;
  1094. }
  1095. function addStruct($vals)
  1096. {
  1097. if ($this->mytype != 0)
  1098. {
  1099. echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
  1100. return 0;
  1101. }
  1102. $this->mytype = $this->xmlrpcTypes['struct'];
  1103. $this->me['struct'] = $vals;
  1104. return 1;
  1105. }
  1106. function kindOf()
  1107. {
  1108. switch($this->mytype)
  1109. {
  1110. case 3:
  1111. return 'struct';
  1112. break;
  1113. case 2:
  1114. return 'array';
  1115. break;
  1116. case 1:
  1117. return 'scalar';
  1118. break;
  1119. default:
  1120. return 'undef';
  1121. }
  1122. }
  1123. function serializedata($typ, $val)
  1124. {
  1125. $rs = '';
  1126. switch($this->xmlrpcTypes[$typ])
  1127. {
  1128. case 3:
  1129. // struct
  1130. $rs .= "<struct>\n";
  1131. reset($val);
  1132. while (list($key2, $val2) = each($val))
  1133. {
  1134. $rs .= "<member>\n<name>{$key2}</name>\n";
  1135. $rs .= $this->serializeval($val2);
  1136. $rs .= "</member>\n";
  1137. }
  1138. $rs .= '</struct>';
  1139. break;
  1140. case 2:
  1141. // array
  1142. $rs .= "<array>\n<data>\n";
  1143. for($i=0; $i < count($val); $i++)
  1144. {
  1145. $rs .= $this->serializeval($val[$i]);
  1146. }
  1147. $rs.="</data>\n</array>\n";
  1148. break;
  1149. case 1:
  1150. // others
  1151. switch ($typ)
  1152. {
  1153. case $this->xmlrpcBase64:
  1154. $rs .= "<{$typ}>" . base64_encode((string)$val) . "</{$typ}>\n";
  1155. break;
  1156. case $this->xmlrpcBoolean:
  1157. $rs .= "<{$typ}>" . ((bool)$val ? '1' : '0') . "</{$typ}>\n";
  1158. break;
  1159. case $this->xmlrpcString:
  1160. $rs .= "<{$typ}>" . htmlspecialchars((string)$val). "</{$typ}>\n";
  1161. break;
  1162. default:
  1163. $rs .= "<{$typ}>{$val}</{$typ}>\n";
  1164. break;
  1165. }
  1166. default:
  1167. break;
  1168. }
  1169. return $rs;
  1170. }
  1171. function serialize_class()
  1172. {
  1173. return $this->serializeval($this);
  1174. }
  1175. function serializeval($o)
  1176. {
  1177. $ar = $o->me;
  1178. reset($ar);
  1179. list($typ, $val) = each($ar);
  1180. $rs = "<value>\n".$this->serializedata($typ, $val)."</value>\n";
  1181. return $rs;
  1182. }
  1183. function scalarval()
  1184. {
  1185. reset($this->me);
  1186. list($a,$b) = each($this->me);
  1187. return $b;
  1188. }
  1189. //-------------------------------------
  1190. // Encode time in ISO-8601 form.
  1191. //-------------------------------------
  1192. // Useful for sending time in XML-RPC
  1193. function iso8601_encode($time, $utc=0)
  1194. {
  1195. if ($utc == 1)
  1196. {
  1197. $t = strftime("%Y%m%dT%H:%i:%s", $time);
  1198. }
  1199. else
  1200. {
  1201. if (function_exists('gmstrftime'))
  1202. $t = gmstrftime("%Y%m%dT%H:%i:%s", $time);
  1203. else
  1204. $t = strftime("%Y%m%dT%H:%i:%s", $time - date('Z'));
  1205. }
  1206. return $t;
  1207. }
  1208. }
  1209. // END XML_RPC_Values Class
  1210. /* End of file Xmlrpc.php */
  1211. /* Location: ./system/libraries/Xmlrpc.php */