PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/system/core/core.xmlrpc.php

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