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