Differences Between: [Versions 310 and 311] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 // xmlrpc_decode_request returns an array of parameters, and the $method 252 // variable (which is passed by reference) is instantiated with the value from 253 // the methodName tag in the xml payload 254 // xmlrpc_decode_request($xml, &$method) 255 $params = xmlrpc_decode_request($payload, $method); 256 257 // $method is something like: "mod/forum/lib.php/forum_add_instance" 258 // $params is an array of parameters. A parameter might itself be an array. 259 260 // Check that the method name consists of allowed characters only. 261 // The method name must not begin with a / - avoid absolute paths 262 // A dot character . is only allowed in the filename, i.e. something.php 263 if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_\.-]+(\.php/)?[A-Za-z0-9_-]+$@",$method)) { 264 throw new mnet_server_exception(713, 'nosuchfunction'); 265 } 266 267 if(preg_match("/^system\./", $method)) { 268 $callstack = explode('.', $method); 269 } else { 270 $callstack = explode('/', $method); 271 // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); 272 } 273 274 /** 275 * What has the site administrator chosen as his dispatcher setting? 276 * strict: Only execute functions that are in specific files 277 * off: The default - don't execute anything 278 */ 279 ////////////////////////////////////// OFF 280 if (!isset($CFG->mnet_dispatcher_mode) ) { 281 set_config('mnet_dispatcher_mode', 'off'); 282 throw new mnet_server_exception(704, 'nosuchservice'); 283 } elseif ('off' == $CFG->mnet_dispatcher_mode) { 284 throw new mnet_server_exception(704, 'nosuchservice'); 285 286 ////////////////////////////////////// SYSTEM METHODS 287 } elseif ($callstack[0] == 'system') { 288 $functionname = $callstack[1]; 289 $xmlrpcserver = xmlrpc_server_create(); 290 291 // register all the system methods 292 $systemmethods = array('listMethods', 'methodSignature', 'methodHelp', 'listServices', 'listFiles', 'retrieveFile', 'keyswap'); 293 foreach ($systemmethods as $m) { 294 // I'm adding the canonical xmlrpc references here, however we've 295 // already forbidden that the period (.) should be allowed in the call 296 // stack, so if someone tries to access our XMLRPC in the normal way, 297 // they'll already have received a RPC server fault message. 298 299 // Maybe we should allow an easement so that regular XMLRPC clients can 300 // call our system methods, and find out what we have to offer? 301 $handler = 'mnet_system'; 302 if ($m == 'keyswap') { 303 $handler = 'mnet_keyswap'; 304 } 305 if ($method == 'system.' . $m || $method == 'system/' . $m) { 306 xmlrpc_server_register_method($xmlrpcserver, $method, $handler); 307 $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $remoteclient, array("encoding" => "utf-8")); 308 $response = mnet_server_prepare_response($response); 309 echo $response; 310 xmlrpc_server_destroy($xmlrpcserver); 311 return; 312 } 313 } 314 throw new mnet_server_exception(7018, 'nosuchfunction'); 315 316 //////////////////////////////////// NORMAL PLUGIN DISPATCHER 317 } else { 318 // anything else comes from some sort of plugin 319 if ($rpcrecord = $DB->get_record('mnet_rpc', array('xmlrpcpath' => $method))) { 320 $response = mnet_server_invoke_plugin_method($method, $callstack, $rpcrecord, $payload); 321 $response = mnet_server_prepare_response($response); 322 echo $response; 323 return; 324 // if the rpc record isn't found, check to see if dangerous mode is on 325 ////////////////////////////////////// DANGEROUS 326 } else if ('dangerous' == $CFG->mnet_dispatcher_mode && $remoteclient->plaintext_is_ok()) { 327 $functionname = array_pop($callstack); 328 329 $filename = clean_param(implode('/',$callstack), PARAM_PATH); 330 if (0 == preg_match("/php$/", $filename)) { 331 // Filename doesn't end in 'php'; possible attack? 332 // Generate error response - unable to locate function 333 throw new mnet_server_exception(7012, 'nosuchfunction'); 334 } 335 336 // The call stack holds the path to any include file 337 $includefile = $CFG->dirroot.'/'.$filename; 338 339 $response = mnet_server_invoke_dangerous_method($includefile, $functionname, $method, $payload); 340 echo $response; 341 return; 342 } 343 } 344 throw new mnet_server_exception(7012, 'nosuchfunction'); 345 } 346 347 /** 348 * Execute the system functions - mostly for introspection 349 * 350 * @param string $method XMLRPC method name, e.g. system.listMethods 351 * @param array $params Array of parameters from the XMLRPC request 352 * @param string $hostinfo Hostinfo object from the mnet_host table 353 * 354 * @throws mnet_server_exception 355 * 356 * @return mixed Response data - any kind of PHP variable 357 */ 358 function mnet_system($method, $params, $hostinfo) { 359 global $CFG, $DB; 360 361 if (empty($hostinfo)) return array(); 362 363 $id_list = $hostinfo->id; 364 if (!empty($CFG->mnet_all_hosts_id)) { 365 $id_list .= ', '.$CFG->mnet_all_hosts_id; 366 } 367 368 if ('system.listMethods' == $method || 'system/listMethods' == $method) { 369 $query = ' 370 SELECT DISTINCT 371 rpc.functionname, 372 rpc.xmlrpcpath 373 FROM 374 {mnet_host2service} h2s 375 JOIN {mnet_service2rpc} s2r ON h2s.serviceid = s2r.serviceid 376 JOIN {mnet_rpc} rpc ON s2r.rpcid = rpc.id 377 JOIN {mnet_service} svc ON svc.id = s2r.serviceid 378 WHERE 379 h2s.hostid in ('.$id_list .') AND 380 h2s.publish = 1 AND rpc.enabled = 1 381 ' . ((count($params) > 0) ? 'AND svc.name = ? ' : '') . ' 382 ORDER BY 383 rpc.xmlrpcpath ASC'; 384 if (count($params) > 0) { 385 $params = array($params[0]); 386 } 387 $methods = array(); 388 foreach ($DB->get_records_sql($query, $params) as $result) { 389 $methods[] = $result->xmlrpcpath; 390 } 391 return $methods; 392 } elseif (in_array($method, array('system.methodSignature', 'system/methodSignature', 'system.methodHelp', 'system/methodHelp'))) { 393 $query = ' 394 SELECT DISTINCT 395 rpc.functionname, 396 rpc.help, 397 rpc.profile 398 FROM 399 {mnet_host2service} h2s, 400 {mnet_service2rpc} s2r, 401 {mnet_rpc} rpc 402 WHERE 403 rpc.xmlrpcpath = ? AND 404 s2r.rpcid = rpc.id AND 405 h2s.publish = 1 AND rpc.enabled = 1 AND 406 h2s.serviceid = s2r.serviceid AND 407 h2s.hostid in ('.$id_list .')'; 408 $params = array($params[0]); 409 410 if (!$result = $DB->get_record_sql($query, $params)) { 411 return false; 412 } 413 if (strpos($method, 'methodSignature') !== false) { 414 return unserialize($result->profile); 415 } 416 return $result->help; 417 } elseif ('system.listServices' == $method || 'system/listServices' == $method) { 418 $query = ' 419 SELECT DISTINCT 420 s.id, 421 s.name, 422 s.apiversion, 423 h2s.publish, 424 h2s.subscribe 425 FROM 426 {mnet_host2service} h2s, 427 {mnet_service} s 428 WHERE 429 h2s.serviceid = s.id AND 430 (h2s.publish = 1 OR h2s.subscribe = 1) AND 431 h2s.hostid in ('.$id_list .') 432 ORDER BY 433 s.name ASC'; 434 $params = array(); 435 436 $result = $DB->get_records_sql($query, $params); 437 $services = array(); 438 439 if (is_array($result)) { 440 foreach($result as $service) { 441 $services[] = array('name' => $service->name, 442 'apiversion' => $service->apiversion, 443 'publish' => $service->publish, 444 'subscribe' => $service->subscribe); 445 } 446 } 447 448 return $services; 449 } 450 throw new mnet_server_exception(7019, 'nosuchfunction'); 451 } 452 453 /** 454 * Invoke a normal style plugin method 455 * This will verify permissions first. 456 * 457 * @param string $method the full xmlrpc method that was called eg auth/mnet/auth.php/user_authorise 458 * @param array $callstack the exploded callstack 459 * @param stdclass $rpcrecord the record from mnet_rpc 460 * 461 * @return mixed the response from the invoked method 462 */ 463 function mnet_server_invoke_plugin_method($method, $callstack, $rpcrecord, $payload) { 464 mnet_verify_permissions($rpcrecord); // will throw exceptions 465 mnet_setup_dummy_method($method, $callstack, $rpcrecord); 466 $methodname = array_pop($callstack); 467 468 $xmlrpcserver = xmlrpc_server_create(); 469 xmlrpc_server_register_method($xmlrpcserver, $method, 'mnet_server_dummy_method'); 470 $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $methodname, array("encoding" => "utf-8")); 471 xmlrpc_server_destroy($xmlrpcserver); 472 return $response; 473 } 474 475 /** 476 * Initialize the object (if necessary), execute the method or function, and 477 * return the response 478 * 479 * @param string $includefile The file that contains the object definition 480 * @param string $methodname The name of the method to execute 481 * @param string $method The full path to the method 482 * @param string $payload The XML-RPC request payload 483 * @param string $class The name of the class to instantiate (or false) 484 * 485 * @throws mnet_server_exception 486 * 487 * @return string The XML-RPC response 488 */ 489 function mnet_server_invoke_dangerous_method($includefile, $methodname, $method, $payload) { 490 491 if (file_exists($CFG->dirroot . $includefile)) { 492 require_once $CFG->dirroot . $includefile; 493 // $callprefix matches the rpc convention 494 // of not having a leading slash 495 $callprefix = preg_replace('!^/!', '', $includefile); 496 } else { 497 throw new mnet_server_exception(705, "nosuchfile"); 498 } 499 500 if ($functionname != clean_param($functionname, PARAM_PATH)) { 501 throw new mnet_server_exception(7012, "nosuchfunction"); 502 } 503 504 if (!function_exists($functionname)) { 505 throw new mnet_server_exception(7012, "nosuchfunction"); 506 } 507 $xmlrpcserver = xmlrpc_server_create(); 508 xmlrpc_server_register_method($xmlrpcserver, $method, 'mnet_server_dummy_method'); 509 $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $methodname, array("encoding" => "utf-8")); 510 xmlrpc_server_destroy($xmlrpcserver); 511 return $response; 512 } 513 514 515 /** 516 * Accepts a public key from a new remote host and returns the public key for 517 * this host. If 'register all hosts' is turned on, it will bootstrap a record 518 * for the remote host in the mnet_host table (if it's not already there) 519 * 520 * @param string $function XML-RPC requires this but we don't... discard! 521 * @param array $params Array of parameters 522 * $params[0] is the remote wwwroot 523 * $params[1] is the remote public key 524 * @return string The XML-RPC response 525 */ 526 function mnet_keyswap($function, $params) { 527 global $CFG; 528 $return = array(); 529 $mnet = get_mnet_environment(); 530 531 if (!empty($CFG->mnet_register_allhosts)) { 532 $mnet_peer = new mnet_peer(); 533 list($wwwroot, $pubkey, $application) = $params; 534 $keyok = $mnet_peer->bootstrap($wwwroot, $pubkey, $application); 535 if ($keyok) { 536 $mnet_peer->commit(); 537 } 538 } 539 return $mnet->public_key; 540 } 541 542 /** 543 * Verify that the requested xmlrpc method can be called 544 * This just checks the method exists in the rpc table and is enabled. 545 * 546 * @param stdclass $rpcrecord the record from mnet_rpc 547 * 548 * @throws mnet_server_exception 549 */ 550 function mnet_verify_permissions($rpcrecord) { 551 global $CFG, $DB; 552 $remoteclient = get_mnet_remote_client(); 553 554 $id_list = $remoteclient->id; 555 if (!empty($CFG->mnet_all_hosts_id)) { 556 $id_list .= ', '.$CFG->mnet_all_hosts_id; 557 } 558 559 $sql = "SELECT 560 r.*, h2s.publish 561 FROM 562 {mnet_rpc} r 563 JOIN {mnet_service2rpc} s2r ON s2r.rpcid = r.id 564 LEFT JOIN {mnet_host2service} h2s ON h2s.serviceid = s2r.serviceid 565 WHERE 566 r.id = ? AND 567 h2s.hostid in ($id_list)"; 568 569 $params = array($rpcrecord->id); 570 571 if (!$permission = $DB->get_record_sql($sql, $params)) { 572 throw new mnet_server_exception(7012, "nosuchfunction"); 573 } else if (!$permission->publish || !$permission->enabled) { 574 throw new mnet_server_exception(707, "nosuchfunction"); 575 } 576 } 577 578 /** 579 * Figure out exactly what needs to be called and stashes it in $remoteclient 580 * Does some further verification that the method is callable 581 * 582 * @param string $method the full xmlrpc method that was called eg auth/mnet/auth.php/user_authorise 583 * @param array $callstack the exploded callstack 584 * @param stdclass $rpcrecord the record from mnet_rpc 585 * 586 * @throws mnet_server_exception 587 */ 588 function mnet_setup_dummy_method($method, $callstack, $rpcrecord) { 589 global $CFG; 590 $remoteclient = get_mnet_remote_client(); 591 // verify that the callpath in the stack matches our records 592 // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); 593 $path = core_component::get_plugin_directory($rpcrecord->plugintype, $rpcrecord->pluginname); 594 $path = substr($path, strlen($CFG->dirroot)+1); // this is a bit hacky and fragile, it is not guaranteed that plugins are in dirroot 595 array_pop($callstack); 596 $providedpath = implode('/', $callstack); 597 if ($providedpath != $path . '/' . $rpcrecord->filename) { 598 throw new mnet_server_exception(705, "nosuchfile"); 599 } 600 if (!file_exists($CFG->dirroot . '/' . $providedpath)) { 601 throw new mnet_server_exception(705, "nosuchfile"); 602 } 603 require_once($CFG->dirroot . '/' . $providedpath); 604 if (!empty($rpcrecord->classname)) { 605 if (!class_exists($rpcrecord->classname)) { 606 throw new mnet_server_exception(708, 'nosuchclass'); 607 } 608 if (!$rpcrecord->static) { 609 try { 610 $object = new $rpcrecord->classname; 611 } catch (Exception $e) { 612 throw new mnet_server_exception(709, "classerror"); 613 } 614 if (!is_callable(array($object, $rpcrecord->functionname))) { 615 throw new mnet_server_exception(706, "nosuchfunction"); 616 } 617 $remoteclient->object_to_call($object); 618 } else { 619 if (!is_callable(array($rpcrecord->classname, $rpcrecord->functionname))) { 620 throw new mnet_server_exception(706, "nosuchfunction"); 621 } 622 $remoteclient->static_location($rpcrecord->classname); 623 } 624 } 625 } 626 627 /** 628 * Dummy function for the XML-RPC dispatcher - use to call a method on an object 629 * or to call a function 630 * 631 * Translate XML-RPC's strange function call syntax into a more straightforward 632 * PHP-friendly alternative. This dummy function will be called by the 633 * dispatcher, and can be used to call a method on an object, or just a function 634 * 635 * The methodName argument (eg. mnet/testlib/mnet_concatenate_strings) 636 * is ignored. 637 * 638 * @throws mnet_server_exception 639 * 640 * @param string $methodname We discard this - see 'functionname' 641 * @param array $argsarray Each element is an argument to the real 642 * function 643 * @param string $functionname The name of the PHP function you want to call 644 * @return mixed The return value will be that of the real 645 * function, whatever it may be. 646 */ 647 function mnet_server_dummy_method($methodname, $argsarray, $functionname) { 648 $remoteclient = get_mnet_remote_client(); 649 try { 650 if (is_object($remoteclient->object_to_call)) { 651 return @call_user_func_array(array($remoteclient->object_to_call,$functionname), $argsarray); 652 } else if (!empty($remoteclient->static_location)) { 653 return @call_user_func_array(array($remoteclient->static_location, $functionname), $argsarray); 654 } else { 655 return @call_user_func_array($functionname, $argsarray); 656 } 657 } catch (Exception $e) { 658 exit(mnet_server_fault($e->getCode(), $e->getMessage())); 659 } 660 } 661 /** 662 * mnet server exception. extends moodle_exception, but takes slightly different arguments. 663 * and unlike the rest of moodle, the actual int error code is used. 664 * this exception should only be used during an xmlrpc server request, ie, not for client requests. 665 */ 666 class mnet_server_exception extends moodle_exception { 667 668 /** 669 * @param int $intcode the numerical error associated with this fault. this is <b>not</b> the string errorcode 670 * @param string $langkey the error message in full (<b>get_string will not be used</b>) 671 * @param string $module the language module, defaults to 'mnet' 672 * @param mixed $a params for get_string 673 */ 674 public function __construct($intcode, $languagekey, $module='mnet', $a=null) { 675 parent::__construct($languagekey, $module, '', $a); 676 $this->code = $intcode; 677 678 } 679 } 680
title
Description
Body
title
Description
Body
title
Description
Body
title
Body