--- loncom/lonnet/perl/lonnet.pm	2012/01/06 18:13:41	1.1056.4.34
+++ loncom/lonnet/perl/lonnet.pm	2011/07/18 10:32:48	1.1119
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1056.4.34 2012/01/06 18:13:41 raeburn Exp $
+# $Id: lonnet.pm,v 1.1119 2011/07/18 10:32:48 foxr Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -76,8 +76,7 @@ use HTTP::Date;
 use Image::Magick;
 
 use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir
-            $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease
-            %managerstab);
+            $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease);
 
 my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash,
     %userrolehash, $processmarker, $dumpcount, %coursedombuf,
@@ -96,6 +95,7 @@ use Math::Random;
 use File::MMagic;
 use LONCAPA qw(:DEFAULT :match);
 use LONCAPA::Configuration;
+
 use File::Copy;
 
 my $readit;
@@ -288,7 +288,7 @@ sub get_server_homeID {
     }
     my $cachetime = 12*3600;
     my $serverhomeID;
-    if ($caller eq 'loncron') {
+    if ($caller eq 'loncron') { 
         my @machine_ids = &machine_ids($hostname);
         foreach my $id (@machine_ids) {
             my $response = &reply('serverhomeID',$id);
@@ -636,11 +636,20 @@ sub appenv {
 # ----------------------------------------------------- Delete from Environment
 
 sub delenv {
-    my ($delthis,$regexp) = @_;
-    if (($delthis=~/user\.role/) || ($delthis=~/user\.priv/)) {
-        &logthis("<font color=\"blue\">WARNING: ".
-                "Attempt to delete from environment ".$delthis);
-        return 'error';
+    my ($delthis,$regexp,$roles) = @_;
+    if (($delthis=~/^user\.role/) || ($delthis=~/^user\.priv/)) {
+        my $refused = 1;
+        if (ref($roles) eq 'ARRAY') {
+            my ($type,$role) = ($delthis =~ /^user\.(role|priv)\.([^.]+)\./);
+            if (grep(/^\Q$role\E$/,@{$roles})) {
+                $refused = 0;
+            }
+        }
+        if ($refused) {
+            &logthis("<font color=\"blue\">WARNING: ".
+                     "Attempt to delete from environment ".$delthis);
+            return 'error';
+        }
     }
     my $opened = open(my $env_file,'+<',$env{'user.environment'});
     if ($opened
@@ -749,30 +758,6 @@ sub userload {
     return $userloadpercent;
 }
 
-# ------------------------------------------ Fight off request when overloaded
-
-sub overloaderror {
-    my ($r,$checkserver)=@_;
-    unless ($checkserver) { $checkserver=$perlvar{'lonHostID'}; }
-    my $loadavg;
-    if ($checkserver eq $perlvar{'lonHostID'}) {
-       open(my $loadfile,'/proc/loadavg');
-       $loadavg=<$loadfile>;
-       $loadavg =~ s/\s.*//g;
-       $loadavg = 100*$loadavg/$perlvar{'lonLoadLim'};
-       close($loadfile);
-    } else {
-       $loadavg=&reply('load',$checkserver);
-    }
-    my $overload=$loadavg-100;
-    if ($overload>0) {
-	$r->err_headers_out->{'Retry-After'}=$overload;
-        $r->log_error('Overload of '.$overload.' on '.$checkserver);
-        return 413;
-    }    
-    return '';
-}
-
 # ------------------------------ Find server with least workload from spare.tab
 
 sub spareserver {
@@ -790,8 +775,8 @@ sub spareserver {
     }
     foreach my $try_server (@{ $spareid{'primary'} }) {
         if ($uint_dom) {
-            next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
-                                         $try_server));
+             next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
+                                          $try_server));
         }
 	($spare_server, $lowest_load) =
 	    &compare_server_load($try_server, $spare_server, $lowest_load);
@@ -832,7 +817,7 @@ sub compare_server_load {
     my $userloadans = &reply('userload',$try_server);
 
     if ($loadans !~ /\d/ && $userloadans !~ /\d/) {
-        return ($spare_server, $lowest_load); #didn't get a number from the server
+	return ($spare_server, $lowest_load); #didn't get a number from the server
     }
 
     my $load;
@@ -890,7 +875,7 @@ sub choose_server {
             if ($loginvia) {
                 my ($server,$path) = split(/:/,$loginvia);
                 ($login_host, $lowest_load) =
-                    &compare_server_load($lonhost, $login_host, $lowest_load);
+                    &compare_server_load($server, $login_host, $lowest_load);
                 if ($login_host eq $server) {
                     $portal_path = $path;
                 }
@@ -909,7 +894,7 @@ sub choose_server {
     if ($login_host ne '') {
         $hostname = &hostname($login_host);
     }
-    return ($login_host,$hostname);
+    return ($login_host,$hostname,$portal_path);
 }
 
 # --------------------------------------------- Try to change a user's password
@@ -1050,19 +1035,15 @@ sub can_host_session {
     }
     if ($canhost) {
         if (ref($hostedsessions) eq 'HASH') {
-            my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
-            my $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
             if (ref($hostedsessions->{'excludedomain'}) eq 'ARRAY') {
-                if (($uint_dom ne '') && 
-                    (grep(/^\Q$uint_dom\E$/,@{$hostedsessions->{'excludedomain'}}))) {
+                if (grep(/^\Q$udom\E$/,@{$hostedsessions->{'excludedomain'}})) {
                     $canhost = 0;
                 } else {
                     $canhost = 1;
                 }
             }
             if (ref($hostedsessions->{'includedomain'}) eq 'ARRAY') {
-                if (($uint_dom ne '') && 
-                    (grep(/^\Q$uint_dom\E$/,@{$hostedsessions->{'includedomain'}}))) {
+                if (grep(/^\Q$udom\E$/,@{$hostedsessions->{'includedomain'}})) {
                     $canhost = 1;
                 } else {
                     $canhost = 0;
@@ -1576,7 +1557,6 @@ sub get_domain_defaults {
         $domdefaults{'auth_arg_def'} = $domconfig{'defaults'}{'auth_arg_def'};
         $domdefaults{'timezone_def'} = $domconfig{'defaults'}{'timezone_def'};
         $domdefaults{'datelocale_def'} = $domconfig{'defaults'}{'datelocale_def'};
-        $domdefaults{'portal_def'} = $domconfig{'defaults'}{'portal_def'};
     } else {
         $domdefaults{'lang_def'} = &domain($domain,'lang_def');
         $domdefaults{'auth_def'} = &domain($domain,'auth_def');
@@ -2036,20 +2016,29 @@ sub getversion {
 
 sub currentversion {
     my $fname=shift;
-    my ($result,$cached)=&is_cached_new('resversion',$fname);
-    if (defined($cached)) { return $result; }
     my $author=$fname;
     $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
     my ($udom,$uname)=split(/\//,$author);
-    my $home=homeserver($uname,$udom);
+    my $home=&homeserver($uname,$udom);
     if ($home eq 'no_host') { 
         return -1; 
     }
-    my $answer=reply("currentversion:$fname",$home);
+    my $answer=&reply("currentversion:$fname",$home);
     if (($answer eq 'con_lost') || ($answer eq 'rejected')) {
 	return -1;
     }
-    return &do_cache_new('resversion',$fname,$answer,600);
+    return $answer;
+}
+
+#
+# Return special version number of resource if set by override, empty otherwise
+#
+sub usedversion {
+    my $fname=shift;
+    unless ($fname) { $fname=$env{'request.uri'}; }
+    my ($urlversion)=($fname=~/\.(\d+)\.\w+$/);
+    if ($urlversion) { return $urlversion; }
+    return '';
 }
 
 # ----------------------------- Subscribe to a resource, return URL if possible
@@ -2311,7 +2300,7 @@ sub process_coursefile {
                 }
                 if (ref($mimetype)) {
                     $$mimetype = $type;
-                }
+                } 
             }
             $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file,
                                  $home);
@@ -2429,11 +2418,11 @@ sub resizeImage {
 # input: $formname - the contents of the file are in $env{"form.$formname"}
 #                    the desired filename is in $env{"form.$formname.filename"}
 #        $context - possible values: coursedoc, existingfile, overwrite, 
-#                                    canceloverwrite, or ''.
+#                                    canceloverwrite, or ''. 
 #                   if 'coursedoc': upload to the current course
-#                   if 'existingfile': write file to tmp/overwrites directory
+#                   if 'existingfile': write file to tmp/overwrites directory 
 #                   if 'canceloverwrite': delete file written to tmp/overwrites directory
-#                   $context is passed as argument to &finishuserfileupload 
+#                   $context is passed as argument to &finishuserfileupload
 #        $subdir - directory in userfile to store the file into
 #        $parser - instruction to parse file for objects ($parser = parse)    
 #        $allfiles - reference to hash for embedded objects
@@ -2585,11 +2574,11 @@ sub finishuserfileupload {
 	    return '/adm/notfound.html';
 	}
         if ($context eq 'overwrite') {
-            my $source =  $perlvar{'lonDaemons'}.'/tmp/overwrites/'.$docudom.'/'.$docuname.'/'.$fname;
+            my $source =  LONCAPA::tempdir().'/overwrites/'.$docudom.'/'.$docuname.'/'.$fname;
             my $target = $filepath.'/'.$file;
             if (-e $source) {
                 my @info = stat($source);
-                if ($info[9] eq $env{'form.timestamp'}) {
+                if ($info[9] eq $env{'form.timestamp'}) {   
                     unless (&File::Copy::move($source,$target)) {
                         &logthis('Failed to overwrite '.$filepath.'/'.$file);
                         return "Moving from $source failed";
@@ -2600,7 +2589,7 @@ sub finishuserfileupload {
             } else {
                 return "Temporary file: $source missing";
             }
-	} elsif (!print FH ($env{'form.'.$formname})) {
+        } elsif (!print FH ($env{'form.'.$formname})) {
 	    &logthis('Failed to write to '.$filepath.'/'.$file);
 	    print STDERR ('Failed to write to '.$filepath.'/'.$file."\n");
 	    return '/adm/notfound.html';
@@ -3161,7 +3150,7 @@ sub get_my_roles {
     unless (defined($uname)) { $uname=$env{'user.name'}; }
     unless (defined($udom)) { $udom=$env{'user.domain'}; }
     my (%dumphash,%nothide);
-    if ($context eq 'userroles') { 
+    if ($context eq 'userroles') {
         my $extra = &freeze_escape({'skipcheck' => 1});
         %dumphash = &dump('roles',$udom,$uname,'.',undef,$extra);
     } else {
@@ -3223,6 +3212,10 @@ sub get_my_roles {
                     if (!grep(/^cr$/,@{$roles})) {
                         next;
                     }
+                } elsif ($role =~ /^gr\//) {
+                    if (!grep(/^gr$/,@{$roles})) {
+                        next;
+                    }
                 } else {
                     next;
                 }
@@ -3491,7 +3484,7 @@ sub get_domain_roles {
     return %personnel;
 }
 
-# ----------------------------------------------------------- Check out an item
+# ----------------------------------------------------------- Interval timing 
 
 sub get_first_access {
     my ($type,$argsymb)=@_;
@@ -3527,91 +3520,6 @@ sub set_first_access {
     return 'already_set';
 }
 
-sub checkout {
-    my ($symb,$tuname,$tudom,$tcrsid)=@_;
-    my $now=time;
-    my $lonhost=$perlvar{'lonHostID'};
-    my $infostr=&escape(
-                 'CHECKOUTTOKEN&'.
-                 $tuname.'&'.
-                 $tudom.'&'.
-                 $tcrsid.'&'.
-                 $symb.'&'.
-		 $now.'&'.$ENV{'REMOTE_ADDR'});
-    my $token=&reply('tmpput:'.$infostr,$lonhost);
-    if ($token=~/^error\:/) { 
-        &logthis("<font color=\"blue\">WARNING: ".
-                "Checkout tmpput failed ".$tudom.' - '.$tuname.' - '.$symb.
-                 "</font>");
-        return ''; 
-    }
-
-    $token=~s/^(\d+)\_.*\_(\d+)$/$1\*$2\*$lonhost/;
-    $token=~tr/a-z/A-Z/;
-
-    my %infohash=('resource.0.outtoken' => $token,
-                  'resource.0.checkouttime' => $now,
-                  'resource.0.outremote' => $ENV{'REMOTE_ADDR'});
-
-    unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
-       return '';
-    } else {
-        &logthis("<font color=\"blue\">WARNING: ".
-                "Checkout cstore failed ".$tudom.' - '.$tuname.' - '.$symb.
-                 "</font>");
-    }    
-
-    if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
-                         &escape('Checkout '.$infostr.' - '.
-                                                 $token)) ne 'ok') {
-	return '';
-    } else {
-        &logthis("<font color=\"blue\">WARNING: ".
-                "Checkout log failed ".$tudom.' - '.$tuname.' - '.$symb.
-                 "</font>");
-    }
-    return $token;
-}
-
-# ------------------------------------------------------------ Check in an item
-
-sub checkin {
-    my $token=shift;
-    my $now=time;
-    my ($ta,$tb,$lonhost)=split(/\*/,$token);
-    $lonhost=~tr/A-Z/a-z/;
-    my $dtoken=$ta.'_'.&hostname($lonhost).'_'.$tb;
-    $dtoken=~s/\W/\_/g;
-    my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=
-                 split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));
-
-    unless (($tuname) && ($tudom)) {
-        &logthis('Check in '.$token.' ('.$dtoken.') failed');
-        return '';
-    }
-    
-    unless (&allowed('mgr',$tcrsid)) {
-        &logthis('Check in '.$token.' ('.$dtoken.') unauthorized: '.
-                 $env{'user.name'}.' - '.$env{'user.domain'});
-        return '';
-    }
-
-    my %infohash=('resource.0.intoken' => $token,
-                  'resource.0.checkintime' => $now,
-                  'resource.0.inremote' => $ENV{'REMOTE_ADDR'});
-
-    unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
-       return '';
-    }    
-
-    if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
-                         &escape('Checkin - '.$token)) ne 'ok') {
-	return '';
-    }
-
-    return ($symb,$tuname,$tudom,$tcrsid);    
-}
-
 # --------------------------------------------- Set Expire Date for Spreadsheet
 
 sub expirespread {
@@ -3716,7 +3624,7 @@ sub hashref2str {
       $result.='=';
       #print("Got a ref of ".(ref($key))." skipping.");
     } else {
-        if (defined($key)) {$result.=&escape($key).'=';} else { last; }
+	if ($key) {$result.=&escape($key).'=';} else { last; }
     }
 
     if(ref($hashref->{$key}) eq 'ARRAY') {
@@ -3868,7 +3776,7 @@ sub tmpreset {
   if ($domain eq 'public' && $stuname eq 'public') {
       $stuname=$ENV{'REMOTE_ADDR'};
   }
-  my $path=$perlvar{'lonDaemons'}.'/tmp';
+  my $path=LONCAPA::tempdir();
   my %hash;
   if (tie(%hash,'GDBM_File',
 	  $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
@@ -3907,7 +3815,7 @@ sub tmpstore {
   }
   my $now=time;
   my %hash;
-  my $path=$perlvar{'lonDaemons'}.'/tmp';
+  my $path=LONCAPA::tempdir();
   if (tie(%hash,'GDBM_File',
 	  $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
 	  &GDBM_WRCREAT(),0640)) {
@@ -3953,7 +3861,7 @@ sub tmprestore {
   $namespace=~s/\//\_/g;
   $namespace=~s/\W//g;
   my %hash;
-  my $path=$perlvar{'lonDaemons'}.'/tmp';
+  my $path=LONCAPA::tempdir();
   if (tie(%hash,'GDBM_File',
 	  $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
 	  &GDBM_READER(),0640)) {
@@ -4090,6 +3998,8 @@ sub restore {
 }
 
 # ---------------------------------------------------------- Course Description
+#
+#  
 
 sub coursedescription {
     my ($courseid,$args)=@_;
@@ -4119,7 +4029,8 @@ sub coursedescription {
 	return %returnhash;
     }
 
-    # get the data agin
+    # get the data again
+
     if (!$args->{'one_time'}) {
 	$envhash{'course.'.$normalid.'.last_cache'}=time;
     }
@@ -4127,6 +4038,10 @@ sub coursedescription {
     if ($chome ne 'no_host') {
        %returnhash=&dump('environment',$cdomain,$cnum);
        if (!exists($returnhash{'con_lost'})) {
+	   my $username = $env{'user.name'}; # Defult username
+	   if(defined $args->{'user'}) {
+	       $username = $args->{'user'};
+	   }
            $returnhash{'home'}= $chome;
 	   $returnhash{'domain'} = $cdomain;
 	   $returnhash{'num'} = $cnum;
@@ -4137,8 +4052,8 @@ sub coursedescription {
                $envhash{'course.'.$normalid.'.'.$name}=$value;
            }
            $returnhash{'url'}=&clutter($returnhash{'url'});
-           $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.
-	       $env{'user.name'}.'_'.$cdomain.'_'.$cnum;
+           $returnhash{'fn'}=LONCAPA::tempdir() .
+	       $username.'_'.$cdomain.'_'.$cnum;
            $envhash{'course.'.$normalid.'.home'}=$chome;
            $envhash{'course.'.$normalid.'.domain'}=$cdomain;
            $envhash{'course.'.$normalid.'.num'}=$cnum;
@@ -4235,7 +4150,6 @@ sub rolesinit {
     }
     my %allroles=();
     my %allgroups=();   
-    my $group_privs;
 
     if ($rolesdump ne '') {
         foreach my $entry (split(/&/,$rolesdump)) {
@@ -4252,6 +4166,7 @@ sub rolesinit {
 		}
             } elsif ($role =~ m|^gr/|) {
                 ($trole,$tend,$tstart) = split(/_/,$role);
+                next if ($tstart eq '-1');
                 ($trole,$group_privs) = split(/\//,$trole);
                 $group_privs = &unescape($group_privs);
 	    } else {
@@ -4356,7 +4271,7 @@ sub set_userprivs {
     my $adv=0;
     my %grouproles = ();
     if (keys(%{$allgroups}) > 0) {
-        my @groupkeys;
+        my @groupkeys; 
         foreach my $role (keys(%{$allroles})) {
             push(@groupkeys,$role);
         }
@@ -4404,7 +4319,7 @@ sub set_userprivs {
             }
         }
         my $thesestr='';
-        foreach my $priv (keys(%thesepriv)) {
+        foreach my $priv (sort(keys(%thesepriv))) {
 	    $thesestr.=':'.$priv.'&'.$thesepriv{$priv};
 	}
         $userroles->{'user.priv.'.$role} = $thesestr;
@@ -4413,7 +4328,7 @@ sub set_userprivs {
 }
 
 sub role_status {
-    my ($rolekey,$then,$refresh,$now,$role,$where,$trolecode,$tstatus,$tstart,$tend) = @_;
+    my ($rolekey,$update,$refresh,$now,$role,$where,$trolecode,$tstatus,$tstart,$tend) = @_;
     my @pwhere = ();
     if (exists($env{$rolekey}) && $env{$rolekey} ne '') {
         (undef,undef,$$role,@pwhere)=split(/\./,$rolekey);
@@ -4422,7 +4337,7 @@ sub role_status {
             $$trolecode=$$role.'.'.$$where;
             ($$tstart,$$tend)=split(/\./,$env{$rolekey});
             $$tstatus='is';
-            if ($$tstart && $$tstart>$then) {
+            if ($$tstart && $$tstart>$update) {
                 $$tstatus='future';
                 if ($$tstart<$now) {
                     if ($$tstart && $$tstart>$refresh) {
@@ -4432,7 +4347,7 @@ sub role_status {
                             my %userroles = (
                                 'user.role.'.$$role.'.'.$$where => $$tstart.'.'.$$tend
                             );
-                            @rolecodes = ('cm');
+                            @rolecodes = ('cm'); 
                             my $spec=$$role.'.'.$$where;
                             my ($tdummy,$tdomain,$trest)=split(/\//,$$where);
                             if ($$role =~ /^cr\//) {
@@ -4447,32 +4362,9 @@ sub role_status {
                                 $group_privs = &unescape($group_privs);
                                 &group_roleprivs(\%allgroups,$$where,$group_privs,$$tend,$$tstart);
                                 my %course_roles = &get_my_roles($env{'user.name'},$env{'user.domain'},'userroles',['active'],['cc','co','in','ta','ep','ad','st','cr'],[$tdomain],1);
-                                if (keys(%course_roles) > 0) {
-                                    my ($tnum) = ($trest =~ /^($match_courseid)/);
-                                    if ($tdomain ne '' && $tnum ne '') {
-                                        foreach my $key (keys(%course_roles)) {
-                                            if ($key =~ /^\Q$tnum\E:\Q$tdomain\E:([^:]+):?([^:]*)/) {
-                                                my $crsrole = $1;
-                                                my $crssec = $2;
-                                                if ($crsrole =~ /^cr/) {
-                                                    unless (grep(/^cr$/,@rolecodes)) {
-                                                        push(@rolecodes,'cr');
-                                                    }
-                                                } else {
-                                                    unless(grep(/^\Q$crsrole\E$/,@rolecodes)) {
-                                                        push(@rolecodes,$crsrole);
-                                                    }
-                                                }
-                                                my $rolekey = $crsrole.'./'.$tdomain.'/'.$tnum;
-                                                if ($crssec ne '') {
-                                                    $rolekey .= '/'.$crssec;
-                                                }
-                                                $rolekey .= './';
-                                                $groups_roles{$rolekey} = \@rolecodes;
-                                            }
-                                        }
-                                    }
-                                }
+                                &get_groups_roles($tdomain,$trest,
+                                                  \%course_roles,\@rolecodes,
+                                                  \%groups_roles);
                             } else {
                                 push(@rolecodes,$$role);
                                 &standard_roleprivs(\%allroles,$$role,$tdomain,$spec,$trest,$$where);
@@ -4486,7 +4378,7 @@ sub role_status {
                 }
             }
             if ($$tend) {
-                if ($$tend<$then) {
+                if ($$tend<$update) {
                     $$tstatus='expired';
                 } elsif ($$tend<$now) {
                     $$tstatus='will_not';
@@ -4496,12 +4388,70 @@ sub role_status {
     }
 }
 
+sub get_groups_roles {
+    my ($cdom,$rest,$cdom_courseroles,$rolecodes,$groups_roles) = @_;
+    return unless((ref($cdom_courseroles) eq 'HASH') && 
+                  (ref($rolecodes) eq 'ARRAY') && 
+                  (ref($groups_roles) eq 'HASH')); 
+    if (keys(%{$cdom_courseroles}) > 0) {
+        my ($cnum) = ($rest =~ /^($match_courseid)/);
+        if ($cdom ne '' && $cnum ne '') {
+            foreach my $key (keys(%{$cdom_courseroles})) {
+                if ($key =~ /^\Q$cnum\E:\Q$cdom\E:([^:]+):?([^:]*)/) {
+                    my $crsrole = $1;
+                    my $crssec = $2;
+                    if ($crsrole =~ /^cr/) {
+                        unless (grep(/^cr$/,@{$rolecodes})) {
+                            push(@{$rolecodes},'cr');
+                        }
+                    } else {
+                        unless(grep(/^\Q$crsrole\E$/,@{$rolecodes})) {
+                            push(@{$rolecodes},$crsrole);
+                        }
+                    }
+                    my $rolekey = "$crsrole./$cdom/$cnum";
+                    if ($crssec ne '') {
+                        $rolekey .= "/$crssec";
+                    }
+                    $rolekey .= './';
+                    $groups_roles->{$rolekey} = $rolecodes;
+                }
+            }
+        }
+    }
+    return;
+}
+
+sub delete_env_groupprivs {
+    my ($where,$courseroles,$possroles) = @_;
+    return unless((ref($courseroles) eq 'HASH') && (ref($possroles) eq 'ARRAY'));
+    my ($dummy,$udom,$uname,$group) = split(/\//,$where);
+    unless (ref($courseroles->{$udom}) eq 'HASH') {
+        %{$courseroles->{$udom}} =
+            &get_my_roles('','','userroles',['active'],
+                          $possroles,[$udom],1);
+    }
+    if (ref($courseroles->{$udom}) eq 'HASH') {
+        foreach my $item (keys(%{$courseroles->{$udom}})) {
+            my ($cnum,$cdom,$crsrole,$crssec) = split(/:/,$item);
+            my $area = '/'.$cdom.'/'.$cnum;
+            my $privkey = "user.priv.$crsrole.$area";
+            if ($crssec ne '') {
+                $privkey .= '/'.$crssec;
+            }
+            $privkey .= ".$area/$group";
+            &Apache::lonnet::delenv($privkey,undef,[$crsrole]);
+        }
+    }
+    return;
+}
+
 sub check_adhoc_privs {
-    my ($cdom,$cnum,$then,$refresh,$now,$checkrole,$caller) = @_;
+    my ($cdom,$cnum,$update,$refresh,$now,$checkrole,$caller) = @_;
     my $cckey = 'user.role.'.$checkrole.'./'.$cdom.'/'.$cnum;
     if ($env{$cckey}) {
         my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend);
-        &role_status($cckey,$then,$refresh,$now,\$role,\$where,\$trolecode,\$tstatus,\$tstart,\$tend);
+        &role_status($cckey,$update,$refresh,$now,\$role,\$where,\$trolecode,\$tstatus,\$tstart,\$tend);
         unless (($tstatus eq 'is') || ($tstatus eq 'will_not')) {
             &set_adhoc_privileges($cdom,$cnum,$checkrole,$caller);
         }
@@ -4592,15 +4542,18 @@ sub dump {
     my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range:$extra",$uhome);
     my @pairs=split(/\&/,$rep);
     my %returnhash=();
-    foreach my $item (@pairs) {
-	my ($key,$value)=split(/=/,$item,2);
-	$key = &unescape($key);
-	next if ($key =~ /^error: 2 /);
-	$returnhash{$key}=&thaw_unescape($value);
+    if (!($rep =~ /^error/ )) {
+	foreach my $item (@pairs) {
+	    my ($key,$value)=split(/=/,$item,2);
+	    $key = &unescape($key);
+	    next if ($key =~ /^error: 2 /);
+	    $returnhash{$key}=&thaw_unescape($value);
+	}
     }
     return %returnhash;
 }
 
+
 # --------------------------------------------------------- dumpstore interface
 
 sub dumpstore {
@@ -4883,7 +4836,7 @@ sub tmpget {
     return %returnhash;
 }
 
-# ------------------------------------------------------------ tmpget interface
+# ------------------------------------------------------------ tmpdel interface
 sub tmpdel {
     my ($token,$server)=@_;
     if (!defined($server)) { $server = $perlvar{'lonHostID'}; }
@@ -5134,7 +5087,7 @@ sub is_portfolio_file {
 }
 
 sub usertools_access {
-    my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref) = @_;
+    my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref)=@_;
     my ($access,%tools);
     if ($context eq '') {
         $context = 'tools';
@@ -5281,7 +5234,7 @@ sub is_advanced_user {
     my ($udom,$uname) = @_;
     if ($udom ne '' && $uname ne '') {
         if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
-            return $env{'user.adv'};
+            return $env{'user.adv'};  
         }
     }
     my %roleshash = &get_my_roles($uname,$udom,'userroles',undef,undef,undef,1);
@@ -5799,7 +5752,7 @@ sub allowed {
        my $unamedom=$env{'user.name'}.':'.$env{'user.domain'};
        if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.roles.denied'}
 	   =~/\Q$rolecode\E/) {
-           if (($priv ne 'pch') && ($priv ne 'plc')) {
+	   if (($priv ne 'pch') && ($priv ne 'plc')) { 
 	       &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
 			'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
 			$env{'request.course.id'});
@@ -5809,7 +5762,7 @@ sub allowed {
 
        if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}
 	   =~/\Q$unamedom\E/) {
-           if (($priv ne 'pch') && ($priv ne 'plc')) {
+	   if (($priv ne 'pch') && ($priv ne 'plc')) { 
 	       &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.
 			'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
 			$env{'request.course.id'});
@@ -5823,7 +5776,7 @@ sub allowed {
    if ($thisallowed=~/R/) {
        my $rolecode=(split(/\./,$env{'request.role'}))[0];
        if (&metadata($uri,'roledeny')=~/\Q$rolecode\E/) {
-           if (($priv ne 'pch') && ($priv ne 'plc')) {
+	   if (($priv ne 'pch') && ($priv ne 'plc')) { 
 	       &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
 			'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode);
 	   }
@@ -6050,7 +6003,7 @@ sub fetch_enrollment_query {
                 $$replyref{$key} = $value;
             }
         } else {
-            my $pathname = $perlvar{'lonDaemons'}.'/tmp';
+            my $pathname = LONCAPA::tempdir();
             foreach my $line (@responses) {
                 my ($key,$value) = split(/=/,$line);
                 $$replyref{$key} = $value;
@@ -6080,7 +6033,7 @@ sub fetch_enrollment_query {
 
 sub get_query_reply {
     my $queryid=shift;
-    my $replyfile=$perlvar{'lonDaemons'}.'/tmp/'.$queryid;
+    my $replyfile=LONCAPA::tempdir().$queryid;
     my $reply='';
     for (1..100) {
 	sleep 2;
@@ -6750,13 +6703,6 @@ sub assignrole {
                     return 'refused';
                 }
             }
-        } elsif ($role eq 'au') {
-            if ($url ne '/'.$udom.'/') {
-                &logthis('Attempt by '.$env{'user.name'}.':'.$env{'user.domain'}.
-                         ' to assign author role for '.$uname.':'.$udom.
-                         ' in domain: '.$url.' refused (wrong domain).');
-                return 'refused';
-            }
         }
         $mrole=$role;
     }
@@ -6931,7 +6877,7 @@ sub modifyuser {
     }
     &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.
              $umode.', '.$first.', '.$middle.', '.
-             $last.', '.$gene.'(forceid: '.$forceid.'; candelete: '.$showcandelete.')'.
+	     $last.', '.$gene.'(forceid: '.$forceid.'; candelete: '.$showcandelete.')'.
              (defined($desiredhome) ? ' desiredhome = '.$desiredhome :
                                      ' desiredhome not specified'). 
              ' by '.$env{'user.name'}.' at '.$env{'user.domain'}.
@@ -7077,7 +7023,7 @@ sub modifyuser {
         return 'ok';
     }
     my $reply = &put('environment', \%names, $udom,$uname);
-    if ($reply ne 'ok') {
+    if ($reply ne 'ok') { 
         return 'error: '.$reply;
     }
     if ($names{'permanentemail'} ne $oldnames{'permanentemail'}) {
@@ -7459,7 +7405,7 @@ sub is_locked {
     my ($file_name, $domain, $user, $which) = @_;
     my @check;
     my $is_locked;
-    push(@check,$file_name);
+    push (@check,$file_name);
     my %locked = &get('file_permissions',\@check,
 		      $env{'user.domain'},$env{'user.name'});
     my ($tmp)=keys(%locked);
@@ -7524,7 +7470,7 @@ sub save_selected_files {
 sub clear_selected_files {
     my ($user) = @_;
     my $filename = $user."savedfiles";
-    open (OUT, '>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+    open (OUT, '>'.LONCAPA::tempdir().$filename);
     print (OUT undef);
     close (OUT);
     return ("ok");    
@@ -7534,7 +7480,7 @@ sub files_in_path {
     my ($user, $path) = @_;
     my $filename = $user."savedfiles";
     my %return_files;
-    open (IN, '<'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+    open (IN, '<'.LONCAPA::tempdir().$filename);
     while (my $line_in = <IN>) {
         chomp ($line_in);
         my @paths_and_file = split (m!/!, $line_in);
@@ -7556,7 +7502,7 @@ sub files_not_in_path {
     my $filename = $user."savedfiles";
     my @return_files;
     my $path_part;
-    open(IN, '<'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+    open(IN, '<'.LONCAPA::.$filename);
     while (my $line = <IN>) {
         #ok, I know it's clunky, but I want it to work
         my @paths_and_file = split(m|/|, $line);
@@ -8623,6 +8569,7 @@ sub add_prefix_and_part {
 # ---------------------------------------------------------------- Get metadata
 
 my %metaentry;
+my %importedpartids;
 sub metadata {
     my ($uri,$what,$liburi,$prefix,$depthcount)=@_;
     $uri=&declutter($uri);
@@ -8649,6 +8596,10 @@ sub metadata {
 	if (defined($cached)) { return $result->{':'.$what}; }
     }
     {
+# Imported parts would go here
+        my %importedids=();
+        my @origfileimportpartids=();
+        my $importedparts=0;
 #
 # Is this a recursive call for a library?
 #
@@ -8733,27 +8684,55 @@ sub metadata {
 # This is not a package - some other kind of start tag
 #
 		    my $entry=$token->[1];
-		    my $unikey;
-		    if ($entry eq 'import') {
-			$unikey='';
-		    } else {
-			$unikey=$entry;
-		    }
-		    $unikey.=&add_prefix_and_part($prefix,$token->[2]->{'part'});
-
-		    if (defined($token->[2]->{'id'})) { 
-			$unikey.='_'.$token->[2]->{'id'}; 
-		    }
+		    my $unikey='';
 
 		    if ($entry eq 'import') {
 #
 # Importing a library here
 #
+                        my $location=$parser->get_text('/import');
+                        my $dir=$filename;
+                        $dir=~s|[^/]*$||;
+                        $location=&filelocation($dir,$location);
+                       
+                        my $importmode=$token->[2]->{'importmode'};
+                        if ($importmode eq 'problem') {
+# Import as problem/response
+                           $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
+                        } elsif ($importmode eq 'part') {
+# Import as part(s)
+                           $importedparts=1;
+# We need to get the original file and the imported file to get the part order correct
+# Good news: we do not need to worry about nested libraries, since parts cannot be nested
+# Load and inspect original file
+                           if ($#origfileimportpartids<0) {
+                              undef(%importedpartids);
+                              my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri);
+                              my $origfile=&getfile($origfilelocation);
+                              @origfileimportpartids=($origfile=~/<(part|import)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+                           }
+
+# Load and inspect imported file
+                           my $impfile=&getfile($location);
+                           my @impfilepartids=($impfile=~/<part[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+                           if ($#impfilepartids>=0) {
+# This problem had parts
+                               $importedpartids{$token->[2]->{'id'}}=join(',',@impfilepartids);
+                           } else {
+# Importing by turning a single problem into a problem part
+# It gets the import-tags ID as part-ID
+                               $unikey=&add_prefix_and_part($prefix,$token->[2]->{'id'});
+                               $importedpartids{$token->[2]->{'id'}}=$token->[2]->{'id'};
+                           }
+                        } else {
+# Normal import
+                           $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
+                           if (defined($token->[2]->{'id'})) {
+                              $unikey.='_'.$token->[2]->{'id'};
+                           }
+                        }
+
 			if ($depthcount<20) {
-			    my $location=$parser->get_text('/import');
-			    my $dir=$filename;
-			    $dir=~s|[^/]*$||;
-			    $location=&filelocation($dir,$location);
 			    my $metadata = 
 				&metadata($uri,'keys', $location,$unikey,
 					  $depthcount+1);
@@ -8761,8 +8740,16 @@ sub metadata {
 				$metaentry{':'.$meta}=$metaentry{':'.$meta};
 				$metathesekeys{$meta}=1;
 			    }
-			}
-		    } else { 
+			
+                        }
+		    } else {
+#
+# Not importing, some other kind of non-package, non-library start tag
+# 
+                        $unikey=$entry.&add_prefix_and_part($prefix,$token->[2]->{'part'});
+                        if (defined($token->[2]->{'id'})) {
+                            $unikey.='_'.$token->[2]->{'id'};
+                        }
 			if (defined($token->[2]->{'name'})) { 
 			    $unikey.='_'.$token->[2]->{'name'}; 
 			}
@@ -8836,6 +8823,22 @@ sub metadata {
 	    grep { ! $seen{$_} ++ } (split(',',$metaentry{':packages'}));
 	$metaentry{':packages'} = join(',',@uniq_packages);
 
+        if ($importedparts) {
+# We had imported parts and need to rebuild partorder
+           $metaentry{':partorder'}='';
+           $metathesekeys{'partorder'}=1;
+           for (my $index=0;$index<$#origfileimportpartids;$index+=2) {
+               if ($origfileimportpartids[$index] eq 'part') {
+# original part, part of the problem
+                  $metaentry{':partorder'}.=','.$origfileimportpartids[$index+1];
+               } else {
+# we have imported parts at this position
+                  $metaentry{':partorder'}.=','.$importedpartids{$origfileimportpartids[$index+1]};
+               }
+           }
+           $metaentry{':partorder'}=~s/^\,//;
+        }
+
 	$metaentry{':keys'} = join(',',keys(%metathesekeys));
 	&metadata_generate_part0(\%metathesekeys,\%metaentry,$uri);
 	$metaentry{':allpossiblekeys'}=join(',',keys %metathesekeys);
@@ -9020,9 +9023,9 @@ sub symbverify {
             $thisurl =~ s/\?.+$//;
         }
         my $ids=$bighash{'ids_'.&clutter($thisurl)};
-        unless ($ids) { 
-           my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl;
-           $ids=$bighash{$idkey};
+        unless ($ids) {
+            my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl;  
+            $ids=$bighash{$idkey};
         }
         if ($ids) {
 # ------------------------------------------------------------------- Has ID(s)
@@ -9036,7 +9039,7 @@ sub symbverify {
    eq $symb) { 
 		   if (($env{'request.role.adv'}) ||
 		       ($bighash{'encrypted_'.$id} eq $env{'request.enc'}) ||
-		       ($thisurl eq '/adm/navmaps')) {
+                       ($thisurl eq '/adm/navmaps')) {
 		       $okay=1; 
 		   }
 	       }
@@ -9793,7 +9796,7 @@ sub filelocation {
         my @ids=&current_machine_ids();
         foreach my $id (@ids) { if ($id eq $home) { $is_me=1; } }
         if ($is_me) {
-  	    $location=&propath($udom,$uname).'/userfiles/'.$filename;
+  	    $location=propath($udom,$uname).'/userfiles/'.$filename;
         } else {
   	  $location=$Apache::lonnet::perlvar{'lonDocRoot'}.'/userfiles/'.
   	      $udom.'/'.$uname.'/'.$filename;
@@ -10031,7 +10034,6 @@ sub get_dns {
     while (%alldns) {
 	my ($dns) = keys(%alldns);
 	my $ua=new LWP::UserAgent;
-        $ua->timeout(30);
 	my $request=new HTTP::Request('GET',"$alldns{$dns}://$dns$url");
 	my $response=$ua->request($request);
         delete($alldns{$dns});
@@ -10123,7 +10125,7 @@ sub get_dns {
 	foreach my $configline (@$file) {
 	    next if ($configline =~ /^(\#|\s*$ )/x);
             chomp($configline);
-            if ($configline =~ /^\^/) {
+	    if ($configline =~ /^\^/) {
                 if ($configline =~ /^\^([\w.\-]+)/) {
                     $LC_dns_serv{$1} = 1;
                 }
@@ -10210,7 +10212,7 @@ sub get_dns {
     }
 
     sub unique_library {
-        #2x reverse removes all hostnames that appear more than once
+	#2x reverse removes all hostnames that appear more than once
         my %unique = reverse &all_library();
         return reverse %unique;
     }
@@ -10240,7 +10242,7 @@ sub get_dns {
 
     sub get_unique_servers {
         my %unique = reverse &get_servers(@_);
-        return reverse %unique;
+	return reverse %unique;
     }
 
     sub host_domain {
@@ -10547,25 +10549,9 @@ BEGIN {
     }
 }
 
-# ---------------------------------------------------------- Read managers table
-{
-    if (-e "$perlvar{'lonTabDir'}/managers.tab") {
-        if (open(my $config,"<$perlvar{'lonTabDir'}/managers.tab")) {
-            while (my $configline=<$config>) {
-                chomp($configline);
-                next if ($configline =~ /^\#/);
-                if (($configline =~ /^[\w\-]+$/) || ($configline =~ /^[\w\-]+\:[\w\-]+$/)) {
-                    $managerstab{$configline} = 1;
-                }
-            }
-            close($config);
-        }
-    }
-}
-
 # ------------- set up temporary directory
 {
-    $tmpdir = $perlvar{'lonDaemons'}.'/tmp/';
+    $tmpdir = LONCAPA::tempdir();
 
 }
 
@@ -10800,7 +10786,7 @@ $checkdefauth is optional (value is 1 if
    authenticate user using default authentication method, and allow
    account creation if username does not have account in the domain).
 $clientcancheckhost is optional (value is 1 if checking whether the
-   server can host will occur on the client side in lonauth.pm).
+   server can host will occur on the client side in lonauth.pm).   
 
 =item *
 X<homeserver()>
@@ -11059,11 +11045,32 @@ revokecustomrole($udom,$uname,$url,$role
 
 =item *
 
-coursedescription($courseid) : returns a hash of information about the
+coursedescription($courseid,$options) : returns a hash of information about the
 specified course id, including all environment settings for the
 course, the description of the course will be in the hash under the
 key 'description'
 
+$options is an optional parameter that if supplied is a hash reference that controls
+what how this function works.  It has the following key/values:
+
+=over 4
+
+=item freshen_cache
+
+If defined, and the environment cache for the course is valid, it is 
+returned in the returned hash.
+
+=item one_time
+
+If defined, the last cache time is set to _now_
+
+=item user
+
+If defined, the supplied username is used instead of the current user.
+
+
+=back
+
 =item *
 
 resdata($name,$domain,$type,@which) : request for current parameter
@@ -11456,6 +11463,7 @@ splitting on '&', supports elements that
 
 =head2 Logging Routines
 
+
 These routines allow one to make log messages in the lonnet.log and
 lonnet.perm logfiles.
 
@@ -11476,6 +11484,7 @@ logperm() : append a permanent message t
 file never gets deleted by any automated portion of the system, only
 messages of critical importance should go in here.
 
+
 =back
 
 =head2 General File Helper Routines
@@ -11550,7 +11559,7 @@ userfileupload(): main rotine for puttin
            the filename is in $env{'form.'.$formname.'.filename'} and the
            contents of the file is located in $env{'form.'.$formname}
  context - if coursedoc, store the file in the course of the active role
-             of the current user;
+             of the current user; 
            if 'existingfile': store in 'overwrites' in /home/httpd/perl/tmp
            if 'canceloverwrite': delete file in tmp/overwrites directory
  subdir - required - subdirectory to put the file in under ../userfiles/
@@ -11596,7 +11605,7 @@ userspace, probably shouldn't be called
  returns either the url of the uploaded file (/uploaded/....) if successful
  and /adm/notfound.html if unsuccessful (or an error message if context 
  was 'overwrite').
-
+ 
 
 =item *