Xmlrpcs.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  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. if ( ! class_exists('CI_Xmlrpc'))
  21. {
  22. show_error('You must load the Xmlrpc class before loading the Xmlrpcs class in order to create a server.');
  23. }
  24. // ------------------------------------------------------------------------
  25. /**
  26. * XML-RPC server class
  27. *
  28. * @package CodeIgniter
  29. * @subpackage Libraries
  30. * @category XML-RPC
  31. * @author EllisLab Dev Team
  32. * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
  33. */
  34. class CI_Xmlrpcs extends CI_Xmlrpc
  35. {
  36. var $methods = array(); //array of methods mapped to function names and signatures
  37. var $debug_msg = ''; // Debug Message
  38. var $system_methods = array(); // XML RPC Server methods
  39. var $controller_obj;
  40. var $object = FALSE;
  41. /**
  42. * Constructor
  43. */
  44. public function __construct($config=array())
  45. {
  46. parent::__construct();
  47. $this->set_system_methods();
  48. if (isset($config['functions']) && is_array($config['functions']))
  49. {
  50. $this->methods = array_merge($this->methods, $config['functions']);
  51. }
  52. log_message('debug', "XML-RPC Server Class Initialized");
  53. }
  54. // --------------------------------------------------------------------
  55. /**
  56. * Initialize Prefs and Serve
  57. *
  58. * @access public
  59. * @param mixed
  60. * @return void
  61. */
  62. function initialize($config=array())
  63. {
  64. if (isset($config['functions']) && is_array($config['functions']))
  65. {
  66. $this->methods = array_merge($this->methods, $config['functions']);
  67. }
  68. if (isset($config['debug']))
  69. {
  70. $this->debug = $config['debug'];
  71. }
  72. if (isset($config['object']) && is_object($config['object']))
  73. {
  74. $this->object = $config['object'];
  75. }
  76. if (isset($config['xss_clean']))
  77. {
  78. $this->xss_clean = $config['xss_clean'];
  79. }
  80. }
  81. // --------------------------------------------------------------------
  82. /**
  83. * Setting of System Methods
  84. *
  85. * @access public
  86. * @return void
  87. */
  88. function set_system_methods()
  89. {
  90. $this->methods = array(
  91. 'system.listMethods' => array(
  92. 'function' => 'this.listMethods',
  93. 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),
  94. 'docstring' => 'Returns an array of available methods on this server'),
  95. 'system.methodHelp' => array(
  96. 'function' => 'this.methodHelp',
  97. 'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),
  98. 'docstring' => 'Returns a documentation string for the specified method'),
  99. 'system.methodSignature' => array(
  100. 'function' => 'this.methodSignature',
  101. 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),
  102. 'docstring' => 'Returns an array describing the return type and required parameters of a method'),
  103. 'system.multicall' => array(
  104. 'function' => 'this.multicall',
  105. 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),
  106. 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')
  107. );
  108. }
  109. // --------------------------------------------------------------------
  110. /**
  111. * Main Server Function
  112. *
  113. * @access public
  114. * @return void
  115. */
  116. function serve()
  117. {
  118. $r = $this->parseRequest();
  119. $payload = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n";
  120. $payload .= $this->debug_msg;
  121. $payload .= $r->prepare_response();
  122. header("Content-Type: text/xml");
  123. header("Content-Length: ".strlen($payload));
  124. exit($payload);
  125. }
  126. // --------------------------------------------------------------------
  127. /**
  128. * Add Method to Class
  129. *
  130. * @access public
  131. * @param string method name
  132. * @param string function
  133. * @param string signature
  134. * @param string docstring
  135. * @return void
  136. */
  137. function add_to_map($methodname, $function, $sig, $doc)
  138. {
  139. $this->methods[$methodname] = array(
  140. 'function' => $function,
  141. 'signature' => $sig,
  142. 'docstring' => $doc
  143. );
  144. }
  145. // --------------------------------------------------------------------
  146. /**
  147. * Parse Server Request
  148. *
  149. * @access public
  150. * @param string data
  151. * @return object xmlrpc response
  152. */
  153. function parseRequest($data='')
  154. {
  155. global $HTTP_RAW_POST_DATA;
  156. //-------------------------------------
  157. // Get Data
  158. //-------------------------------------
  159. if ($data == '')
  160. {
  161. $data = $HTTP_RAW_POST_DATA;
  162. }
  163. //-------------------------------------
  164. // Set up XML Parser
  165. //-------------------------------------
  166. $parser = xml_parser_create($this->xmlrpc_defencoding);
  167. $parser_object = new XML_RPC_Message("filler");
  168. $parser_object->xh[$parser] = array();
  169. $parser_object->xh[$parser]['isf'] = 0;
  170. $parser_object->xh[$parser]['isf_reason'] = '';
  171. $parser_object->xh[$parser]['params'] = array();
  172. $parser_object->xh[$parser]['stack'] = array();
  173. $parser_object->xh[$parser]['valuestack'] = array();
  174. $parser_object->xh[$parser]['method'] = '';
  175. xml_set_object($parser, $parser_object);
  176. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
  177. xml_set_element_handler($parser, 'open_tag', 'closing_tag');
  178. xml_set_character_data_handler($parser, 'character_data');
  179. //xml_set_default_handler($parser, 'default_handler');
  180. //-------------------------------------
  181. // PARSE + PROCESS XML DATA
  182. //-------------------------------------
  183. if ( ! xml_parse($parser, $data, 1))
  184. {
  185. // return XML error as a faultCode
  186. $r = new XML_RPC_Response(0,
  187. $this->xmlrpcerrxml + xml_get_error_code($parser),
  188. sprintf('XML error: %s at line %d',
  189. xml_error_string(xml_get_error_code($parser)),
  190. xml_get_current_line_number($parser)));
  191. xml_parser_free($parser);
  192. }
  193. elseif ($parser_object->xh[$parser]['isf'])
  194. {
  195. return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
  196. }
  197. else
  198. {
  199. xml_parser_free($parser);
  200. $m = new XML_RPC_Message($parser_object->xh[$parser]['method']);
  201. $plist='';
  202. for ($i=0; $i < count($parser_object->xh[$parser]['params']); $i++)
  203. {
  204. if ($this->debug === TRUE)
  205. {
  206. $plist .= "$i - " . print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE). ";\n";
  207. }
  208. $m->addParam($parser_object->xh[$parser]['params'][$i]);
  209. }
  210. if ($this->debug === TRUE)
  211. {
  212. echo "<pre>";
  213. echo "---PLIST---\n" . $plist . "\n---PLIST END---\n\n";
  214. echo "</pre>";
  215. }
  216. $r = $this->_execute($m);
  217. }
  218. //-------------------------------------
  219. // SET DEBUGGING MESSAGE
  220. //-------------------------------------
  221. if ($this->debug === TRUE)
  222. {
  223. $this->debug_msg = "<!-- DEBUG INFO:\n\n".$plist."\n END DEBUG-->\n";
  224. }
  225. return $r;
  226. }
  227. // --------------------------------------------------------------------
  228. /**
  229. * Executes the Method
  230. *
  231. * @access protected
  232. * @param object
  233. * @return mixed
  234. */
  235. function _execute($m)
  236. {
  237. $methName = $m->method_name;
  238. // Check to see if it is a system call
  239. $system_call = (strncmp($methName, 'system', 5) == 0) ? TRUE : FALSE;
  240. if ($this->xss_clean == FALSE)
  241. {
  242. $m->xss_clean = FALSE;
  243. }
  244. //-------------------------------------
  245. // Valid Method
  246. //-------------------------------------
  247. if ( ! isset($this->methods[$methName]['function']))
  248. {
  249. return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
  250. }
  251. //-------------------------------------
  252. // Check for Method (and Object)
  253. //-------------------------------------
  254. $method_parts = explode(".", $this->methods[$methName]['function']);
  255. $objectCall = (isset($method_parts['1']) && $method_parts['1'] != "") ? TRUE : FALSE;
  256. if ($system_call === TRUE)
  257. {
  258. if ( ! is_callable(array($this,$method_parts['1'])))
  259. {
  260. return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
  261. }
  262. }
  263. else
  264. {
  265. if ($objectCall && ! is_callable(array($method_parts['0'],$method_parts['1'])))
  266. {
  267. return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
  268. }
  269. elseif ( ! $objectCall && ! is_callable($this->methods[$methName]['function']))
  270. {
  271. return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
  272. }
  273. }
  274. //-------------------------------------
  275. // Checking Methods Signature
  276. //-------------------------------------
  277. if (isset($this->methods[$methName]['signature']))
  278. {
  279. $sig = $this->methods[$methName]['signature'];
  280. for ($i=0; $i<count($sig); $i++)
  281. {
  282. $current_sig = $sig[$i];
  283. if (count($current_sig) == count($m->params)+1)
  284. {
  285. for ($n=0; $n < count($m->params); $n++)
  286. {
  287. $p = $m->params[$n];
  288. $pt = ($p->kindOf() == 'scalar') ? $p->scalarval() : $p->kindOf();
  289. if ($pt != $current_sig[$n+1])
  290. {
  291. $pno = $n+1;
  292. $wanted = $current_sig[$n+1];
  293. return new XML_RPC_Response(0,
  294. $this->xmlrpcerr['incorrect_params'],
  295. $this->xmlrpcstr['incorrect_params'] .
  296. ": Wanted {$wanted}, got {$pt} at param {$pno})");
  297. }
  298. }
  299. }
  300. }
  301. }
  302. //-------------------------------------
  303. // Calls the Function
  304. //-------------------------------------
  305. if ($objectCall === TRUE)
  306. {
  307. if ($method_parts[0] == "this" && $system_call == TRUE)
  308. {
  309. return call_user_func(array($this, $method_parts[1]), $m);
  310. }
  311. else
  312. {
  313. if ($this->object === FALSE)
  314. {
  315. $CI =& get_instance();
  316. return $CI->$method_parts['1']($m);
  317. }
  318. else
  319. {
  320. return $this->object->$method_parts['1']($m);
  321. //return call_user_func(array(&$method_parts['0'],$method_parts['1']), $m);
  322. }
  323. }
  324. }
  325. else
  326. {
  327. return call_user_func($this->methods[$methName]['function'], $m);
  328. }
  329. }
  330. // --------------------------------------------------------------------
  331. /**
  332. * Server Function: List Methods
  333. *
  334. * @access public
  335. * @param mixed
  336. * @return object
  337. */
  338. function listMethods($m)
  339. {
  340. $v = new XML_RPC_Values();
  341. $output = array();
  342. foreach ($this->methods as $key => $value)
  343. {
  344. $output[] = new XML_RPC_Values($key, 'string');
  345. }
  346. foreach ($this->system_methods as $key => $value)
  347. {
  348. $output[]= new XML_RPC_Values($key, 'string');
  349. }
  350. $v->addArray($output);
  351. return new XML_RPC_Response($v);
  352. }
  353. // --------------------------------------------------------------------
  354. /**
  355. * Server Function: Return Signature for Method
  356. *
  357. * @access public
  358. * @param mixed
  359. * @return object
  360. */
  361. function methodSignature($m)
  362. {
  363. $parameters = $m->output_parameters();
  364. $method_name = $parameters[0];
  365. if (isset($this->methods[$method_name]))
  366. {
  367. if ($this->methods[$method_name]['signature'])
  368. {
  369. $sigs = array();
  370. $signature = $this->methods[$method_name]['signature'];
  371. for ($i=0; $i < count($signature); $i++)
  372. {
  373. $cursig = array();
  374. $inSig = $signature[$i];
  375. for ($j=0; $j<count($inSig); $j++)
  376. {
  377. $cursig[]= new XML_RPC_Values($inSig[$j], 'string');
  378. }
  379. $sigs[]= new XML_RPC_Values($cursig, 'array');
  380. }
  381. $r = new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));
  382. }
  383. else
  384. {
  385. $r = new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));
  386. }
  387. }
  388. else
  389. {
  390. $r = new XML_RPC_Response(0,$this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
  391. }
  392. return $r;
  393. }
  394. // --------------------------------------------------------------------
  395. /**
  396. * Server Function: Doc String for Method
  397. *
  398. * @access public
  399. * @param mixed
  400. * @return object
  401. */
  402. function methodHelp($m)
  403. {
  404. $parameters = $m->output_parameters();
  405. $method_name = $parameters[0];
  406. if (isset($this->methods[$method_name]))
  407. {
  408. $docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : '';
  409. return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));
  410. }
  411. else
  412. {
  413. return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
  414. }
  415. }
  416. // --------------------------------------------------------------------
  417. /**
  418. * Server Function: Multi-call
  419. *
  420. * @access public
  421. * @param mixed
  422. * @return object
  423. */
  424. function multicall($m)
  425. {
  426. // Disabled
  427. return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
  428. $parameters = $m->output_parameters();
  429. $calls = $parameters[0];
  430. $result = array();
  431. foreach ($calls as $value)
  432. {
  433. //$attempt = $this->_execute(new XML_RPC_Message($value[0], $value[1]));
  434. $m = new XML_RPC_Message($value[0]);
  435. $plist='';
  436. for ($i=0; $i < count($value[1]); $i++)
  437. {
  438. $m->addParam(new XML_RPC_Values($value[1][$i], 'string'));
  439. }
  440. $attempt = $this->_execute($m);
  441. if ($attempt->faultCode() != 0)
  442. {
  443. return $attempt;
  444. }
  445. $result[] = new XML_RPC_Values(array($attempt->value()), 'array');
  446. }
  447. return new XML_RPC_Response(new XML_RPC_Values($result, 'array'));
  448. }
  449. // --------------------------------------------------------------------
  450. /**
  451. * Multi-call Function: Error Handling
  452. *
  453. * @access public
  454. * @param mixed
  455. * @return object
  456. */
  457. function multicall_error($err)
  458. {
  459. $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString();
  460. $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode();
  461. $struct['faultCode'] = new XML_RPC_Values($code, 'int');
  462. $struct['faultString'] = new XML_RPC_Values($str, 'string');
  463. return new XML_RPC_Values($struct, 'struct');
  464. }
  465. // --------------------------------------------------------------------
  466. /**
  467. * Multi-call Function: Processes method
  468. *
  469. * @access public
  470. * @param mixed
  471. * @return object
  472. */
  473. function do_multicall($call)
  474. {
  475. if ($call->kindOf() != 'struct')
  476. {
  477. return $this->multicall_error('notstruct');
  478. }
  479. elseif ( ! $methName = $call->me['struct']['methodName'])
  480. {
  481. return $this->multicall_error('nomethod');
  482. }
  483. list($scalar_type,$scalar_value)=each($methName->me);
  484. $scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
  485. if ($methName->kindOf() != 'scalar' OR $scalar_type != 'string')
  486. {
  487. return $this->multicall_error('notstring');
  488. }
  489. elseif ($scalar_value == 'system.multicall')
  490. {
  491. return $this->multicall_error('recursion');
  492. }
  493. elseif ( ! $params = $call->me['struct']['params'])
  494. {
  495. return $this->multicall_error('noparams');
  496. }
  497. elseif ($params->kindOf() != 'array')
  498. {
  499. return $this->multicall_error('notarray');
  500. }
  501. list($a,$b)=each($params->me);
  502. $numParams = count($b);
  503. $msg = new XML_RPC_Message($scalar_value);
  504. for ($i = 0; $i < $numParams; $i++)
  505. {
  506. $msg->params[] = $params->me['array'][$i];
  507. }
  508. $result = $this->_execute($msg);
  509. if ($result->faultCode() != 0)
  510. {
  511. return $this->multicall_error($result);
  512. }
  513. return new XML_RPC_Values(array($result->value()), 'array');
  514. }
  515. }
  516. // END XML_RPC_Server class
  517. /* End of file Xmlrpcs.php */
  518. /* Location: ./system/libraries/Xmlrpcs.php */