webmaster@1: 'xmlrpc_server_multicall', webmaster@1: array( webmaster@1: 'system.methodSignature', webmaster@1: 'xmlrpc_server_method_signature', webmaster@1: array('array', 'string'), webmaster@1: 'Returns an array describing the return type and required parameters of a method.' webmaster@1: ), webmaster@1: array( webmaster@1: 'system.getCapabilities', webmaster@1: 'xmlrpc_server_get_capabilities', webmaster@1: array('struct'), webmaster@1: 'Returns a struct describing the XML-RPC specifications supported by this server.' webmaster@1: ), webmaster@1: array( webmaster@1: 'system.listMethods', webmaster@1: 'xmlrpc_server_list_methods', webmaster@1: array('array'), webmaster@1: 'Returns an array of available methods on this server.'), webmaster@1: array( webmaster@1: 'system.methodHelp', webmaster@1: 'xmlrpc_server_method_help', webmaster@1: array('string', 'string'), webmaster@1: 'Returns a documentation string for the specified method.') webmaster@1: ); webmaster@1: // We build an array of all method names by combining the built-ins webmaster@1: // with those defined by modules implementing the _xmlrpc hook. webmaster@1: // Built-in methods are overridable. webmaster@1: foreach (array_merge($defaults, (array)$callbacks) as $key => $callback) { webmaster@1: // we could check for is_array($callback) webmaster@1: if (is_int($key)) { webmaster@1: $method = $callback[0]; webmaster@1: $xmlrpc_server->callbacks[$method] = $callback[1]; webmaster@1: $xmlrpc_server->signatures[$method] = $callback[2]; webmaster@1: $xmlrpc_server->help[$method] = $callback[3]; webmaster@1: } webmaster@1: else { webmaster@1: $xmlrpc_server->callbacks[$key] = $callback; webmaster@1: $xmlrpc_server->signatures[$key] = ''; webmaster@1: $xmlrpc_server->help[$key] = ''; webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: $data = file_get_contents('php://input'); webmaster@1: if (!$data) { webmaster@1: die('XML-RPC server accepts POST requests only.'); webmaster@1: } webmaster@1: $xmlrpc_server->message = xmlrpc_message($data); webmaster@1: if (!xmlrpc_message_parse($xmlrpc_server->message)) { webmaster@1: xmlrpc_server_error(-32700, t('Parse error. Request not well formed.')); webmaster@1: } webmaster@1: if ($xmlrpc_server->message->messagetype != 'methodCall') { webmaster@1: xmlrpc_server_error(-32600, t('Server error. Invalid XML-RPC. Request must be a methodCall.')); webmaster@1: } webmaster@1: xmlrpc_server_set($xmlrpc_server); webmaster@1: $result = xmlrpc_server_call($xmlrpc_server, $xmlrpc_server->message->methodname, $xmlrpc_server->message->params); webmaster@1: webmaster@1: if ($result->is_error) { webmaster@1: xmlrpc_server_error($result); webmaster@1: } webmaster@1: // Encode the result webmaster@1: $r = xmlrpc_value($result); webmaster@1: // Create the XML webmaster@1: $xml = ' webmaster@1: webmaster@1: webmaster@1: webmaster@1: '. webmaster@1: xmlrpc_value_get_xml($r) webmaster@1: .' webmaster@1: webmaster@1: webmaster@1: webmaster@1: webmaster@1: '; webmaster@1: // Send it webmaster@1: xmlrpc_server_output($xml); webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Throw an XML-RPC error. webmaster@1: * webmaster@1: * @param $error webmaster@1: * an error object OR integer error code webmaster@1: * @param $message webmaster@1: * description of error, used only if integer error code was passed webmaster@1: */ webmaster@1: function xmlrpc_server_error($error, $message = FALSE) { webmaster@1: if ($message && !is_object($error)) { webmaster@1: $error = xmlrpc_error($error, $message); webmaster@1: } webmaster@1: xmlrpc_server_output(xmlrpc_error_get_xml($error)); webmaster@1: } webmaster@1: webmaster@1: function xmlrpc_server_output($xml) { webmaster@1: $xml = ''."\n". $xml; webmaster@1: header('Connection: close'); webmaster@1: header('Content-Length: '. strlen($xml)); webmaster@1: header('Content-Type: text/xml'); webmaster@1: header('Date: '. date('r')); webmaster@1: echo $xml; webmaster@1: exit; webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Store a copy of the request temporarily. webmaster@1: * webmaster@1: * @param $xmlrpc_server webmaster@1: * Request object created by xmlrpc_server(). webmaster@1: */ webmaster@1: function xmlrpc_server_set($xmlrpc_server = NULL) { webmaster@1: static $server; webmaster@1: if (!isset($server)) { webmaster@1: $server = $xmlrpc_server; webmaster@1: } webmaster@1: return $server; webmaster@1: } webmaster@1: webmaster@1: // Retrieve the stored request. webmaster@1: function xmlrpc_server_get() { webmaster@1: return xmlrpc_server_set(); webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Dispatch the request and any parameters to the appropriate handler. webmaster@1: * webmaster@1: * @param $xmlrpc_server webmaster@1: * @param $methodname webmaster@1: * The external XML-RPC method name, e.g. 'system.methodHelp' webmaster@1: * @param $args webmaster@1: * Array containing any parameters that were sent along with the request. webmaster@1: */ webmaster@1: function xmlrpc_server_call($xmlrpc_server, $methodname, $args) { webmaster@1: // Make sure parameters are in an array webmaster@1: if ($args && !is_array($args)) { webmaster@1: $args = array($args); webmaster@1: } webmaster@1: // Has this method been mapped to a Drupal function by us or by modules? webmaster@1: if (!isset($xmlrpc_server->callbacks[$methodname])) { webmaster@1: return xmlrpc_error(-32601, t('Server error. Requested method %methodname not specified.', array("%methodname" => $xmlrpc_server->message->methodname))); webmaster@1: } webmaster@1: $method = $xmlrpc_server->callbacks[$methodname]; webmaster@1: $signature = $xmlrpc_server->signatures[$methodname]; webmaster@1: webmaster@1: // If the method has a signature, validate the request against the signature webmaster@1: if (is_array($signature)) { webmaster@1: $ok = TRUE; webmaster@1: $return_type = array_shift($signature); webmaster@1: // Check the number of arguments webmaster@1: if (count($args) != count($signature)) { webmaster@1: return xmlrpc_error(-32602, t('Server error. Wrong number of method parameters.')); webmaster@1: } webmaster@1: // Check the argument types webmaster@1: foreach ($signature as $key => $type) { webmaster@1: $arg = $args[$key]; webmaster@1: switch ($type) { webmaster@1: case 'int': webmaster@1: case 'i4': webmaster@1: if (is_array($arg) || !is_int($arg)) { webmaster@1: $ok = FALSE; webmaster@1: } webmaster@1: break; webmaster@1: case 'base64': webmaster@1: case 'string': webmaster@1: if (!is_string($arg)) { webmaster@1: $ok = FALSE; webmaster@1: } webmaster@1: break; webmaster@1: case 'boolean': webmaster@1: if ($arg !== FALSE && $arg !== TRUE) { webmaster@1: $ok = FALSE; webmaster@1: } webmaster@1: break; webmaster@1: case 'float': webmaster@1: case 'double': webmaster@1: if (!is_float($arg)) { webmaster@1: $ok = FALSE; webmaster@1: } webmaster@1: break; webmaster@1: case 'date': webmaster@1: case 'dateTime.iso8601': webmaster@1: if (!$arg->is_date) { webmaster@1: $ok = FALSE; webmaster@1: } webmaster@1: break; webmaster@1: } webmaster@1: if (!$ok) { webmaster@1: return xmlrpc_error(-32602, t('Server error. Invalid method parameters.')); webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: if (!function_exists($method)) { webmaster@1: return xmlrpc_error(-32601, t('Server error. Requested function %method does not exist.', array("%method" => $method))); webmaster@1: } webmaster@1: // Call the mapped function webmaster@1: return call_user_func_array($method, $args); webmaster@1: } webmaster@1: webmaster@1: function xmlrpc_server_multicall($methodcalls) { webmaster@1: // See http://www.xmlrpc.com/discuss/msgReader$1208 webmaster@1: $return = array(); webmaster@1: $xmlrpc_server = xmlrpc_server_get(); webmaster@1: foreach ($methodcalls as $call) { webmaster@1: $ok = TRUE; webmaster@1: if (!isset($call['methodName']) || !isset($call['params'])) { webmaster@1: $result = xmlrpc_error(3, t('Invalid syntax for system.multicall.')); webmaster@1: $ok = FALSE; webmaster@1: } webmaster@1: $method = $call['methodName']; webmaster@1: $params = $call['params']; webmaster@1: if ($method == 'system.multicall') { webmaster@1: $result = xmlrpc_error(-32600, t('Recursive calls to system.multicall are forbidden.')); webmaster@1: } webmaster@1: elseif ($ok) { webmaster@1: $result = xmlrpc_server_call($xmlrpc_server, $method, $params); webmaster@1: } webmaster@1: if ($result->is_error) { webmaster@1: $return[] = array( webmaster@1: 'faultCode' => $result->code, webmaster@1: 'faultString' => $result->message webmaster@1: ); webmaster@1: } webmaster@1: else { webmaster@1: $return[] = $result; webmaster@1: } webmaster@1: } webmaster@1: return $return; webmaster@1: } webmaster@1: webmaster@1: webmaster@1: /** webmaster@1: * XML-RPC method system.listMethods maps to this function. webmaster@1: */ webmaster@1: function xmlrpc_server_list_methods() { webmaster@1: $xmlrpc_server = xmlrpc_server_get(); webmaster@1: return array_keys($xmlrpc_server->callbacks); webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * XML-RPC method system.getCapabilities maps to this function. webmaster@1: * See http://groups.yahoo.com/group/xml-rpc/message/2897 webmaster@1: */ webmaster@1: function xmlrpc_server_get_capabilities() { webmaster@1: return array( webmaster@1: 'xmlrpc' => array( webmaster@1: 'specUrl' => 'http://www.xmlrpc.com/spec', webmaster@1: 'specVersion' => 1 webmaster@1: ), webmaster@1: 'faults_interop' => array( webmaster@1: 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', webmaster@1: 'specVersion' => 20010516 webmaster@1: ), webmaster@1: 'system.multicall' => array( webmaster@1: 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', webmaster@1: 'specVersion' => 1 webmaster@1: ), webmaster@1: 'introspection' => array( webmaster@1: 'specUrl' => 'http://scripts.incutio.com/xmlrpc/introspection.html', webmaster@1: 'specVersion' => 1 webmaster@1: ) webmaster@1: ); webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * XML-RPC method system.methodSignature maps to this function. webmaster@1: * webmaster@1: * @param $methodname webmaster@1: * Name of method for which we return a method signature. webmaster@1: * @return array webmaster@1: * An array of types representing the method signature of the webmaster@1: * function that the methodname maps to. The methodSignature of webmaster@1: * this function is 'array', 'string' because it takes an array webmaster@1: * and returns a string. webmaster@1: */ webmaster@1: function xmlrpc_server_method_signature($methodname) { webmaster@1: $xmlrpc_server = xmlrpc_server_get(); webmaster@1: if (!isset($xmlrpc_server->callbacks[$methodname])) { webmaster@1: return xmlrpc_error(-32601, t('Server error. Requested method %methodname not specified.', array("%methodname" => $methodname))); webmaster@1: } webmaster@1: if (!is_array($xmlrpc_server->signatures[$methodname])) { webmaster@1: return xmlrpc_error(-32601, t('Server error. Requested method %methodname signature not specified.', array("%methodname" => $methodname))); webmaster@1: } webmaster@1: // We array of types webmaster@1: $return = array(); webmaster@1: foreach ($xmlrpc_server->signatures[$methodname] as $type) { webmaster@1: $return[] = $type; webmaster@1: } webmaster@1: return $return; webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * XML-RPC method system.methodHelp maps to this function. webmaster@1: * webmaster@1: * @param $method webmaster@1: * Name of method for which we return a help string. webmaster@1: */ webmaster@1: function xmlrpc_server_method_help($method) { webmaster@1: $xmlrpc_server = xmlrpc_server_get(); webmaster@1: return $xmlrpc_server->help[$method]; webmaster@1: }