jsonrpcs.inc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <?php
  2. /**
  3. * @package modx
  4. * @subpackage jsonrpc
  5. */
  6. /**
  7. * JSON extension to the PHP-XMLRPC lib: server components
  8. *
  9. * For more info see:
  10. * http://www.json.org/
  11. * http://json-rpc.org/
  12. *
  13. * @author Gaetano Giunta
  14. * @version $Id: jsonrpcs.inc,v 1.10 2007/02/15 21:48:38 ggiunta Exp $
  15. * @copyright (c) 2005 G. Giunta
  16. *
  17. * @todo implement dispatching of multicall requests, json way
  18. * @todo test system.XXX methods, with special care to multicall
  19. * @todo support for 'ping' calls, i.e. if id is null, echo back nothing
  20. **/
  21. // JSON RPC Server class
  22. // requires: jsonrpc.inc, xmlrpcs.inc, xmlrpc.inc
  23. // add to list of supported capaibilities the jsonrpc spec
  24. $GLOBALS['xmlrpcs_capabilities']['json-rpc'] = new xmlrpcval(array(
  25. 'specUrl' => new xmlrpcval('http://json-rpc.org/wiki/specification', 'string'),
  26. 'specVersion' => new xmlrpcval(1, 'int')
  27. ), 'struct');
  28. // NB: if building jsonrpc-only webservers, you should at least undeclare the xmlrpc capability:
  29. // unset($GLOBALS['xmlrpcs_capabilities']['xmlrpc']);
  30. class jsonrpc_server extends xmlrpc_server
  31. {
  32. //var $allow_system_funcs = false;
  33. var $functions_parameters_type='jsonrpcvals';
  34. function serializeDebug($charset_encoding='')
  35. {
  36. $out = '';
  37. if ($this->debug_info != '')
  38. {
  39. $out .= "/* SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n*/\n";
  40. }
  41. if ($GLOBALS['_xmlrpc_debuginfo'] != '')
  42. {
  43. $out .= "/* DEBUG INFO:\n\n" . json_encode_entitites($GLOBALS['_xmlrpc_debuginfo'], null, $charset_encoding) . "\n*/\n";
  44. }
  45. return $out;
  46. }
  47. /**
  48. * Note: syntax differs from overridden method, by adding an ID param
  49. * @access private
  50. */
  51. function execute($m, $params=null, $paramtypes=null, $msgID=null)
  52. {
  53. if (is_object($m))
  54. {
  55. // watch out: if $m is an xmlrpcmsg obj, this will raise a warning: no id memeber...
  56. $methName = $m->method();
  57. $msgID = $m->id;
  58. }
  59. else
  60. {
  61. $methName = $m;
  62. }
  63. $sysCall = $this->allow_system_funcs && @ereg("^system\.", $methName);
  64. $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
  65. if(!isset($dmap[$methName]['function']))
  66. {
  67. // No such method
  68. return new jsonrpcresp(0,
  69. $GLOBALS['xmlrpcerr']['unknown_method'],
  70. $GLOBALS['xmlrpcstr']['unknown_method']);
  71. }
  72. // Check signature
  73. if(isset($dmap[$methName]['signature']))
  74. {
  75. $sig = $dmap[$methName]['signature'];
  76. if (is_object($m))
  77. {
  78. list($ok, $errstr) = $this->verifySignature($m, $sig);
  79. }
  80. else
  81. {
  82. list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
  83. }
  84. if(!$ok)
  85. {
  86. // Didn't match.
  87. return new jsonrpcresp(
  88. 0,
  89. $GLOBALS['xmlrpcerr']['incorrect_params'],
  90. $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
  91. );
  92. }
  93. }
  94. $func = $dmap[$methName]['function'];
  95. // let the 'class::function' syntax be accepted in dispatch maps
  96. if(is_string($func) && strpos($func, '::'))
  97. {
  98. $func = explode('::', $func);
  99. }
  100. // verify that function to be invoked is in fact callable
  101. if(!is_callable($func))
  102. {
  103. error_log("XML-RPC: jsonrpc_server::execute: function $func registered as method handler is not callable");
  104. return new jsonrpcresp(
  105. 0,
  106. $GLOBALS['xmlrpcerr']['server_error'],
  107. $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
  108. );
  109. }
  110. // If debug level is 3, we should catch all errors generated during
  111. // processing of user function, and log them as part of response
  112. if($this->debug > 2)
  113. {
  114. $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
  115. }
  116. if (is_object($m))
  117. {
  118. if($sysCall)
  119. {
  120. $r = call_user_func($func, $this, $m);
  121. }
  122. else
  123. {
  124. $r = call_user_func($func, $m);
  125. }
  126. if (!($r instanceof xmlrpcresp))
  127. {
  128. error_log("XML-RPC: jsonrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object");
  129. if ($r instanceof xmlrpcval) {
  130. $r = new jsonrpcresp($r);
  131. }
  132. else
  133. {
  134. $r = new jsonrpcresp(
  135. 0,
  136. $GLOBALS['xmlrpcerr']['server_error'],
  137. $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return jsonrpcresp or xmlrpcresp object"
  138. );
  139. }
  140. }
  141. }
  142. else
  143. {
  144. // call a 'plain php' function
  145. if($sysCall)
  146. {
  147. array_unshift($params, $this);
  148. $r = call_user_func_array($func,$params);
  149. }
  150. else
  151. {
  152. // 3rd API convention for method-handling functions: EPI-style
  153. if ($this->functions_parameters_type == 'epivals')
  154. {
  155. $r = call_user_func_array($func, array($methName, $params, $this->user_data));
  156. // mimic EPI behaviour: if we get an array that looks like an error, make it
  157. // an eror response
  158. if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
  159. {
  160. $r = new jsonrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
  161. }
  162. else
  163. {
  164. // functions using EPI api should NOT return resp objects,
  165. // so make sure we encode the return type correctly
  166. $r = new jsonrpcresp(php_xmlrpc_encode($r, array('extension_api')));
  167. }
  168. }
  169. else
  170. {
  171. $r = call_user_func_array($func, $params);
  172. }
  173. }
  174. // the return type can be either an xmlrpcresp object or a plain php value...
  175. if (!($r instanceof xmlrpcresp))
  176. {
  177. // what should we assume here about automatic encoding of datetimes
  178. // and php classes instances???
  179. $r = new jsonrpcresp(php_jsonrpc_encode($r));
  180. }
  181. }
  182. // here $r is either an xmlrpcresp or jsonrpcresp
  183. if (!($r instanceof jsonrpcresp))
  184. {
  185. // dirty trick: user has given us back an xmlrpc response,
  186. // since he had an existing xmlrpc server with boatloads of code.
  187. // Be nice to him, and serialize the xmlrpc stuff into JSON.
  188. // We also override the content_type of the xmlrpc response,
  189. // but lack knoweledge of intented response charset...
  190. $r->content_type = 'application/json';
  191. $r->payload = serialize_jsonrpcresp($r, $msgID);
  192. }
  193. else
  194. {
  195. $r->id = $msgID;
  196. }
  197. if($this->debug > 2)
  198. {
  199. // note: restore the error handler we found before calling the
  200. // user func, even if it has been changed inside the func itself
  201. if($GLOBALS['_xmlrpcs_prev_ehandler'])
  202. {
  203. set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
  204. }
  205. else
  206. {
  207. restore_error_handler();
  208. }
  209. }
  210. return $r;
  211. }
  212. /**
  213. * @access private
  214. */
  215. function parseRequest($data, $content_encoding='')
  216. {
  217. $GLOBALS['_xh']=array();
  218. if (!jsonrpc_parse_req($data, $this->functions_parameters_type == 'phpvals' || $this->functions_parameters_type == 'epivals', false, $content_encoding))
  219. {
  220. $r = new jsonrpcresp(0,
  221. $GLOBALS['xmlrpcerr']['invalid_request'],
  222. $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
  223. }
  224. else
  225. {
  226. if ($this->functions_parameters_type == 'phpvals' || $this->functions_parameters_type == 'epivals')
  227. {
  228. if($this->debug > 1)
  229. {
  230. $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
  231. }
  232. $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt'], $GLOBALS['_xh']['id']);
  233. }
  234. else
  235. {
  236. // build an xmlrpcmsg object with data parsed from xml
  237. $m = new jsonrpcmsg($GLOBALS['_xh']['method'], 0, $GLOBALS['_xh']['id']);
  238. // now add parameters in
  239. /// @todo for more speeed, we could just substitute the array...
  240. for($i = 0; $i < sizeof($GLOBALS['_xh']['params']); $i++)
  241. {
  242. $m->addParam($GLOBALS['_xh']['params'][$i]);
  243. }
  244. if($this->debug > 1)
  245. {
  246. $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
  247. }
  248. $r = $this->execute($m);
  249. }
  250. }
  251. return $r;
  252. }
  253. /**
  254. * No xml header generated by the server, since we are sending json
  255. * @access private
  256. */
  257. function xml_header($charset_encoding='')
  258. {
  259. return '';
  260. }
  261. }
  262. ?>