--- loncom/lonnet/perl/lonnet.pm	2001/12/04 15:19:11	1.180
+++ loncom/lonnet/perl/lonnet.pm	2001/12/27 17:00:30	1.194
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.180 2001/12/04 15:19:11 albertel Exp $
+# $Id: lonnet.pm,v 1.194 2001/12/27 17:00:30 www Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -60,108 +60,14 @@
 # 10/2 Gerd Kortemeyer
 # 10/5,10/10,11/13,11/15 Scott Harrison
 # 11/17,11/20,11/22,11/29 Gerd Kortemeyer
-#
-# $Id: lonnet.pm,v 1.180 2001/12/04 15:19:11 albertel Exp $
+# 12/5 Matthew Hall
+# 12/5 Guy Albertelli
+# 12/6,12/7,12/12 Gerd Kortemeyer
+# 12/18 Scott Harrison
+# 12/21,12/22,12/27 Gerd Kortemeyer
 #
 ###
 
-# Functions for use by content handlers:
-#
-# metadata_query(sql-query-string,custom-metadata-regex) : 
-#                                    returns file handle of where sql and
-#                                    regex results will be stored for query
-# plaintext(short)   : plain text explanation of short term
-# fileembstyle(ext)  : embed style in page for file extension
-# filedescription(ext) : descriptor text for file extension
-# allowed(short,url) : returns codes for allowed actions 
-#                      F: full access
-#                      U,I,K: authentication modes (cxx only)
-#                      '': forbidden
-#                      1: user needs to choose course
-#                      2: browse allowed
-# definerole(rolename,sys,dom,cou) : define a custom role rolename
-#                      set privileges in format of lonTabs/roles.tab for
-#                      system, domain and course level, 
-# assignrole(udom,uname,url,role,end,start) : give a role to a user for the
-#                      level given by url. Optional start and end dates
-#                      (leave empty string or zero for "no date") 
-# assigncustomrole (udom,uname,url,rdom,rnam,rolename,end,start) : give a
-#                      custom role to a user for the level given by url.
-#                      Specify name and domain of role author, and role name
-# revokerole (udom,uname,url,role) : Revoke a role for url
-# revokecustomrole (udom,uname,url,rdom,rnam,rolename) : Revoke a custom role
-# appenv(hash)       : adds hash to session environment
-# delenv(varname)    : deletes all environment entries starting with varname
-# store(hashref,symb,courseid,udom,uname)
-#                    : stores hash permanently for this url
-#                      hashref needs to be given, and should be a \%hashname
-#                      the remaining args aren't required and if they aren't
-#                      passed or are '' they will be derived from the ENV
-# cstore(hashref,symb,courseid,udom,uname)
-#                    : same as store but uses the critical interface to 
-#                      guarentee a store
-# restore(symb,courseid,udom,uname)
-#                    : returns hash for this symb, all args are optional
-#                      if they aren't given they will be derived from the 
-#                      current enviroment
-#
-#
-# for the next 6 functions udom and uname are optional
-#         if supplied they use udom as the domain and uname
-#         as the username for the function (supply a courseid
-#         for the uname if you want a course database)
-#         if not supplied it uses %ENV and looks at 
-#         user. attribute for the values
-#
-# eget(namesp,arrayref,udom,uname)
-#                    : returns hash with keys from array  reference filled
-#                      in from namesp (encrypts the return communication)
-# get(namesp,arrayref,udom,uname)
-#                    : returns hash with keys from array  reference filled
-#                      in from namesp
-# dump(namesp,udom,uname) : dumps the complete namespace into a hash
-# del(namesp,array,udom,uname)  : deletes keys out of array from namesp
-# put(namesp,hash,udom,uname)   : stores hash in namesp
-# cput(namesp,hash,udom,uname)  : critical put
-#
-#
-# ssi(url,hash)      : does a complete request cycle on url to localhost, posts
-#                      hash
-# coursedescription(id) : returns and caches course description for id
-# repcopy(filename)  : replicate file
-# dirlist(url)       : gets a directory listing
-# directcondval(index) : reading condition value of single condition from 
-#                        state string
-# condval(index)     : value of condition index based on state
-# EXT(name)          : value of a variable
-# symblist(map,hash) : Updates symbolic storage links
-# symbread([filename]) : returns the data handle (filename optional)
-# rndseed([symb,courseid,domain,uname])
-#                    : returns a random seed, all arguments are optional,
-#                      if they aren't sent it use the environment to derive
-#                      them
-#                      Note: if symb isn't sent and it can't get one from
-#                      &symbread it will use the current time as it's return
-# receipt()          : returns a receipt to be given out to users 
-# getfile(filename)  : returns the contents of filename, or a -1 if it can't
-#                      be found, replicates and subscribes to the file
-# filelocation(dir,file) : returns a fairly clean absolute reference to file 
-#                          from the directory dir
-# hreflocation(dir,file) : same as filelocation, but for hrefs
-# log(domain,user,home,msg) : write to permanent log for user
-# usection(domain,user,courseid) : output of section name/number or '' for
-#                                  "not in course" and '-1' for "no section"
-# userenvironment(domain,user,what) : puts out any environment parameter 
-#                                     for a user
-# idput(domain,hash) : writes IDs for users from hash (name=>id,name=>id)
-# idget(domain,array): returns hash with usernames (id=>name,id=>name) for
-#                      an array of IDs
-# idrget(domain,array): returns hash with IDs for usernames (name=>id,...) for
-#                       an array of names
-# metadata(file,entry): returns the metadata entry for a file. entry='keys'
-#                       returns a comma separated list of keys
-#
-
 package Apache::lonnet;
 
 use strict;
@@ -169,7 +75,10 @@ use Apache::File;
 use LWP::UserAgent();
 use HTTP::Headers;
 use vars 
-qw(%perlvar %hostname %homecache %hostip %spareid %hostdom %libserv %pr %prp %fe %fd $readit %metacache %packagetab %courselogs);
+qw(%perlvar %hostname %homecache %hostip %spareid %hostdom 
+   %libserv %pr %prp %metacache %packagetab 
+   %courselogs %accesshash $processmarker $dumpcount 
+   %coursedombuf %coursehombuf);
 use IO::Socket;
 use GDBM_File;
 use Apache::Constants qw(:common :http);
@@ -321,7 +230,7 @@ sub critical {
 
 sub appenv {
     my %newenv=@_;
-    map {
+    foreach (keys %newenv) {
 	if (($newenv{$_}=~/^user\.role/) || ($newenv{$_}=~/^user\.priv/)) {
             &logthis("<font color=blue>WARNING: ".
                 "Attempt to modify environment ".$_." to ".$newenv{$_}
@@ -330,7 +239,7 @@ sub appenv {
         } else {
             $ENV{$_}=$newenv{$_};
         }
-    } keys %newenv;
+    }
 
     my $lockfh;
     unless ($lockfh=Apache::File->new("$ENV{'user.environment'}")) {
@@ -412,9 +321,9 @@ sub delenv {
          $fh->close();
          return 'error: '.$!;
      }
-     map {
+     foreach (@oldenv) {
 	 unless ($_=~/^$delthis/) { print $fh $_; }
-     } @oldenv;
+     }
      $fh->close();
     }
     return 'ok';
@@ -567,9 +476,9 @@ sub idget {
 sub idrget {
     my ($udom,@unames)=@_;
     my %returnhash=();
-    map {
+    foreach (@unames) {
         $returnhash{$_}=(&userenvironment($udom,$_,'id'))[1];
-    } @unames;
+    }
     return %returnhash;
 }
 
@@ -578,7 +487,7 @@ sub idrget {
 sub idput {
     my ($udom,%ids)=@_;
     my %servers=();
-    map {
+    foreach (keys %ids) {
         my $uhom=&homeserver($_,$udom);
         if ($uhom ne 'no_host') {
             my $id=&escape($ids{$_});
@@ -591,10 +500,10 @@ sub idput {
             }
             &critical('put:'.$udom.':'.$unam.':environment:id='.$id,$uhom);
         }
-    } keys %ids;
-    map {
+    }
+    foreach (keys %servers) {
         &critical('idput:'.$udom.':'.$servers{$_},$_);
-    } keys %servers;
+    }
 }
 
 # ------------------------------------- Find the section of student in a course
@@ -603,7 +512,8 @@ sub usection {
     my ($udom,$unam,$courseid)=@_;
     $courseid=~s/\_/\//g;
     $courseid=~s/^(\w)/\/$1/;
-    map {
+    foreach (split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles',
+                        &homeserver($unam,$udom)))) {
         my ($key,$value)=split(/\=/,$_);
         $key=&unescape($key);
         if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) {
@@ -620,8 +530,7 @@ sub usection {
             } 
             unless ($notactive) { return $section; }
         }
-    } split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles',
-                        &homeserver($unam,$udom)));
+    }
     return '-1';
 }
 
@@ -750,12 +659,11 @@ sub log {
 
 sub flushcourselogs {
     &logthis('Flushing course log buffers');
-    map {
+    foreach (keys %courselogs) {
         my $crsid=$_;
-        if (&reply('log:'.$ENV{'course.'.$crsid.'.domain'}.':'.
-		          $ENV{'course.'.$crsid.'.num'}.':'.
-		           &escape($courselogs{$crsid}),
-		          $ENV{'course.'.$crsid.'.home'}) eq 'ok') {
+        if (&reply('log:'.$coursedombuf{$crsid}.':'.
+		          &escape($courselogs{$crsid}),
+		          $coursehombuf{$crsid}) eq 'ok') {
 	    delete $courselogs{$crsid};
         } else {
             &logthis('Failed to flush log buffer for '.$crsid);
@@ -765,13 +673,28 @@ sub flushcourselogs {
                delete $courselogs{$crsid};
             }
         }        
-    } keys %courselogs;
+    }
+    &logthis('Flushing access logs');
+    foreach (keys %accesshash) {
+        my $entry=$_;
+        $entry=~/\_\_\_(\w+)\/(\w+)\/(.*)\_\_\_(\w+)$/;
+        my %temphash=($entry => $accesshash{$entry});
+        if (&Apache::lonnet::put('resevaldata',\%temphash,$1,$2) eq 'ok') {
+	    delete $accesshash{$entry};
+        }
+    }
+    $dumpcount++;
 }
 
 sub courselog {
     my $what=shift;
     $what=time.':'.$what;
     unless ($ENV{'request.course.id'}) { return ''; }
+    $coursedombuf{$ENV{'request.course.id'}}=
+       $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'.
+       $ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+    $coursehombuf{$ENV{'request.course.id'}}=
+       $ENV{'course.'.$ENV{'request.course.id'}.'.home'};
     if (defined $courselogs{$ENV{'request.course.id'}}) {
 	$courselogs{$ENV{'request.course.id'}}.='&'.$what;
     } else {
@@ -786,16 +709,29 @@ sub courseacclog {
     my $fnsymb=shift;
     unless ($ENV{'request.course.id'}) { return ''; }
     my $what=$fnsymb.':'.$ENV{'user.name'}.':'.$ENV{'user.domain'};
-    if ($what=~/(problem|exam|quiz|assess|survey|form)$/) {
-	map {
+    if ($fnsymb=~/(problem|exam|quiz|assess|survey|form)$/) {
+        $what.=':POST';
+	foreach (keys %ENV) {
             if ($_=~/^form\.(.*)/) {
 		$what.=':'.$1.'='.$ENV{$_};
             }
-        } keys %ENV;
+        }
     }
     &courselog($what);
 }
 
+sub countacc {
+    my $url=&declutter(shift);
+    unless ($ENV{'request.course.id'}) { return ''; }
+    $accesshash{$ENV{'request.course.id'}.'___'.$url.'___course'}=1;
+    my $key=$processmarker.'_'.$dumpcount.'___'.$url.'___count';
+    if (defined($accesshash{$key})) {
+	$accesshash{$key}++;
+    } else {
+        $accesshash{$key}=1;
+    }
+}
+    
 # ----------------------------------------------------------- Check out an item
 
 sub checkout {
@@ -925,7 +861,7 @@ sub devalidate {
 sub hash2str {
   my (%hash)=@_;
   my $result='';
-  map { $result.=escape($_).'='.escape($hash{$_}).'&'; } keys %hash;
+  foreach (keys %hash) { $result.=escape($_).'='.escape($hash{$_}).'&'; }
   $result=~s/\&$//;
   return $result;
 }
@@ -933,10 +869,10 @@ sub hash2str {
 sub str2hash {
   my ($string) = @_;
   my %returnhash;
-  map {
+  foreach (split(/\&/,$string)) {
     my ($name,$value)=split(/\=/,$_);
     $returnhash{&unescape($name)}=&unescape($value);
-  } split(/\&/,$string);
+  }
   return %returnhash;
 }
 
@@ -1077,15 +1013,20 @@ sub store {
     &devalidate($symb);
 
     $symb=escape($symb);
-    if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { return ''; } }
+    if (!$namespace) { 
+       unless ($namespace=$ENV{'request.course.id'}) { 
+          return ''; 
+       } 
+    }
     if (!$domain) { $domain=$ENV{'user.domain'}; }
     if (!$stuname) { $stuname=$ENV{'user.name'}; }
     if (!$home) { $home=$ENV{'user.home'}; }
     my $namevalue='';
-    map {
+    foreach (keys %$storehash) {
         $namevalue.=escape($_).'='.escape($$storehash{$_}).'&';
-    } keys %$storehash;
+    }
     $namevalue=~s/\&$//;
+    &courselog($symb.':'.$stuname.':'.$domain.':STORE:'.$namevalue);
     return reply("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
 }
 
@@ -1102,17 +1043,23 @@ sub cstore {
     &devalidate($symb);
 
     $symb=escape($symb);
-    if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { return ''; } }
+    if (!$namespace) { 
+       unless ($namespace=$ENV{'request.course.id'}) { 
+          return ''; 
+       } 
+    }
     if (!$domain) { $domain=$ENV{'user.domain'}; }
     if (!$stuname) { $stuname=$ENV{'user.name'}; }
     if (!$home) { $home=$ENV{'user.home'}; }
 
     my $namevalue='';
-    map {
+    foreach (keys %$storehash) {
         $namevalue.=escape($_).'='.escape($$storehash{$_}).'&';
-    } keys %$storehash;
+    }
     $namevalue=~s/\&$//;
-    return critical("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
+    &courselog($symb.':'.$stuname.':'.$domain.':CSTORE:'.$namevalue);
+    return critical
+                ("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
 }
 
 # --------------------------------------------------------------------- Restore
@@ -1128,22 +1075,26 @@ sub restore {
     } else {
       $symb=&escape($symb);
     }
-    if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { return ''; } }
+    if (!$namespace) { 
+       unless ($namespace=$ENV{'request.course.id'}) { 
+          return ''; 
+       } 
+    }
     if (!$domain) { $domain=$ENV{'user.domain'}; }
     if (!$stuname) { $stuname=$ENV{'user.name'}; }
     if (!$home) { $home=$ENV{'user.home'}; }
     my $answer=&reply("restore:$domain:$stuname:$namespace:$symb","$home");
 
     my %returnhash=();
-    map {
+    foreach (split(/\&/,$answer)) {
 	my ($name,$value)=split(/\=/,$_);
         $returnhash{&unescape($name)}=&unescape($value);
-    } split(/\&/,$answer);
+    }
     my $version;
     for ($version=1;$version<=$returnhash{'version'};$version++) {
-       map {
+       foreach (split(/\:/,$returnhash{$version.':keys'})) {
           $returnhash{$_}=$returnhash{$version.':'.$_};
-       } split(/\:/,$returnhash{$version.':keys'});
+       }
     }
     return %returnhash;
 }
@@ -1194,7 +1145,7 @@ sub rolesinit {
     my $thesestr;
 
     if ($rolesdump ne '') {
-        map {
+        foreach (split(/&/,$rolesdump)) {
 	  if ($_!~/^rolesdef\&/) {
             my ($area,$role)=split(/=/,$_);
             $area=~s/\_\w\w$//;
@@ -1250,14 +1201,14 @@ sub rolesinit {
 	       }
             }
           } 
-        } split(/&/,$rolesdump);
+        }
         my $adv=0;
         my $author=0;
-        map {
+        foreach (keys %allroles) {
             %thesepriv=();
             if (($_!~/^st/) && ($_!~/^ta/) && ($_!~/^cm/)) { $adv=1; }
             if (($_=~/^au/) || ($_=~/^ca/)) { $author=1; }
-            map {
+            foreach (split(/:/,$allroles{$_})) {
                 if ($_ ne '') {
 		    my ($privilege,$restrictions)=split(/&/,$_);
                     if ($restrictions eq '') {
@@ -1268,11 +1219,11 @@ sub rolesinit {
                         }
                     }
                 }
-            } split(/:/,$allroles{$_});
+            }
             $thesestr='';
-            map { $thesestr.=':'.$_.'&'.$thesepriv{$_}; } keys %thesepriv;
+            foreach (keys %thesepriv) { $thesestr.=':'.$_.'&'.$thesepriv{$_}; }
             $userroles.='user.priv.'.$_.'='.$thesestr."\n";
-        } keys %allroles;            
+        }
         $userroles.='user.adv='.$adv."\n".
 	            'user.author='.$author."\n";
         $ENV{'user.adv'}=$adv;
@@ -1285,9 +1236,9 @@ sub rolesinit {
 sub get {
    my ($namespace,$storearr,$udomain,$uname)=@_;
    my $items='';
-   map {
+   foreach (@$storearr) {
        $items.=escape($_).'&';
-   } @$storearr;
+   }
    $items=~s/\&$//;
    if (!$udomain) { $udomain=$ENV{'user.domain'}; }
    if (!$uname) { $uname=$ENV{'user.name'}; }
@@ -1297,10 +1248,10 @@ sub get {
    my @pairs=split(/\&/,$rep);
    my %returnhash=();
    my $i=0;
-   map {
+   foreach (@$storearr) {
       $returnhash{$_}=unescape($pairs[$i]);
       $i++;
-   } @$storearr;
+   }
    return %returnhash;
 }
 
@@ -1309,9 +1260,9 @@ sub get {
 sub del {
    my ($namespace,$storearr,$udomain,$uname)=@_;
    my $items='';
-   map {
+   foreach (@$storearr) {
        $items.=escape($_).'&';
-   } @$storearr;
+   }
    $items=~s/\&$//;
    if (!$udomain) { $udomain=$ENV{'user.domain'}; }
    if (!$uname) { $uname=$ENV{'user.name'}; }
@@ -1323,17 +1274,22 @@ sub del {
 # -------------------------------------------------------------- dump interface
 
 sub dump {
-   my ($namespace,$udomain,$uname)=@_;
+   my ($namespace,$udomain,$uname,$regexp)=@_;
    if (!$udomain) { $udomain=$ENV{'user.domain'}; }
    if (!$uname) { $uname=$ENV{'user.name'}; }
    my $uhome=&homeserver($uname,$udomain);
-   my $rep=reply("dump:$udomain:$uname:$namespace",$uhome);
+   if ($regexp) {
+       $regexp=&escape($regexp);
+   } else {
+       $regexp='.';
+   }
+   my $rep=reply("dump:$udomain:$uname:$namespace:$regexp",$uhome);
    my @pairs=split(/\&/,$rep);
    my %returnhash=();
-   map {
+   foreach (@pairs) {
       my ($key,$value)=split(/=/,$_);
       $returnhash{unescape($key)}=unescape($value);
-   } @pairs;
+   }
    return %returnhash;
 }
 
@@ -1345,9 +1301,9 @@ sub put {
    if (!$uname) { $uname=$ENV{'user.name'}; }
    my $uhome=&homeserver($uname,$udomain);
    my $items='';
-   map {
+   foreach (keys %$storehash) {
        $items.=&escape($_).'='.&escape($$storehash{$_}).'&';
-   } keys %$storehash;
+   }
    $items=~s/\&$//;
    return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
 }
@@ -1360,9 +1316,9 @@ sub cput {
    if (!$uname) { $uname=$ENV{'user.name'}; }
    my $uhome=&homeserver($uname,$udomain);
    my $items='';
-   map {
+   foreach (keys %$storehash) {
        $items.=escape($_).'='.escape($$storehash{$_}).'&';
-   } keys %$storehash;
+   }
    $items=~s/\&$//;
    return &critical("put:$udomain:$uname:$namespace:$items",$uhome);
 }
@@ -1372,9 +1328,9 @@ sub cput {
 sub eget {
    my ($namespace,$storearr,$udomain,$uname)=@_;
    my $items='';
-   map {
+   foreach (@$storearr) {
        $items.=escape($_).'&';
-   } @$storearr;
+   }
    $items=~s/\&$//;
    if (!$udomain) { $udomain=$ENV{'user.domain'}; }
    if (!$uname) { $uname=$ENV{'user.name'}; }
@@ -1383,10 +1339,10 @@ sub eget {
    my @pairs=split(/\&/,$rep);
    my %returnhash=();
    my $i=0;
-   map {
+   foreach (@$storearr) {
       $returnhash{$_}=unescape($pairs[$i]);
       $i++;
-   } @$storearr;
+   }
    return %returnhash;
 }
 
@@ -1479,7 +1435,7 @@ sub allowed {
 	  my $refuri=$ENV{'httpref.'.$orguri};
 
             unless ($refuri) {
-                map {
+                foreach (keys %ENV) {
 		    if ($_=~/^httpref\..*\*/) {
 			my $pattern=$_;
                         $pattern=~s/^httpref\.\/res\///;
@@ -1489,7 +1445,7 @@ sub allowed {
 			    $refuri=$ENV{$_};
                         }
                     }
-                } keys %ENV;
+                }
             }
          if ($refuri) { 
 	  $refuri=&declutter($refuri);
@@ -1598,7 +1554,7 @@ sub allowed {
    if ($thisallowed=~/C/) {
        my $rolecode=(split(/\./,$ENV{'request.role'}))[0];
        if ($ENV{'course.'.$ENV{'request.course.id'}.'.'.$priv.'.roles.denied'}
-	   =~/\,$rolecode\,/) {
+	   =~/$rolecode/) {
            &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},
                 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
                 $ENV{'request.course.id'});
@@ -1645,7 +1601,7 @@ sub allowed {
 sub definerole {
   if (allowed('mcr','/')) {
     my ($rolename,$sysrole,$domrole,$courole)=@_;
-    map {
+    foreach (split('/',$sysrole)) {
 	my ($crole,$cqual)=split(/\&/,$_);
         if ($pr{'cr:s'}!~/$crole/) { return "refused:s:$crole"; }
         if ($pr{'cr:s'}=~/$crole\&/) {
@@ -1653,8 +1609,8 @@ sub definerole {
                return "refused:s:$crole&$cqual"; 
             }
         }
-    } split('/',$sysrole);
-    map {
+    }
+    foreach (split('/',$domrole)) {
 	my ($crole,$cqual)=split(/\&/,$_);
         if ($pr{'cr:d'}!~/$crole/) { return "refused:d:$crole"; }
         if ($pr{'cr:d'}=~/$crole\&/) {
@@ -1662,8 +1618,8 @@ sub definerole {
                return "refused:d:$crole&$cqual"; 
             }
         }
-    } split('/',$domrole);
-    map {
+    }
+    foreach (split('/',$courole)) {
 	my ($crole,$cqual)=split(/\&/,$_);
         if ($pr{'cr:c'}!~/$crole/) { return "refused:c:$crole"; }
         if ($pr{'cr:c'}=~/$crole\&/) {
@@ -1671,7 +1627,7 @@ sub definerole {
                return "refused:c:$crole&$cqual"; 
             }
         }
-    } split('/',$courole);
+    }
     my $command="encrypt:rolesput:$ENV{'user.domain'}:$ENV{'user.name'}:".
                 "$ENV{'user.domain'}:$ENV{'user.name'}:".
 	        "rolesdef_$rolename=".
@@ -1709,20 +1665,6 @@ sub plaintext {
     return $prp{$short};
 }
 
-# ------------------------------------------------------------------ Plain Text
-
-sub fileembstyle {
-    my $ending=shift;
-    return $fe{$ending};
-}
-
-# ------------------------------------------------------------ Description Text
-
-sub filedescription {
-    my $ending=shift;
-    return $fd{$ending};
-}
-
 # ----------------------------------------------------------------- Assign Role
 
 sub assignrole {
@@ -1891,9 +1833,9 @@ sub writecoursepref {
 	return 'error: no such course';
     }
     my $cstring='';
-    map {
+    foreach (keys %prefs) {
 	$cstring.=escape($_).'='.escape($prefs{$_}).'&';
-    } keys %prefs;
+    }
     $cstring=~s/\&$//;
     return reply('put:'.$cdomain.':'.$cnum.':environment:'.$cstring,$chome);
 }
@@ -1982,17 +1924,17 @@ sub dirlist {
 			       $tryserver);
              if (($listing ne 'no_such_dir') && ($listing ne 'empty')
               && ($listing ne 'con_lost')) {
-                map {
+                foreach (split(/:/,$listing)) {
                   my ($entry,@stat)=split(/&/,$_);
                   $allusers{$entry}=1;
-                } split(/:/,$listing);
+                }
              }
 	  }
        }
        my $alluserstr='';
-       map {
+       foreach (sort keys %allusers) {
            $alluserstr.=$_.'&user:';
-       } sort keys %allusers;
+       }
        $alluserstr=~s/:$//;
        return split(/:/,$alluserstr);
      } 
@@ -2003,9 +1945,9 @@ sub dirlist {
 	   $alldom{$hostdom{$tryserver}}=1;
        }
        my $alldomstr='';
-       map {
+       foreach (sort keys %alldom) {
           $alldomstr.=$perlvar{'lonDocRoot'}.'/res/'.$_.'&domain:';
-       } sort keys %alldom;
+       }
        $alldomstr=~s/:$//;
        return split(/:/,$alldomstr);       
    }
@@ -2026,18 +1968,18 @@ sub condval {
     my $condidx=shift;
     my $result=0;
     my $allpathcond='';
-    map {
+    foreach (split(/\|/,$condidx)) {
        if (defined($ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$_})) {
 	   $allpathcond.=
                '('.$ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$_}.')|';
        }
-    } split(/\|/,$condidx);
+    }
     $allpathcond=~s/\|$//;
     if ($ENV{'request.course.id'}) {
        if ($allpathcond) {
           my $operand='|';
 	  my @stack;
-          map {
+           foreach ($allpathcond=~/(\d+|\(|\)|\&|\|)/g) {
               if ($_ eq '(') {
                  push @stack,($operand,$result)
               } elsif ($_ eq ')') {
@@ -2055,9 +1997,9 @@ sub condval {
                      $result=$result>$new?$new:$result;
                   } else {
                      $result=$result>$new?$result:$new;
-                  }                  
+                  }
               }
-          } ($allpathcond=~/(\d+|\(|\)|\&|\|)/g);
+          }
        }
     }
     return $result;
@@ -2191,9 +2133,9 @@ sub EXT {
    &escape($courselevelr).'&'.&escape($courselevelm).'&'.&escape($courselevel),
 		   $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
       if ($reply!~/^error\:/) {
-	  map {
+	  foreach (split(/\&/,$reply)) {
 	      if ($_) { return &unescape($_); }
-          } split(/\&/,$reply);
+          }
       }
       if (($reply=~/^con_lost/) || ($reply=~/^error\:/)) {
 	  &logthis("<font color=blue>WARNING:".
@@ -2303,7 +2245,7 @@ sub metadata {
               } else {
                  $metacache{$uri.':packages'}=$package.$keyroot;
 	      }
-              map {
+              foreach (keys %packagetab) {
 		  if ($_=~/^$package\&/) {
 		      my ($pack,$name,$subp)=split(/\&/,$_);
                       my $value=$packagetab{$_};
@@ -2320,7 +2262,7 @@ sub metadata {
                          $metacache{$uri.':'.$unikey.'.'.$subp}=$value;
 		      }
                   }
-              } keys %packagetab;
+              }
              } else {
 #
 # This is not a package - some other kind of start tag
@@ -2350,11 +2292,11 @@ sub metadata {
 		 if (defined($depthcount)) { $depthcount++; } else 
                                            { $depthcount=0; }
                  if ($depthcount<20) {
-		     map {
-                         $metathesekeys{$_}=1;
-		     } split(/\,/,&metadata($uri,'keys',
+		     foreach (split(/\,/,&metadata($uri,'keys',
                                   $parser->get_text('/import'),$unikey,
-                                  $depthcount));
+                                  $depthcount))) {
+                         $metathesekeys{$_}=1;
+		     }
 		 }
              } else { 
 
@@ -2362,9 +2304,9 @@ sub metadata {
                  $unikey.='_'.$token->[2]->{'name'}; 
 	      }
               $metathesekeys{$unikey}=1;
-              map {
+              foreach (@{$token->[3]}) {
 		  $metacache{$uri.':'.$unikey.'.'.$_}=$token->[2]->{$_};
-              } @{$token->[3]};
+              }
               unless (
                  $metacache{$uri.':'.$unikey}=$parser->get_text('/'.$entry)
 		      ) { $metacache{$uri.':'.$unikey}=
@@ -2393,9 +2335,9 @@ sub symblist {
     if (($ENV{'request.course.fn'}) && (%newhash)) {
         if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',
                       &GDBM_WRCREAT,0640)) {
-	    map {
+	    foreach (keys %newhash) {
                 $hash{declutter($_)}=$mapname.'___'.$newhash{$_};
-            } keys %newhash;
+            }
             if (untie(%hash)) {
 		return 'ok';
             }
@@ -2450,7 +2392,7 @@ sub symbread {
                  } else {
 # ------------------------------------------ There is more than one possibility
                      my $realpossible=0;
-                     map {
+                     foreach (@possibilities) {
 			 my $file=$bighash{'src_'.$_};
                          if (&allowed('bre',$file)) {
          		    my ($mapid,$resid)=split(/\./,$_);
@@ -2460,7 +2402,7 @@ sub symbread {
                                        '___'.$resid;
                             }
 			 }
-                     } @possibilities;
+                     }
 		     if ($realpossible!=1) { $syval=''; }
                  }
 	      }
@@ -2569,7 +2511,7 @@ sub filelocation {
 
 sub hreflocation {
     my ($dir,$file)=@_;
-    unless (($_=~/^http:\/\//i) || ($_=~/^\//)) {
+    unless (($file=~/^http:\/\//i) || ($file=~/^\//)) {
        my $finalpath=filelocation($dir,$file);
        $finalpath=~s/^\/home\/httpd\/html//;
        return $finalpath;
@@ -2606,6 +2548,11 @@ sub unescape {
 
 # ================================================================ Main Program
 
+sub goodbye {
+   &flushcourselogs();
+   &logthis("Shutting down");
+}
+
 BEGIN {
 # ------------------------------------------------------------ Read access.conf
 {
@@ -2686,25 +2633,405 @@ BEGIN {
     }
 }
 
-# ------------------------------------------------------------- Read file types
-{
-    my $config=Apache::File->new("$perlvar{'lonTabDir'}/filetypes.tab");
-
-    while (my $configline=<$config>) {
-       chomp($configline);
-       my ($ending,$emb,@descr)=split(/\s+/,$configline);
-       if ($descr[0] ne '') { 
-         $fe{$ending}=$emb;
-         $fd{$ending}=join(' ',@descr);
-       }
-    }
-}
-
 %metacache=();
 
-$readit='done';
+$processmarker=$$.'_'.time.'_'.$perlvar{'lonHostID'};
+$dumpcount=0;
+
 &logtouch();
 &logthis('<font color=yellow>INFO: Read configuration</font>');
 }
 
 1;
+__END__
+
+=head1 NAME
+
+Apache::lonnet - TCP networking package
+
+=head1 SYNOPSIS
+
+Invoked by other LON-CAPA modules.
+
+ &Apache::lonnet::SUBROUTINENAME(ARGUMENTS);
+
+=head1 INTRODUCTION
+
+This module provides subroutines which interact with the
+lonc/lond (TCP) network layer of LON-CAPA.
+
+This is part of the LearningOnline Network with CAPA project
+described at http://www.lon-capa.org.
+
+=head1 HANDLER SUBROUTINE
+
+There is no handler routine for this module.
+
+=head1 OTHER SUBROUTINES
+
+=over 4
+
+=item *
+
+logtouch() : make sure the logfile, lonnet.log, exists
+
+=item *
+
+logthis() : append message to lonnet.log
+
+=item *
+
+logperm() : append a permanent message to lonnet.perm.log
+
+=item *
+
+subreply() : non-critical communication, called by &reply
+
+=item *
+
+reply() : makes two attempts to pass message; logs refusals and rejections
+
+=item *
+
+reconlonc() : tries to reconnect lonc client processes.
+
+=item *
+
+critical() : passes a critical message to another server; if cannot get
+through then place message in connection buffer
+
+=item *
+
+appenv(%hash) : read in current user environment, append new environment
+values to make new user environment
+
+=item *
+
+delenv($varname) : read in current user environment, remove all values
+beginning with $varname, write new user environment (note: flock is used
+to prevent conflicting shared read/writes with file)
+
+=item *
+
+spareserver() : find server with least workload from spare.tab
+
+=item *
+
+queryauthenticate($uname,$udom) : try to determine user's current
+authentication scheme
+
+=item *
+
+authenticate($uname,$upass,$udom) : try to authenticate user from domain's lib
+servers (first use the current one)
+
+=item *
+
+homeserver($uname,$udom) : find the homebase for a user from domain's lib
+servers
+
+=item *
+
+idget($udom,@ids) : find the usernames behind a list of IDs (returns hash:
+id=>name,id=>name)
+
+=item *
+
+idrget($udom,@unames) : find the IDs behind a list of usernames (returns hash:
+name=>id,name=>id)
+
+=item *
+
+idput($udom,%ids) : store away a list of names and associated IDs
+
+=item *
+
+usection($domain,$user,$courseid) : output of section name/number or '' for
+"not in course" and '-1' for "no section"
+
+=item *
+
+userenvironment($domain,$user,$what) : puts out any environment parameter 
+for a user
+
+=item *
+
+subscribe($fname) : subscribe to a resource, return URL if possible
+
+=item *
+
+repcopy($filename) : replicate file
+
+=item *
+
+ssi($url,%hash) : server side include, does a complete request cycle on url to
+localhost, posts hash
+
+=item *
+
+log($domain,$name,$home,$message) : write to permanent log for user; use
+critical subroutine
+
+=item *
+
+flushcourselogs() : flush (save) buffer logs and access logs
+
+=item *
+
+courselog($what) : save message for course in hash
+
+=item *
+
+courseacclog($what) : save message for course using &courselog().  Perform
+special processing for specific resource types (problems, exams, quizzes, etc).
+
+=item *
+
+countacc($url) : count the number of accesses to a given URL
+
+=item *
+
+sub checkout($symb,$tuname,$tudom,$tcrsid) : check out an item
+
+=item *
+
+sub checkin($token) : check in an item
+
+=item *
+
+sub expirespread($uname,$udom,$stype,$usymb) : set expire date for spreadsheet
+
+=item *
+
+devalidate($symb) : devalidate spreadsheets
+
+=item *
+
+hash2str(%hash) : convert a hash into a string complete with escaping and '='
+and '&' separators
+
+=item *
+
+str2hash($string) : convert string to hash using unescaping and splitting on
+'=' and '&'
+
+=item *
+
+tmpreset($symb,$namespace,$domain,$stuname) : temporary storage
+
+=item *
+
+tmprestore($symb,$namespace,$domain,$stuname) : temporary restore
+
+=item *
+
+store($storehash,$symb,$namespace,$domain,$stuname) : stores hash permanently
+for this url; hashref needs to be given and should be a \%hashname; the
+remaining args aren't required and if they aren't passed or are '' they will
+be derived from the ENV
+
+=item *
+
+cstore($storehash,$symb,$namespace,$domain,$stuname) : same as store but
+uses critical subroutine
+
+=item *
+
+restore($symb,$namespace,$domain,$stuname) : returns hash for this symb;
+all args are optional
+
+=item *
+
+coursedescription($courseid) : course description
+
+=item *
+
+rolesinit($domain,$username,$authhost) : get user privileges
+
+=item *
+
+get($namespace,$storearr,$udomain,$uname) : returns hash with keys from array
+reference filled in from namesp ($udomain and $uname are optional)
+
+=item *
+
+del($namespace,$storearr,$udomain,$uname) : deletes keys out of array from
+namesp ($udomain and $uname are optional)
+
+=item *
+
+dump($namespace,$udomain,$uname,$regexp) : 
+dumps the complete (or key matching regexp) namespace into a hash
+($udomain, $uname and $regexp are optional)
+
+=item *
+
+put($namespace,$storehash,$udomain,$uname) : stores hash in namesp
+($udomain and $uname are optional)
+
+=item *
+
+cput($namespace,$storehash,$udomain,$uname) : critical put
+($udomain and $uname are optional)
+
+=item *
+
+eget($namespace,$storearr,$udomain,$uname) : returns hash with keys from array
+reference filled in from namesp (encrypts the return communication)
+($udomain and $uname are optional)
+
+=item *
+
+allowed($priv,$uri) : check for a user privilege; returns codes for allowed
+actions
+ F: full access
+ U,I,K: authentication modes (cxx only)
+ '': forbidden
+ 1: user needs to choose course
+ 2: browse allowed
+
+=item *
+
+definerole($rolename,$sysrole,$domrole,$courole) : define role; define a custom
+role rolename set privileges in format of lonTabs/roles.tab for system, domain,
+and course level
+
+=item *
+
+metadata_query($query,$custom,$customshow) : make a metadata query against the
+network of library servers; returns file handle of where SQL and regex results
+will be stored for query
+
+=item *
+
+plaintext($short) : return value in %prp hash (rolesplain.tab); plain text
+explanation of a user role term
+
+=item *
+
+assignrole($udom,$uname,$url,$role,$end,$start) : assign role; give a role to a
+user for the level given by URL.  Optional start and end dates (leave empty
+string or zero for "no date")
+
+=item *
+
+modifyuserauth($udom,$uname,$umode,$upass) : modify user authentication
+
+=item *
+
+modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene) : 
+modify user
+
+=item *
+
+modifystudent($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec,
+$end,$start) : modify student
+
+=item *
+
+writecoursepref($courseid,%prefs) : write preferences for a course
+
+=item *
+
+createcourse($udom,$description,$url) : make/modify course
+
+=item *
+
+assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start) : assign
+custom role; give a custom role to a user for the level given by URL.  Specify
+name and domain of role author, and role name
+
+=item *
+
+revokerole($udom,$uname,$url,$role) : revoke a role for url
+
+=item *
+
+revokecustomrole($udom,$uname,$url,$role) : revoke a custom role
+
+=item *
+
+dirlist($uri) : return directory list based on URI
+
+=item *
+
+directcondval($number) : get current value of a condition; reads from a state
+string
+
+=item *
+
+condval($condidx) : value of condition index based on state
+
+=item *
+
+EXT($varname,$symbparm) : value of a variable
+
+=item *
+
+metadata($uri,$what,$liburi,$prefix,$depthcount) : get metadata; returns the
+metadata entry for a file; entry='keys', returns a comma separated list of keys
+
+=item *
+
+symblist($mapname,%newhash) : update symbolic storage links
+
+=item *
+
+symbread($filename) : return symbolic list entry (filename argument optional);
+returns the data handle
+
+=item *
+
+numval($salt) : return random seed value (addend for rndseed)
+
+=item *
+
+rndseed($symb,$courseid,$domain,$username) : create a random sum; returns
+a random seed, all arguments are optional, if they aren't sent it uses the
+environment to derive them. Note: if symb isn't sent and it can't get one
+from &symbread it will use the current time as its return value
+
+=item *
+
+ireceipt($funame,$fudom,$fucourseid,$fusymb) : return unique,
+unfakeable, receipt
+
+=item *
+
+receipt() : API to ireceipt working off of ENV values; given out to users
+
+=item *
+
+getfile($file) : serves up a file, returns the contents of a file or -1;
+replicates and subscribes to the file
+
+=item *
+
+filelocation($dir,$file) : returns file system location of a file based on URI;
+meant to be "fairly clean" absolute reference
+
+=item *
+
+hreflocation($dir,$file) : returns file system location or a URL; same as
+filelocation except for hrefs
+
+=item *
+
+declutter() : declutters URLs (remove docroot, beginning slashes, 'res' etc)
+
+=item *
+
+escape() : unpack non-word characters into CGI-compatible hex codes
+
+=item *
+
+unescape() : pack CGI-compatible hex codes into actual non-word ASCII character
+
+=item *
+
+goodbye() : flush course logs and log shutting down; it is called in srm.conf
+as a PerlChildExitHandler
+
+=back
+
+=cut