xmlrpcs.inc

  1. 7.x drupal-7.x/includes/xmlrpcs.inc
  2. 6.x drupal-6.x/includes/xmlrpcs.inc

File

drupal-6.x/includes/xmlrpcs.inc
View source
  1. <?php
  2. /**
  3. * The main entry point for XML-RPC requests.
  4. *
  5. * @param $callbacks
  6. * Array of external XML-RPC method names with the callbacks they map to.
  7. */
  8. function xmlrpc_server($callbacks) {
  9. $xmlrpc_server = new stdClass();
  10. // Define built-in XML-RPC method names
  11. $defaults = array(
  12. 'system.multicall' => 'xmlrpc_server_multicall',
  13. array(
  14. 'system.methodSignature',
  15. 'xmlrpc_server_method_signature',
  16. array('array', 'string'),
  17. 'Returns an array describing the return type and required parameters of a method.'
  18. ),
  19. array(
  20. 'system.getCapabilities',
  21. 'xmlrpc_server_get_capabilities',
  22. array('struct'),
  23. 'Returns a struct describing the XML-RPC specifications supported by this server.'
  24. ),
  25. array(
  26. 'system.listMethods',
  27. 'xmlrpc_server_list_methods',
  28. array('array'),
  29. 'Returns an array of available methods on this server.'),
  30. array(
  31. 'system.methodHelp',
  32. 'xmlrpc_server_method_help',
  33. array('string', 'string'),
  34. 'Returns a documentation string for the specified method.')
  35. );
  36. // We build an array of all method names by combining the built-ins
  37. // with those defined by modules implementing the _xmlrpc hook.
  38. // Built-in methods are overridable.
  39. foreach (array_merge($defaults, (array)$callbacks) as $key => $callback) {
  40. // we could check for is_array($callback)
  41. if (is_int($key)) {
  42. $method = $callback[0];
  43. $xmlrpc_server->callbacks[$method] = $callback[1];
  44. $xmlrpc_server->signatures[$method] = $callback[2];
  45. $xmlrpc_server->help[$method] = $callback[3];
  46. }
  47. else {
  48. $xmlrpc_server->callbacks[$key] = $callback;
  49. $xmlrpc_server->signatures[$key] = '';
  50. $xmlrpc_server->help[$key] = '';
  51. }
  52. }
  53. $data = file_get_contents('php://input');
  54. if (!$data) {
  55. die('XML-RPC server accepts POST requests only.');
  56. }
  57. $xmlrpc_server->message = xmlrpc_message($data);
  58. if (!xmlrpc_message_parse($xmlrpc_server->message)) {
  59. xmlrpc_server_error(-32700, t('Parse error. Request not well formed.'));
  60. }
  61. if ($xmlrpc_server->message->messagetype != 'methodCall') {
  62. xmlrpc_server_error(-32600, t('Server error. Invalid XML-RPC. Request must be a methodCall.'));
  63. }
  64. if (!isset($xmlrpc_server->message->params)) {
  65. $xmlrpc_server->message->params = array();
  66. }
  67. xmlrpc_server_set($xmlrpc_server);
  68. $result = xmlrpc_server_call($xmlrpc_server, $xmlrpc_server->message->methodname, $xmlrpc_server->message->params);
  69. if (is_object($result) && !empty($result->is_error)) {
  70. xmlrpc_server_error($result);
  71. }
  72. // Encode the result
  73. $r = xmlrpc_value($result);
  74. // Create the XML
  75. $xml = '
  76. <methodResponse>
  77. <params>
  78. <param>
  79. <value>'.
  80. xmlrpc_value_get_xml($r)
  81. .'</value>
  82. </param>
  83. </params>
  84. </methodResponse>
  85. ';
  86. // Send it
  87. xmlrpc_server_output($xml);
  88. }
  89. /**
  90. * Throw an XML-RPC error.
  91. *
  92. * @param $error
  93. * an error object OR integer error code
  94. * @param $message
  95. * description of error, used only if integer error code was passed
  96. */
  97. function xmlrpc_server_error($error, $message = FALSE) {
  98. if ($message && !is_object($error)) {
  99. $error = xmlrpc_error($error, $message);
  100. }
  101. xmlrpc_server_output(xmlrpc_error_get_xml($error));
  102. }
  103. function xmlrpc_server_output($xml) {
  104. $xml = '<?xml version="1.0"?>'."\n". $xml;
  105. header('Connection: close');
  106. header('Content-Length: '. strlen($xml));
  107. header('Content-Type: text/xml');
  108. header('Date: '. date('r'));
  109. echo $xml;
  110. exit;
  111. }
  112. /**
  113. * Store a copy of the request temporarily.
  114. *
  115. * @param $xmlrpc_server
  116. * Request object created by xmlrpc_server().
  117. */
  118. function xmlrpc_server_set($xmlrpc_server = NULL) {
  119. static $server;
  120. if (!isset($server)) {
  121. $server = $xmlrpc_server;
  122. }
  123. return $server;
  124. }
  125. // Retrieve the stored request.
  126. function xmlrpc_server_get() {
  127. return xmlrpc_server_set();
  128. }
  129. /**
  130. * Dispatch the request and any parameters to the appropriate handler.
  131. *
  132. * @param $xmlrpc_server
  133. * @param $methodname
  134. * The external XML-RPC method name, e.g. 'system.methodHelp'
  135. * @param $args
  136. * Array containing any parameters that were sent along with the request.
  137. */
  138. function xmlrpc_server_call($xmlrpc_server, $methodname, $args) {
  139. // Make sure parameters are in an array
  140. if ($args && !is_array($args)) {
  141. $args = array($args);
  142. }
  143. // Has this method been mapped to a Drupal function by us or by modules?
  144. if (!isset($xmlrpc_server->callbacks[$methodname])) {
  145. return xmlrpc_error(-32601, t('Server error. Requested method @methodname not specified.', array("@methodname" => $xmlrpc_server->message->methodname)));
  146. }
  147. $method = $xmlrpc_server->callbacks[$methodname];
  148. $signature = $xmlrpc_server->signatures[$methodname];
  149. // If the method has a signature, validate the request against the signature
  150. if (is_array($signature)) {
  151. $ok = TRUE;
  152. $return_type = array_shift($signature);
  153. // Check the number of arguments
  154. if (count($args) != count($signature)) {
  155. return xmlrpc_error(-32602, t('Server error. Wrong number of method parameters.'));
  156. }
  157. // Check the argument types
  158. foreach ($signature as $key => $type) {
  159. $arg = $args[$key];
  160. switch ($type) {
  161. case 'int':
  162. case 'i4':
  163. if (is_array($arg) || !is_int($arg)) {
  164. $ok = FALSE;
  165. }
  166. break;
  167. case 'base64':
  168. case 'string':
  169. if (!is_string($arg)) {
  170. $ok = FALSE;
  171. }
  172. break;
  173. case 'boolean':
  174. if ($arg !== FALSE && $arg !== TRUE) {
  175. $ok = FALSE;
  176. }
  177. break;
  178. case 'float':
  179. case 'double':
  180. if (!is_float($arg)) {
  181. $ok = FALSE;
  182. }
  183. break;
  184. case 'date':
  185. case 'dateTime.iso8601':
  186. if (!$arg->is_date) {
  187. $ok = FALSE;
  188. }
  189. break;
  190. }
  191. if (!$ok) {
  192. return xmlrpc_error(-32602, t('Server error. Invalid method parameters.'));
  193. }
  194. }
  195. }
  196. if (!function_exists($method)) {
  197. return xmlrpc_error(-32601, t('Server error. Requested function @method does not exist.', array("@method" => $method)));
  198. }
  199. // Call the mapped function
  200. return call_user_func_array($method, $args);
  201. }
  202. function xmlrpc_server_multicall($methodcalls) {
  203. // See http://www.xmlrpc.com/discuss/msgReader$1208
  204. $return = array();
  205. $xmlrpc_server = xmlrpc_server_get();
  206. foreach ($methodcalls as $call) {
  207. $ok = TRUE;
  208. if (!isset($call['methodName']) || !isset($call['params'])) {
  209. $result = xmlrpc_error(3, t('Invalid syntax for system.multicall.'));
  210. $ok = FALSE;
  211. }
  212. $method = $call['methodName'];
  213. $params = $call['params'];
  214. if ($method == 'system.multicall') {
  215. $result = xmlrpc_error(-32600, t('Recursive calls to system.multicall are forbidden.'));
  216. }
  217. elseif ($ok) {
  218. $result = xmlrpc_server_call($xmlrpc_server, $method, $params);
  219. }
  220. if (is_object($result) && !empty($result->is_error)) {
  221. $return[] = array(
  222. 'faultCode' => $result->code,
  223. 'faultString' => $result->message
  224. );
  225. }
  226. else {
  227. $return[] = $result;
  228. }
  229. }
  230. return $return;
  231. }
  232. /**
  233. * XML-RPC method system.listMethods maps to this function.
  234. */
  235. function xmlrpc_server_list_methods() {
  236. $xmlrpc_server = xmlrpc_server_get();
  237. return array_keys($xmlrpc_server->callbacks);
  238. }
  239. /**
  240. * XML-RPC method system.getCapabilities maps to this function.
  241. * See http://groups.yahoo.com/group/xml-rpc/message/2897
  242. */
  243. function xmlrpc_server_get_capabilities() {
  244. return array(
  245. 'xmlrpc' => array(
  246. 'specUrl' => 'http://www.xmlrpc.com/spec',
  247. 'specVersion' => 1
  248. ),
  249. 'faults_interop' => array(
  250. 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
  251. 'specVersion' => 20010516
  252. ),
  253. 'system.multicall' => array(
  254. 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
  255. 'specVersion' => 1
  256. ),
  257. 'introspection' => array(
  258. 'specUrl' => 'http://scripts.incutio.com/xmlrpc/introspection.html',
  259. 'specVersion' => 1
  260. )
  261. );
  262. }
  263. /**
  264. * XML-RPC method system.methodSignature maps to this function.
  265. *
  266. * @param $methodname
  267. * Name of method for which we return a method signature.
  268. * @return array
  269. * An array of types representing the method signature of the
  270. * function that the methodname maps to. The methodSignature of
  271. * this function is 'array', 'string' because it takes an array
  272. * and returns a string.
  273. */
  274. function xmlrpc_server_method_signature($methodname) {
  275. $xmlrpc_server = xmlrpc_server_get();
  276. if (!isset($xmlrpc_server->callbacks[$methodname])) {
  277. return xmlrpc_error(-32601, t('Server error. Requested method @methodname not specified.', array("@methodname" => $methodname)));
  278. }
  279. if (!is_array($xmlrpc_server->signatures[$methodname])) {
  280. return xmlrpc_error(-32601, t('Server error. Requested method @methodname signature not specified.', array("@methodname" => $methodname)));
  281. }
  282. // We array of types
  283. $return = array();
  284. foreach ($xmlrpc_server->signatures[$methodname] as $type) {
  285. $return[] = $type;
  286. }
  287. return $return;
  288. }
  289. /**
  290. * XML-RPC method system.methodHelp maps to this function.
  291. *
  292. * @param $method
  293. * Name of method for which we return a help string.
  294. */
  295. function xmlrpc_server_method_help($method) {
  296. $xmlrpc_server = xmlrpc_server_get();
  297. return $xmlrpc_server->help[$method];
  298. }