See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 3 /** 4 * -----XML-Envelope--------------------------------- 5 * | | 6 * | Encrypted-Symmetric-key---------------- | 7 * | |_____________________________________| | 8 * | | 9 * | Encrypted data------------------------- | 10 * | | | | 11 * | | -XML-Envelope------------------ | | 12 * | | | | | | 13 * | | | --Signature------------- | | | 14 * | | | |______________________| | | | 15 * | | | | | | 16 * | | | --Signed-Payload-------- | | | 17 * | | | | | | | | 18 * | | | | XML-RPC Request | | | | 19 * | | | |______________________| | | | 20 * | | | | | | 21 * | | |_____________________________| | | 22 * | |_____________________________________| | 23 * | | 24 * |________________________________________________| 25 * 26 */ 27 28 /* Strip encryption envelope (if present) and decrypt data 29 * 30 * @param string $rawpostdata The XML that the client sent 31 * 32 * @throws mnet_server_exception 33 * 34 * @return string XML with any encryption envolope removed 35 */ 36 function mnet_server_strip_encryption($rawpostdata) { 37 $remoteclient = get_mnet_remote_client(); 38 $crypt_parser = new mnet_encxml_parser(); 39 $crypt_parser->parse($rawpostdata); 40 $mnet = get_mnet_environment(); 41 42 if (!$crypt_parser->payload_encrypted) { 43 return $rawpostdata; 44 } 45 46 // Make sure we know who we're talking to 47 $host_record_exists = $remoteclient->set_wwwroot($crypt_parser->remote_wwwroot); 48 49 if (false == $host_record_exists) { 50 throw new mnet_server_exception(7020, 'wrong-wwwroot', $crypt_parser->remote_wwwroot); 51 } 52 53 // This key is symmetric, and is itself encrypted. Can be decrypted using our private key 54 $key = array_pop($crypt_parser->cipher); 55 // This data is symmetrically encrypted, can be decrypted using the above key 56 $data = array_pop($crypt_parser->cipher); 57 58 $crypt_parser->free_resource(); 59 $payload = ''; // Initialize payload var 60 61 // &$payload 62 $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $mnet->get_private_key(), 'RC4'); 63 if ($isOpen) { 64 $remoteclient->was_encrypted(); 65 return $payload; 66 } 67 68 // Decryption failed... let's try our archived keys 69 $openssl_history = get_config('mnet', 'openssl_history'); 70 if(empty($openssl_history)) { 71 $openssl_history = array(); 72 set_config('openssl_history', serialize($openssl_history), 'mnet'); 73 } else { 74 $openssl_history = unserialize($openssl_history); 75 } 76 foreach($openssl_history as $keyset) { 77 $keyresource = openssl_pkey_get_private($keyset['keypair_PEM']); 78 $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $keyresource, 'RC4'); 79 if ($isOpen) { 80 // It's an older code, sir, but it checks out 81 $remoteclient->was_encrypted(); 82 $remoteclient->encrypted_to($keyresource); 83 $remoteclient->set_pushkey(); 84 return $payload; 85 } 86 } 87 88 //If after all that we still couldn't decrypt the message, error out. 89 throw new mnet_server_exception(7023, 'encryption-invalid'); 90 } 91 92 /* Strip signature envelope (if present), try to verify any signature using our record of remote peer's public key. 93 * 94 * @param string $plaintextmessage XML envelope containing XMLRPC request and signature 95 * 96 * @return string XMLRPC request 97 */ 98 function mnet_server_strip_signature($plaintextmessage) { 99 $remoteclient = get_mnet_remote_client(); 100 $sig_parser = new mnet_encxml_parser(); 101 $sig_parser->parse($plaintextmessage); 102 103 if ($sig_parser->signature == '') { 104 return $plaintextmessage; 105 } 106 107 // Record that the request was signed in some way 108 $remoteclient->was_signed(); 109 110 // Load any information we have about this mnet peer 111 $remoteclient->set_wwwroot($sig_parser->remote_wwwroot); 112 113 $payload = base64_decode($sig_parser->data_object); 114 $signature = base64_decode($sig_parser->signature); 115 $certificate = $remoteclient->public_key; 116 117 // If we don't have any certificate for the host, don't try to check the signature 118 // Just return the parsed request 119 if ($certificate == false) { 120 return $payload; 121 } 122 123 // Does the signature match the data and the public cert? 124 $signature_verified = openssl_verify($payload, $signature, $certificate); 125 if ($signature_verified == 0) { 126 // $signature was not generated for $payload using $certificate 127 // Get the key the remote peer is currently publishing: 128 $currkey = mnet_get_public_key($remoteclient->wwwroot, $remoteclient->application); 129 // If the key the remote peer is currently publishing is different to $certificate 130 if($currkey != $certificate) { 131 // if pushkey is already set, it means the request was encrypted to an old key 132 // in mnet_server_strip_encryption. 133 // if we call refresh_key() here before pushing out our new key, 134 // and the other site ALSO has a new key, 135 // we'll get into an infinite keyswap loop 136 // so push just bail here, and push out the new key. 137 // the next request will get through to refresh_key 138 if ($remoteclient->pushkey) { 139 return false; 140 } 141 // Try and get the server's new key through trusted means 142 $remoteclient->refresh_key(); 143 // If we did manage to re-key, try to verify the signature again using the new public key. 144 $certificate = $remoteclient->public_key; 145 $signature_verified = openssl_verify($payload, $signature, $certificate); 146 } 147 } 148 149 if ($signature_verified == 1) { 150 $remoteclient->signature_verified(); 151 $remoteclient->touch(); 152 } 153 154 $sig_parser->free_resource(); 155 156 return $payload; 157 } 158 159 /** 160 * Return the proper XML-RPC content to report an error in the local language. 161 * 162 * @param int $code The ID code of the error message 163 * @param string $text The full string of the error message (get_string will <b>not be called</b>) 164 * @param string $param The $a param for the error message in the lang file 165 * @return string $text The text of the error message 166 */ 167 function mnet_server_fault($code, $text, $param = null) { 168 if (!is_numeric($code)) { 169 $code = 0; 170 } 171 $code = intval($code); 172 return mnet_server_fault_xml($code, $text); 173 } 174 175 /** 176 * Return the proper XML-RPC content to report an error. 177 * 178 * @param int $code The ID code of the error message 179 * @param string $text The error message 180 * @param resource $privatekey The private key that should be used to sign the response 181 * @return string $text The XML text of the error message 182 */ 183 function mnet_server_fault_xml($code, $text, $privatekey = null) { 184 global $CFG; 185 // Replace illegal XML chars - is this already in a lib somewhere? 186 $text = str_replace(array('<','>','&','"',"'"), array('<','>','&','"','''), $text); 187 188 $return = mnet_server_prepare_response('<?xml version="1.0"?> 189 <methodResponse> 190 <fault> 191 <value> 192 <struct> 193 <member> 194 <name>faultCode</name> 195 <value><int>'.$code.'</int></value> 196 </member> 197 <member> 198 <name>faultString</name> 199 <value><string>'.$text.'</string></value> 200 </member> 201 </struct> 202 </value> 203 </fault> 204 </methodResponse>', $privatekey); 205 206 if ($code != 7025) { // new key responses 207 mnet_debug("XMLRPC Error Response $code: $text"); 208 //mnet_debug($return); 209 } 210 211 return $return; 212 } 213 214 215 /** 216 * Package a response in any required envelope, and return it to the client 217 * 218 * @param string $response The XMLRPC response string 219 * @param resource $privatekey The private key to sign the response with 220 * @return string The encoded response string 221 */ 222 function mnet_server_prepare_response($response, $privatekey = null) { 223 $remoteclient = get_mnet_remote_client(); 224 if ($remoteclient->request_was_signed) { 225 $response = mnet_sign_message($response, $privatekey); 226 } 227 228 if ($remoteclient->request_was_encrypted) { 229 $response = mnet_encrypt_message($response, $remoteclient->public_key); 230 } 231 232 return $response; 233 } 234 235 /** 236 * If security checks are passed, dispatch the request to the function/method 237 * 238 * The config variable 'mnet_dispatcher_mode' can be: 239 * strict: Only execute functions that are in specific files 240 * off: The default - don't execute anything 241 * 242 * @param string $payload The XML-RPC request 243 * 244 * @throws mnet_server_exception 245 * 246 * @return No return val - just echo the response 247 */ 248 function mnet_server_dispatch($payload) { 249 global $CFG, $DB; 250 $remoteclient = get_mnet_remote_client(); 251 // Decode the request to method + params. 252 $method = null; 253 $params = null; 254 $encoder = new \PhpXmlRpc\Encoder(); 255 $orequest = $encoder->decodeXML($payload); // First, to internal PhpXmlRpc\Response structure. 256 if ($orequest && $orequest instanceof \PhpXmlRpc\Request) { 257 $method = $orequest->method(); 258 $numparams = $orequest->getNumParams(); 259 for ($i = 0; $i < $numparams; $i++) { 260 $params[] = $encoder->decode($orequest->getParam($i)); 261 } 262 } 263 264 // $method is something like: "mod/forum/lib.php/forum_add_instance" 265 // $params is an array of parameters. A parameter might itself be an array. 266 267 // Check that the method name consists of allowed characters only. 268 // The method name must not begin with a / - avoid absolute paths 269 // A dot character . is only allowed in the filename, i.e. something.php 270 if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_\.-]+(\.php/)?[A-Za-z0-9_-]+$@",$method)) { 271 throw new mnet_server_exception(713, 'nosuchfunction'); 272 } 273 274 if(preg_match("/^system\./", $method)) { 275 $callstack = explode('.', $method); 276 } else { 277 $callstack = explode('/', $method); 278 // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); 279 } 280 281 /** 282 * What has the site administrator chosen as his dispatcher setting? 283 * strict: Only execute functions that are in specific files 284 * off: The default - don't execute anything 285 */ 286 ////////////////////////////////////// OFF 287 if (!isset($CFG->mnet_dispatcher_mode) ) { 288 set_config('mnet_dispatcher_mode', 'off'); 289 throw new mnet_server_exception(704, 'nosuchservice'); 290 } elseif ('off' == $CFG->mnet_dispatcher_mode) { 291 throw new mnet_server_exception(704, 'nosuchservice'); 292 293 ////////////////////////////////////// SYSTEM METHODS 294 } elseif ($callstack[0] == 'system') { 295 $functionname = $callstack[1]; 296 $xmlrpcserver = new \PhpXmlRpc\Server(); 297 $xmlrpcserver->functions_parameters_type = 'epivals'; 298 $xmlrpcserver->compress_response = false; 299 300 // register all the system methods 301 $systemmethods = array('listMethods', 'methodSignature', 'methodHelp', 'listServices', 'listFiles', 'retrieveFile', 'keyswap'); 302 foreach ($systemmethods as $m) { 303 // I'm adding the canonical xmlrpc references here, however we've 304 // already forbidden that the period (.) should be allowed in the call 305 // stack, so if someone tries to access our XMLRPC in the normal way, 306 // they'll already have received a RPC server fault message. 307 308 // Maybe we should allow an easement so that regular XMLRPC clients can 309 // call our system methods, and find out what we have to offer? 310 $handler = 'mnet_system'; 311 if ($m == 'keyswap') { 312 $handler = 'mnet_keyswap'; 313 } 314 if ($method == 'system.' . $m || $method == 'system/' . $m) { 315 $xmlrpcserver->add_to_map($method, $handler); 316 $xmlrpcserver->user_data = $remoteclient; 317 $response = $xmlrpcserver->service($payload, true); 318 $response = mnet_server_prepare_response($response); 319 echo $response; 320 return; 321 } 322 } 323 throw new mnet_server_exception(7018, 'nosuchfunction'); 324 325 //////////////////////////////////// NORMAL PLUGIN DISPATCHER 326 } else { 327 // anything else comes from some sort of plugin 328 if ($rpcrecord = $DB->get_record('mnet_rpc', array('xmlrpcpath' => $method))) { 329 $response = mnet_server_invoke_plugin_method($method, $callstack, $rpcrecord, $payload); 330 $response = mnet_server_prepare_response($response); 331 echo $response; 332 return; 333 // if the rpc record isn't found, check to see if dangerous mode is on 334 ////////////////////////////////////// DANGEROUS 335 } else if ('dangerous' == $CFG->mnet_dispatcher_mode && $remoteclient->plaintext_is_ok()) { 336 $functionname = array_pop($callstack); 337 338 $filename = clean_param(implode('/',$callstack), PARAM_PATH); 339 if (0 == preg_match("/php$/", $filename)) { 340 // Filename doesn't end in 'php'; possible attack? 341 // Generate error response - unable to locate function 342 throw new mnet_server_exception(7012, 'nosuchfunction'); 343 } 344 345 // The call stack holds the path to any include file 346 $includefile = $CFG->dirroot.'/'.$filename; 347 348 $response = mnet_server_invoke_dangerous_method($includefile, $functionname, $method, $payload); 349 echo $response; 350 return; 351 } 352 } 353 throw new mnet_server_exception(7012, 'nosuchfunction'); 354 } 355 356 /** 357 * Execute the system functions - mostly for introspection 358 * 359 * @param string $method XMLRPC method name, e.g. system.listMethods 360 * @param array $params Array of parameters from the XMLRPC request 361 * @param string $hostinfo Hostinfo object from the mnet_host table 362 * 363 * @throws mnet_server_exception 364 * 365 * @return mixed Response data - any kind of PHP variable 366 */ 367 function mnet_system($method, $params, $hostinfo) { 368 global $CFG, $DB; 369 370 if (empty($hostinfo)) return array(); 371 372 $id_list = $hostinfo->id; 373 if (!empty($CFG->mnet_all_hosts_id)) { 374 $id_list .= ', '.$CFG->mnet_all_hosts_id; 375 } 376 377 if ('system.listMethods' == $method || 'system/listMethods' == $method) { 378 $query = ' 379 SELECT DISTINCT 380 rpc.functionname, 381 rpc.xmlrpcpath 382 FROM 383 {mnet_host2service} h2s 384 JOIN {mnet_service2rpc} s2r ON h2s.serviceid = s2r.serviceid 385 JOIN {mnet_rpc} rpc ON s2r.rpcid = rpc.id 386 JOIN {mnet_service} svc ON svc.id = s2r.serviceid 387 WHERE 388 h2s.hostid in ('.$id_list .') AND 389 h2s.publish = 1 AND rpc.enabled = 1 390 ' . ((count($params) > 0) ? 'AND svc.name = ? ' : '') . ' 391 ORDER BY 392 rpc.xmlrpcpath ASC'; 393 if (count($params) > 0) { 394 $params = array($params[0]); 395 } 396 $methods = array(); 397 foreach ($DB->get_records_sql($query, $params) as $result) { 398 $methods[] = $result->xmlrpcpath; 399 } 400 return $methods; 401 } elseif (in_array($method, array('system.methodSignature', 'system/methodSignature', 'system.methodHelp', 'system/methodHelp'))) { 402 $query = ' 403 SELECT DISTINCT 404 rpc.functionname, 405 rpc.help, 406 rpc.profile 407 FROM 408 {mnet_host2service} h2s, 409 {mnet_service2rpc} s2r, 410 {mnet_rpc} rpc 411 WHERE 412 rpc.xmlrpcpath = ? AND 413 s2r.rpcid = rpc.id AND 414 h2s.publish = 1 AND rpc.enabled = 1 AND 415 h2s.serviceid = s2r.serviceid AND 416 h2s.hostid in ('.$id_list .')'; 417 $params = array($params[0]); 418 419 if (!$result = $DB->get_record_sql($query, $params)) { 420 return false; 421 } 422 if (strpos($method, 'methodSignature') !== false) { 423 return unserialize($result->profile); 424 } 425 return $result->help; 426 } elseif ('system.listServices' == $method || 'system/listServices' == $method) { 427 $query = ' 428 SELECT DISTINCT 429 s.id, 430 s.name, 431 s.apiversion, 432 h2s.publish, 433 h2s.subscribe 434 FROM 435 {mnet_host2service} h2s, 436 {mnet_service} s 437 WHERE 438 h2s.serviceid = s.id AND 439 (h2s.publish = 1 OR h2s.subscribe = 1) AND 440 h2s.hostid in ('.$id_list .') 441 ORDER BY 442 s.name ASC'; 443 $params = array(); 444 445 $result = $DB->get_records_sql($query, $params); 446 $services = array(); 447 448 if (is_array($result)) { 449 foreach($result as $service) { 450 $services[] = array('name' => $service->name, 451 'apiversion' => $service->apiversion, 452 'publish' => $service->publish, 453 'subscribe' => $service->subscribe); 454 } 455 } 456 457 return $services; 458 } 459 throw new mnet_server_exception(7019, 'nosuchfunction'); 460 } 461 462 /** 463 * Invoke a normal style plugin method 464 * This will verify permissions first. 465 * 466 * @param string $method the full xmlrpc method that was called eg auth/mnet/auth.php/user_authorise 467 * @param array $callstack the exploded callstack 468 * @param stdclass $rpcrecord the record from mnet_rpc 469 * 470 * @return mixed the response from the invoked method 471 */ 472 function mnet_server_invoke_plugin_method($method, $callstack, $rpcrecord, $payload) { 473 mnet_verify_permissions($rpcrecord); // will throw exceptions 474 mnet_setup_dummy_method($method, $callstack, $rpcrecord); 475 $methodname = array_pop($callstack); 476 477 $xmlrpcserver = new \PhpXmlRpc\Server(); 478 $xmlrpcserver->functions_parameters_type = 'epivals'; 479 $xmlrpcserver->compress_response = false; 480 481 $xmlrpcserver->add_to_map($method, 'mnet_server_dummy_method'); 482 $xmlrpcserver->user_data = $methodname; 483 $response = $xmlrpcserver->service($payload, true); 484 485 return $response; 486 } 487 488 /** 489 * Initialize the object (if necessary), execute the method or function, and 490 * return the response 491 * 492 * @param string $includefile The file that contains the object definition 493 * @param string $methodname The name of the method to execute 494 * @param string $method The full path to the method 495 * @param string $payload The XML-RPC request payload 496 * @param string $class The name of the class to instantiate (or false) 497 * 498 * @throws mnet_server_exception 499 * 500 * @return string The XML-RPC response 501 */ 502 function mnet_server_invoke_dangerous_method($includefile, $methodname, $method, $payload) { 503 504 if (file_exists($CFG->dirroot . $includefile)) { 505 require_once $CFG->dirroot . $includefile; 506 // $callprefix matches the rpc convention 507 // of not having a leading slash 508 $callprefix = preg_replace('!^/!', '', $includefile); 509 } else { 510 throw new mnet_server_exception(705, "nosuchfile"); 511 } 512 513 if ($functionname != clean_param($functionname, PARAM_PATH)) { 514 throw new mnet_server_exception(7012, "nosuchfunction"); 515 } 516 517 if (!function_exists($functionname)) { 518 throw new mnet_server_exception(7012, "nosuchfunction"); 519 } 520 521 $xmlrpcserver = new \PhpXmlRpc\Server(); 522 $xmlrpcserver->functions_parameters_type = 'epivals'; 523 $xmlrpcserver->compress_response = false; 524 525 $xmlrpcserver->add_to_map($method, 'mnet_server_dummy_method'); 526 $xmlrpcserver->user_data = $methodname; 527 $response = $xmlrpcserver->service($payload, true); 528 529 return $response; 530 } 531 532 533 /** 534 * Accepts a public key from a new remote host and returns the public key for 535 * this host. If 'register all hosts' is turned on, it will bootstrap a record 536 * for the remote host in the mnet_host table (if it's not already there) 537 * 538 * @param string $function XML-RPC requires this but we don't... discard! 539 * @param array $params Array of parameters 540 * $params[0] is the remote wwwroot 541 * $params[1] is the remote public key 542 * @return string The XML-RPC response 543 */ 544 function mnet_keyswap($function, $params) { 545 global $CFG; 546 $return = array(); 547 $mnet = get_mnet_environment(); 548 549 if (!empty($CFG->mnet_register_allhosts)) { 550 $mnet_peer = new mnet_peer(); 551 list($wwwroot, $pubkey, $application) = $params; 552 $keyok = $mnet_peer->bootstrap($wwwroot, $pubkey, $application); 553 if ($keyok) { 554 $mnet_peer->commit(); 555 } 556 } 557 return $mnet->public_key; 558 } 559 560 /** 561 * Verify that the requested xmlrpc method can be called 562 * This just checks the method exists in the rpc table and is enabled. 563 * 564 * @param stdclass $rpcrecord the record from mnet_rpc 565 * 566 * @throws mnet_server_exception 567 */ 568 function mnet_verify_permissions($rpcrecord) { 569 global $CFG, $DB; 570 $remoteclient = get_mnet_remote_client(); 571 572 $id_list = $remoteclient->id; 573 if (!empty($CFG->mnet_all_hosts_id)) { 574 $id_list .= ', '.$CFG->mnet_all_hosts_id; 575 } 576 577 // TODO: Change this to avoid the first column duplicate debugging, keeping current behaviour 100%. 578 579 $sql = "SELECT 580 r.*, h2s.publish 581 FROM 582 {mnet_rpc} r 583 JOIN {mnet_service2rpc} s2r ON s2r.rpcid = r.id 584 LEFT JOIN {mnet_host2service} h2s ON h2s.serviceid = s2r.serviceid 585 WHERE 586 r.id = ? AND 587 h2s.hostid in ($id_list)"; 588 589 $params = array($rpcrecord->id); 590 591 if (!$permission = $DB->get_record_sql($sql, $params)) { 592 throw new mnet_server_exception(7012, "nosuchfunction"); 593 } else if (!$permission->publish || !$permission->enabled) { 594 throw new mnet_server_exception(707, "nosuchfunction"); 595 } 596 } 597 598 /** 599 * Figure out exactly what needs to be called and stashes it in $remoteclient 600 * Does some further verification that the method is callable 601 * 602 * @param string $method the full xmlrpc method that was called eg auth/mnet/auth.php/user_authorise 603 * @param array $callstack the exploded callstack 604 * @param stdclass $rpcrecord the record from mnet_rpc 605 * 606 * @throws mnet_server_exception 607 */ 608 function mnet_setup_dummy_method($method, $callstack, $rpcrecord) { 609 global $CFG; 610 $remoteclient = get_mnet_remote_client(); 611 // verify that the callpath in the stack matches our records 612 // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); 613 $path = core_component::get_plugin_directory($rpcrecord->plugintype, $rpcrecord->pluginname); 614 $path = substr($path, strlen($CFG->dirroot)+1); // this is a bit hacky and fragile, it is not guaranteed that plugins are in dirroot 615 array_pop($callstack); 616 $providedpath = implode('/', $callstack); 617 if ($providedpath != $path . '/' . $rpcrecord->filename) { 618 throw new mnet_server_exception(705, "nosuchfile"); 619 } 620 if (!file_exists($CFG->dirroot . '/' . $providedpath)) { 621 throw new mnet_server_exception(705, "nosuchfile"); 622 } 623 require_once($CFG->dirroot . '/' . $providedpath); 624 if (!empty($rpcrecord->classname)) { 625 if (!class_exists($rpcrecord->classname)) { 626 throw new mnet_server_exception(708, 'nosuchclass'); 627 } 628 if (!$rpcrecord->static) { 629 try { 630 $object = new $rpcrecord->classname; 631 } catch (Exception $e) { 632 throw new mnet_server_exception(709, "classerror"); 633 } 634 if (!is_callable(array($object, $rpcrecord->functionname))) { 635 throw new mnet_server_exception(706, "nosuchfunction"); 636 } 637 $remoteclient->object_to_call($object); 638 } else { 639 if (!is_callable(array($rpcrecord->classname, $rpcrecord->functionname))) { 640 throw new mnet_server_exception(706, "nosuchfunction"); 641 } 642 $remoteclient->static_location($rpcrecord->classname); 643 } 644 } 645 } 646 647 /** 648 * Dummy function for the XML-RPC dispatcher - use to call a method on an object 649 * or to call a function 650 * 651 * Translate XML-RPC's strange function call syntax into a more straightforward 652 * PHP-friendly alternative. This dummy function will be called by the 653 * dispatcher, and can be used to call a method on an object, or just a function 654 * 655 * The methodName argument (eg. mnet/testlib/mnet_concatenate_strings) 656 * is ignored. 657 * 658 * @throws mnet_server_exception 659 * 660 * @param string $methodname We discard this - see 'functionname' 661 * @param array $argsarray Each element is an argument to the real 662 * function 663 * @param string $functionname The name of the PHP function you want to call 664 * @return mixed The return value will be that of the real 665 * function, whatever it may be. 666 */ 667 function mnet_server_dummy_method($methodname, $argsarray, $functionname) { 668 $remoteclient = get_mnet_remote_client(); 669 try { 670 if (is_object($remoteclient->object_to_call)) { 671 return @call_user_func_array(array($remoteclient->object_to_call,$functionname), $argsarray); 672 } else if (!empty($remoteclient->static_location)) { 673 return @call_user_func_array(array($remoteclient->static_location, $functionname), $argsarray); 674 } else { 675 return @call_user_func_array($functionname, $argsarray); 676 } 677 } catch (Exception $e) { 678 exit(mnet_server_fault($e->getCode(), $e->getMessage())); 679 } 680 } 681 /** 682 * mnet server exception. extends moodle_exception, but takes slightly different arguments. 683 * and unlike the rest of moodle, the actual int error code is used. 684 * this exception should only be used during an xmlrpc server request, ie, not for client requests. 685 */ 686 class mnet_server_exception extends moodle_exception { 687 688 /** 689 * @param int $intcode the numerical error associated with this fault. this is <b>not</b> the string errorcode 690 * @param string $langkey the error message in full (<b>get_string will not be used</b>) 691 * @param string $module the language module, defaults to 'mnet' 692 * @param mixed $a params for get_string 693 */ 694 public function __construct($intcode, $languagekey, $module='mnet', $a=null) { 695 parent::__construct($languagekey, $module, '', $a); 696 $this->code = $intcode; 697 698 } 699 } 700
title
Description
Body
title
Description
Body
title
Description
Body
title
Body