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