diff --git a/enrol/imsenterprise/lang/en/enrol_imsenterprise.php b/enrol/imsenterprise/lang/en/enrol_imsenterprise.php
index 515a1f1..9a74e91 100644
--- a/enrol/imsenterprise/lang/en/enrol_imsenterprise.php
+++ b/enrol/imsenterprise/lang/en/enrol_imsenterprise.php
@@ -26,6 +26,8 @@ $string['aftersaving...'] = 'Once you have saved your settings, you may wish to'
 $string['allowunenrol'] = 'Allow the IMS data to <strong>unenrol</strong> students/teachers';
 $string['allowunenrol_desc'] = 'If enabled, course enrolments will be removed when specified in the Enterprise data.';
 $string['basicsettings'] = 'Basic settings';
+$string['categoryseparator'] = 'Category Separator Character';
+$string['categoryseparator_desc'] = 'You can create nested categories (if you allow category creation). Use the character specified here in your IMS data to separate your categories. Do not use spaces or tabs.';
 $string['coursesettings'] = 'Course data options';
 $string['createnewcategories'] = 'Create new (hidden) course categories if not found in Moodle';
 $string['createnewcategories_desc'] = 'If the <org><orgunit> element is present in a course\'s incoming data, its content will be used to specify a category if the course is to be created from scratch. The plugin will NOT re-categorise existing courses.
@@ -75,6 +77,10 @@ $string['sourcedidfallback_desc'] = 'In IMS data, the <sourcedid> field represen
 Some student information systems fail to output the <userid> field. If this is the case, you should enable this setting to allow for using the <sourcedid> as the Moodle user ID. Otherwise, leave this setting disabled.';
 $string['truncatecoursecodes'] = 'Truncate course codes to this length';
 $string['truncatecoursecodes_desc'] = 'In some situations you may have course codes which you wish to truncate to a specified length before processing. If so, enter the number of characters in this box. Otherwise, leave the box blank and no truncation will occur.';
+$string['updatecourses'] = 'Update course full names';
+$string['updatecourses_desc'] = 'If enabled, the IMS Enterprise enrolment plugin can update course full names (if the "recstatus" flag is set to 2).';
+$string['updateusers'] = 'Update user accounts when specified in IMS data';
+$string['updateusers_desc'] = 'If enabled, IMS Enterprise enrolment data can specify changes to user accounts (if the "recstatus" flag is set to 2, which represents updates to an account).';
 $string['usecapitafix'] = 'Tick this box if using &quot;Capita&quot; (their XML format is slightly wrong)';
 $string['usecapitafix_desc'] = 'The student data system produced by Capita has been found to have one slight error in its XML output. If you are using Capita you should enable this setting - otherwise leave it un-ticked.';
 $string['usersettings'] = 'User data options';
diff --git a/enrol/imsenterprise/lib.php b/enrol/imsenterprise/lib.php
index 56c4d06..2e3a05d 100644
--- a/enrol/imsenterprise/lib.php
+++ b/enrol/imsenterprise/lib.php
@@ -276,12 +276,18 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
         // Get configs.
         $truncatecoursecodes    = $this->get_config('truncatecoursecodes');
         $createnewcourses       = $this->get_config('createnewcourses');
+        $updatecourses          = $this->get_config('updatecourses');
         $createnewcategories    = $this->get_config('createnewcategories');
+        $categoryseparator      = trim($this->get_config('categoryseparator'));
 
         if ($createnewcourses) {
             require_once("$CFG->dirroot/course/lib.php");
         }
 
+        if (empty($categoryseparator)) {
+            $categoryseparator = '|';
+        }
+
         // Process tag contents.
         $group = new stdClass();
         if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
@@ -362,26 +368,41 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
 
                         // Handle course categorisation (taken from the group.org.orgunit field if present).
                         if (!empty($group->category)) {
-                            // If the category is defined and exists in Moodle, we want to store it in that one.
-                            if ($catid = $DB->get_field('course_categories', 'id', array('name' => $group->category))) {
-                                $course->category = $catid;
-                            } else if ($createnewcategories) {
-                                // Else if we're allowed to create new categories, let's create this one.
-                                $newcat = new stdClass();
-                                $newcat->name = $group->category;
-                                $newcat->visible = 0;
-                                $catid = $DB->insert_record('course_categories', $newcat);
-                                $course->category = $catid;
-                                $this->log_line("Created new (hidden) category, #$catid: $newcat->name");
-                            } else {
-                                // If not found and not allowed to create, stick with default.
-                                $this->log_line('Category '.$group->category.' not found in Moodle database, so using '.
-                                    'default category instead.');
-                                $course->category = $this->get_default_category_id();
+                            // Categories can be nested!
+                            $sep = '{\\'.$categoryseparator.'}';
+                            $matches = preg_split($sep, $group->category, -1, PREG_SPLIT_NO_EMPTY);
+                            $catid = 0;
+                            $tempcatname = '';
+                            foreach ($matches as $catname) {
+                                $catname = trim($catname);
+                                if (!empty($tempcatname)) {
+                                    $tempcatname .= ' / ';
+                                }
+                                $tempcatname .= $catname;
+                                $parentid = $catid;
+                                if ($catid = $DB->get_field('course_categories', 'id', array('name'=>$catname, 'parent'=>$parentid))) {
+                                    $course->category = $catid;
+                                    continue;
+                                }
+                                if ($createnewcategories) {
+                                    $newcat = new stdClass();
+                                    $newcat->name = $catname;
+                                    $newcat->visible = 0;
+                                    $newcat->parent = $parentid;
+                                    $catid = $DB->insert_record('course_categories', $newcat);
+                                    $course->category = $catid;
+                                    $this->log_line("Created new (hidden) category '$tempcatname'");
+                                } else {
+                                    $course->category = $this->get_default_category_id();
+                                    $msg = 'Cannot create requested category '.$group->category;
+                                    $msg .= '; Setting to default category.';
+                                    $this->log_line($msg);
+                                }
                             }
                         } else {
                             $course->category = $this->get_default_category_id();
                         }
+                        $course->timecreated = time();
                         $course->startdate = time();
                         // Choose a sort order that puts us at the start of the list!
                         $course->sortorder = 0;
@@ -390,6 +411,18 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
 
                         $this->log_line("Created course $coursecode in Moodle (Moodle ID is $course->id)");
                     }
+                } else if ($recstatus == 2 && ($courseid = $DB->get_field('course', 'id', array('idnumber'=>$coursecode)))) {
+                    if ($updatecourses) {
+                        $value = $DB->get_field('course', 'fullname', array('idnumber'=>$coursecode));
+                        $mapfullname = $this->coursemappings['fullname'];
+                        if ($value != $group->{$mapfullname}) {
+                            $DB->set_field('course', 'fullname', $group->{$mapfullname}, array('idnumber'=>$coursecode));
+                            add_to_log(SITEID, "course", "update", "view.php?id=$courseid", "$group->description (ID $courseid)");
+                            $this->log_line("Updated course $coursecode in Moodle (Moodle ID is $courseid)");
+                        }
+                    } else {
+                        $this->log_line("Ignoring update to course $coursecode");
+                    }
                 } else if ($recstatus == 3 && ($courseid = $DB->get_field('course', 'id', array('idnumber' => $coursecode)))) {
                     // If course does exist, but recstatus==3 (delete), then set the course as hidden.
                     $DB->set_field('course', 'visible', '0', array('id' => $courseid));
@@ -412,6 +445,7 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
         $fixcasepersonalnames   = $this->get_config('fixcasepersonalnames');
         $imsdeleteusers         = $this->get_config('imsdeleteusers');
         $createnewusers         = $this->get_config('createnewusers');
+        $imsupdateusers         = $this->get_config('imsupdateusers');
 
         $person = new stdClass();
         if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
@@ -423,7 +457,10 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
         if (preg_match('{<name>.*?<n>.*?<family>(.+?)</family>.*?</n>.*?</name>}is', $tagcontents, $matches)) {
             $person->lastname = trim($matches[1]);
         }
-        if (preg_match('{<userid>(.*?)</userid>}is', $tagcontents, $matches)) {
+        if (preg_match('{<userid\s+authenticationtype\s*=\s*"*(.+?)"*>.*?</userid>}is', $tagcontents, $matches)) {
+            $person->auth = trim($matches[1]);
+        }
+        if (preg_match('{<userid.*?>(.*?)</userid>}is', $tagcontents, $matches)) {
             $person->username = trim($matches[1]);
         }
         if ($imssourcedidfallback && trim($person->username) == '') {
@@ -477,9 +514,17 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
             } else {
                 $this->log_line("Ignoring deletion request for user '$person->username' (ID number $person->idnumber).");
             }
-
-        } else { // Add or update record.
-
+        } else if ($recstatus == 2) { // Update.
+            if ($imsupdateusers) {
+                if ($id = $DB->get_field('user', 'id', array('idnumber'=>$person->idnumber))) {
+                    $person->id = $id;
+                    $DB->update_record('user', $person);
+                    $this->log_line("Updated user $person->username");
+                } else {
+                    $this->log_line("Ignoring update request for non-existent user $person->username");
+                }
+            }
+        } else if ($recstatus == 1) { // Add new record.
             // If the user exists (matching sourcedid) then we don't need to do anything.
             if (!$DB->get_field('user', 'id', array('idnumber' => $person->idnumber)) && $createnewusers) {
                 // If they don't exist and haven't a defined username, we log this as a potential problem.
@@ -492,16 +537,20 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
                 } else {
 
                     // If they don't exist and they have a defined username, and $createnewusers == true, we create them.
-                    $person->lang = $CFG->lang;
-                    // TODO: MDL-15863 this needs more work due to multiauth changes, use first auth for now.
-                    $auth = explode(',', $CFG->auth);
-                    $auth = reset($auth);
-                    $person->auth = $auth;
-                    $person->confirmed = 1;
-                    $person->timemodified = time();
-                    $person->mnethostid = $CFG->mnet_localhost_id;
-                    $id = $DB->insert_record('user', $person);
-                    $this->log_line("Created user record ('.$id.') for user '$person->username' (ID number $person->idnumber).");
+                    if ($createnewusers) {
+                        $person->lang = $CFG->lang;
+                        // TODO: MDL-15863 this needs more work due to multiauth changes, use first auth for now.
+                        $auth = explode(',', $CFG->auth);
+                        $auth = reset($auth);
+                        $person->auth = $auth;
+                        $person->confirmed = 1;
+                        $person->timemodified = time();
+                        $person->mnethostid = $CFG->mnet_localhost_id;
+                        $id = $DB->insert_record('user', $person);
+                        $this->log_line("Created user record ('.$id.') for user '$person->username' (ID number $person->idnumber).");
+                    } else {
+                        $this->log_line("Ignoring create user request for user '$person->username' (ID number $person->idnumber).");
+                    }
                 }
             } else if ($createnewusers) {
                 $this->log_line("User record already exists for user '$person->username' (ID number $person->idnumber).");
diff --git a/enrol/imsenterprise/settings.php b/enrol/imsenterprise/settings.php
index 8f7c734..07d640c 100644
--- a/enrol/imsenterprise/settings.php
+++ b/enrol/imsenterprise/settings.php
@@ -50,6 +50,9 @@ if ($ADMIN->fulltree) {
     $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/createnewusers',
         get_string('createnewusers', 'enrol_imsenterprise'), get_string('createnewusers_desc', 'enrol_imsenterprise'), 0));
 
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imsupdateusers',
+        get_string('updateusers', 'enrol_imsenterprise'), get_string('updateusers_desc', 'enrol_imsenterprise'), 0));
+
     $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imsdeleteusers',
         get_string('deleteusers', 'enrol_imsenterprise'), get_string('deleteusers_desc', 'enrol_imsenterprise'), 0));
 
@@ -88,10 +91,16 @@ if ($ADMIN->fulltree) {
     $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/createnewcourses',
         get_string('createnewcourses', 'enrol_imsenterprise'), get_string('createnewcourses_desc', 'enrol_imsenterprise'), 0));
 
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/updatecourses',
+        get_string('updatecourses', 'enrol_imsenterprise'), get_string('updatecourses_desc', 'enrol_imsenterprise'), 0));
+
     $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/createnewcategories',
         get_string('createnewcategories', 'enrol_imsenterprise'), get_string('createnewcategories_desc', 'enrol_imsenterprise'),
         0));
 
+    $settings->add(new admin_setting_configtext('enrol_imsenterprise/categoryseparator',
+        get_string('categoryseparator', 'enrol_imsenterprise'), get_string('categoryseparator_desc', 'enrol_imsenterprise'), '|', PARAM_TEXT, 3));
+
     $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imsunenrol',
         get_string('allowunenrol', 'enrol_imsenterprise'), get_string('allowunenrol_desc', 'enrol_imsenterprise'), 0));
 
