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