71558f85 |
1 | <?php |
2 | /** |
3 | * An XML-RPC server |
4 | * |
5 | * @author Donal McMullan donal@catalyst.net.nz |
6 | * @version 0.0.1 |
7 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License |
8 | * @package mnet |
9 | */ |
10 | |
11 | // Make certain that config.php doesn't display any errors, and that it doesn't |
12 | // override our do-not-display-errors setting: |
13 | ini_set('display_errors',0); |
14 | require_once(dirname(dirname(dirname(__FILE__))) . '/config.php'); |
15 | ini_set('display_errors',0); |
16 | |
17 | // Include MNET stuff: |
18 | require_once $CFG->dirroot.'/mnet/lib.php'; |
19 | require_once $CFG->dirroot.'/mnet/remote_client.php'; |
20 | |
600be062 |
21 | if ($CFG->mnet_dispatcher_mode === 'off') { |
22 | print_error('mnetdisabled', 'mnet'); |
23 | } |
24 | |
71558f85 |
25 | // Content type for output is not html: |
1008dad6 |
26 | header('Content-type: text/xml; charset=utf-8'); |
71558f85 |
27 | |
25202581 |
28 | // PHP 5.2.2: $HTTP_RAW_POST_DATA not populated bug: |
29 | // http://bugs.php.net/bug.php?id=41293 |
30 | if (empty($HTTP_RAW_POST_DATA)) { |
31 | $HTTP_RAW_POST_DATA = file_get_contents('php://input'); |
32 | } |
33 | |
71558f85 |
34 | if (!empty($CFG->mnet_rpcdebug)) { |
35 | trigger_error("HTTP_RAW_POST_DATA"); |
36 | trigger_error($HTTP_RAW_POST_DATA); |
37 | } |
38 | |
8d60e942 |
39 | if (!isset($_SERVER)) { |
40 | exit(mnet_server_fault(712, "phperror")); |
41 | } |
42 | |
43 | |
71558f85 |
44 | // New global variable which ONLY gets set in this server page, so you know that |
45 | // if you've been called by a remote Moodle, this should be set: |
46 | $MNET_REMOTE_CLIENT = new mnet_remote_client(); |
47 | |
8d60e942 |
48 | $plaintextmessage = mnet_server_strip_encryption($HTTP_RAW_POST_DATA); |
49 | $xmlrpcrequest = mnet_server_strip_signature($plaintextmessage); |
50 | |
334e66c1 |
51 | if($MNET_REMOTE_CLIENT->pushkey == true) { |
8d60e942 |
52 | // The peer used one of our older public keys, we will return a |
53 | // signed/encrypted error message containing our new public key |
54 | // Sign message with our old key, and encrypt to the peer's private key. |
334e66c1 |
55 | exit(mnet_server_fault_xml(7025, $MNET->public_key, $MNET_REMOTE_CLIENT->useprivatekey)); |
8d60e942 |
56 | } |
57 | // Have a peek at what the request would be if we were to process it |
58 | $params = xmlrpc_decode_request($xmlrpcrequest, $method); |
59 | |
60 | // One of three conditions need to be met before we continue processing this request: |
61 | // 1. Request is properly encrypted and signed |
62 | // 2. Request is for a keyswap (we don't mind enencrypted or unsigned requests for a public key) |
63 | // 3. Request is properly signed and we're happy with it being unencrypted |
64 | if ((($MNET_REMOTE_CLIENT->request_was_encrypted == true) && ($MNET_REMOTE_CLIENT->signatureok == true)) |
65 | || (($method == 'system.keyswap') || ($method == 'system/keyswap')) |
66 | || (($MNET_REMOTE_CLIENT->signatureok == true) && ($MNET_REMOTE_CLIENT->plaintext_is_ok() == true))) { |
67 | $response = mnet_server_dispatch($xmlrpcrequest); |
68 | } else { |
69 | if (($MNET_REMOTE_CLIENT->request_was_encrypted == false) && ($MNET_REMOTE_CLIENT->plaintext_is_ok() == false)) { |
70 | exit(mnet_server_fault(7021, 'forbidden-transport')); |
71 | } |
72 | |
73 | if ($MNET_REMOTE_CLIENT->request_was_signed == false) { |
74 | // Request was not signed |
75 | exit(mnet_server_fault(711, 'verifysignature-error')); |
76 | } |
77 | |
78 | if ($MNET_REMOTE_CLIENT->signatureok == false) { |
79 | // We were unable to verify the signature |
80 | exit(mnet_server_fault(710, 'verifysignature-invalid')); |
81 | } |
82 | } |
83 | |
71558f85 |
84 | |
85 | if (!empty($CFG->mnet_rpcdebug)) { |
86 | trigger_error("XMLRPC Payload"); |
5f6b28fa |
87 | trigger_error(print_r($xmlrpcrequest,1)); |
71558f85 |
88 | } |
89 | |
71558f85 |
90 | /** |
71558f85 |
91 | * -----XML-Envelope--------------------------------- |
92 | * | | |
93 | * | Encrypted-Symmetric-key---------------- | |
94 | * | |_____________________________________| | |
95 | * | | |
96 | * | Encrypted data------------------------- | |
97 | * | | | | |
98 | * | | -XML-Envelope------------------ | | |
99 | * | | | | | | |
100 | * | | | --Signature------------- | | | |
101 | * | | | |______________________| | | | |
102 | * | | | | | | |
103 | * | | | --Signed-Payload-------- | | | |
104 | * | | | | | | | | |
105 | * | | | | XML-RPC Request | | | | |
106 | * | | | |______________________| | | | |
107 | * | | | | | | |
108 | * | | |_____________________________| | | |
109 | * | |_____________________________________| | |
110 | * | | |
111 | * |________________________________________________| |
112 | * |
71558f85 |
113 | */ |
5f6b28fa |
114 | |
8d60e942 |
115 | /* Strip encryption envelope (if present) and decrypt data |
5f6b28fa |
116 | * |
117 | * @param string $HTTP_RAW_POST_DATA The XML that the client sent |
8d60e942 |
118 | * @return string XML with any encryption envolope removed |
5f6b28fa |
119 | */ |
8d60e942 |
120 | function mnet_server_strip_encryption($HTTP_RAW_POST_DATA) { |
71558f85 |
121 | global $MNET, $MNET_REMOTE_CLIENT; |
2a75520f |
122 | $crypt_parser = new mnet_encxml_parser(); |
123 | $crypt_parser->parse($HTTP_RAW_POST_DATA); |
71558f85 |
124 | |
8d60e942 |
125 | if (!$crypt_parser->payload_encrypted) { |
126 | return $HTTP_RAW_POST_DATA; |
127 | } |
128 | |
2a75520f |
129 | // Make sure we know who we're talking to |
130 | $host_record_exists = $MNET_REMOTE_CLIENT->set_wwwroot($crypt_parser->remote_wwwroot); |
9dd0e611 |
131 | |
2a75520f |
132 | if (false == $host_record_exists) { |
133 | exit(mnet_server_fault(7020, 'wrong-wwwroot', $crypt_parser->remote_wwwroot)); |
134 | } |
9dd0e611 |
135 | |
5f6b28fa |
136 | // This key is symmetric, and is itself encrypted. Can be decrypted using our private key |
137 | $key = array_pop($crypt_parser->cipher); |
138 | // This data is symmetrically encrypted, can be decrypted using the above key |
139 | $data = array_pop($crypt_parser->cipher); |
140 | |
141 | $crypt_parser->free_resource(); |
142 | $payload = ''; // Initialize payload var |
143 | |
144 | // &$payload |
145 | $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $MNET->get_private_key()); |
8d60e942 |
146 | if ($isOpen) { |
147 | $MNET_REMOTE_CLIENT->was_encrypted(); |
148 | return $payload; |
149 | } |
5f6b28fa |
150 | |
8d60e942 |
151 | // Decryption failed... let's try our archived keys |
152 | $openssl_history = get_config('mnet', 'openssl_history'); |
153 | if(empty($openssl_history)) { |
154 | $openssl_history = array(); |
155 | set_config('openssl_history', serialize($openssl_history), 'mnet'); |
156 | } else { |
157 | $openssl_history = unserialize($openssl_history); |
158 | } |
159 | foreach($openssl_history as $keyset) { |
160 | $keyresource = openssl_pkey_get_private($keyset['keypair_PEM']); |
161 | $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $keyresource); |
162 | if ($isOpen) { |
163 | // It's an older code, sir, but it checks out |
164 | |
165 | $MNET_REMOTE_CLIENT->was_encrypted(); |
166 | $MNET_REMOTE_CLIENT->encrypted_to($keyresource); |
167 | $MNET_REMOTE_CLIENT->set_pushkey(); |
168 | return $payload; |
71558f85 |
169 | } |
5f6b28fa |
170 | } |
71558f85 |
171 | |
8d60e942 |
172 | //If after all that we still couldn't decrypt the message, error out. |
173 | exit(mnet_server_fault(7023, 'encryption-invalid')); |
5f6b28fa |
174 | } |
71558f85 |
175 | |
8d60e942 |
176 | /* Strip signature envelope (if present), try to verify any signature using our record of remote peer's public key. |
5f6b28fa |
177 | * |
178 | * @param string $plaintextmessage XML envelope containing XMLRPC request and signature |
179 | * @return string XMLRPC request |
180 | */ |
8d60e942 |
181 | function mnet_server_strip_signature($plaintextmessage) { |
5f6b28fa |
182 | global $MNET, $MNET_REMOTE_CLIENT; |
183 | $sig_parser = new mnet_encxml_parser(); |
184 | $sig_parser->parse($plaintextmessage); |
71558f85 |
185 | |
8d60e942 |
186 | if ($sig_parser->signature == '') { |
187 | return $plaintextmessage; |
2a75520f |
188 | } |
189 | |
8d60e942 |
190 | // Record that the request was signed in some way |
191 | $MNET_REMOTE_CLIENT->was_signed(); |
192 | |
193 | // Load any information we have about this mnet peer |
194 | $MNET_REMOTE_CLIENT->set_wwwroot($crypt_parser->remote_wwwroot); |
195 | |
2a75520f |
196 | $payload = base64_decode($sig_parser->data_object); |
5f6b28fa |
197 | $signature = base64_decode($sig_parser->signature); |
8d60e942 |
198 | $certificate = $MNET_REMOTE_CLIENT->public_key; |
199 | |
c8a6dfee |
200 | // If we don't have any certificate for the host, don't try to check the signature |
8d60e942 |
201 | // Just return the parsed request |
202 | if ($certificate == false) { |
203 | return $payload; |
204 | } |
2a75520f |
205 | |
206 | // Does the signature match the data and the public cert? |
5f6b28fa |
207 | $signature_verified = openssl_verify($payload, $signature, $certificate); |
454a6e7c |
208 | if ($signature_verified == 0) { |
209 | // $signature was not generated for $payload using $certificate |
210 | // Get the key the remote peer is currently publishing: |
2a75520f |
211 | $currkey = mnet_get_public_key($MNET_REMOTE_CLIENT->wwwroot, $MNET_REMOTE_CLIENT->application); |
454a6e7c |
212 | // If the key the remote peer is currently publishing is different to $certificate |
2a75520f |
213 | if($currkey != $certificate) { |
8d60e942 |
214 | // Try and get the server's new key through trusted means |
215 | $MNET_REMOTE_CLIENT->refresh_key(); |
988b0b22 |
216 | // If we did manage to re-key, try to verify the signature again using the new public key. |
217 | $certificate = $MNET_REMOTE_CLIENT->public_key; |
250f84d6 |
218 | $signature_verified = openssl_verify($payload, $signature, $certificate); |
71558f85 |
219 | } |
454a6e7c |
220 | } |
221 | |
222 | if ($signature_verified == 1) { |
8d60e942 |
223 | $MNET_REMOTE_CLIENT->signature_verified(); |
454a6e7c |
224 | $MNET_REMOTE_CLIENT->touch(); |
71558f85 |
225 | } |
2a75520f |
226 | |
227 | $sig_parser->free_resource(); |
228 | |
229 | return $payload; |
71558f85 |
230 | } |
231 | |
232 | /** |
151a9872 |
233 | * Return the proper XML-RPC content to report an error in the local language. |
71558f85 |
234 | * |
235 | * @param int $code The ID code of the error message |
151a9872 |
236 | * @param string $text The array-key of the error message in the lang file |
ea676d6e |
237 | * or the full string (will be detected by the function |
151a9872 |
238 | * @param string $param The $a param for the error message in the lang file |
71558f85 |
239 | * @return string $text The text of the error message |
240 | */ |
241 | function mnet_server_fault($code, $text, $param = null) { |
151a9872 |
242 | global $MNET_REMOTE_CLIENT; |
71558f85 |
243 | if (!is_numeric($code)) { |
244 | $code = 0; |
245 | } |
246 | $code = intval($code); |
247 | |
ea676d6e |
248 | $string = get_string($text, 'mnet', $param); |
249 | if (strpos($string, '[[') === 0) { |
250 | $string = $text; |
251 | } |
252 | |
253 | return mnet_server_fault_xml($code, $string); |
151a9872 |
254 | } |
71558f85 |
255 | |
151a9872 |
256 | /** |
257 | * Return the proper XML-RPC content to report an error. |
258 | * |
09f0abb2 |
259 | * @param int $code The ID code of the error message |
260 | * @param string $text The error message |
261 | * @param resource $privatekey The private key that should be used to sign the response |
262 | * @return string $text The XML text of the error message |
151a9872 |
263 | */ |
09f0abb2 |
264 | function mnet_server_fault_xml($code, $text, $privatekey = null) { |
573f8b02 |
265 | global $MNET_REMOTE_CLIENT, $CFG; |
71558f85 |
266 | // Replace illegal XML chars - is this already in a lib somewhere? |
267 | $text = str_replace(array('<','>','&','"',"'"), array('<','>','&','"','''), $text); |
268 | |
269 | $return = mnet_server_prepare_response('<?xml version="1.0"?> |
270 | <methodResponse> |
271 | <fault> |
272 | <value> |
273 | <struct> |
274 | <member> |
275 | <name>faultCode</name> |
276 | <value><int>'.$code.'</int></value> |
277 | </member> |
278 | <member> |
279 | <name>faultString</name> |
280 | <value><string>'.$text.'</string></value> |
25202581 |
281 | </member> |
282 | </struct> |
283 | </value> |
284 | </fault> |
09f0abb2 |
285 | </methodResponse>', $privatekey); |
573f8b02 |
286 | |
287 | if (!empty($CFG->mnet_rpcdebug)) { |
3d706b49 |
288 | trigger_error("XMLRPC Error Response $code: $text"); |
573f8b02 |
289 | trigger_error(print_r($return,1)); |
290 | } |
291 | |
71558f85 |
292 | return $return; |
293 | } |
294 | |
295 | /** |
296 | * Dummy function for the XML-RPC dispatcher - use to call a method on an object |
297 | * or to call a function |
298 | * |
299 | * Translate XML-RPC's strange function call syntax into a more straightforward |
300 | * PHP-friendly alternative. This dummy function will be called by the |
301 | * dispatcher, and can be used to call a method on an object, or just a function |
302 | * |
303 | * The methodName argument (eg. mnet/testlib/mnet_concatenate_strings) |
304 | * is ignored. |
305 | * |
306 | * @param string $methodname We discard this - see 'functionname' |
307 | * @param array $argsarray Each element is an argument to the real |
308 | * function |
309 | * @param string $functionname The name of the PHP function you want to call |
310 | * @return mixed The return value will be that of the real |
311 | * function, whateber it may be. |
312 | */ |
313 | function mnet_server_dummy_method($methodname, $argsarray, $functionname) { |
314 | global $MNET_REMOTE_CLIENT; |
315 | |
b91b274b |
316 | if (is_object($MNET_REMOTE_CLIENT->object_to_call)) { |
71558f85 |
317 | return @call_user_method_array($functionname, $MNET_REMOTE_CLIENT->object_to_call, $argsarray); |
b91b274b |
318 | } else if (!empty($MNET_REMOTE_CLIENT->static_location)) { |
319 | return @call_user_func_array(array($MNET_REMOTE_CLIENT->static_location, $functionname), $argsarray); |
320 | } else { |
321 | return @call_user_func_array($functionname, $argsarray); |
71558f85 |
322 | } |
323 | } |
324 | |
325 | /** |
326 | * Package a response in any required envelope, and return it to the client |
327 | * |
09f0abb2 |
328 | * @param string $response The XMLRPC response string |
329 | * @param resource $privatekey The private key to sign the response with |
330 | * @return string The encoded response string |
71558f85 |
331 | */ |
09f0abb2 |
332 | function mnet_server_prepare_response($response, $privatekey = null) { |
71558f85 |
333 | global $MNET_REMOTE_CLIENT; |
334 | |
335 | if ($MNET_REMOTE_CLIENT->request_was_signed) { |
09f0abb2 |
336 | $response = mnet_sign_message($response, $privatekey); |
71558f85 |
337 | } |
338 | |
339 | if ($MNET_REMOTE_CLIENT->request_was_encrypted) { |
340 | $response = mnet_encrypt_message($response, $MNET_REMOTE_CLIENT->public_key); |
341 | } |
342 | |
343 | return $response; |
344 | } |
345 | |
346 | /** |
347 | * If security checks are passed, dispatch the request to the function/method |
348 | * |
349 | * The config variable 'mnet_dispatcher_mode' can be: |
350 | * strict: Only execute functions that are in specific files |
351 | * off: The default - don't execute anything |
352 | * |
353 | * @param string $payload The XML-RPC request |
354 | * @return No return val - just echo the response |
355 | */ |
356 | function mnet_server_dispatch($payload) { |
357 | global $CFG, $MNET_REMOTE_CLIENT; |
358 | // xmlrpc_decode_request returns an array of parameters, and the $method |
359 | // variable (which is passed by reference) is instantiated with the value from |
360 | // the methodName tag in the xml payload |
361 | // xmlrpc_decode_request($xml, &$method) |
362 | $params = xmlrpc_decode_request($payload, $method); |
363 | |
b26a847b |
364 | // $method is something like: "mod/forum/lib.php/forum_add_instance" |
71558f85 |
365 | // $params is an array of parameters. A parameter might itself be an array. |
366 | |
367 | // Whitelist characters that are permitted in a method name |
368 | // The method name must not begin with a / - avoid absolute paths |
369 | // A dot character . is only allowed in the filename, i.e. something.php |
370 | if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_-]+(\.php/)?[A-Za-z0-9_-]+$@",$method)) { |
371 | exit(mnet_server_fault(713, 'nosuchfunction')); |
372 | } |
373 | |
4fc66605 |
374 | if(preg_match("/^system\./", $method)) { |
b26a847b |
375 | $callstack = explode('.', $method); |
376 | } else { |
377 | $callstack = explode('/', $method); |
378 | // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); |
379 | } |
71558f85 |
380 | |
381 | /** |
382 | * What has the site administrator chosen as his dispatcher setting? |
383 | * strict: Only execute functions that are in specific files |
384 | * off: The default - don't execute anything |
385 | */ |
386 | ////////////////////////////////////// OFF |
387 | if (!isset($CFG->mnet_dispatcher_mode) ) { |
388 | set_config('mnet_dispatcher_mode', 'off'); |
389 | exit(mnet_server_fault(704, 'nosuchservice')); |
390 | } elseif ('off' == $CFG->mnet_dispatcher_mode) { |
391 | exit(mnet_server_fault(704, 'nosuchservice')); |
392 | |
393 | ////////////////////////////////////// SYSTEM METHODS |
394 | } elseif ($callstack[0] == 'system') { |
395 | $functionname = $callstack[1]; |
396 | $xmlrpcserver = xmlrpc_server_create(); |
397 | |
398 | // I'm adding the canonical xmlrpc references here, however we've |
399 | // already forbidden that the period (.) should be allowed in the call |
400 | // stack, so if someone tries to access our XMLRPC in the normal way, |
401 | // they'll already have received a RPC server fault message. |
402 | |
403 | // Maybe we should allow an easement so that regular XMLRPC clients can |
404 | // call our system methods, and find out what we have to offer? |
405 | |
406 | xmlrpc_server_register_method($xmlrpcserver, 'system.listMethods', 'mnet_system'); |
407 | xmlrpc_server_register_method($xmlrpcserver, 'system/listMethods', 'mnet_system'); |
408 | |
409 | xmlrpc_server_register_method($xmlrpcserver, 'system.methodSignature', 'mnet_system'); |
410 | xmlrpc_server_register_method($xmlrpcserver, 'system/methodSignature', 'mnet_system'); |
411 | |
412 | xmlrpc_server_register_method($xmlrpcserver, 'system.methodHelp', 'mnet_system'); |
413 | xmlrpc_server_register_method($xmlrpcserver, 'system/methodHelp', 'mnet_system'); |
414 | |
415 | xmlrpc_server_register_method($xmlrpcserver, 'system.listServices', 'mnet_system'); |
416 | xmlrpc_server_register_method($xmlrpcserver, 'system/listServices', 'mnet_system'); |
417 | |
418 | xmlrpc_server_register_method($xmlrpcserver, 'system.keyswap', 'mnet_keyswap'); |
419 | xmlrpc_server_register_method($xmlrpcserver, 'system/keyswap', 'mnet_keyswap'); |
420 | |
421 | if ($method == 'system.listMethods' || |
422 | $method == 'system/listMethods' || |
423 | $method == 'system.methodSignature' || |
424 | $method == 'system/methodSignature' || |
425 | $method == 'system.methodHelp' || |
426 | $method == 'system/methodHelp' || |
427 | $method == 'system.listServices' || |
428 | $method == 'system/listServices' || |
429 | $method == 'system.keyswap' || |
430 | $method == 'system/keyswap') { |
431 | |
1008dad6 |
432 | $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $MNET_REMOTE_CLIENT, array("encoding" => "utf-8")); |
71558f85 |
433 | $response = mnet_server_prepare_response($response); |
434 | } else { |
435 | exit(mnet_server_fault(7018, 'nosuchfunction')); |
436 | } |
437 | |
438 | xmlrpc_server_destroy($xmlrpcserver); |
439 | echo $response; |
440 | ////////////////////////////////////// STRICT AUTH |
441 | } elseif ($callstack[0] == 'auth') { |
442 | |
443 | // Break out the callstack into its elements |
444 | list($base, $plugin, $filename, $methodname) = $callstack; |
445 | |
446 | // We refuse to include anything that is not auth.php |
447 | if ($filename == 'auth.php' && is_enabled_auth($plugin)) { |
448 | $authclass = 'auth_plugin_'.$plugin; |
449 | $includefile = '/auth/'.$plugin.'/auth.php'; |
450 | $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $authclass); |
451 | $response = mnet_server_prepare_response($response); |
452 | echo $response; |
453 | } else { |
454 | // Generate error response - unable to locate function |
455 | exit(mnet_server_fault(702, 'nosuchfunction')); |
456 | } |
457 | |
458 | ////////////////////////////////////// STRICT ENROL |
459 | } elseif ($callstack[0] == 'enrol') { |
460 | |
461 | // Break out the callstack into its elements |
462 | list($base, $plugin, $filename, $methodname) = $callstack; |
463 | |
464 | if ($filename == 'enrol.php' && is_enabled_enrol($plugin)) { |
465 | $enrolclass = 'enrolment_plugin_'.$plugin; |
466 | $includefile = '/enrol/'.$plugin.'/enrol.php'; |
467 | $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $enrolclass); |
468 | $response = mnet_server_prepare_response($response); |
469 | echo $response; |
470 | } else { |
471 | // Generate error response - unable to locate function |
472 | exit(mnet_server_fault(703, 'nosuchfunction')); |
473 | } |
474 | |
b91b274b |
475 | } else if ($callstack[0] == 'portfolio') { |
476 | // Break out the callstack into its elements |
477 | list($base, $plugin, $filename, $methodname) = $callstack; |
478 | |
479 | if ($filename == 'lib.php') { |
480 | $pluginclass = 'portfolio_plugin_' . $plugin; |
481 | $includefile = '/portfolio/type/'.$plugin.'/lib.php'; |
482 | $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $pluginclass); |
483 | $response = mnet_server_prepare_response($response); |
484 | echo $response; |
485 | } else { |
486 | // Generate error response - unable to locate function |
487 | exit(mnet_server_fault(7012, 'nosuchfunction')); |
488 | } |
489 | |
71558f85 |
490 | ////////////////////////////////////// STRICT MOD/* |
68bcd355 |
491 | } elseif ($callstack[0] == 'mod' || 'dangerous' == $CFG->mnet_dispatcher_mode) { |
71558f85 |
492 | list($base, $module, $filename, $functionname) = $callstack; |
493 | |
494 | ////////////////////////////////////// STRICT MOD/* |
495 | if ($base == 'mod' && $filename == 'rpclib.php') { |
496 | $includefile = '/mod/'.$module.'/rpclib.php'; |
497 | $response = mnet_server_invoke_method($includefile, $functionname, $method, $payload); |
498 | $response = mnet_server_prepare_response($response); |
499 | echo $response; |
500 | |
68bcd355 |
501 | ////////////////////////////////////// DANGEROUS |
502 | } elseif ('dangerous' == $CFG->mnet_dispatcher_mode && $MNET_REMOTE_CLIENT->plaintext_is_ok()) { |
71558f85 |
503 | |
504 | $functionname = array_pop($callstack); |
71558f85 |
505 | |
506 | if ($MNET_REMOTE_CLIENT->plaintext_is_ok()) { |
507 | |
1bef950e |
508 | $filename = clean_param(implode('/',$callstack), PARAM_PATH); |
509 | if (0 == preg_match("/php$/", $filename)) { |
510 | // Filename doesn't end in 'php'; possible attack? |
511 | // Generate error response - unable to locate function |
512 | exit(mnet_server_fault(7012, 'nosuchfunction')); |
513 | } |
514 | |
71558f85 |
515 | // The call stack holds the path to any include file |
1bef950e |
516 | $includefile = $CFG->dirroot.'/'.$filename; |
71558f85 |
517 | |
b26a847b |
518 | $response = mnet_server_invoke_method($includefile, $functionname, $method, $payload); |
71558f85 |
519 | echo $response; |
520 | } |
521 | |
522 | } else { |
523 | // Generate error response - unable to locate function |
524 | exit(mnet_server_fault(7012, 'nosuchfunction')); |
525 | } |
526 | |
527 | } else { |
528 | // Generate error response - unable to locate function |
529 | exit(mnet_server_fault(7012, 'nosuchfunction')); |
530 | } |
531 | } |
532 | |
533 | /** |
534 | * Execute the system functions - mostly for introspection |
535 | * |
536 | * @param string $method XMLRPC method name, e.g. system.listMethods |
537 | * @param array $params Array of parameters from the XMLRPC request |
538 | * @param string $hostinfo Hostinfo object from the mnet_host table |
539 | * @return mixed Response data - any kind of PHP variable |
540 | */ |
541 | function mnet_system($method, $params, $hostinfo) { |
cc38ff5d |
542 | global $CFG, $DB; |
71558f85 |
543 | |
544 | if (empty($hostinfo)) return array(); |
545 | |
546 | $id_list = $hostinfo->id; |
547 | if (!empty($CFG->mnet_all_hosts_id)) { |
548 | $id_list .= ', '.$CFG->mnet_all_hosts_id; |
549 | } |
550 | |
551 | if ('system.listMethods' == $method || 'system/listMethods' == $method) { |
552 | if (count($params) == 0) { |
553 | $query = ' |
554 | SELECT DISTINCT |
555 | rpc.function_name, |
556 | rpc.xmlrpc_path, |
557 | rpc.enabled, |
558 | rpc.help, |
559 | rpc.profile |
560 | FROM |
cc38ff5d |
561 | {mnet_host2service} h2s, |
562 | {mnet_service2rpc} s2r, |
563 | {mnet_rpc} rpc |
71558f85 |
564 | WHERE |
565 | s2r.rpcid = rpc.id AND |
566 | h2s.serviceid = s2r.serviceid AND |
f633d85d |
567 | h2s.hostid in ('.$id_list .') AND |
568 | h2s.publish =\'1\' |
71558f85 |
569 | ORDER BY |
570 | rpc.xmlrpc_path ASC'; |
cc38ff5d |
571 | $params = array(); |
71558f85 |
572 | |
573 | } else { |
574 | $query = ' |
575 | SELECT DISTINCT |
576 | rpc.function_name, |
577 | rpc.xmlrpc_path, |
578 | rpc.enabled, |
579 | rpc.help, |
580 | rpc.profile |
581 | FROM |
cc38ff5d |
582 | {mnet_host2service} h2s, |
583 | {mnet_service2rpc} s2r, |
584 | {mnet_service} svc, |
585 | {mnet_rpc} rpc |
71558f85 |
586 | WHERE |
587 | s2r.rpcid = rpc.id AND |
588 | h2s.serviceid = s2r.serviceid AND |
589 | h2s.hostid in ('.$id_list .') AND |
f633d85d |
590 | h2s.publish =\'1\' AND |
71558f85 |
591 | svc.id = h2s.serviceid AND |
cc38ff5d |
592 | svc.name = ? |
71558f85 |
593 | ORDER BY |
594 | rpc.xmlrpc_path ASC'; |
cc38ff5d |
595 | $params = array($params[0]); |
71558f85 |
596 | |
597 | } |
cc38ff5d |
598 | $resultset = array_values($DB->get_records_sql($query, $params)); |
71558f85 |
599 | $methods = array(); |
600 | foreach($resultset as $result) { |
601 | $methods[] = $result->xmlrpc_path; |
602 | } |
603 | return $methods; |
604 | } elseif ('system.methodSignature' == $method || 'system/methodSignature' == $method) { |
605 | $query = ' |
606 | SELECT DISTINCT |
607 | rpc.function_name, |
608 | rpc.xmlrpc_path, |
609 | rpc.enabled, |
610 | rpc.help, |
611 | rpc.profile |
612 | FROM |
cc38ff5d |
613 | {mnet_host2service} h2s, |
614 | {mnet_service2rpc} s2r, |
615 | {mnet_rpc rpc} |
71558f85 |
616 | WHERE |
cc38ff5d |
617 | rpc.xmlrpc_path = ? AND |
71558f85 |
618 | s2r.rpcid = rpc.id AND |
619 | h2s.serviceid = s2r.serviceid AND |
f633d85d |
620 | h2s.publish =\'1\' AND |
71558f85 |
621 | h2s.hostid in ('.$id_list .')'; |
cc38ff5d |
622 | $params = array($params[0]); |
71558f85 |
623 | |
cc38ff5d |
624 | $result = $DB->get_records_sql($query, $params); |
71558f85 |
625 | $methodsigs = array(); |
626 | |
627 | if (is_array($result)) { |
628 | foreach($result as $method) { |
629 | $methodsigs[] = unserialize($method->profile); |
630 | } |
631 | } |
632 | |
633 | return $methodsigs; |
634 | } elseif ('system.methodHelp' == $method || 'system/methodHelp' == $method) { |
635 | $query = ' |
636 | SELECT DISTINCT |
637 | rpc.function_name, |
638 | rpc.xmlrpc_path, |
639 | rpc.enabled, |
640 | rpc.help, |
641 | rpc.profile |
642 | FROM |
cc38ff5d |
643 | {mnet_host2service} h2s, |
644 | {mnet_service2rpc} s2r, |
645 | {mnet_rpc} rpc |
71558f85 |
646 | WHERE |
cc38ff5d |
647 | rpc.xmlrpc_path = ? AND |
71558f85 |
648 | s2r.rpcid = rpc.id AND |
f633d85d |
649 | h2s.publish =\'1\' AND |
71558f85 |
650 | h2s.serviceid = s2r.serviceid AND |
651 | h2s.hostid in ('.$id_list .')'; |
cc38ff5d |
652 | $params = array($params[0]); |
71558f85 |
653 | |
cc38ff5d |
654 | $result = $DB->get_record_sql($query, $params); |
71558f85 |
655 | |
656 | if (is_object($result)) { |
657 | return $result->help; |
658 | } |
659 | } elseif ('system.listServices' == $method || 'system/listServices' == $method) { |
660 | $query = ' |
661 | SELECT DISTINCT |
662 | s.id, |
663 | s.name, |
664 | s.apiversion, |
665 | h2s.publish, |
666 | h2s.subscribe |
667 | FROM |
cc38ff5d |
668 | {mnet_host2service} h2s, |
669 | {mnet_service} s |
71558f85 |
670 | WHERE |
671 | h2s.serviceid = s.id AND |
f633d85d |
672 | (h2s.publish =\'1\' OR h2s.subscribe =\'1\') AND |
71558f85 |
673 | h2s.hostid in ('.$id_list .') |
674 | ORDER BY |
675 | s.name ASC'; |
cc38ff5d |
676 | $params = array(); |
71558f85 |
677 | |
cc38ff5d |
678 | $result = $DB->get_records_sql($query, $params); |
71558f85 |
679 | $services = array(); |
680 | |
681 | if (is_array($result)) { |
682 | foreach($result as $service) { |
683 | $services[] = array('name' => $service->name, |
684 | 'apiversion' => $service->apiversion, |
685 | 'publish' => $service->publish, |
686 | 'subscribe' => $service->subscribe); |
687 | } |
688 | } |
689 | |
690 | return $services; |
691 | } |
692 | exit(mnet_server_fault(7019, 'nosuchfunction')); |
693 | } |
694 | |
695 | /** |
696 | * Initialize the object (if necessary), execute the method or function, and |
697 | * return the response |
698 | * |
699 | * @param string $includefile The file that contains the object definition |
700 | * @param string $methodname The name of the method to execute |
701 | * @param string $method The full path to the method |
702 | * @param string $payload The XML-RPC request payload |
703 | * @param string $class The name of the class to instantiate (or false) |
704 | * @return string The XML-RPC response |
705 | */ |
706 | function mnet_server_invoke_method($includefile, $methodname, $method, $payload, $class=false) { |
707 | |
708 | $permission = mnet_permit_rpc_call($includefile, $methodname, $class); |
709 | |
710 | if (RPC_NOSUCHFILE == $permission) { |
711 | // Generate error response - unable to locate function |
712 | exit(mnet_server_fault(705, 'nosuchfile', $includefile)); |
713 | } |
714 | |
715 | if (RPC_NOSUCHFUNCTION == $permission) { |
716 | // Generate error response - unable to locate function |
717 | exit(mnet_server_fault(706, 'nosuchfunction')); |
718 | } |
719 | |
720 | if (RPC_FORBIDDENFUNCTION == $permission) { |
721 | // Generate error response - unable to locate function |
722 | exit(mnet_server_fault(707, 'forbidden-function')); |
723 | } |
724 | |
725 | if (RPC_NOSUCHCLASS == $permission) { |
726 | // Generate error response - unable to locate function |
727 | exit(mnet_server_fault(7013, 'nosuchfunction')); |
728 | } |
729 | |
730 | if (RPC_NOSUCHMETHOD == $permission) { |
731 | // Generate error response - unable to locate function |
732 | exit(mnet_server_fault(7014, 'nosuchmethod')); |
733 | } |
734 | |
735 | if (RPC_NOSUCHFUNCTION == $permission) { |
736 | // Generate error response - unable to locate function |
737 | exit(mnet_server_fault(7014, 'nosuchmethod')); |
738 | } |
739 | |
740 | if (RPC_FORBIDDENMETHOD == $permission) { |
741 | // Generate error response - unable to locate function |
742 | exit(mnet_server_fault(7015, 'nosuchfunction')); |
743 | } |
744 | |
745 | if (0 < $permission) { |
746 | // Generate error response - unable to locate function |
747 | exit(mnet_server_fault(7019, 'unknownerror')); |
748 | } |
749 | |
750 | if (RPC_OK == $permission) { |
751 | $xmlrpcserver = xmlrpc_server_create(); |
752 | $bool = xmlrpc_server_register_method($xmlrpcserver, $method, 'mnet_server_dummy_method'); |
1008dad6 |
753 | $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $methodname, array("encoding" => "utf-8")); |
71558f85 |
754 | $bool = xmlrpc_server_destroy($xmlrpcserver); |
755 | return $response; |
756 | } |
757 | } |
758 | |
85d2d959 |
759 | /** |
760 | * Accepts a public key from a new remote host and returns the public key for |
761 | * this host. If 'register all hosts' is turned on, it will bootstrap a record |
762 | * for the remote host in the mnet_host table (if it's not already there) |
763 | * |
764 | * @param string $function XML-RPC requires this but we don't... discard! |
765 | * @param array $params Array of parameters |
766 | * $params[0] is the remote wwwroot |
767 | * $params[1] is the remote public key |
768 | * @return string The XML-RPC response |
769 | */ |
71558f85 |
770 | function mnet_keyswap($function, $params) { |
735c7beb |
771 | global $CFG, $MNET; |
71558f85 |
772 | $return = array(); |
773 | |
774 | if (!empty($CFG->mnet_register_allhosts)) { |
775 | $mnet_peer = new mnet_peer(); |
25202581 |
776 | @list($wwwroot, $pubkey, $application) = each($params); |
777 | $keyok = $mnet_peer->bootstrap($wwwroot, $pubkey, $application); |
71558f85 |
778 | if ($keyok) { |
779 | $mnet_peer->commit(); |
780 | } |
781 | } |
735c7beb |
782 | return $MNET->public_key; |
71558f85 |
783 | } |
85d2d959 |
784 | |
71558f85 |
785 | ?> |