Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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('&lt;','&gt;','&amp;','&quot;','&apos;'), $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