--- loncom/lonnet/perl/lonnet.pm	2007/04/11 21:37:20	1.868
+++ loncom/lonnet/perl/lonnet.pm	2007/12/08 00:28:27	1.931
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.868 2007/04/11 21:37:20 raeburn Exp $
+# $Id: lonnet.pm,v 1.931 2007/12/08 00:28:27 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -31,22 +31,21 @@ package Apache::lonnet;
 
 use strict;
 use LWP::UserAgent();
-use HTTP::Headers;
 use HTTP::Date;
 # use Date::Parse;
-use vars 
-qw(%perlvar %badServerCache %spareid 
-   %pr %prp $memcache %packagetab 
-   %courselogs %accesshash %userrolehash %domainrolehash $processmarker $dumpcount 
-   %coursedombuf %coursenumbuf %coursehombuf %coursedescrbuf %courseinstcodebuf %courseownerbuf %coursetypebuf
-   $tmpdir $_64bit %env);
+use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir
+            $_64bit %env);
+
+my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash,
+    %userrolehash, $processmarker, $dumpcount, %coursedombuf,
+    %coursenumbuf, %coursehombuf, %coursedescrbuf, %courseinstcodebuf,
+    %courseownerbuf, %coursetypebuf);
 
 use IO::Socket;
 use GDBM_File;
 use HTML::LCParser;
-use HTML::Parser;
 use Fcntl qw(:flock);
-use Storable qw(lock_store lock_nstore lock_retrieve freeze thaw nfreeze);
+use Storable qw(thaw nfreeze);
 use Time::HiRes qw( gettimeofday tv_interval );
 use Cache::Memcached;
 use Digest::MD5;
@@ -150,7 +149,7 @@ sub create_connection {
 				     Type    => SOCK_STREAM,
 				     Timeout => 10);
     return 0 if (!$client);
-    print $client (join(':',$hostname,$lonid,&machine_ids($lonid))."\n");
+    print $client (join(':',$hostname,$lonid,&machine_ids($hostname))."\n");
     my $result = <$client>;
     chomp($result);
     return 1 if ($result eq 'done');
@@ -182,7 +181,7 @@ sub subreply {
 	$client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
 				      Type    => SOCK_STREAM,
 				      Timeout => 10);
-	if($client) {
+	if ($client) {
 	    last;		# Connected!
 	} else {
 	    &create_connection(&hostname($server),$server);
@@ -215,6 +214,24 @@ sub reply {
 # ----------------------------------------------------------- Send USR1 to lonc
 
 sub reconlonc {
+    my ($lonid) = @_;
+    my $hostname = &hostname($lonid);
+    if ($lonid) {
+	my $peerfile="$perlvar{'lonSockDir'}/$hostname";
+	if ($hostname && -e $peerfile) {
+	    &logthis("Trying to reconnect lonc for $lonid ($hostname)");
+	    my $client=IO::Socket::UNIX->new(Peer    => $peerfile,
+					     Type    => SOCK_STREAM,
+					     Timeout => 10);
+	    if ($client) {
+		print $client ("reset_retries\n");
+		my $answer=<$client>;
+		#reset just this one.
+	    }
+	}
+	return;
+    }
+
     &logthis("Trying to reconnect lonc");
     my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
     if (open(my $fh,"<$loncfile")) {
@@ -303,7 +320,10 @@ sub convert_and_load_session_env {
     my ($lonidsdir,$handle)=@_;
     my @profile;
     {
-	open(my $idf,"$lonidsdir/$handle.id");
+	my $opened = open(my $idf,'+<',"$lonidsdir/$handle.id");
+	if (!$opened) {
+	    return 0;
+	}
 	flock($idf,LOCK_SH);
 	@profile=<$idf>;
 	close($idf);
@@ -342,7 +362,10 @@ sub transfer_profile_to_env {
 
     my $convert;
     {
-    	open(my $idf,"$lonidsdir/$handle.id");
+    	my $opened = open(my $idf,'+<',"$lonidsdir/$handle.id");
+	if (!$opened) {
+	    return;
+	}
 	flock($idf,LOCK_SH);
 	if (tie(my %disk_env,'GDBM_File',"$lonidsdir/$handle.id",
 		&GDBM_READER(),0640)) {
@@ -374,6 +397,34 @@ sub transfer_profile_to_env {
     }
 }
 
+# ---------------------------------------------------- Check for valid session 
+sub check_for_valid_session {
+    my ($r) = @_;
+    my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
+    my $lonid=$cookies{'lonID'};
+    return undef if (!$lonid);
+
+    my $handle=&LONCAPA::clean_handle($lonid->value);
+    my $lonidsdir=$r->dir_config('lonIDsDir');
+    return undef if (!-e "$lonidsdir/$handle.id");
+
+    my $opened = open(my $idf,'+<',"$lonidsdir/$handle.id");
+    return undef if (!$opened);
+
+    flock($idf,LOCK_SH);
+    my %disk_env;
+    if (!tie(%disk_env,'GDBM_File',"$lonidsdir/$handle.id",
+	    &GDBM_READER(),0640)) {
+	return undef;	
+    }
+
+    if (!defined($disk_env{'user.name'})
+	|| !defined($disk_env{'user.domain'})) {
+	return undef;
+    }
+    return $handle;
+}
+
 sub timed_flock {
     my ($file,$lock_type) = @_;
     my $failed=0;
@@ -408,8 +459,9 @@ sub appenv {
             $env{$key}=$newenv{$key};
         }
     }
-    open(my $env_file,$env{'user.environment'});
-    if (&timed_flock($env_file,LOCK_EX)
+    my $opened = open(my $env_file,'+<',$env{'user.environment'});
+    if ($opened
+	&& &timed_flock($env_file,LOCK_EX)
 	&&
 	tie(my %disk_env,'GDBM_File',$env{'user.environment'},
 	    (&GDBM_WRITER()|&GDBM_NOLOCK()),0640)) {
@@ -429,16 +481,17 @@ sub delenv {
                 "Attempt to delete from environment ".$delthis);
         return 'error';
     }
-    open(my $env_file,$env{'user.environment'});
-    if (&timed_flock($env_file,LOCK_EX)
+    my $opened = open(my $env_file,'+<',$env{'user.environment'});
+    if ($opened
+	&& &timed_flock($env_file,LOCK_EX)
 	&&
 	tie(my %disk_env,'GDBM_File',$env{'user.environment'},
 	    (&GDBM_WRITER()|&GDBM_NOLOCK()),0640)) {
 	foreach my $key (keys(%disk_env)) {
 	    if ($key=~/^$delthis/) { 
-                delete($env{$key});
-                delete($disk_env{$key});
-            }
+		delete($env{$key});
+		delete($disk_env{$key});
+	    }
 	}
 	untie(%disk_env);
     }
@@ -460,7 +513,6 @@ sub get_env_multiple {
 }
 
 # ------------------------------------------ Find out current server userload
-# there is a copy in lond
 sub userload {
     my $numusers=0;
     {
@@ -468,7 +520,8 @@ sub userload {
 	my $filename;
 	my $curtime=time;
 	while ($filename=readdir(LONIDS)) {
-	    if ($filename eq '.' || $filename eq '..') {next;}
+	    next if ($filename eq '.' || $filename eq '..');
+	    next if ($filename =~ /publicuser_\d+\.id/);
 	    my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9];
 	    if ($curtime-$mtime < 1800) { $numusers++; }
 	}
@@ -565,6 +618,27 @@ sub compare_server_load {
     }
     return ($spare_server,$lowest_load);
 }
+
+# --------------------------- ask offload servers if user already has a session
+sub find_existing_session {
+    my ($udom,$uname) = @_;
+    foreach my $try_server (@{ $spareid{'primary'} },
+			    @{ $spareid{'default'} }) {
+	return $try_server if (&has_user_session($try_server, $udom, $uname));
+    }
+    return;
+}
+
+# -------------------------------- ask if server already has a session for user
+sub has_user_session {
+    my ($lonid,$udom,$uname) = @_;
+    my $result = &reply(join(':','userhassession',
+			     map {&escape($_)} ($udom,$uname)),$lonid);
+    return 1 if ($result eq 'ok');
+
+    return 0;
+}
+
 # --------------------------------------------- Try to change a user's password
 
 sub changepass {
@@ -745,7 +819,7 @@ sub get_dom {
         if (defined(&domain($udom,'primary'))) {
             $uhome=&domain($udom,'primary');
         } else {
-            $uhome eq '';
+            undef($uhome);
         }
     } else {
         if (!$uhome) {
@@ -757,14 +831,13 @@ sub get_dom {
     if ($udom && $uhome && ($uhome ne 'no_host')) {
         my $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
         my %returnhash;
-        if ($rep =~ /^error: 2 /) {
+        if ($rep eq '' || $rep =~ /^error: 2 /) {
             return %returnhash;
         }
         my @pairs=split(/\&/,$rep);
         if ( $#pairs==0 && $pairs[0] =~ /^(con_lost|error|no_such_host)/i) {
             return @pairs;
         }
-        my %returnhash=();
         my $i=0;
         foreach my $item (@$storearr) {
             $returnhash{$item}=&thaw_unescape($pairs[$i]);
@@ -772,7 +845,7 @@ sub get_dom {
         }
         return %returnhash;
     } else {
-        &logthis("get_dom failed - no homeserver and/or domain");
+        &logthis("get_dom failed - no homeserver and/or domain ($udom) ($uhome)");
     }
 }
 
@@ -785,7 +858,7 @@ sub put_dom {
         if (defined(&domain($udom,'primary'))) {
             $uhome=&domain($udom,'primary');
         } else {
-            $uhome eq '';
+            undef($uhome);
         }
     } else {
         if (!$uhome) {
@@ -840,6 +913,208 @@ sub is_domainimage {
     return;
 }
 
+sub inst_directory_query {
+    my ($srch) = @_;
+    my $udom = $srch->{'srchdomain'};
+    my %results;
+    my $homeserver = &domain($udom,'primary');
+    my $outcome;
+    if ($homeserver ne '') {
+	my $queryid=&reply("querysend:instdirsearch:".
+			   &escape($srch->{'srchby'}).':'.
+			   &escape($srch->{'srchterm'}).':'.
+			   &escape($srch->{'srchtype'}),$homeserver);
+	my $host=&hostname($homeserver);
+	if ($queryid !~/^\Q$host\E\_/) {
+	    &logthis('instituional directory search invalid queryid: '.$queryid.' for host: '.$homeserver.'in domain '.$udom);
+	    return;
+	}
+	my $response = &get_query_reply($queryid);
+	my $maxtries = 5;
+	my $tries = 1;
+	while (($response=~/^timeout/) && ($tries < $maxtries)) {
+	    $response = &get_query_reply($queryid);
+	    $tries ++;
+	}
+
+        if (!&error($response) && $response ne 'refused') {
+            if ($response eq 'unavailable') {
+                $outcome = $response;
+            } else {
+                $outcome = 'ok';
+                my @matches = split(/\n/,$response);
+                foreach my $match (@matches) {
+                    my ($key,$value) = split(/=/,$match);
+                    $results{&unescape($key).':'.$udom} = &thaw_unescape($value);
+                }
+            }
+        }
+    }
+    return ($outcome,%results);
+}
+
+sub usersearch {
+    my ($srch) = @_;
+    my $dom = $srch->{'srchdomain'};
+    my %results;
+    my %libserv = &all_library();
+    my $query = 'usersearch';
+    foreach my $tryserver (keys(%libserv)) {
+        if (&host_domain($tryserver) eq $dom) {
+            my $host=&hostname($tryserver);
+            my $queryid=
+                &reply("querysend:".&escape($query).':'.
+                       &escape($srch->{'srchby'}).':'.
+                       &escape($srch->{'srchtype'}).':'.
+                       &escape($srch->{'srchterm'}),$tryserver);
+            if ($queryid !~/^\Q$host\E\_/) {
+                &logthis('usersearch: invalid queryid: '.$queryid.' for host: '.$host.'in domain '.$dom.' and server: '.$tryserver);
+                next;
+            }
+            my $reply = &get_query_reply($queryid);
+            my $maxtries = 1;
+            my $tries = 1;
+            while (($reply=~/^timeout/) && ($tries < $maxtries)) {
+                $reply = &get_query_reply($queryid);
+                $tries ++;
+            }
+            if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) {
+                &logthis('usersrch error: '.$reply.' for '.$dom.' - searching for : '.$srch->{'srchterm'}.' by '.$srch->{'srchby'}.' ('.$srch->{'srchtype'}.') -  maxtries: '.$maxtries.' tries: '.$tries);
+            } else {
+                my @matches;
+                if ($reply =~ /\n/) {
+                    @matches = split(/\n/,$reply);
+                } else {
+                    @matches = split(/\&/,$reply);
+                }
+                foreach my $match (@matches) {
+                    my ($uname,$udom,%userhash);
+                    foreach my $entry (split(/:/,$match)) {
+                        my ($key,$value) =
+                            map {&unescape($_);} split(/=/,$entry);
+                        $userhash{$key} = $value;
+                        if ($key eq 'username') {
+                            $uname = $value;
+                        } elsif ($key eq 'domain') {
+                            $udom = $value;
+                        }
+                    }
+                    $results{$uname.':'.$udom} = \%userhash;
+                }
+            }
+        }
+    }
+    return %results;
+}
+
+sub get_instuser {
+    my ($udom,$uname,$id) = @_;
+    my $homeserver = &domain($udom,'primary');
+    my ($outcome,%results);
+    if ($homeserver ne '') {
+        my $queryid=&reply("querysend:getinstuser:".&escape($uname).':'.
+                           &escape($id).':'.&escape($udom),$homeserver);
+        my $host=&hostname($homeserver);
+        if ($queryid !~/^\Q$host\E\_/) {
+            &logthis('get_instuser invalid queryid: '.$queryid.' for host: '.$homeserver.'in domain '.$udom);
+            return;
+        }
+        my $response = &get_query_reply($queryid);
+        my $maxtries = 5;
+        my $tries = 1;
+        while (($response=~/^timeout/) && ($tries < $maxtries)) {
+            $response = &get_query_reply($queryid);
+            $tries ++;
+        }
+        if (!&error($response) && $response ne 'refused') {
+            if ($response eq 'unavailable') {
+                $outcome = $response;
+            } else {
+                $outcome = 'ok';
+                my @matches = split(/\n/,$response);
+                foreach my $match (@matches) {
+                    my ($key,$value) = split(/=/,$match);
+                    $results{&unescape($key)} = &thaw_unescape($value);
+                }
+            }
+        }
+    }
+    my %userinfo;
+    if (ref($results{$uname}) eq 'HASH') {
+        %userinfo = %{$results{$uname}};
+    } 
+    return ($outcome,%userinfo);
+}
+
+sub inst_rulecheck {
+    my ($udom,$uname,$id,$item,$rules) = @_;
+    my %returnhash;
+    if ($udom ne '') {
+        if (ref($rules) eq 'ARRAY') {
+            @{$rules} = map {&escape($_);} (@{$rules});
+            my $rulestr = join(':',@{$rules});
+            my $homeserver=&domain($udom,'primary');
+            if (($homeserver ne '') && ($homeserver ne 'no_host')) {
+                my $response;
+                if ($item eq 'username') {                
+                    $response=&unescape(&reply('instrulecheck:'.&escape($udom).
+                                              ':'.&escape($uname).':'.$rulestr,
+                                              $homeserver));
+                } elsif ($item eq 'id') {
+                    $response=&unescape(&reply('instidrulecheck:'.&escape($udom).
+                                              ':'.&escape($id).':'.$rulestr,
+                                              $homeserver));
+                }
+                if ($response ne 'refused') {
+                    my @pairs=split(/\&/,$response);
+                    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;
+}
+
+sub inst_userrules {
+    my ($udom,$check) = @_;
+    my (%ruleshash,@ruleorder);
+    if ($udom ne '') {
+        my $homeserver=&domain($udom,'primary');
+        if (($homeserver ne '') && ($homeserver ne 'no_host')) {
+            my $response;
+            if ($check eq 'id') {
+                $response=&reply('instidrules:'.&escape($udom),
+                                 $homeserver);
+            } else {
+                $response=&reply('instuserrules:'.&escape($udom),
+                                 $homeserver);
+            }
+            if (($response ne 'refused') && ($response ne 'error') && 
+                ($response ne 'unknown_cmd') && 
+                ($response ne 'no_such_host')) {
+                my ($hashitems,$orderitems) = split(/:/,$response);
+                my @pairs=split(/\&/,$hashitems);
+                foreach my $item (@pairs) {
+                    my ($key,$value)=split(/=/,$item,2);
+                    $key = &unescape($key);
+                    next if ($key =~ /^error: 2 /);
+                    $ruleshash{$key}=&thaw_unescape($value);
+                }
+                my @esc_order = split(/\&/,$orderitems);
+                foreach my $item (@esc_order) {
+                    push(@ruleorder,&unescape($item));
+                }
+            }
+        }
+    }
+    return (\%ruleshash,\@ruleorder);
+}
+
 # --------------------------------------------------- Assign a key to a student
 
 sub assign_access_key {
@@ -1066,7 +1341,10 @@ my $kicks=0;
 my $hits=0;
 sub make_key {
     my ($name,$id) = @_;
-    if (length($id) > 200) { $id=length($id).':'.&Digest::MD5::md5_hex($id); }
+    if (length($id) > 65 
+	&& length(&escape($id)) > 200) {
+	$id=length($id).':'.&Digest::MD5::md5_hex($id);
+    }
     return &escape($name.':'.$id);
 }
 
@@ -1113,15 +1391,21 @@ sub do_cache_new {
 	$time=600;
     }
     if ($debug) { &Apache::lonnet::logthis("Setting $id to $value"); }
-    $memcache->set($id,$setvalue,$time);
+    my $result = $memcache->set($id,$setvalue,$time);
+    if (! $result) {
+	&logthis("caching of id -> $id  failed");
+	$memcache->disconnect_all();
+    }
     # need to make a copy of $value
-    #&make_room($id,$value,$debug);
+    &make_room($id,$value,$debug);
     return $value;
 }
 
 sub make_room {
     my ($id,$value,$debug)=@_;
-    $remembered{$id}=$value;
+
+    $remembered{$id}= (ref($value)) ? &Storable::dclone($value)
+                                    : $value;
     if ($to_remember<0) { return; }
     $accessed{$id}=[&gettimeofday()];
     if (scalar(keys(%remembered)) <= $to_remember) { return; }
@@ -1351,7 +1635,7 @@ sub ssi_body {
                                      &ssi($filelink,%form));
     $output=~s|//(\s*<!--)? BEGIN LON-CAPA Internal.+?// END LON-CAPA Internal\s*(-->)?\s||gs;
     $output=~s/^.*?\<body[^\>]*\>//si;
-    $output=~s/(.*)\<\/body\s*\>.*?$/$1/si;
+    $output=~s/\<\/body\s*\>.*?$//si;
     return $output;
 }
 
@@ -1375,7 +1659,7 @@ sub ssi {
     my $request;
 
     $form{'no_update_last_known'}=1;
-
+    &Apache::lonenc::check_encrypt(\$fn);
     if (%form) {
       $request=new HTTP::Request('POST',&absolute_url().$fn);
       $request->content(join('&',map { &escape($_).'='.&escape($form{$_}) } keys %form));
@@ -1736,13 +2020,16 @@ sub extract_embedded_items {
     while (my $t=$p->get_token()) {
 	if ($t->[0] eq 'S') {
 	    my ($tagname, $attr) = ($t->[1],$t->[2]);
-	    push (@state, $tagname);
+	    push(@state, $tagname);
             if (lc($tagname) eq 'allow') {
                 &add_filetype($allfiles,$attr->{'src'},'src');
             }
 	    if (lc($tagname) eq 'img') {
 		&add_filetype($allfiles,$attr->{'src'},'src');
 	    }
+	    if (lc($tagname) eq 'a') {
+		&add_filetype($allfiles,$attr->{'href'},'href');
+	    }
             if (lc($tagname) eq 'script') {
                 if ($attr->{'archive'} =~ /\.jar$/i) {
                     &add_filetype($allfiles,$attr->{'archive'},'archive');
@@ -1899,7 +2186,7 @@ sub flushcourselogs {
 # times and course titles for all courseids
 #
     my %courseidbuffer=();
-    foreach my $crsid (keys %courselogs) {
+    foreach my $crsid (keys(%courselogs)) {
         if (&reply('log:'.$coursedombuf{$crsid}.':'.$coursenumbuf{$crsid}.':'.
 		          &escape($courselogs{$crsid}),
 		          $coursehombuf{$crsid}) eq 'ok') {
@@ -1912,23 +2199,21 @@ sub flushcourselogs {
                delete $courselogs{$crsid};
             }
         }
-        if ($courseidbuffer{$coursehombuf{$crsid}}) {
-           $courseidbuffer{$coursehombuf{$crsid}}.='&'.
-			 &escape($crsid).'='.&escape($coursedescrbuf{$crsid}).
-                         ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid}).':'.&escape($coursetypebuf{$crsid});
-        } else {
-           $courseidbuffer{$coursehombuf{$crsid}}=
-			 &escape($crsid).'='.&escape($coursedescrbuf{$crsid}).
-                         ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid}).':'.&escape($coursetypebuf{$crsid});
-        }
+        $courseidbuffer{$coursehombuf{$crsid}}{$crsid} = {
+            'description' => &escape($coursedescrbuf{$crsid}),
+            'inst_code'    => &escape($courseinstcodebuf{$crsid}),
+            'type'        => &escape($coursetypebuf{$crsid}),
+            'owner'       => &escape($courseownerbuf{$crsid}),
+        };
     }
 #
 # Write course id database (reverse lookup) to homeserver of courses 
 # Is used in pickcourse
 #
     foreach my $crs_home (keys(%courseidbuffer)) {
-        &courseidput(&host_domain($crs_home),$courseidbuffer{$crs_home},
-		     $crs_home);
+        my $response = &courseidput(&host_domain($crs_home),
+                                    $courseidbuffer{$crs_home},
+                                    $crs_home,'timeonly');
     }
 #
 # File accesses
@@ -1984,7 +2269,7 @@ sub flushcourselogs {
 #
     my %domrolebuffer = ();
     foreach my $entry (keys %domainrolehash) {
-        my ($role,$uname,$udom,$runame,$rudom,$rsec)=split/:/,$entry;
+        my ($role,$uname,$udom,$runame,$rudom,$rsec)=split(/:/,$entry);
         if ($domrolebuffer{$rudom}) {
             $domrolebuffer{$rudom}.='&'.&escape($entry).
                       '='.&escape($domainrolehash{$entry});
@@ -2089,6 +2374,14 @@ sub userrolelog {
          {$trole.':'.$username.':'.$domain.':'.$runame.':'.$rudom.':'.$rsec}
                     =$tend.':'.$tstart;
     }
+    if (($env{'request.role'} =~ /dc\./) &&
+	(($trole=~/^au/) || ($trole=~/^in/) ||
+	 ($trole=~/^cc/) || ($trole=~/^ep/) ||
+	 ($trole=~/^cr/) || ($trole=~/^ta/))) {
+       $userrolehash
+         {$trole.':'.$username.':'.$domain.':'.$env{'user.name'}.':'.$env{'user.domain'}.':'}
+                    =$tend.':'.$tstart;
+    }
     if (($trole=~/^dc/) || ($trole=~/^ad/) ||
         ($trole=~/^li/) || ($trole=~/^li/) ||
         ($trole=~/^au/) || ($trole=~/^dg/) ||
@@ -2184,7 +2477,13 @@ sub get_my_roles {
         }
         if (ref($roles) eq 'ARRAY') {
             if (!grep(/^\Q$role\E$/,@{$roles})) {
-                next;
+                if ($role =~ /^cr\//) {
+                    if (!grep(/^cr$/,@{$roles})) {
+                        next;
+                    }
+                } else {
+                    next;
+                }
             }
         }
 	$returnhash{$username.':'.$domain.':'.$role}=$tstart.':'.$tend;
@@ -2226,31 +2525,77 @@ sub getannounce {
 #
 
 sub courseidput {
-    my ($domain,$what,$coursehome)=@_;
-    return &reply('courseidput:'.$domain.':'.$what,$coursehome);
+    my ($domain,$storehash,$coursehome,$caller) = @_;
+    my $outcome;
+    if ($caller eq 'timeonly') {
+        my $cids = '';
+        foreach my $item (keys(%$storehash)) {
+            $cids.=&escape($item).'&';
+        }
+        $cids=~s/\&$//;
+        $outcome = &reply('courseidputhash:'.$domain.':'.$caller.':'.$cids,
+                          $coursehome);       
+    } else {
+        my $items = '';
+        foreach my $item (keys(%$storehash)) {
+            $items.= &escape($item).'='.
+                     &freeze_escape($$storehash{$item}).'&';
+        }
+        $items=~s/\&$//;
+        $outcome = &reply('courseidputhash:'.$domain.':'.$caller.':'.$items,
+                          $coursehome);
+    }
+    if ($outcome eq 'unknown_cmd') {
+        my $what;
+        foreach my $cid (keys(%$storehash)) {
+            $what .= &escape($cid).'=';
+            foreach my $item ('description','inst_code','owner','type') {
+                $what .= &escape($storehash->{$item}).':';
+            }
+            $what =~ s/\:$/&/;
+        }
+        $what =~ s/\&$//;  
+        return &reply('courseidput:'.$domain.':'.$what,$coursehome);
+    } else {
+        return $outcome;
+    }
 }
 
 sub courseiddump {
-    my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,$coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok)=@_;
-    my %returnhash=();
-    unless ($domfilter) { $domfilter=''; }
+    my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,
+        $coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok)=@_;
+    my $as_hash = 1;
+    my %returnhash;
+    if (!$domfilter) { $domfilter=''; }
     my %libserv = &all_library();
     foreach my $tryserver (keys(%libserv)) {
         if ( (  $hostidflag == 1 
 	        && grep(/^\Q$tryserver\E$/,@{$hostidref}) ) 
 	     || (!defined($hostidflag)) ) {
 
-	    if ($domfilter eq ''
-		|| (&host_domain($tryserver) eq $domfilter)) {
-	        foreach my $line (
-                 split(/\&/,&reply('courseiddump:'.&host_domain($tryserver).':'.
-			       $sincefilter.':'.&escape($descfilter).':'.
-                               &escape($instcodefilter).':'.&escape($ownerfilter).':'.&escape($coursefilter).':'.&escape($typefilter).':'.&escape($regexp_ok),
-                               $tryserver))) {
-		    my ($key,$value)=split(/\=/,$line,2);
-                    if (($key) && ($value)) {
-		        $returnhash{&unescape($key)}=$value;
-                    }
+	    if (($domfilter eq '') ||
+		(&host_domain($tryserver) eq $domfilter)) {
+                my $rep = 
+                  &reply('courseiddump:'.&host_domain($tryserver).':'.
+                         $sincefilter.':'.&escape($descfilter).':'.
+                         &escape($instcodefilter).':'.&escape($ownerfilter).
+                         ':'.&escape($coursefilter).':'.&escape($typefilter).
+                         ':'.&escape($regexp_ok).':'.$as_hash,$tryserver);
+                my @pairs=split(/\&/,$rep);
+                foreach my $item (@pairs) {
+                    my ($key,$value)=split(/\=/,$item,2);
+                    $key = &unescape($key);
+                    next if ($key =~ /^error: 2 /);
+                    my $result = &thaw_unescape($value);
+                    if (ref($result) eq 'HASH') {
+                        $returnhash{$key}=$result;
+                    } else {
+                        my @responses = split(/:/,$value);
+                        my @items = ('description','inst_code','owner','type');
+                        for (my $i=0; $i<@responses; $i++) {
+                            $returnhash{$key}{$items[$i]} = &unescape($responses[$i]);
+                        }
+                    } 
                 }
             }
         }
@@ -2296,7 +2641,10 @@ sub get_domain_roles {
     if (undef($enddate) || $enddate eq '') {
         $enddate = '.';
     }
-    my $rolelist = join(':',@{$roles});
+    my $rolelist;
+    if (ref($roles) eq 'ARRAY') {
+        $rolelist = join(':',@{$roles});
+    }
     my %personnel = ();
 
     my %servers = &get_servers($dom,'library');
@@ -2322,7 +2670,9 @@ sub get_first_access {
     my ($symb,$courseid,$udom,$uname)=&whichuser();
     if ($argsymb) { $symb=$argsymb; }
     my ($map,$id,$res)=&decode_symb($symb);
-    if ($type eq 'map') {
+    if ($type eq 'course') {
+	$res='course';
+    } elsif ($type eq 'map') {
 	$res=&symbread($map);
     } else {
 	$res=$symb;
@@ -2335,7 +2685,9 @@ sub set_first_access {
     my ($type)=@_;
     my ($symb,$courseid,$udom,$uname)=&whichuser();
     my ($map,$id,$res)=&decode_symb($symb);
-    if ($type eq 'map') {
+    if ($type eq 'course') {
+	$res='course';
+    } elsif ($type eq 'map') {
 	$res=&symbread($map);
     } else {
 	$res=$symb;
@@ -3130,7 +3482,7 @@ sub set_userprivs {
     if (keys(%{$allgroups}) > 0) {
         foreach my $role (keys %{$allroles}) {
             my ($trole,$area,$sec,$extendedarea);
-            if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)-) {
+            if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)\.-) {
                 $trole = $1;
                 $area = $2;
                 $sec = $3;
@@ -3780,26 +4132,40 @@ sub customaccess {
     $ucrs = &LONCAPA::clean_username($ucrs);
     my $access=0;
     foreach my $right (split(/\s*\,\s*/,&metadata($uri,'rule_rights'))) {
-	my ($effect,$realm,$role)=split(/\:/,$right);
-        if ($role) {
-	   if ($role ne $urole) { next; }
-        }
-        foreach my $scope (split(/\s*\,\s*/,$realm)) {
-            my ($tdom,$tcrs,$tsec)=split(/\_/,$scope);
-            if ($tdom) {
-		if ($tdom ne $udom) { next; }
-            }
-            if ($tcrs) {
-		if ($tcrs ne $ucrs) { next; }
-            }
-            if ($tsec) {
-		if ($tsec ne $usec) { next; }
-            }
-            $access=($effect eq 'allow');
-            last;
-        }
-	if ($realm eq '' && $role eq '') {
-            $access=($effect eq 'allow');
+	my ($effect,$realm,$role,$type)=split(/\:/,$right);
+	if ($type eq 'user') {
+	    foreach my $scope (split(/\s*\,\s*/,$realm)) {
+		my ($tdom,$tuname)=split(m{/},$scope);
+		if ($tdom) {
+		    if ($tdom ne $env{'user.domain'}) { next; }
+		}
+		if ($tuname) {
+		    if ($tuname ne $env{'user.name'}) { next; }
+		}
+		$access=($effect eq 'allow');
+		last;
+	    }
+	} else {
+	    if ($role) {
+		if ($role ne $urole) { next; }
+	    }
+	    foreach my $scope (split(/\s*\,\s*/,$realm)) {
+		my ($tdom,$tcrs,$tsec)=split(/\_/,$scope);
+		if ($tdom) {
+		    if ($tdom ne $udom) { next; }
+		}
+		if ($tcrs) {
+		    if ($tcrs ne $ucrs) { next; }
+		}
+		if ($tsec) {
+		    if ($tsec ne $usec) { next; }
+		}
+		$access=($effect eq 'allow');
+		last;
+	    }
+	    if ($realm eq '' && $role eq '') {
+		$access=($effect eq 'allow');
+	    }
 	}
     }
     return $access;
@@ -4328,6 +4694,23 @@ sub update_portfolio_table {
     return $reply;
 }
 
+# -------------------------- Update MySQL allusers table
+
+sub update_allusers_table {
+    my ($uname,$udom,$names) = @_;
+    my $homeserver = &homeserver($uname,$udom);
+    my $queryid=
+        &reply('querysend:allusers:'.&escape($uname).':'.&escape($udom).':'.
+               'lastname='.&escape($names->{'lastname'}).'%%'.
+               'firstname='.&escape($names->{'firstname'}).'%%'.
+               'middlename='.&escape($names->{'middlename'}).'%%'.
+               'generation='.&escape($names->{'generation'}).'%%'.
+               'permanentemail='.&escape($names->{'permanentemail'}).'%%'.
+               'id='.&escape($names->{'id'}),$homeserver);
+    my $reply = &get_query_reply($queryid);
+    return $reply;
+}
+
 # ------- Request retrieval of institutional classlists for course(s)
 
 sub fetch_enrollment_query {
@@ -4362,7 +4745,7 @@ sub fetch_enrollment_query {
     if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) {
         &logthis('fetch_enrollment_query error: '.$reply.' for '.$dom.' '.$env{'user.name'}.' for '.$queryid.' context: '.$context.' '.$cnum.' maxtries: '.$maxtries.' tries: '.$tries);
     } else {
-        my @responses = split/:/,$reply;
+        my @responses = split(/:/,$reply);
         if ($homeserver eq $perlvar{'lonHostID'}) {
             foreach my $line (@responses) {
                 my ($key,$value) = split(/=/,$line,2);
@@ -4405,8 +4788,8 @@ sub get_query_reply {
 	sleep 2;
         if (-e $replyfile.'.end') {
 	    if (open(my $fh,$replyfile)) {
-               $reply.=<$fh>;
-               close($fh);
+		$reply = join('',<$fh>);
+		close($fh);
 	   } else { return 'error: reply_file_error'; }
            return &unescape($reply);
 	}
@@ -4451,8 +4834,18 @@ sub userlog_query {
 
 sub auto_run {
     my ($cnum,$cdom) = @_;
-    my $homeserver = &homeserver($cnum,$cdom);
-    my $response = &reply('autorun:'.$cdom,$homeserver);
+    my $response = 0;
+    my $settings;
+    my %domconfig = &get_dom('configuration',['autoenroll'],$cdom);
+    if (ref($domconfig{'autoenroll'}) eq 'HASH') {
+        $settings = $domconfig{'autoenroll'};
+        if ($settings->{'run'} eq '1') {
+            $response = 1;
+        }
+    } else {
+        my $homeserver = &homeserver($cnum,$cdom);
+        $response = &reply('autorun:'.$cdom,$homeserver);
+    }
     return $response;
 }
 
@@ -4462,7 +4855,7 @@ sub auto_get_sections {
     my @secs = ();
     my $response=&unescape(&reply('autogetsections:'.$inst_coursecode.':'.$cdom,$homeserver));
     unless ($response eq 'refused') {
-        @secs = split/:/,$response;
+        @secs = split(/:/,$response);
     }
     return @secs;
 }
@@ -4482,15 +4875,27 @@ sub auto_validate_courseID {
 }
 
 sub auto_create_password {
-    my ($cnum,$cdom,$authparam) = @_;
-    my $homeserver = &homeserver($cnum,$cdom); 
+    my ($cnum,$cdom,$authparam,$udom) = @_;
+    my ($homeserver,$response);
     my $create_passwd = 0;
     my $authchk = '';
-    my $response=&unescape(&reply('autocreatepassword:'.$authparam.':'.$cdom,$homeserver));
-    if ($response eq 'refused') {
-        $authchk = 'refused';
+    if ($udom =~ /^$match_domain$/) {
+        $homeserver = &domain($udom,'primary');
+    }
+    if ($homeserver eq '') {
+        if (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/)) {
+            $homeserver = &homeserver($cnum,$cdom);
+        }
+    }
+    if ($homeserver eq '') {
+        $authchk = 'nodomain';
     } else {
-        ($authparam,$create_passwd,$authchk) = split/:/,$response;
+        $response=&unescape(&reply('autocreatepassword:'.$authparam.':'.$cdom,$homeserver));
+        if ($response eq 'refused') {
+            $authchk = 'refused';
+        } else {
+            ($authparam,$create_passwd,$authchk) = split(/:/,$response);
+        }
     }
     return ($authparam,$create_passwd,$authchk);
 }
@@ -4597,7 +5002,7 @@ sub auto_instcode_format {
         $response=&reply('autoinstcodeformat:'.$codedom.':'.$courses,$server);
         if ($response !~ /(con_lost|error|no_such_host|refused)/) {
             my ($codes_str,$codetitles_str,$cat_titles_str,$cat_order_str) = 
-		split/:/,$response;
+		split(/:/,$response);
             %{$codes} = (%{$codes},&str2hash($codes_str));
             push(@{$codetitles},&str2array($codetitles_str));
             %{$cat_titles} = (%{$cat_titles},&str2hash($cat_titles_str));
@@ -4643,10 +5048,16 @@ sub auto_instcode_defaults {
 } 
 
 sub auto_validate_class_sec {
-    my ($cdom,$cnum,$owner,$inst_class) = @_;
+    my ($cdom,$cnum,$owners,$inst_class) = @_;
     my $homeserver = &homeserver($cnum,$cdom);
+    my $ownerlist;
+    if (ref($owners) eq 'ARRAY') {
+        $ownerlist = join(',',@{$owners});
+    } else {
+        $ownerlist = $owners;
+    }
     my $response=&reply('autovalidateclass_sec:'.$inst_class.':'.
-                        &escape($owner).':'.$cdom,$homeserver);
+                        &escape($ownerlist).':'.$cdom,$homeserver);
     return $response;
 }
 
@@ -4977,7 +5388,8 @@ sub modifyuser {
     }
 # -------------------------------------------------------------- Add names, etc
     my @tmp=&get('environment',
-		   ['firstname','middlename','lastname','generation'],
+		   ['firstname','middlename','lastname','generation','id',
+                    'permanentemail'],
 		   $udom,$uname);
     my %names;
     if ($tmp[0] =~ m/^error:.*/) { 
@@ -4999,8 +5411,10 @@ sub modifyuser {
 			   $names{'critnotification'} = $email;
 			   $names{'permanentemail'} = $email; }
     }
+    if ($uid) { $names{'id'}  = $uid; }
     my $reply = &put('environment', \%names, $udom,$uname);
     if ($reply ne 'ok') { return 'error: '.$reply; }
+    my $sqlresult = &update_allusers_table($uname,$udom,\%names);
     &devalidate_cache_new('namescache',$uname.':'.$udom);
     &logthis('Success modifying user '.$udom.', '.$uname.', '.$uid.', '.
              $umode.', '.$first.', '.$middle.', '.
@@ -5172,10 +5586,15 @@ sub createcourse {
     }
 # ----------------------------------------------------------------- Course made
 # log existence
-    &courseidput($udom,&escape($udom.'_'.$uname).'='.&escape($description).
-                 ':'.&escape($inst_code).':'.&escape($course_owner).':'.
-                  &escape($crstype),$uhome);
-    &flushcourselogs();
+    my $newcourse = {
+                    $udom.'_'.$uname => {
+                                     description => $description,
+                                     inst_code   => $inst_code,
+                                     owner       => $course_owner,
+                                     type        => $crstype,
+                                                },
+                    };
+    &courseidput($udom,$newcourse,$uhome,'notime');
 # set toplevel url
     my $topurl=$url;
     unless ($nonstandard) {
@@ -5205,7 +5624,7 @@ ENDINITMAP
 sub is_course {
     my ($cdom,$cnum) = @_;
     my %courses = &courseiddump($cdom,'.',1,'.','.',$cnum,undef,
-				undef,'.');
+				undef,'.',undef,1);
     if (exists($courses{$cdom.'_'.$cnum})) {
         return 1;
     }
@@ -5295,7 +5714,7 @@ sub save_selected_files {
     my ($user, $path, @files) = @_;
     my $filename = $user."savedfiles";
     my @other_files = &files_not_in_path($user, $path);
-    open (OUT, '>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+    open (OUT, '>'.$tmpdir.$filename);
     foreach my $file (@files) {
         print (OUT $env{'form.currentpath'}.$file."\n");
     }
@@ -5887,6 +6306,13 @@ sub devalidatecourseresdata {
 
 
 # --------------------------------------------------- Course Resourcedata Query
+#
+#  Parameters:
+#      $coursenum    - Number of the course.
+#      $coursedomain - Domain at which the course was created.
+#  Returns:
+#     A hash of the course parameters along (I think) with timestamps
+#     and version info.
 
 sub get_courseresdata {
     my ($coursenum,$coursedomain)=@_;
@@ -5945,7 +6371,21 @@ sub get_userresdata {
     }
     return $tmp;
 }
-
+#----------------------------------------------- resdata - return resource data
+#  Purpose:
+#    Return resource data for either users or for a course.
+#  Parameters:
+#     $name      - Course/user name.
+#     $domain    - Name of the domain the user/course is registered on.
+#     $type      - Type of thing $name is (must be 'course' or 'user'
+#     @which     - Array of names of resources desired.
+#  Returns:
+#     The value of the first reasource in @which that is found in the
+#     resource hash.
+#  Exceptional Conditions:
+#     If the $type passed in is not valid (not the string 'course' or 
+#     'user', an undefined  reference is returned.
+#     If none of the resources are found, an undef is returned
 sub resdata {
     my ($name,$domain,$type,@which)=@_;
     my $result;
@@ -5956,8 +6396,8 @@ sub resdata {
     }
     if (!ref($result)) { return $result; }    
     foreach my $item (@which) {
-	if (defined($result->{$item})) {
-	    return $result->{$item};
+	if (defined($result->{$item->[0]})) {
+	    return [$result->{$item->[0]},$item->[1]];
 	}
     }
     return undef;
@@ -6123,6 +6563,12 @@ sub EXT {
 	    my ($map) = &decode_symb($symbparm);
 	    return &symbread($map);
 	}
+	if ($space eq 'filename') {
+	    if ($symbparm) {
+		return &clutter((&decode_symb($symbparm))[2]);
+	    }
+	    return &hreflocation('',$env{'request.filename'});
+	}
 
 	my ($section, $group, @groups);
 	my ($courselevelm,$courselevel);
@@ -6163,24 +6609,27 @@ sub EXT {
 # ----------------------------------------------------------- first, check user
 
 	    my $userreply=&resdata($uname,$udom,'user',
-				       ($courselevelr,$courselevelm,
-					$courselevel));
-	    if (defined($userreply)) { return $userreply; }
+				       ([$courselevelr,'resource'],
+					[$courselevelm,'map'     ],
+					[$courselevel, 'course'  ]));
+	    if (defined($userreply)) { return &get_reply($userreply); }
 
 # ------------------------------------------------ second, check some of course
             my $coursereply;
             if (@groups > 0) {
                 $coursereply = &check_group_parms($courseid,\@groups,$symbparm,
                                        $mapparm,$spacequalifierrest);
-                if (defined($coursereply)) { return $coursereply; }
+                if (defined($coursereply)) { return &get_reply($coursereply); }
             }
 
 	    $coursereply=&resdata($env{'course.'.$courseid.'.num'},
-				     $env{'course.'.$courseid.'.domain'},
-				     'course',
-				     ($seclevelr,$seclevelm,$seclevel,
-				      $courselevelr));
-	    if (defined($coursereply)) { return $coursereply; }
+				  $env{'course.'.$courseid.'.domain'},
+				  'course',
+				  ([$seclevelr,   'resource'],
+				   [$seclevelm,   'map'     ],
+				   [$seclevel,    'course'  ],
+				   [$courselevelr,'resource']));
+	    if (defined($coursereply)) { return &get_reply($coursereply); }
 
 # ------------------------------------------------------ third, check map parms
 	    my %parmhash=();
@@ -6191,7 +6640,7 @@ sub EXT {
 		$thisparm=$parmhash{$symbparm};
 		untie(%parmhash);
 	    }
-	    if ($thisparm) { return $thisparm; }
+	    if ($thisparm) { return &get_reply([$thisparm,'resource']); }
 	}
 # ------------------------------------------ fourth, look in resource metadata
 
@@ -6204,18 +6653,19 @@ sub EXT {
 	    $filename=$env{'request.filename'};
 	}
 	my $metadata=&metadata($filename,$spacequalifierrest);
-	if (defined($metadata)) { return $metadata; }
+	if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
 	$metadata=&metadata($filename,'parameter_'.$spacequalifierrest);
-	if (defined($metadata)) { return $metadata; }
+	if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
 
-# ---------------------------------------------- fourth, look in rest pf course
+# ---------------------------------------------- fourth, look in rest of course
 	if ($symbparm && defined($courseid) && 
 	    $courseid eq $env{'request.course.id'}) {
 	    my $coursereply=&resdata($env{'course.'.$courseid.'.num'},
 				     $env{'course.'.$courseid.'.domain'},
 				     'course',
-				     ($courselevelm,$courselevel));
-	    if (defined($coursereply)) { return $coursereply; }
+				     ([$courselevelm,'map'   ],
+				      [$courselevel, 'course']));
+	    if (defined($coursereply)) { return &get_reply($coursereply); }
 	}
 # ------------------------------------------------------------------ Cascade up
 	unless ($space eq '0') {
@@ -6223,14 +6673,13 @@ sub EXT {
 	    my $id=pop(@parts);
 	    my $part=join('_',@parts);
 	    if ($part eq '') { $part='0'; }
-	    my $partgeneral=&EXT('resource.'.$part.'.'.$qualifierrest,
+	    my @partgeneral=&EXT('resource.'.$part.'.'.$qualifierrest,
 				 $symbparm,$udom,$uname,$section,1);
-	    if (defined($partgeneral)) { return $partgeneral; }
+	    if (@partgeneral) { return &get_reply(\@partgeneral); }
 	}
 	if ($recurse) { return undef; }
 	my $pack_def=&packages_tab_default($filename,$varname);
-	if (defined($pack_def)) { return $pack_def; }
-
+	if (defined($pack_def)) { return &get_reply([$pack_def,'resource']); }
 # ---------------------------------------------------- Any other user namespace
     } elsif ($realm eq 'environment') {
 # ----------------------------------------------------------------- environment
@@ -6258,15 +6707,23 @@ sub EXT {
     return '';
 }
 
+sub get_reply {
+    my ($reply_value) = @_;
+    if (wantarray) {
+	return @$reply_value;
+    }
+    return $reply_value->[0];
+}
+
 sub check_group_parms {
     my ($courseid,$groups,$symbparm,$mapparm,$what) = @_;
     my @groupitems = ();
     my $resultitem;
-    my @levels = ($symbparm,$mapparm,$what);
+    my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$what,'course']);
     foreach my $group (@{$groups}) {
         foreach my $level (@levels) {
-             my $item = $courseid.'.['.$group.'].'.$level;
-             push(@groupitems,$item);
+             my $item = $courseid.'.['.$group.'].'.$level->[0];
+             push(@groupitems,[$item,$level->[1]]);
         }
     }
     my $coursereply = &resdata($env{'course.'.$courseid.'.num'},
@@ -6292,7 +6749,7 @@ sub packages_tab_default {
 	    $do_default=1;
 	} elsif ($pack_type eq 'extension') {
 	    push(@extension,[$package,$pack_type,$pack_part]);
-	} elsif ($pack_part eq $part) {
+	} elsif ($pack_part eq $part || $pack_type eq 'part') {
 	    # only look at packages defaults for packages that this id is
 	    push(@specifics,[$package,$pack_type,$pack_part]);
 	}
@@ -6359,8 +6816,11 @@ sub metadata {
     if (($uri eq '') || 
 	(($uri =~ m|^/*adm/|) && 
 	     ($uri !~ m|^adm/includes|) && ($uri !~ m|/bulletinboard$|)) ||
-        ($uri =~ m|/$|) || ($uri =~ m|/.meta$|) || ($uri =~ /^~/) ||
-	($uri =~ m|home/$match_username/public_html/|)) {
+        ($uri =~ m|/$|) || ($uri =~ m|/.meta$|) ) {
+	return undef;
+    }
+    if (($uri =~ /^~/ || $uri =~ m{home/$match_username/public_html/}) 
+	&& &Apache::lonxml::get_state('target') =~ /^(|meta)$/) {
 	return undef;
     }
     my $filename=$uri;
@@ -6381,6 +6841,7 @@ sub metadata {
 #	if (! exists($metacache{$uri})) {
 #	    $metacache{$uri}={};
 #	}
+	my $cachetime = 60*60;
         if ($liburi) {
 	    $liburi=&declutter($liburi);
             $filename=$liburi;
@@ -6391,7 +6852,13 @@ sub metadata {
         my %metathesekeys=();
         unless ($filename=~/\.meta$/) { $filename.='.meta'; }
 	my $metastring;
-	if ($uri !~ m -^(editupload)/-) {
+	if ($uri =~ /^~/ || $uri =~ m{home/$match_username/public_html/}) {
+	    my $which = &hreflocation('','/'.($liburi || $uri));
+	    $metastring = 
+		&Apache::lonnet::ssi_body($which,
+					  ('grade_target' => 'meta'));
+	    $cachetime = 1; # only want this cached in the child not long term
+	} elsif ($uri !~ m -^(editupload)/-) {
 	    my $file=&filelocation('',&clutter($filename));
 	    #push(@{$metaentry{$uri.'.file'}},$file);
 	    $metastring=&getfile($file);
@@ -6496,10 +6963,11 @@ sub metadata {
 		 # only ws inside the tag, and not in default, so use default
 		 # as value
 			    $metaentry{':'.$unikey}=$default;
-			} else {
-		  # either something interesting inside the tag or default
-                  # uninteresting
+			} elsif ( $internaltext =~ /\S/ ) {
+		  # something interesting inside the tag
 			    $metaentry{':'.$unikey}=$internaltext;
+			} else {
+		  # no interesting values, don't set a default
 			}
 # end of not-a-package not-a-library import
 		    }
@@ -6509,13 +6977,18 @@ sub metadata {
 	    }
 	}
 	my ($extension) = ($uri =~ /\.(\w+)$/);
+	$extension = lc($extension);
+	if ($extension eq 'htm') { $extension='html'; }
+
 	foreach my $key (keys(%packagetab)) {
 	    #no specific packages #how's our extension
 	    if ($key!~/^extension_\Q$extension\E&/) { next; }
 	    &metadata_create_package_def($uri,$key,'extension_'.$extension,
 					 \%metathesekeys);
 	}
-	if (!exists($metaentry{':packages'})) {
+
+	if (!exists($metaentry{':packages'})
+	    || $packagetab{"import_defaults&extension_$extension"}) {
 	    foreach my $key (keys(%packagetab)) {
 		#no specific packages well let's get default then
 		if ($key!~/^default&/) { next; }
@@ -6552,7 +7025,7 @@ sub metadata {
 	$metaentry{':keys'} = join(',',keys(%metathesekeys));
 	&metadata_generate_part0(\%metathesekeys,\%metaentry,$uri);
 	$metaentry{':allpossiblekeys'}=join(',',keys %metathesekeys);
-	&do_cache_new('meta',$uri,\%metaentry,60*60);
+	&do_cache_new('meta',$uri,\%metaentry,$cachetime);
 # this is the end of "was not already recently cached
     }
     return $metaentry{':'.$what};
@@ -6634,12 +7107,15 @@ sub gettitle {
 	}
 	my ($map,$resid,$url)=&decode_symb($symb);
 	my $title='';
-	my %bighash;
-	if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db',
-		&GDBM_READER(),0640)) {
-	    my $mapid=$bighash{'map_pc_'.&clutter($map)};
-	    $title=$bighash{'title_'.$mapid.'.'.$resid};
-	    untie %bighash;
+	if (!$map && $resid == 0 && $url =~/default\.sequence$/) {
+	    $title = $env{'course.'.$env{'request.course.id'}.'.description'};
+	} else {
+	    if (tie(my %bighash,'GDBM_File',$env{'request.course.fn'}.'.db',
+		    &GDBM_READER(),0640)) {
+		my $mapid=$bighash{'map_pc_'.&clutter($map)};
+		$title=$bighash{'title_'.$mapid.'.'.$resid};
+		untie(%bighash);
+	    }
 	}
 	$title=~s/\&colon\;/\:/gs;
 	if ($title) {
@@ -7012,7 +7488,7 @@ sub getCODE {
 sub rndseed {
     my ($symb,$courseid,$domain,$username)=@_;
     my ($wsymb,$wcourseid,$wdomain,$wusername)=&whichuser();
-    if (!$symb) {
+    if (!defined($symb)) {
 	unless ($symb=$wsymb) { return time; }
     }
     if (!$courseid) { $courseid=$wcourseid; }
@@ -7464,6 +7940,7 @@ sub filelocation {
 	$file=~s-^/adm/wrapper/-/-;
 	$file=~s-^/adm/coursedocs/showdoc/-/-;
     }
+
     if ($file=~m:^/~:) { # is a contruction space reference
         $location = $file;
         $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:;
@@ -7484,6 +7961,8 @@ sub filelocation {
   	  $location=$Apache::lonnet::perlvar{'lonDocRoot'}.'/userfiles/'.
   	      $udom.'/'.$uname.'/'.$filename;
         }
+    } elsif ($file =~ m-^/adm/-) {
+	$location = $perlvar{'lonDocRoot'}.'/'.$file;
     } else {
         $file=~s/^\Q$perlvar{'lonDocRoot'}\E//;
         $file=~s:^/res/:/:;
@@ -7494,7 +7973,13 @@ sub filelocation {
         }
     }
     $location=~s://+:/:g; # remove duplicate /
-    while ($location=~m:/\.\./:) {$location=~ s:/[^/]+/\.\./:/:g;} #remove dir/..
+    while ($location=~m{/\.\./}) {
+	if ($location =~ m{/[^/]+/\.\./}) {
+	    $location=~ s{/[^/]+/\.\./}{/}g;
+	} else {
+	    $location=~ s{/\.\./}{/}g;
+	}
+    } #remove dir/..
     while ($location=~m:/\./:) {$location=~ s:/\./:/:g;} #remove /./
     return $location;
 }
@@ -7515,6 +8000,9 @@ sub hreflocation {
 	$file=~s-^/home/httpd/lonUsers/($match_domain)/./././($match_name)/userfiles/
 	    -/uploaded/$1/$2/-x;
     }
+    if ($file=~ m{^/userfiles/}) {
+	$file =~ s{^/userfiles/}{/uploaded/};
+    }
     return $file;
 }
 
@@ -7543,14 +8031,11 @@ sub machine_ids {
     my ($hostname) = @_;
     $hostname ||= &hostname($perlvar{'lonHostID'});
     my @ids;
-    my %hostname = &all_hostnames();
-    while( my($id, $name) = each(%hostname)) {
-#	&logthis("-$id-$name-$hostname-");
-	if ($hostname eq $name) {
-	    push(@ids,$id);
-	}
+    my %name_to_host = &all_names();
+    if (ref($name_to_host{$hostname}) eq 'ARRAY') {
+	return @{ $name_to_host{$hostname} };
     }
-    return @ids;
+    return;
 }
 
 sub additional_machine_domains {
@@ -7594,7 +8079,8 @@ sub declutter {
 
 sub clutter {
     my $thisfn='/'.&declutter(shift);
-    unless ($thisfn=~/^\/(uploaded|editupload|adm|userfiles|ext|raw|priv|public)\//) { 
+    if ($thisfn !~ m{^/(uploaded|editupload|adm|userfiles|ext|raw|priv|public)/}
+	|| $thisfn =~ m{^/adm/(includes|pages)} ) { 
        $thisfn='/res'.$thisfn; 
     }
     if ($thisfn !~m|/adm|) {
@@ -7663,18 +8149,18 @@ sub correct_line_ends {
 sub goodbye {
    &logthis("Starting Shut down");
 #not converted to using infrastruture and probably shouldn't be
-   &logthis(sprintf("%-20s is %s",'%badServerCache',length(&freeze(\%badServerCache))));
+   &logthis(sprintf("%-20s is %s",'%badServerCache',length(&nfreeze(\%badServerCache))));
 #converted
 #   &logthis(sprintf("%-20s is %s",'%metacache',scalar(%metacache)));
-   &logthis(sprintf("%-20s is %s",'%homecache',length(&freeze(\%homecache))));
-#   &logthis(sprintf("%-20s is %s",'%titlecache',length(&freeze(\%titlecache))));
-#   &logthis(sprintf("%-20s is %s",'%courseresdatacache',length(&freeze(\%courseresdatacache))));
+   &logthis(sprintf("%-20s is %s",'%homecache',length(&nfreeze(\%homecache))));
+#   &logthis(sprintf("%-20s is %s",'%titlecache',length(&nfreeze(\%titlecache))));
+#   &logthis(sprintf("%-20s is %s",'%courseresdatacache',length(&nfreeze(\%courseresdatacache))));
 #1.1 only
-#   &logthis(sprintf("%-20s is %s",'%userresdatacache',length(&freeze(\%userresdatacache))));
-#   &logthis(sprintf("%-20s is %s",'%getsectioncache',length(&freeze(\%getsectioncache))));
-#   &logthis(sprintf("%-20s is %s",'%courseresversioncache',length(&freeze(\%courseresversioncache))));
-#   &logthis(sprintf("%-20s is %s",'%resversioncache',length(&freeze(\%resversioncache))));
-   &logthis(sprintf("%-20s is %s",'%remembered',length(&freeze(\%remembered))));
+#   &logthis(sprintf("%-20s is %s",'%userresdatacache',length(&nfreeze(\%userresdatacache))));
+#   &logthis(sprintf("%-20s is %s",'%getsectioncache',length(&nfreeze(\%getsectioncache))));
+#   &logthis(sprintf("%-20s is %s",'%courseresversioncache',length(&nfreeze(\%courseresversioncache))));
+#   &logthis(sprintf("%-20s is %s",'%resversioncache',length(&nfreeze(\%resversioncache))));
+   &logthis(sprintf("%-20s is %s",'%remembered',length(&nfreeze(\%remembered))));
    &logthis(sprintf("%-20s is %s",'kicks',$kicks));
    &logthis(sprintf("%-20s is %s",'hits',$hits));
    &flushcourselogs();
@@ -7682,19 +8168,41 @@ sub goodbye {
 }
 
 sub get_dns {
-    my ($url,$func) = @_;
+    my ($url,$func,$ignore_cache) = @_;
+    if (!$ignore_cache) {
+	my ($content,$cached)=
+	    &Apache::lonnet::is_cached_new('dns',$url);
+	if ($cached) {
+	    &$func($content);
+	    return;
+	}
+    }
+
+    my %alldns;
     open(my $config,"<$perlvar{'lonTabDir'}/hosts.tab");
     foreach my $dns (<$config>) {
 	next if ($dns !~ /^\^(\S*)/x);
-	$dns = $1;
+	$alldns{$1} = 1;
+    }
+    while (%alldns) {
+	my ($dns) = keys(%alldns);
+	delete($alldns{$dns});
 	my $ua=new LWP::UserAgent;
 	my $request=new HTTP::Request('GET',"http://$dns$url");
 	my $response=$ua->request($request);
 	next if ($response->is_error());
 	my @content = split("\n",$response->content);
+	&Apache::lonnet::do_cache_new('dns',$url,\@content,30*24*60*60);
 	&$func(\@content);
+	return;
     }
     close($config);
+    my $which = (split('/',$url))[3];
+    &logthis("unable to contact DNS defaulting to on disk file dns_$which.tab\n");
+    open($config,"<$perlvar{'lonTabDir'}/dns_$which.tab");
+    my @content = <$config>;
+    &$func(\@content);
+    return;
 }
 # ------------------------------------------------------------ Read domain file
 {
@@ -7724,7 +8232,8 @@ sub get_dns {
     }
 
     sub load_domain_tab {
-	&get_dns('/adm/dns/domain',\&parse_domain_tab);
+	my ($ignore_cache) = @_;
+	&get_dns('/adm/dns/domain',\&parse_domain_tab,$ignore_cache);
 	my $fh;
 	if (open($fh,"<".$perlvar{'lonTabDir'}.'/domain.tab')) {
 	    my @lines = <$fh>;
@@ -7754,6 +8263,7 @@ sub get_dns {
     my %hostdom;
     my %libserv;
     my $loaded;
+    my %name_to_host;
 
     sub parse_hosts_tab {
 	my ($file) = @_;
@@ -7765,6 +8275,7 @@ sub get_dns {
 	    $name=~s/\s//g;
 	    if ($id && $domain && $role && $name) {
 		$hostname{$id}=$name;
+		push(@{$name_to_host{$name}}, $id);
 		$hostdom{$id}=$domain;
 		if ($role eq 'library') { $libserv{$id}=$name; }
 	    }
@@ -7772,8 +8283,10 @@ sub get_dns {
     }
     
     sub reset_hosts_info {
+	&purge_remembered();
 	&reset_domain_info();
 	&reset_hosts_ip_info();
+	undef(%name_to_host);
 	undef(%hostname);
 	undef(%hostdom);
 	undef(%libserv);
@@ -7781,7 +8294,8 @@ sub get_dns {
     }
 
     sub load_hosts_tab {
-	&get_dns('/adm/dns/hosts',\&parse_hosts_tab);
+	my ($ignore_cache) = @_;
+	&get_dns('/adm/dns/hosts',\&parse_hosts_tab,$ignore_cache);
 	open(my $config,"<$perlvar{'lonTabDir'}/hosts.tab");
 	my @config = <$config>;
 	&parse_hosts_tab(\@config);
@@ -7802,6 +8316,12 @@ sub get_dns {
 	return %hostname;
     }
 
+    sub all_names {
+	&load_hosts_tab() if (!$loaded);
+
+	return %name_to_host;
+    }
+
     sub is_library {
 	&load_hosts_tab() if (!$loaded);
 
@@ -7857,6 +8377,7 @@ sub get_dns {
     my %iphost;
     my %name_to_ip;
     my %lonid_to_ip;
+
     sub get_hosts_from_ip {
 	my ($ip) = @_;
 	my %iphosts = &get_iphost();
@@ -7887,25 +8408,59 @@ sub get_dns {
     }
     
     sub get_iphost {
-	if (%iphost) { return %iphost; }
-	my %hostname = &all_hostnames();
-	foreach my $id (keys(%hostname)) {
-	    my $name=&hostname($id);
+	my ($ignore_cache) = @_;
+
+	if (!$ignore_cache) {
+	    if (%iphost) {
+		return %iphost;
+	    }
+	    my ($ip_info,$cached)=
+		&Apache::lonnet::is_cached_new('iphost','iphost');
+	    if ($cached) {
+		%iphost      = %{$ip_info->[0]};
+		%name_to_ip  = %{$ip_info->[1]};
+		%lonid_to_ip = %{$ip_info->[2]};
+		return %iphost;
+	    }
+	}
+
+	# get yesterday's info for fallback
+	my %old_name_to_ip;
+	my ($ip_info,$cached)=
+	    &Apache::lonnet::is_cached_new('iphost','iphost');
+	if ($cached) {
+	    %old_name_to_ip = %{$ip_info->[1]};
+	}
+
+	my %name_to_host = &all_names();
+	foreach my $name (keys(%name_to_host)) {
 	    my $ip;
 	    if (!exists($name_to_ip{$name})) {
 		$ip = gethostbyname($name);
 		if (!$ip || length($ip) ne 4) {
-		    &logthis("Skipping host $id name $name no IP found");
-		    next;
+		    if (defined($old_name_to_ip{$name})) {
+			$ip = $old_name_to_ip{$name};
+			&logthis("Can't find $name defaulting to old $ip");
+		    } else {
+			&logthis("Name $name no IP found");
+			next;
+		    }
+		} else {
+		    $ip=inet_ntoa($ip);
 		}
-		$ip=inet_ntoa($ip);
 		$name_to_ip{$name} = $ip;
 	    } else {
 		$ip = $name_to_ip{$name};
 	    }
-	    $lonid_to_ip{$id} = $ip;
-	    push(@{$iphost{$ip}},$id);
+	    foreach my $id (@{ $name_to_host{$name} }) {
+		$lonid_to_ip{$id} = $ip;
+	    }
+	    push(@{$iphost{$ip}},@{$name_to_host{$name}});
 	}
+	&Apache::lonnet::do_cache_new('iphost','iphost',
+				      [\%iphost,\%name_to_ip,\%lonid_to_ip],
+				      48*60*60);
+
 	return %iphost;
     }
 }
@@ -8293,7 +8848,7 @@ explanation of a user role term
 get_my_roles($uname,$udom,$context,$types,$roles,$roledoms) :
 All arguments are optional. Returns a hash of a roles, either for
 co-author/assistant author roles for a user's Construction Space
-(default), or if $context is 'user', roles for the user himself,
+(default), or if $context is 'userroles', roles for the user himself,
 In the hash, keys are set to colon-sparated $uname,$udom,and $role,
 and value is set to colon-separated start and end times for the role.
 If no username and domain are specified, will default to current
@@ -8445,6 +9000,14 @@ setting for a specific $type, where $typ
 @what should be a list of parameters to ask about. This routine caches
 answers for 5 minutes.
 
+=item *
+
+get_courseresdata($courseid, $domain) : dump the entire course resource
+data base, returning a hash that is keyed by the resource name and has
+values that are the resource value.  I believe that the timestamps and
+versions are also returned.
+
+
 =back
 
 =head2 Course Modification
@@ -9127,3 +9690,4 @@ symblist($mapname,%newhash) : update sym
 =back
 
 =cut
+