From de260e0f7bed431d9584538f3c646db2a5577879 Mon Sep 17 00:00:00 2001 From: Penny Leach Date: Thu, 28 Jan 2010 05:25:50 +0000 Subject: [PATCH] mnet MDL-21261 large overhaul. This commit changes: - The way that mnet rpc functions are registered. Plugins must now create db/mnet.php which is an array, similar to services.php. This *replaces* the old mnet_publishes() functions. version.php must be bumped to trigger this. - More information about each rpc-available function is stored in the database, including the class it belongs to, the file it is found in, and whether or not it is static. Methods that are not static must be in a class with a constructor that takes no arguments (this can easily be achieved with a small wrapper if necessary) - The xmlrpc dispatcher has been rewritten to remove all the dependencies on hardcoded information about auth,mnet,portfolio and repository, and just use the information in the database. - The old hardcoded hidden mnet/testclient.php has been moved to the Admin menu under "Development" and rewritten. - The xmlrpc introspection method profiling is now using php and zend reflection - which is a lot nicer than the old way, which was using a php-based php parser. This fixes some inconsistent handling of methods without arguments that were advertising their return value as the only method parameter. While this is a *fix*, it breaks BC slightly - the old 1.9 broken mnet/testclient.php will now not work properly with 2.0 - Dangerous mode is still supported, but old mod/*/rpclib.php is now unsupported, due to the fact that any plugin can export mnet functions with db/mnet.php. This is a slight BC break. Still TODO: - TEST TEST TEST - Document the two small BC breaks in release notes - Document the contract for db/mnet.php --- admin/mnet/adminlib.php | 314 ++++---- admin/mnet/mnet_services.html | 2 +- admin/mnet/mnet_services.php | 10 +- admin/settings/development.php | 4 +- auth/mnet/auth.php | 35 +- auth/mnet/version.php | 2 +- enrol/mnet/enrol.php | 15 +- lang/en_utf8/mnet.php | 29 + lib/db/install.xml | 15 +- lib/db/upgrade.php | 31 + lib/portfolio/plugin.php | 9 - lib/upgradelib.php | 30 +- lib/zend/readme_moodle.txt | 2 +- mnet/lib.php | 152 ---- mnet/xmlrpc/server.php | 774 +------------------ portfolio/mahara/lib.php | 35 +- portfolio/mahara/preconfig.php | 25 + portfolio/mahara/version.php | 27 +- repository/lib.php | 8 - repository/mahara/repository.class.php | 13 - repository/mahara/version.php | 2 +- repository/remotemoodle/repository.class.php | 13 - repository/remotemoodle/version.php | 2 +- version.php | 3 +- 24 files changed, 355 insertions(+), 1197 deletions(-) diff --git a/admin/mnet/adminlib.php b/admin/mnet/adminlib.php index 701aed27cdb31..399069997a1ae 100644 --- a/admin/mnet/adminlib.php +++ b/admin/mnet/adminlib.php @@ -1,135 +1,153 @@ . + + /** - * Library functions for mnet + * This file contains the admin related mnet functions * - * @author Donal McMullan donal@catalyst.net.nz - * @version 0.0.1 - * @license http://www.gnu.org/copyleft/gpl.html GNU Public License - * @package mnet + * @since 2.0 + * @package moodlecore + * @copyright 2010 Penny Leach + * @copyright 2006 Donal McMullan + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** - * Parse a file to find out what functions/methods exist in it, and add entries - * for the remote-call-enabled functions to the database. + * upgrades the mnet rpc definitions for the given component. + * this method doesn't return status, an exception will be thrown in the case of an error * - * The path to a file, e.g. auth/mnet/auth.php can be thought of as - * type/parentname/docname - * - * @param string $type mod, auth or enrol - * @param string $parentname Implementation of type, e.g. 'mnet' in the - * case of auth/mnet/auth.php - * @return bool True on success, else false + * @param string $component the plugin to upgrade, eg auth_mnet */ -function mnet_get_functions($type, $parentname) { - global $CFG, $DB; +function upgrade_plugin_mnet_functions($component) { + global $DB, $CFG; - $dataobject = new stdClass(); - $docname = $type.'.php'; - $publishes = array(); - if ('mod' == $type) { - $docname = 'rpclib.php'; - $relname = '/mod/'.$parentname.'/'.$docname; - $filename = $CFG->dirroot.$relname; - if (file_exists($filename)) include_once $filename; - $mnet_publishes = $parentname.'_mnet_publishes'; - if (function_exists($mnet_publishes)) { - (array)$publishes = $mnet_publishes(); - } - } else if ('portfolio' == $type) { - $docname = 'lib.php'; - $relname = '/portfolio/' . $parentname . '/'. $docname; - $filename = $CFG->dirroot . $relname; - require_once($CFG->libdir . '/portfoliolib.php'); - $publishes = (array)portfolio_static_function($parentname, 'mnet_publishes'); - } else if ('repository' == $type) { - $docname = 'repository.class.php'; - $relname = '/repository/' . $parentname . '/'. $docname; - $filename = $CFG->dirroot . $relname; - require_once($CFG->dirroot . '/repository/lib.php'); - $publishes = (array)repository::static_function($parentname, 'mnet_publishes'); - } else { - // auth or enrol - $relname = '/'.$type.'/'.$parentname.'/'.$docname; - $filename = $CFG->dirroot.$relname; - if (file_exists($filename)) include_once $filename; - $class = $type.($type=='enrol'? 'ment':'').'_plugin_'.$parentname; - if (class_exists($class)) { - $object = new $class(); - if (method_exists($object, 'mnet_publishes')) { - (array)$publishes = $object->mnet_publishes(); - } - } + list($type, $plugin) = explode('_', $component); + $path = get_plugin_directory($type, $plugin); + + if (file_exists($path . '/db/mnet.php')) { + require_once($path . '/db/mnet.php'); // $publishes comes from this file + } + if (empty($publishes)) { + $publishes = array(); // still need this to be able to disable stuff later } - $methodServiceArray = array(); - foreach($publishes as $service) { + // rekey an array based on the rpc method for easy lookups later + $methodservices = array(); + foreach($publishes as $servicename => $service) { if (is_array($service['methods'])) { foreach($service['methods'] as $methodname) { - $methodServiceArray[$methodname][] = $service; + $service['servicename'] = $servicename; + $methodservices[$methodname][] = $service; } } } // Disable functions that don't exist (any more) in the source // Should these be deleted? What about their permissions records? - $rpcrecords = $DB->get_records('mnet_rpc', array('parent'=>$parentname, 'parent_type'=>$type), 'function_name ASC '); - if (!empty($rpcrecords)) { - foreach($rpcrecords as $rpc) { - if (!array_key_exists($rpc->function_name, $methodServiceArray)) { - $rpc->enabled = 0; - $DB->update_record('mnet_rpc', $rpc); - } + foreach ($DB->get_records('mnet_rpc', array('pluginname'=>$plugin, 'plugintype'=>$type), 'function_name ASC ') as $rpc) { + if (!array_key_exists($rpc->function_name, $methodservices)) { + $DB->set_field('mnet_rpc', 'enabled', 0, array('id' => $rpc->id)); } } - if (!file_exists($filename)) return false; - - if (extension_loaded('tokenizer')) { - include_once "$CFG->dirroot/$CFG->admin/mnet/MethodTable.php"; - $functions = (array)MethodTable::create($filename,false); - } - - foreach($methodServiceArray as $method => $servicearray) { - if (!empty($functions[$method])) { - $details = $functions[$method]; - $profile = $details['arguments']; - if (!isset($details['returns'])) { - array_unshift($profile, array('type' => 'void', 'description' => 'No return value')); + require_once($CFG->dirroot . '/lib/zend/Zend/Server/Reflection.php'); + static $cachedclasses = array(); // to store reflection information in + foreach ($publishes as $service => $data) { + $f = $data['filename']; + $c = $data['classname']; + foreach ($data['methods'] as $method) { + $dataobject = new stdclass; + $dataobject->plugintype = $type; + $dataobject->pluginname = $plugin; + $dataobject->enabled = 1; + $dataobject->classname = $c; + $dataobject->filename = $f; + + if (is_string($method)) { + $dataobject->function_name = $method; + + } else if (is_array($method)) { // wants to override file or class + $dataobject->function_name = $method['method']; + $dataobject->classname = $method['classname']; + $dataobject->filename = $method['filename']; + } + $dataobject->xmlrpc_path = $type.'/'.$plugin.'/'.$dataobject->filename.'/'.$method; + $dataobject->static = false; + + require_once($path . '/' . $dataobject->filename); + $functionreflect = null; // slightly different ways to get this depending on whether it's a class method or a function + if (!empty($dataobject->classname)) { + if (!class_exists($dataobject->classname)) { + throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->function_name, 'class' => $dataobject->classname)); + } + $key = $dataobject->filename . '|' . $dataobject->classname; + if (!array_key_exists($key, $cachedclasses)) { // look to see if we've already got a reflection object + try { + $cachedclasses[$key] = Zend_Server_Reflection::reflectClass($dataobject->classname); + } catch (Zend_Server_Reflection_Exception $e) { // catch these and rethrow them to something more helpful + throw new moodle_exception('installreflectionclasserror', 'mnet', '', (object)array('method' => $dataobject->function_name, 'class' => $dataobject->classname, 'error' => $e->getMessage())); + } + } + $r =& $cachedclasses[$key]; + if (!$r->hasMethod($dataobject->function_name)) { + throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->function_name, 'class' => $dataobject->classname)); + } + // stupid workaround for zend not having a getMethod($name) function + $ms = $r->getMethods(); + foreach ($ms as $m) { + if ($m->getName() == $dataobject->function_name) { + $functionreflect = $m; + break; + } + } + $dataobject->static = (int)$functionreflect->isStatic(); } else { - array_unshift($profile, $details['returns']); + if (!function_exists($dataobject->function_name)) { + throw new moodle_exception('installnosuchfunction', 'mnet', '', (object)array('method' => $dataobject->function_name, 'file' => $dataobject->filename)); + } + try { + $functionreflect = Zend_Server_Reflection::reflectFunction($dataobject->function_name); + } catch (Zend_Server_Reflection_Exception $e) { // catch these and rethrow them to something more helpful + throw new moodle_exception('installreflectionfunctionerror', 'mnet', '', (object)array('method' => $dataobject->function_name, '' => $dataobject->filename, 'error' => $e->getMessage())); + } } - $dataobject->profile = serialize($profile); - $dataobject->help = $details['description']; - } else { - $dataobject->profile = serialize(array(array('type' => 'void', 'description' => 'No return value'))); - $dataobject->help = ''; - } - - $dataobject->function_name = $method; - $dataobject->xmlrpc_path = $type.'/'.$parentname.'/'.$docname.'/'.$method; - $dataobject->parent_type = $type; - $dataobject->parent = $parentname; - $dataobject->enabled = '0'; + $dataobject->profile = serialize(admin_mnet_method_profile($functionreflect)); + $dataobject->help = $functionreflect->getDescription(); - if ($record_exists = $DB->get_record('mnet_rpc', array('xmlrpc_path'=>$dataobject->xmlrpc_path))) { - $dataobject->id = $record_exists->id; - $dataobject->enabled = $record_exists->enabled; - $DB->update_record('mnet_rpc', $dataobject); - } else { - $dataobject->id = $DB->insert_record('mnet_rpc', $dataobject, true); + if ($record_exists = $DB->get_record('mnet_rpc', array('xmlrpc_path'=>$dataobject->xmlrpc_path))) { + $dataobject->id = $record_exists->id; + $dataobject->enabled = $record_exists->enabled; + $DB->update_record('mnet_rpc', $dataobject); + } else { + $dataobject->id = $DB->insert_record('mnet_rpc', $dataobject, true); + } } - foreach($servicearray as $service) { - $serviceobj = $DB->get_record('mnet_service', array('name'=>$service['name'])); - if (false == $serviceobj) { + foreach ($methodservices[$dataobject->function_name] as $service) { + if ($serviceobj = $DB->get_record('mnet_service', array('name'=>$service['servicename']))) { + $serviceobj->apiversion = $service['apiversion']; + $DB->update_record('mnet_service', $serviceobj); + } else { $serviceobj = new stdClass(); - $serviceobj->name = $service['name']; + $serviceobj->name = $service['servicename']; $serviceobj->apiversion = $service['apiversion']; $serviceobj->offer = 1; - $serviceobj->id = $DB->insert_record('mnet_service', $serviceobj, true); + $serviceobj->id = $DB->insert_record('mnet_service', $serviceobj); } - - if (false == $DB->get_record('mnet_service2rpc', array('rpcid'=>$dataobject->id, 'serviceid'=>$serviceobj->id))) { + if (!$DB->record_exists('mnet_service2rpc', array('rpcid'=>$dataobject->id, 'serviceid'=>$serviceobj->id))) { $obj = new stdClass(); $obj->rpcid = $dataobject->id; $obj->serviceid = $serviceobj->id; @@ -137,76 +155,32 @@ function mnet_get_functions($type, $parentname) { } } } - return true; + return true; } -function upgrade_RPC_functions() { - global $CFG; - - // TODO: rewrite this thing so that it: - // 1/ does not include half the world - // 2/ returns status if something upgraded - needed for proper conitnue button - // 3/ upgrade functions in general should not use normal function calls to moodle core and modules - - $basedir = $CFG->dirroot.'/mod'; - if (file_exists($basedir) && filetype($basedir) == 'dir') { - $dirhandle = opendir($basedir); - while (false !== ($dir = readdir($dirhandle))) { - $firstchar = substr($dir, 0, 1); - if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf') { - continue; - } - if (filetype($basedir .'/'. $dir) != 'dir') { - continue; - } - - mnet_get_functions('mod', $dir); - - } - } - - $basedir = $CFG->dirroot.'/auth'; - if (file_exists($basedir) && filetype($basedir) == 'dir') { - $dirhandle = opendir($basedir); - while (false !== ($dir = readdir($dirhandle))) { - $firstchar = substr($dir, 0, 1); - if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf') { - continue; - } - if (filetype($basedir .'/'. $dir) != 'dir') { - continue; - } - - mnet_get_functions('auth', $dir); - } - } - - $basedir = $CFG->dirroot.'/enrol'; - if (file_exists($basedir) && filetype($basedir) == 'dir') { - $dirhandle = opendir($basedir); - while (false !== ($dir = readdir($dirhandle))) { - $firstchar = substr($dir, 0, 1); - if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf') { - continue; - } - if (filetype($basedir .'/'. $dir) != 'dir') { - continue; - } - - mnet_get_functions('enrol', $dir); - } - } - - if ($plugins = get_list_of_plugins('portfolio')) { - foreach ($plugins as $p) { - mnet_get_functions('portfolio', $p); - } - } - - if ($plugins = get_list_of_plugins('repository')) { - foreach ($plugins as $p) { - mnet_get_functions('repository', $p); - } +/** + * Given some sort of Zend Reflection function/method object, return a profile array, ready to be serialized and stored + * + * @param Zend_Server_Reflection_Function_Abstract $function can be any subclass of this object type + * + * @return array + */ +function admin_mnet_method_profile(Zend_Server_Reflection_Function_Abstract $function) { + $proto = array_pop($function->getPrototypes()); + $ret = $proto->getReturnValue(); + $profile = array( + 'parameters' => array(), + 'return' => array( + 'type' => $ret->getType(), + 'description' => $ret->getDescription(), + ), + ); + foreach ($proto->getParameters() as $p) { + $profile['parameters'][] = array( + 'name' => $p->getName(), + 'type' => $p->getType(), + 'description' => $p->getDescription(), + ); } + return $profile; } - diff --git a/admin/mnet/mnet_services.html b/admin/mnet/mnet_services.html index 7df1593baa77d..b133a565eab12 100644 --- a/admin/mnet/mnet_services.html +++ b/admin/mnet/mnet_services.html @@ -16,7 +16,7 @@ $versions) { $version = current($versions); - $langmodule = ($version['parent_type'] == 'mod' ? '' : ($version['parent_type'] . '_')) . $version['parent']; + $langmodule = ($version['plugintype'] == 'mod' ? '' : ($version['plugintype'] . '_')) . $version['pluginname']; ?> diff --git a/admin/mnet/mnet_services.php b/admin/mnet/mnet_services.php index f92cbf3136472..c318f38d783c0 100644 --- a/admin/mnet/mnet_services.php +++ b/admin/mnet/mnet_services.php @@ -83,7 +83,7 @@ $id_list .= ', '.$CFG->mnet_all_hosts_id; } - $concat = $DB->sql_concat('COALESCE(h2s.id,0) ', ' \'-\' ', ' svc.id', '\'-\'', 'r.parent_type', '\'-\'', 'r.parent'); + $concat = $DB->sql_concat('COALESCE(h2s.id,0) ', ' \'-\' ', ' svc.id', '\'-\'', 'r.plugintype', '\'-\'', 'r.pluginname'); $query = " SELECT DISTINCT @@ -92,8 +92,8 @@ svc.name, svc.offer, svc.apiversion, - r.parent_type, - r.parent, + r.plugintype, + r.pluginname, h2s.hostid, h2s.publish, h2s.subscribe @@ -156,8 +156,8 @@ 'name' => $result->name, 'offer' => $result->offer, 'apiversion' => $result->apiversion, - 'parent_type' => $result->parent_type, - 'parent' => $result->parent, + 'plugintype' => $result->plugintype, + 'pluginname' => $result->pluginname, 'hostsubscribes' => $result->hostsubscribes, 'hostpublishes' => $result->hostpublishes ); diff --git a/admin/settings/development.php b/admin/settings/development.php index 70ad9c9ec3925..3021dea4c81fa 100644 --- a/admin/settings/development.php +++ b/admin/settings/development.php @@ -38,5 +38,7 @@ // XMLDB editor $ADMIN->add('development', new admin_externalpage('xmldbeditor', get_string('xmldbeditor'), "$CFG->wwwroot/$CFG->admin/xmldb/")); - + if ($CFG->mnet_dispatcher_mode !== 'off') { + $ADMIN->add('development', new admin_externalpage('mnettestclient', get_string('testclient', 'mnet'), "$CFG->wwwroot/$CFG->admin/mnet/testclient.php")); + } } // end of speedup diff --git a/auth/mnet/auth.php b/auth/mnet/auth.php index a03a8db6b5622..bd1def96aeb12 100644 --- a/auth/mnet/auth.php +++ b/auth/mnet/auth.php @@ -31,27 +31,6 @@ function auth_plugin_mnet() { $this->config = get_config('auth/mnet'); } - /** - * Provides the allowed RPC services from this class as an array. - * @return array Allowed RPC services. - */ - function mnet_publishes() { - - $sso_idp = array(); - $sso_idp['name'] = 'sso_idp'; // Name & Description go in lang file - $sso_idp['apiversion'] = 1; - $sso_idp['methods'] = array('user_authorise','keepalive_server', 'kill_children', - 'refresh_log', 'fetch_user_image', 'fetch_theme_info', - 'update_enrolments'); - - $sso_sp = array(); - $sso_sp['name'] = 'sso_sp'; // Name & Description go in lang file - $sso_sp['apiversion'] = 1; - $sso_sp['methods'] = array('keepalive_client','kill_child'); - - return array($sso_idp, $sso_sp); - } - /** * This function is normally used to determine if the username and password * are correct for local logins. Always returns false, as local users do not @@ -74,7 +53,7 @@ function user_login($username, $password) { */ function user_authorise($token, $useragent) { global $CFG, $MNET, $SITE, $MNET_REMOTE_CLIENT, $DB; - require_once $CFG->dirroot . '/mnet/xmlrpc/server.php'; + require_once $CFG->dirroot . '/mnet/xmlrpc/serverlib.php'; $mnet_session = $DB->get_record('mnet_session', array('token'=>$token, 'useragent'=>$useragent)); if (empty($mnet_session)) { @@ -465,11 +444,11 @@ function confirm_mnet_session($token, $remotewwwroot) { * Invoke this function _on_ the IDP to update it with enrolment info local to * the SP right after calling user_authorise() * - * Normally called by the SP after calling + * Normally called by the SP after calling user_authorise() * - * @param string $username The username - * @param string $courses Assoc array of courses following the structure of mnet_enrol_course - * @return bool + * @param string $username The username + * @param string $courses Assoc array of courses following the structure of mnet_enrol_course + * @return bool */ function update_enrolments($username, $courses) { global $MNET_REMOTE_CLIENT, $CFG, $DB; @@ -623,7 +602,9 @@ function change_password_url() { * This function is called from admin/auth.php, and outputs a full page with * a form for configuring this plugin. * - * @param array $page An object containing all the data for this page. + * @param object $config + * @param object $err + * @param array $user_fields */ function config_form($config, $err, $user_fields) { global $CFG, $DB; diff --git a/auth/mnet/version.php b/auth/mnet/version.php index a744fcaaf762b..73d25f9b7a6dd 100644 --- a/auth/mnet/version.php +++ b/auth/mnet/version.php @@ -1,3 +1,3 @@ version = 2009112400; +$plugin->version = 2010012600; diff --git a/enrol/mnet/enrol.php b/enrol/mnet/enrol.php index e50bca924abcd..7f211d6b7d7db 100644 --- a/enrol/mnet/enrol.php +++ b/enrol/mnet/enrol.php @@ -65,22 +65,11 @@ function get_access_icons($course) { *** MNET functions *** ***/ - function mnet_publishes() { - - $enrol = array(); - $enrol['name'] = 'mnet_enrol'; // Name & Description go in lang file - $enrol['apiversion'] = 1; - $enrol['methods'] = array('available_courses','user_enrolments', 'enrol_user', 'unenrol_user', 'course_enrolments' ); - - return array($enrol); - } /** - * Does Foo + * Returns a list of all courses available for remote login * - * @param string $username The username - * @param int $mnethostid The id of the remote mnethost - * @return bool Whether the user can login from the remote host + * @return array Array of courses */ function available_courses() { global $CFG, $DB; diff --git a/lang/en_utf8/mnet.php b/lang/en_utf8/mnet.php index b4cd63e4f4deb..4197e3a0cc619 100644 --- a/lang/en_utf8/mnet.php +++ b/lang/en_utf8/mnet.php @@ -256,4 +256,33 @@ $string['error709'] = 'The remote site failed to obtain a SSL key from you.'; $string['error7026'] = 'The key that your message was signed with differs from the key that the remote host has on file for your server. Further, the remote host attempted to fetch your current key and failed to do so. Please manually re-key with the remote host and try again.'; +$string['installnosuchmethod'] = 'Coding error! Something is trying to install a mnet xmlrpc method ($a->method) on a class ($a->class) and it can\'t be found!'; +$string['installnosuchfunction'] = 'Coding error! Something is trying to install a mnet xmlrpc function ($a->method) from a file ($a->file) and it can\'t be found!'; +$string['installreflectionfunctionerror'] = 'Coding error! MNET introspection failed for function \'$a->method\' in file \'$a->file\'. The original error message, in case it helps, is: \'$a->error\''; +$string['installreflectionclasserror'] = 'Coding error! MNET introspection failed for method \'$a->method\' in class \'$a->class\'. The original error message, in case it helps, is: \'$a->error\''; + +// admin strings +$string['hostlist'] = 'List of Networked Hosts'; +$string['servicesavailableonhost'] = 'Services available on $a'; +$string['serviceid'] = 'Service ID'; +$string['service'] = 'Service Name'; +$string['version'] = 'Version'; +$string['theypublish'] = 'They publish'; +$string['theysubscribe'] = 'They subscribe'; +$string['listservices'] = 'List services'; +$string['methodsavailableonhostinservice'] = 'Methods available for $a->service on $a->host'; +$string['methodsavailableonhost'] = 'Methods available on $a'; +$string['method'] = 'Method'; +$string['options'] = 'Options'; +$string['inspect'] = 'Inspect'; +$string['methodsignature'] = 'Method signature for $a'; +$string['position'] = 'Position'; +$string['name'] = 'Name'; +$string['type'] = 'Type'; +$string['description'] = 'Description'; +$string['returnvalue'] = 'Return value'; +$string['methodhelp'] = 'Method help for $a'; +$string['testclient'] = 'Moodle Network Test Client'; +$string['unknown'] = 'Unknown'; +$string['notmoodleapplication'] = 'WARNING: This is not a moodle application, so some of the inspection methods may not work properly.'; ?> diff --git a/lib/db/install.xml b/lib/db/install.xml index cfff0a1665082..015872b8aed1c 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -1324,12 +1324,15 @@ - - - - + + + + - + + + + diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index f87379501ccc0..c8576f7c8b345 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2868,10 +2868,41 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint($result, 2010011200); } + if ($result && $oldversion < 2010012500) { upgrade_fix_incorrect_mnethostids(); upgrade_main_savepoint($result, 2010012500); } + + if ($result && $oldversion < 2010012600) { + // do stuff to the mnet table + $table = new xmldb_table('mnet_rpc'); + + $field = new xmldb_field('parent_type', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, 'xmlrpc_path'); + $dbman->rename_field($table, $field, 'plugintype'); + + $field = new xmldb_field('parent', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, 'xmlrpc_path'); + $dbman->rename_field($table, $field, 'pluginname'); + + $field = new xmldb_field('filename', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'profile'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $field = new xmldb_field('classname', XMLDB_TYPE_CHAR, '150', null, null, null, null, 'filename'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $field = new xmldb_field('static', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'classname'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + /// Main savepoint reached + upgrade_main_savepoint($result, 2010012600); + } + return $result; } diff --git a/lib/portfolio/plugin.php b/lib/portfolio/plugin.php index 39b895f8e1b7c..46bdf332183e7 100644 --- a/lib/portfolio/plugin.php +++ b/lib/portfolio/plugin.php @@ -728,15 +728,6 @@ public function cleanup() { return true; } - /** - * return array of mnet methods, keyed by service (pf) - * in the same format used everywhere else in moodle. - * see the mahara portfolio plugin for an example - */ - public static function mnet_publishes() { - return array(); - } - /** * whether this plugin supports multiple exports in the same session * most plugins should handle this, but some that require a redirect for authentication diff --git a/lib/upgradelib.php b/lib/upgradelib.php index 7fb665c4a3bae..e560e71794523 100644 --- a/lib/upgradelib.php +++ b/lib/upgradelib.php @@ -335,6 +335,8 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); $endcallback($component, true, $verbose); } } @@ -366,6 +368,8 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); theme_reset_all_caches(); $endcallback($component, true, $verbose); @@ -394,6 +398,8 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); theme_reset_all_caches(); $endcallback($component, false, $verbose); @@ -462,6 +468,8 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); $endcallback($component, true, $verbose); } } @@ -491,6 +499,8 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); theme_reset_all_caches(); $endcallback($component, true, $verbose); @@ -518,6 +528,8 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); theme_reset_all_caches(); remove_dir($CFG->dataroot.'/cache', true); // flush cache @@ -606,6 +618,8 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); $endcallback($component, true, $verbose); } } @@ -643,6 +657,8 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); theme_reset_all_caches(); $endcallback($component, true, $verbose); @@ -675,6 +691,8 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { external_update_descriptions($component); events_update_definition($component); message_update_providers($component); + require_once($CFG->dirroot . '/' . $CFG->admin . '/mnet/adminlib.php'); + upgrade_plugin_mnet_functions($component); theme_reset_all_caches(); $endcallback($component, false, $verbose); @@ -1258,18 +1276,6 @@ function upgrade_noncore($verbose) { } catch (Exception $ex) { upgrade_handle_exception($ex); } - - // Check for changes to RPC functions - if ($CFG->mnet_dispatcher_mode != 'off') { - try { - // this needs a full rewrite, sorry to mention that :-( - // we have to make it part of standard WS framework - require_once("$CFG->dirroot/$CFG->admin/mnet/adminlib.php"); - upgrade_RPC_functions(); // Return here afterwards - } catch (Exception $ex) { - upgrade_handle_exception($ex); - } - } } /** diff --git a/lib/zend/readme_moodle.txt b/lib/zend/readme_moodle.txt index 5511b306c1ba8..bc7337dc04b99 100644 --- a/lib/zend/readme_moodle.txt +++ b/lib/zend/readme_moodle.txt @@ -1,7 +1,7 @@ Description of Zend framework 1.9.4 import into Moodle Please note the zend framework is severly crippled - everything not needed in /webservice/* is removed. -Do not use outside of our /webservice/* !! +Do not use outside of our /webservice/* or mnet !! Changes: diff --git a/mnet/lib.php b/mnet/lib.php index 57dcfffd7e1eb..5aa0b395b0b3c 100644 --- a/mnet/lib.php +++ b/mnet/lib.php @@ -421,138 +421,6 @@ function mnet_generate_keypair($dn = null, $days=28) { return $keypair; } -/** - * Check that a given function (or method) in an include file has been designated - * ok for export - * - * @param string $includefile The path to the include file - * @param string $functionname The name of the function (or method) to - * execute - * @param mixed $class A class name, or false if we're just testing - * a function - * @return int Zero (RPC_OK) if all ok - appropriate - * constant otherwise - */ -function mnet_permit_rpc_call($includefile, $functionname, $class=false) { - global $CFG, $MNET_REMOTE_CLIENT, $DB; - - if (file_exists($CFG->dirroot . $includefile)) { - include_once $CFG->dirroot . $includefile; - // $callprefix matches the rpc convention - // of not having a leading slash - $callprefix = preg_replace('!^/!', '', $includefile); - } else { - return RPC_NOSUCHFILE; - } - - if ($functionname != clean_param($functionname, PARAM_PATH)) { - // Under attack? - // Todo: Should really return a much more BROKEN! response - return RPC_FORBIDDENMETHOD; - } - - $id_list = $MNET_REMOTE_CLIENT->id; - if (!empty($CFG->mnet_all_hosts_id)) { - $id_list .= ', '.$CFG->mnet_all_hosts_id; - } - - // TODO: change to left-join so we can disambiguate: - // 1. method doesn't exist - // 2. method exists but is prohibited - $sql = " - SELECT - count(r.id) - FROM - {mnet_host2service} h2s, - {mnet_service2rpc} s2r, - {mnet_rpc} r - WHERE - h2s.serviceid = s2r.serviceid AND - s2r.rpcid = r.id AND - r.xmlrpc_path = ? AND - h2s.hostid in ($id_list) AND - h2s.publish = '1'"; - $params = array("$callprefix/$functionname"); - - $permission = $DB->count_records_sql($sql, $params); - - if (!$permission && 'dangerous' != $CFG->mnet_dispatcher_mode) { - return RPC_FORBIDDENMETHOD; - } - - // WE'RE LOOKING AT A CLASS/METHOD - if (false != $class) { - if (!class_exists($class)) { - // Generate error response - unable to locate class - return RPC_NOSUCHCLASS; - } - - $object = false; - $static = false; - - try { - // @todo set here whatever we can to ensure that errors cause exceptions :( - // see MDL-16175 - long term solution is teaching the dispatcher about: - // a: the difference between static and class methods - // b: object constructor arguments - $object = @new $class(); - if (!method_exists($object, $functionname)) { - // Generate error response - unable to locate method - return RPC_NOSUCHMETHOD; - } - - if (!method_exists($object, 'mnet_publishes')) { - // Generate error response - the class doesn't publish - // *any* methods, because it doesn't have an mnet_publishes - // method - return RPC_FORBIDDENMETHOD; - } - - // Get the list of published services - initialise method array - $servicelist = $object->mnet_publishes(); - } - catch (Exception $e) { - if (method_exists($class, $functionname)) { - // static... don't try to instantiate it. - if (!method_exists($class, 'mnet_publishes')) { - return RPC_FORBIDDENMETHOD; - } else { - $servicelist = call_user_func(array($class, 'mnet_publishes')); - } - $static = $class; - } - } - - $methodapproved = false; - - // If the method is in the list of approved methods, set the - // methodapproved flag to true and break - foreach($servicelist as $service) { - if (in_array($functionname, $service['methods'])) { - $methodapproved = true; - break; - } - } - - if (!$methodapproved) { - return RPC_FORBIDDENMETHOD; - } - - // Stash the object so we can call the method on it later - $MNET_REMOTE_CLIENT->object_to_call($object); - $MNET_REMOTE_CLIENT->static_location($static); - - // WE'RE LOOKING AT A FUNCTION - } else { - if (!function_exists($functionname)) { - // Generate error response - unable to locate function - return RPC_NOSUCHFUNCTION; - } - - } - - return RPC_OK; -} function mnet_update_sso_access_control($username, $mnet_host_id, $accessctrl) { global $DB; @@ -655,23 +523,3 @@ function mnet_get_app_jumppath ($applicationid) { return $appjumppaths[$applicationid]; } -/** - * mnet server exception. extends moodle_exception, but takes slightly different arguments. - * error strings are always in mnet, so $module is not necessary, - * and unlike the rest of moodle, the actual int error code is used. - * this exception should only be used during an xmlrpc server request, ie, not for client requests. - */ -class mnet_server_exception extends moodle_exception { - - /** - * @param int $intcode the numerical error associated with this fault. this is not the string errorcode - * @param string $languagekey the key to the language string for the error message, or the text of the message (mnet_server_fault figures it out for you) if you're not using mnet error string file - * @param mixed $a params for get_string - */ - public function __construct($intcode, $languagekey, $a=null) { - parent::__construct($languagekey, 'mnet', '', $a); - $this->code = $intcode; - $this->message = $languagekey; // override this because mnet_server_fault (our handler) uses this directly - - } -} diff --git a/mnet/xmlrpc/server.php b/mnet/xmlrpc/server.php index 2658e899f7ff4..f9cbbf439653c 100644 --- a/mnet/xmlrpc/server.php +++ b/mnet/xmlrpc/server.php @@ -20,6 +20,8 @@ // Include MNET stuff: require_once $CFG->dirroot.'/mnet/lib.php'; require_once $CFG->dirroot.'/mnet/remote_client.php'; +require_once $CFG->dirroot.'/mnet/xmlrpc/serverlib.php'; + if ($CFG->mnet_dispatcher_mode === 'off') { print_error('mnetdisabled', 'mnet'); @@ -35,8 +37,8 @@ } if (!empty($CFG->mnet_rpcdebug)) { - trigger_error("HTTP_RAW_POST_DATA"); - trigger_error($HTTP_RAW_POST_DATA); + error_log("HTTP_RAW_POST_DATA"); + error_log($HTTP_RAW_POST_DATA); } if (!isset($_SERVER)) { @@ -55,6 +57,10 @@ exit(mnet_server_fault($e->getCode(), $e->getMessage(), $e->a)); } +if (!empty($CFG->mnet_rpcdebug)) { + error_log("XMLRPC Payload"); + error_log(print_r($xmlrpcrequest,1)); +} if($MNET_REMOTE_CLIENT->pushkey == true) { // The peer used one of our older public keys, we will return a @@ -73,762 +79,26 @@ || (($method == 'system.keyswap') || ($method == 'system/keyswap')) || (($MNET_REMOTE_CLIENT->signatureok == true) && ($MNET_REMOTE_CLIENT->plaintext_is_ok() == true))) { try { - $response = mnet_server_dispatch($xmlrpcrequest); + // main dispatch call. will echo the response directly + mnet_server_dispatch($xmlrpcrequest); + exit; } catch (Exception $e) { exit(mnet_server_fault($e->getCode(), $e->getMessage(), $e->a)); } -} else { - if (($MNET_REMOTE_CLIENT->request_was_encrypted == false) && ($MNET_REMOTE_CLIENT->plaintext_is_ok() == false)) { - exit(mnet_server_fault(7021, 'forbidden-transport')); - } - - if ($MNET_REMOTE_CLIENT->request_was_signed == false) { - // Request was not signed - exit(mnet_server_fault(711, 'verifysignature-error')); - } - - if ($MNET_REMOTE_CLIENT->signatureok == false) { - // We were unable to verify the signature - exit(mnet_server_fault(710, 'verifysignature-invalid')); - } } - - -if (!empty($CFG->mnet_rpcdebug)) { - trigger_error("XMLRPC Payload"); - trigger_error(print_r($xmlrpcrequest,1)); +// if we get to here, something is wrong +// so detect a few common cases and send appropriate errors +if (($MNET_REMOTE_CLIENT->request_was_encrypted == false) && ($MNET_REMOTE_CLIENT->plaintext_is_ok() == false)) { + exit(mnet_server_fault(7021, 'forbidden-transport')); } -/** - * -----XML-Envelope--------------------------------- - * | | - * | Encrypted-Symmetric-key---------------- | - * | |_____________________________________| | - * | | - * | Encrypted data------------------------- | - * | | | | - * | | -XML-Envelope------------------ | | - * | | | | | | - * | | | --Signature------------- | | | - * | | | |______________________| | | | - * | | | | | | - * | | | --Signed-Payload-------- | | | - * | | | | | | | | - * | | | | XML-RPC Request | | | | - * | | | |______________________| | | | - * | | | | | | - * | | |_____________________________| | | - * | |_____________________________________| | - * | | - * |________________________________________________| - * - */ - -/* Strip encryption envelope (if present) and decrypt data - * - * @param string $HTTP_RAW_POST_DATA The XML that the client sent - * - * @throws mnet_server_exception - * - * @return string XML with any encryption envolope removed - */ -function mnet_server_strip_encryption($HTTP_RAW_POST_DATA) { - global $MNET, $MNET_REMOTE_CLIENT; - $crypt_parser = new mnet_encxml_parser(); - $crypt_parser->parse($HTTP_RAW_POST_DATA); - - if (!$crypt_parser->payload_encrypted) { - return $HTTP_RAW_POST_DATA; - } - - // Make sure we know who we're talking to - $host_record_exists = $MNET_REMOTE_CLIENT->set_wwwroot($crypt_parser->remote_wwwroot); - - if (false == $host_record_exists) { - throw new mnet_server_exception(7020, 'wrong-wwwroot', $crypt_parser->remote_wwwroot); - } - - // This key is symmetric, and is itself encrypted. Can be decrypted using our private key - $key = array_pop($crypt_parser->cipher); - // This data is symmetrically encrypted, can be decrypted using the above key - $data = array_pop($crypt_parser->cipher); - - $crypt_parser->free_resource(); - $payload = ''; // Initialize payload var - - // &$payload - $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $MNET->get_private_key()); - if ($isOpen) { - $MNET_REMOTE_CLIENT->was_encrypted(); - return $payload; - } - - // Decryption failed... let's try our archived keys - $openssl_history = get_config('mnet', 'openssl_history'); - if(empty($openssl_history)) { - $openssl_history = array(); - set_config('openssl_history', serialize($openssl_history), 'mnet'); - } else { - $openssl_history = unserialize($openssl_history); - } - foreach($openssl_history as $keyset) { - $keyresource = openssl_pkey_get_private($keyset['keypair_PEM']); - $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $keyresource); - if ($isOpen) { - // It's an older code, sir, but it checks out - - $MNET_REMOTE_CLIENT->was_encrypted(); - $MNET_REMOTE_CLIENT->encrypted_to($keyresource); - $MNET_REMOTE_CLIENT->set_pushkey(); - return $payload; - } - } - - //If after all that we still couldn't decrypt the message, error out. - throw new mnet_server_exception(7023, 'encryption-invalid'); +if ($MNET_REMOTE_CLIENT->request_was_signed == false) { + // Request was not signed + exit(mnet_server_fault(711, 'verifysignature-error')); } -/* Strip signature envelope (if present), try to verify any signature using our record of remote peer's public key. - * - * @param string $plaintextmessage XML envelope containing XMLRPC request and signature - * - * @return string XMLRPC request - */ -function mnet_server_strip_signature($plaintextmessage) { - global $MNET, $MNET_REMOTE_CLIENT; - $sig_parser = new mnet_encxml_parser(); - $sig_parser->parse($plaintextmessage); - - if ($sig_parser->signature == '') { - return $plaintextmessage; - } - - // Record that the request was signed in some way - $MNET_REMOTE_CLIENT->was_signed(); - - // Load any information we have about this mnet peer - $MNET_REMOTE_CLIENT->set_wwwroot($sig_parser->remote_wwwroot); - - $payload = base64_decode($sig_parser->data_object); - $signature = base64_decode($sig_parser->signature); - $certificate = $MNET_REMOTE_CLIENT->public_key; - - // If we don't have any certificate for the host, don't try to check the signature - // Just return the parsed request - if ($certificate == false) { - return $payload; - } - - // Does the signature match the data and the public cert? - $signature_verified = openssl_verify($payload, $signature, $certificate); - if ($signature_verified == 0) { - // $signature was not generated for $payload using $certificate - // Get the key the remote peer is currently publishing: - $currkey = mnet_get_public_key($MNET_REMOTE_CLIENT->wwwroot, $MNET_REMOTE_CLIENT->application); - // If the key the remote peer is currently publishing is different to $certificate - if($currkey != $certificate) { - // Try and get the server's new key through trusted means - $MNET_REMOTE_CLIENT->refresh_key(); - // If we did manage to re-key, try to verify the signature again using the new public key. - $certificate = $MNET_REMOTE_CLIENT->public_key; - $signature_verified = openssl_verify($payload, $signature, $certificate); - } - } - - if ($signature_verified == 1) { - $MNET_REMOTE_CLIENT->signature_verified(); - $MNET_REMOTE_CLIENT->touch(); - } - - $sig_parser->free_resource(); - - return $payload; -} - -/** - * Return the proper XML-RPC content to report an error in the local language. - * - * @param int $code The ID code of the error message - * @param string $text The array-key of the error message in the lang file - * or the full string (will be detected by the function - * @param string $param The $a param for the error message in the lang file - * @return string $text The text of the error message - */ -function mnet_server_fault($code, $text, $param = null) { - global $MNET_REMOTE_CLIENT; - if (!is_numeric($code)) { - $code = 0; - } - $code = intval($code); - - $string = get_string($text, 'mnet', $param); - if (strpos($string, '[[') === 0) { - $string = $text; - } - - return mnet_server_fault_xml($code, $string); -} - -/** - * Return the proper XML-RPC content to report an error. - * - * @param int $code The ID code of the error message - * @param string $text The error message - * @param resource $privatekey The private key that should be used to sign the response - * @return string $text The XML text of the error message - */ -function mnet_server_fault_xml($code, $text, $privatekey = null) { - global $MNET_REMOTE_CLIENT, $CFG; - // Replace illegal XML chars - is this already in a lib somewhere? - $text = str_replace(array('<','>','&','"',"'"), array('<','>','&','"','''), $text); - - $return = mnet_server_prepare_response(' - - - - - - faultCode - '.$code.' - - - faultString - '.$text.' - - - - -', $privatekey); - - if (!empty($CFG->mnet_rpcdebug)) { - trigger_error("XMLRPC Error Response $code: $text"); - trigger_error(print_r($return,1)); - } - - return $return; -} - -/** - * Dummy function for the XML-RPC dispatcher - use to call a method on an object - * or to call a function - * - * Translate XML-RPC's strange function call syntax into a more straightforward - * PHP-friendly alternative. This dummy function will be called by the - * dispatcher, and can be used to call a method on an object, or just a function - * - * The methodName argument (eg. mnet/testlib/mnet_concatenate_strings) - * is ignored. - * - * @throws mnet_server_exception - * - * @param string $methodname We discard this - see 'functionname' - * @param array $argsarray Each element is an argument to the real - * function - * @param string $functionname The name of the PHP function you want to call - * @return mixed The return value will be that of the real - * function, whateber it may be. - */ -function mnet_server_dummy_method($methodname, $argsarray, $functionname) { - global $MNET_REMOTE_CLIENT; - - try { - if (is_object($MNET_REMOTE_CLIENT->object_to_call)) { - return @call_user_func_array(array($MNET_REMOTE_CLIENT->object_to_call,$functionname), $argsarray); - } else if (!empty($MNET_REMOTE_CLIENT->static_location)) { - return @call_user_func_array(array($MNET_REMOTE_CLIENT->static_location, $functionname), $argsarray); - } else { - return @call_user_func_array($functionname, $argsarray); - } - } catch (Exception $e) { - exit(mnet_server_fault($e->getCode(), $e->getMessage())); - } -} - -/** - * Package a response in any required envelope, and return it to the client - * - * @param string $response The XMLRPC response string - * @param resource $privatekey The private key to sign the response with - * @return string The encoded response string - */ -function mnet_server_prepare_response($response, $privatekey = null) { - global $MNET_REMOTE_CLIENT; - - if ($MNET_REMOTE_CLIENT->request_was_signed) { - $response = mnet_sign_message($response, $privatekey); - } - - if ($MNET_REMOTE_CLIENT->request_was_encrypted) { - $response = mnet_encrypt_message($response, $MNET_REMOTE_CLIENT->public_key); - } - - return $response; -} - -/** - * If security checks are passed, dispatch the request to the function/method - * - * The config variable 'mnet_dispatcher_mode' can be: - * strict: Only execute functions that are in specific files - * off: The default - don't execute anything - * - * @param string $payload The XML-RPC request - * - * @throws mnet_server_exception - * - * @return No return val - just echo the response - */ -function mnet_server_dispatch($payload) { - global $CFG, $MNET_REMOTE_CLIENT; - // xmlrpc_decode_request returns an array of parameters, and the $method - // variable (which is passed by reference) is instantiated with the value from - // the methodName tag in the xml payload - // xmlrpc_decode_request($xml, &$method) - $params = xmlrpc_decode_request($payload, $method); - - // $method is something like: "mod/forum/lib.php/forum_add_instance" - // $params is an array of parameters. A parameter might itself be an array. - - // Whitelist characters that are permitted in a method name - // The method name must not begin with a / - avoid absolute paths - // A dot character . is only allowed in the filename, i.e. something.php - if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_\.-]+(\.php/)?[A-Za-z0-9_-]+$@",$method)) { - throw new mnet_server_exception(713, 'nosuchfunction'); - } - - if(preg_match("/^system\./", $method)) { - $callstack = explode('.', $method); - } else { - $callstack = explode('/', $method); - // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); - } - - /** - * What has the site administrator chosen as his dispatcher setting? - * strict: Only execute functions that are in specific files - * off: The default - don't execute anything - */ - ////////////////////////////////////// OFF - if (!isset($CFG->mnet_dispatcher_mode) ) { - set_config('mnet_dispatcher_mode', 'off'); - throw new mnet_server_exception(704, 'nosuchservice'); - } elseif ('off' == $CFG->mnet_dispatcher_mode) { - throw new mnet_server_exception(704, 'nosuchservice'); - - ////////////////////////////////////// SYSTEM METHODS - } elseif ($callstack[0] == 'system') { - $functionname = $callstack[1]; - $xmlrpcserver = xmlrpc_server_create(); - - // I'm adding the canonical xmlrpc references here, however we've - // already forbidden that the period (.) should be allowed in the call - // stack, so if someone tries to access our XMLRPC in the normal way, - // they'll already have received a RPC server fault message. - - // Maybe we should allow an easement so that regular XMLRPC clients can - // call our system methods, and find out what we have to offer? - - xmlrpc_server_register_method($xmlrpcserver, 'system.listMethods', 'mnet_system'); - xmlrpc_server_register_method($xmlrpcserver, 'system/listMethods', 'mnet_system'); - - xmlrpc_server_register_method($xmlrpcserver, 'system.methodSignature', 'mnet_system'); - xmlrpc_server_register_method($xmlrpcserver, 'system/methodSignature', 'mnet_system'); - - xmlrpc_server_register_method($xmlrpcserver, 'system.methodHelp', 'mnet_system'); - xmlrpc_server_register_method($xmlrpcserver, 'system/methodHelp', 'mnet_system'); - - xmlrpc_server_register_method($xmlrpcserver, 'system.listServices', 'mnet_system'); - xmlrpc_server_register_method($xmlrpcserver, 'system/listServices', 'mnet_system'); - - xmlrpc_server_register_method($xmlrpcserver, 'system.listFiles', 'mnet_system'); - xmlrpc_server_register_method($xmlrpcserver, 'system/listFiles', 'mnet_system'); - - xmlrpc_server_register_method($xmlrpcserver, 'system.retrieveFile', 'mnet_system'); - xmlrpc_server_register_method($xmlrpcserver, 'system/retrieveFile', 'mnet_system'); - - xmlrpc_server_register_method($xmlrpcserver, 'system.keyswap', 'mnet_keyswap'); - xmlrpc_server_register_method($xmlrpcserver, 'system/keyswap', 'mnet_keyswap'); - - if ($method == 'system.listMethods' || - $method == 'system/listMethods' || - $method == 'system.methodSignature' || - $method == 'system/methodSignature' || - $method == 'system.methodHelp' || - $method == 'system/methodHelp' || - $method == 'system.listServices' || - $method == 'system/listServices' || - $method == 'system.listFiles' || - $method == 'system/listFiles' || - $method == 'system.retrieveFile' || - $method == 'system/retrieveFile' || - $method == 'system.keyswap' || - $method == 'system/keyswap') { - - $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $MNET_REMOTE_CLIENT, array("encoding" => "utf-8")); - $response = mnet_server_prepare_response($response); - } else { - throw new mnet_server_exception(7018, 'nosuchfunction'); - } - - xmlrpc_server_destroy($xmlrpcserver); - echo $response; - ////////////////////////////////////// STRICT AUTH - } elseif ($callstack[0] == 'auth') { - - // Break out the callstack into its elements - list($base, $plugin, $filename, $methodname) = $callstack; - - // We refuse to include anything that is not auth.php - if ($filename == 'auth.php' && is_enabled_auth($plugin)) { - $authclass = 'auth_plugin_'.$plugin; - $includefile = '/auth/'.$plugin.'/auth.php'; - $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $authclass); - $response = mnet_server_prepare_response($response); - echo $response; - } else { - // Generate error response - unable to locate function - throw new mnet_server_exception(702, 'nosuchfunction'); - } - - ////////////////////////////////////// STRICT ENROL - } elseif ($callstack[0] == 'enrol') { - - // Break out the callstack into its elements - list($base, $plugin, $filename, $methodname) = $callstack; - - if ($filename == 'enrol.php' && is_enabled_enrol($plugin)) { - $enrolclass = 'enrolment_plugin_'.$plugin; - $includefile = '/enrol/'.$plugin.'/enrol.php'; - $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $enrolclass); - $response = mnet_server_prepare_response($response); - echo $response; - } else { - // Generate error response - unable to locate function - throw new mnet_server_exception(703, 'nosuchfunction'); - } - - } else if ($callstack[0] == 'portfolio') { - // Break out the callstack into its elements - list($base, $plugin, $filename, $methodname) = $callstack; - - if ($filename == 'lib.php') { - $pluginclass = 'portfolio_plugin_' . $plugin; - $includefile = '/portfolio/'.$plugin.'/lib.php'; - $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $pluginclass); - $response = mnet_server_prepare_response($response); - echo $response; - } else { - // Generate error response - unable to locate function - throw new mnet_server_exception(7012, 'nosuchfunction'); - } - - } else if ($callstack[0] == 'repository') { - // Break out the callstack into its elements - list($base, $plugin, $filename, $methodname) = $callstack; - - if ($filename == 'repository.class.php') { - $pluginclass = 'repository_' . $plugin; - $includefile = '/repository/'.$plugin.'/repository.class.php'; - $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $pluginclass); - $response = mnet_server_prepare_response($response); - echo $response; - } else { - // Generate error response - unable to locate function - throw new mnet_server_exception(7012, 'nosuchfunction'); - } - - ////////////////////////////////////// STRICT MOD/* - } elseif ($callstack[0] == 'mod' || 'dangerous' == $CFG->mnet_dispatcher_mode) { - list($base, $module, $filename, $functionname) = $callstack; - - ////////////////////////////////////// STRICT MOD/* - if ($base == 'mod' && $filename == 'rpclib.php') { - $includefile = '/mod/'.$module.'/rpclib.php'; - $response = mnet_server_invoke_method($includefile, $functionname, $method, $payload); - $response = mnet_server_prepare_response($response); - echo $response; - - ////////////////////////////////////// DANGEROUS - } elseif ('dangerous' == $CFG->mnet_dispatcher_mode && $MNET_REMOTE_CLIENT->plaintext_is_ok()) { - - $functionname = array_pop($callstack); - - if ($MNET_REMOTE_CLIENT->plaintext_is_ok()) { - - $filename = clean_param(implode('/',$callstack), PARAM_PATH); - if (0 == preg_match("/php$/", $filename)) { - // Filename doesn't end in 'php'; possible attack? - // Generate error response - unable to locate function - throw new mnet_server_exception(7012, 'nosuchfunction'); - } - - // The call stack holds the path to any include file - $includefile = $CFG->dirroot.'/'.$filename; - - $response = mnet_server_invoke_method($includefile, $functionname, $method, $payload); - echo $response; - } - - } else { - // Generate error response - unable to locate function - throw new mnet_server_exception(7012, 'nosuchfunction'); - } - - } else { - // Generate error response - unable to locate function - throw new mnet_server_exception(7012, 'nosuchfunction'); - } -} - -/** - * Execute the system functions - mostly for introspection - * - * @param string $method XMLRPC method name, e.g. system.listMethods - * @param array $params Array of parameters from the XMLRPC request - * @param string $hostinfo Hostinfo object from the mnet_host table - * - * @throws mnet_server_exception - * - * @return mixed Response data - any kind of PHP variable - */ -function mnet_system($method, $params, $hostinfo) { - global $CFG, $DB; - - if (empty($hostinfo)) return array(); - - $id_list = $hostinfo->id; - if (!empty($CFG->mnet_all_hosts_id)) { - $id_list .= ', '.$CFG->mnet_all_hosts_id; - } - - if ('system.listMethods' == $method || 'system/listMethods' == $method) { - if (count($params) == 0) { - $query = ' - SELECT DISTINCT - rpc.function_name, - rpc.xmlrpc_path, - rpc.enabled, - rpc.help, - rpc.profile - FROM - {mnet_host2service} h2s, - {mnet_service2rpc} s2r, - {mnet_rpc} rpc - WHERE - s2r.rpcid = rpc.id AND - h2s.serviceid = s2r.serviceid AND - h2s.hostid in ('.$id_list .') AND - h2s.publish =\'1\' - ORDER BY - rpc.xmlrpc_path ASC'; - $params = array(); - - } else { - $query = ' - SELECT DISTINCT - rpc.function_name, - rpc.xmlrpc_path, - rpc.enabled, - rpc.help, - rpc.profile - FROM - {mnet_host2service} h2s, - {mnet_service2rpc} s2r, - {mnet_service} svc, - {mnet_rpc} rpc - WHERE - s2r.rpcid = rpc.id AND - h2s.serviceid = s2r.serviceid AND - h2s.hostid in ('.$id_list .') AND - h2s.publish =\'1\' AND - svc.id = h2s.serviceid AND - svc.name = ? - ORDER BY - rpc.xmlrpc_path ASC'; - $params = array($params[0]); - - } - $resultset = array_values($DB->get_records_sql($query, $params)); - $methods = array(); - foreach($resultset as $result) { - $methods[] = $result->xmlrpc_path; - } - return $methods; - } elseif ('system.methodSignature' == $method || 'system/methodSignature' == $method) { - $query = ' - SELECT DISTINCT - rpc.function_name, - rpc.xmlrpc_path, - rpc.enabled, - rpc.help, - rpc.profile - FROM - {mnet_host2service} h2s, - {mnet_service2rpc} s2r, - {mnet_rpc rpc} - WHERE - rpc.xmlrpc_path = ? AND - s2r.rpcid = rpc.id AND - h2s.serviceid = s2r.serviceid AND - h2s.publish =\'1\' AND - h2s.hostid in ('.$id_list .')'; - $params = array($params[0]); - - $result = $DB->get_records_sql($query, $params); - $methodsigs = array(); - - if (is_array($result)) { - foreach($result as $method) { - $methodsigs[] = unserialize($method->profile); - } - } - - return $methodsigs; - } elseif ('system.methodHelp' == $method || 'system/methodHelp' == $method) { - $query = ' - SELECT DISTINCT - rpc.function_name, - rpc.xmlrpc_path, - rpc.enabled, - rpc.help, - rpc.profile - FROM - {mnet_host2service} h2s, - {mnet_service2rpc} s2r, - {mnet_rpc} rpc - WHERE - rpc.xmlrpc_path = ? AND - s2r.rpcid = rpc.id AND - h2s.publish =\'1\' AND - h2s.serviceid = s2r.serviceid AND - h2s.hostid in ('.$id_list .')'; - $params = array($params[0]); - - $result = $DB->get_record_sql($query, $params); - - if (is_object($result)) { - return $result->help; - } - } elseif ('system.listServices' == $method || 'system/listServices' == $method) { - $query = ' - SELECT DISTINCT - s.id, - s.name, - s.apiversion, - h2s.publish, - h2s.subscribe - FROM - {mnet_host2service} h2s, - {mnet_service} s - WHERE - h2s.serviceid = s.id AND - (h2s.publish =\'1\' OR h2s.subscribe =\'1\') AND - h2s.hostid in ('.$id_list .') - ORDER BY - s.name ASC'; - $params = array(); - - $result = $DB->get_records_sql($query, $params); - $services = array(); - - if (is_array($result)) { - foreach($result as $service) { - $services[] = array('name' => $service->name, - 'apiversion' => $service->apiversion, - 'publish' => $service->publish, - 'subscribe' => $service->subscribe); - } - } - - return $services; - } - throw new mnet_server_exception(7019, 'nosuchfunction'); -} - -/** - * Initialize the object (if necessary), execute the method or function, and - * return the response - * - * @param string $includefile The file that contains the object definition - * @param string $methodname The name of the method to execute - * @param string $method The full path to the method - * @param string $payload The XML-RPC request payload - * @param string $class The name of the class to instantiate (or false) - * - * @throws mnet_server_exception - * - * @return string The XML-RPC response - */ -function mnet_server_invoke_method($includefile, $methodname, $method, $payload, $class=false) { - - $permission = mnet_permit_rpc_call($includefile, $methodname, $class); - - if (RPC_NOSUCHFILE == $permission) { - // Generate error response - unable to locate function - throw new mnet_server_exception(705, 'nosuchfile', $includefile); - } - - if (RPC_NOSUCHFUNCTION == $permission) { - // Generate error response - unable to locate function - throw new mnet_server_exception(706, 'nosuchfunction'); - } - - if (RPC_FORBIDDENFUNCTION == $permission) { - // Generate error response - unable to locate function - throw new mnet_server_exception(707, 'forbidden-function'); - } - - if (RPC_NOSUCHCLASS == $permission) { - // Generate error response - unable to locate function - throw new mnet_server_exception(7013, 'nosuchfunction'); - } - - if (RPC_NOSUCHMETHOD == $permission) { - // Generate error response - unable to locate function - throw new mnet_server_exception(7014, 'nosuchmethod'); - } - - if (RPC_FORBIDDENMETHOD == $permission) { - // Generate error response - unable to locate function - throw new mnet_server_exception(7015, 'nosuchfunction'); - } - - if (0 < $permission) { - // Generate error response - unable to locate function - throw new mnet_server_exception(7019, 'unknownerror'); - } - - if (RPC_OK == $permission) { - $xmlrpcserver = xmlrpc_server_create(); - $bool = xmlrpc_server_register_method($xmlrpcserver, $method, 'mnet_server_dummy_method'); - $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $methodname, array("encoding" => "utf-8")); - $bool = xmlrpc_server_destroy($xmlrpcserver); - return $response; - } -} - -/** - * Accepts a public key from a new remote host and returns the public key for - * this host. If 'register all hosts' is turned on, it will bootstrap a record - * for the remote host in the mnet_host table (if it's not already there) - * - * @param string $function XML-RPC requires this but we don't... discard! - * @param array $params Array of parameters - * $params[0] is the remote wwwroot - * $params[1] is the remote public key - * @return string The XML-RPC response - */ -function mnet_keyswap($function, $params) { - global $CFG, $MNET; - $return = array(); - - if (!empty($CFG->mnet_register_allhosts)) { - $mnet_peer = new mnet_peer(); - @list($wwwroot, $pubkey, $application) = each($params); - $keyok = $mnet_peer->bootstrap($wwwroot, $pubkey, $application); - if ($keyok) { - $mnet_peer->commit(); - } - } - return $MNET->public_key; +if ($MNET_REMOTE_CLIENT->signatureok == false) { + // We were unable to verify the signature + exit(mnet_server_fault(710, 'verifysignature-invalid')); } +exit(mnet_server_fault(7000, 'unknownerror')); diff --git a/portfolio/mahara/lib.php b/portfolio/mahara/lib.php index 2e65ae570ca38..c21bdcf24bbb5 100644 --- a/portfolio/mahara/lib.php +++ b/portfolio/mahara/lib.php @@ -1,4 +1,30 @@ . + + +/** + * This file contains the class definition for the mahara portfolio plugin + * + * @since 2.0 + * @package moodlecore + * @subpackage portfolio + * @copyright 2009 Penny Leach + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + define('PORTFOLIO_MAHARA_ERR_NETWORKING_OFF', 'err_networkingoff'); define('PORTFOLIO_MAHARA_ERR_NOHOSTS', 'err_nomnethosts'); @@ -303,15 +329,6 @@ private function ensure_mnethost() { $this->mnethost->set_wwwroot($this->hostrecord->wwwroot); } - public static function mnet_publishes() { - $pf= array(); - $pf['name'] = 'pf'; // Name & Description go in lang file - $pf['apiversion'] = 1; - $pf['methods'] = array('send_content_intent', 'send_content_ready', 'fetch_file'); - - return array($pf); - } - /** * xmlrpc (mnet) function to get the file. * reads in the file and returns it base_64 encoded diff --git a/portfolio/mahara/preconfig.php b/portfolio/mahara/preconfig.php index a3c9a202d3194..f4cc3a7e58774 100644 --- a/portfolio/mahara/preconfig.php +++ b/portfolio/mahara/preconfig.php @@ -1,4 +1,29 @@ . + + +/** + * This file is the landing point for returning to moodle after authenticating at mahara + * + * @since 2.0 + * @package moodlecore + * @subpackage portfolio + * @copyright 2009 Penny Leach + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ require_once(dirname(dirname(dirname(__FILE__))). '/config.php'); if (empty($CFG->enableportfolios)) { diff --git a/portfolio/mahara/version.php b/portfolio/mahara/version.php index b0f0203add5b2..2b5a72278cd7d 100644 --- a/portfolio/mahara/version.php +++ b/portfolio/mahara/version.php @@ -1,6 +1,31 @@ . -$plugin->version = 2008072500; + +/** + * This file contains the version information for the mahara portfolio plugin + * + * @since 2.0 + * @package moodlecore + * @subpackage portfolio + * @copyright 2009 Penny Leach + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$plugin->version = 2010012600; $plugin->requires = 2008072500; $plugin->cron = 0; diff --git a/repository/lib.php b/repository/lib.php index 1c1504a984df9..4034f8a913683 100644 --- a/repository/lib.php +++ b/repository/lib.php @@ -1562,14 +1562,6 @@ public static function get_instance_option_names() { return array(); } - /** - * Override it if you need to implement need mnet function - * @return array - */ - public static function mnet_publishes() { - return array(); - } - } /** diff --git a/repository/mahara/repository.class.php b/repository/mahara/repository.class.php index 7c9359331c5a9..967721b94b235 100644 --- a/repository/mahara/repository.class.php +++ b/repository/mahara/repository.class.php @@ -44,19 +44,6 @@ public function __construct($repositoryid, $context = SITEID, $options = array() parent::__construct($repositoryid, $context, $options); } - /** - * Declaration of the methods avalaible from mnet - * @return - */ - public static function mnet_publishes() { - $pf= array(); - $pf['name'] = 'remoterep'; // Name & Description go in lang file - $pf['apiversion'] = 1; - $pf['methods'] = array('get_folder_files', 'get_file', 'search_folders_and_files'); - - return array($pf); - } - /** * * @return diff --git a/repository/mahara/version.php b/repository/mahara/version.php index 885ef3cf03dd7..cb9a91ec06202 100644 --- a/repository/mahara/version.php +++ b/repository/mahara/version.php @@ -15,4 +15,4 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -$plugin->version = 2009080101; +$plugin->version = 2010012600; diff --git a/repository/remotemoodle/repository.class.php b/repository/remotemoodle/repository.class.php index a837894c6104d..b861e0bc4fb44 100644 --- a/repository/remotemoodle/repository.class.php +++ b/repository/remotemoodle/repository.class.php @@ -46,19 +46,6 @@ public function __construct($repositoryid, $context = SITEID, $options = array() parent::__construct($repositoryid, $context, $options); } - /** - * Declaration of the methods avalaible from mnet - * @return - */ - public static function mnet_publishes() { - $pf= array(); - $pf['name'] = 'remoterep'; // Name & Description go in lang file - $pf['apiversion'] = 1; - $pf['methods'] = array('getFileList', 'retrieveFile'); - - return array($pf); - } - /** * Retrieve a file for a user of the Moodle client calling this function * The file is encoded in base64 diff --git a/repository/remotemoodle/version.php b/repository/remotemoodle/version.php index 885ef3cf03dd7..cb9a91ec06202 100644 --- a/repository/remotemoodle/version.php +++ b/repository/remotemoodle/version.php @@ -15,4 +15,4 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -$plugin->version = 2009080101; +$plugin->version = 2010012600; diff --git a/version.php b/version.php index ff8bd6a87f1e7..91fd9f6735a86 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,8 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2010012500; // YYYYMMDD = date of the last version bump + $version = 2010012600; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20100128)'; // Human-friendly version name +