Simpletest Coverage - includes/xmlrpcs.inc

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

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.