--- loncom/lonnet/perl/lonnet.pm	2000/09/04 11:25:46	1.28
+++ loncom/lonnet/perl/lonnet.pm	2000/10/04 15:59:07	1.35
@@ -6,7 +6,12 @@
 # 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,R,S,X,C
+# 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 priviledges in format of lonTabs/roles.tab for
 #                      system, domain and course level, 
@@ -28,10 +33,14 @@
 # dump(namesp)       : dumps the complete namespace into a hash
 # 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
 # condval(index)     : value of condition index based on state
-# varval(name)       : value of a variable 
+# varval(name)       : value of a variable
+# refreshstate()     : refresh the state information string
+# symblist(map,hash) : Updates symbolic storage links
+# rndseed()          : returns a random seed  
 #
 # 6/1/99,6/2,6/10,6/11,6/12,6/14,6/26,6/28,6/29,6/30,
 # 7/1,7/2,7/9,7/10,7/12,7/14,7/15,7/19,
@@ -43,7 +52,8 @@
 # 06/26 Ben Tyszka
 # 06/30,07/15,07/17,07/18,07/20,07/21,07/22,07/25 Gerd Kortemeyer
 # 08/14 Ben Tyszka
-# 08/22,08/28,08/31,09/01,09/02 Gerd Kortemeyer
+# 08/22,08/28,08/31,09/01,09/02,09/04,09/05,09/25,09/28,09/30,
+# 10/04 Gerd Kortemeyer
 
 package Apache::lonnet;
 
@@ -54,6 +64,7 @@ use HTTP::Headers;
 use vars 
 qw(%perlvar %hostname %homecache %spareid %hostdom %libserv %pr %prp %fe %fd $readit);
 use IO::Socket;
+use GDBM_File;
 use Apache::Constants qw(:common :http);
 
 # --------------------------------------------------------------------- Logging
@@ -187,6 +198,15 @@ sub critical {
 
 sub appenv {
     my %newenv=@_;
+    map {
+	if (($newenv{$_}=~/^user\.role/) || ($newenv{$_}=~/^user\.priv/)) {
+            &logthis("<font color=blue>WARNING: ".
+                "Attempt to modify environment ".$_." to ".$newenv{$_});
+	    delete($newenv{$_});
+        } else {
+            $ENV{$_}=$newenv{$_};
+        }
+    } keys %newenv;
     my @oldenv;
     {
      my $fh;
@@ -400,31 +420,75 @@ sub log {
 # ----------------------------------------------------------------------- Store
 
 sub store {
-    my %storehash=shift;
+    my %storehash=@_;
+    my $symb;
+    unless ($symb=escape(&symbread())) { return ''; }
+    my $namespace;
+    unless ($namespace=$ENV{'request.course.id'}) { return ''; }
     my $namevalue='';
     map {
         $namevalue.=escape($_).'='.escape($storehash{$_}).'&';
     } keys %storehash;
     $namevalue=~s/\&$//;
-    return reply("store:$ENV{'user.domain'}:$ENV{'user.name'}:"
-               ."$ENV{'user.class'}:$ENV{'request.filename'}:$namevalue",
+    return reply(
+     "store:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$symb:$namevalue",
 		 "$ENV{'user.home'}");
 }
 
 # --------------------------------------------------------------------- Restore
 
 sub restore {
-    my $answer=reply("restore:$ENV{'user.domain'}:$ENV{'user.name'}:"
-               ."$ENV{'user.class'}:$ENV{'request.filename'}",
-                "$ENV{'user.home'}");
+    my $symb;
+    unless ($symb=escape(&symbread())) { return ''; }
+    my $namespace;
+    unless ($namespace=$ENV{'request.course.id'}) { return ''; }
+    my $answer=reply(
+              "restore:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$symb",
+              "$ENV{'user.home'}");
     my %returnhash=();
     map {
 	my ($name,$value)=split(/\=/,$_);
         $returnhash{&unescape($name)}=&unescape($value);
     } split(/\&/,$answer);
+    map {
+        $returnhash{$_}=$returnhash{$returnhash{'version'}.':'.$_};
+    } split(/\:/,$returnhash{$returnhash{'version'}.':keys'});
     return %returnhash;
 }
 
+# ---------------------------------------------------------- Course Description
+
+sub coursedescription {
+    my $courseid=shift;
+    $courseid=~s/^\///;
+    my ($cdomain,$cnum)=split(/\//,$courseid);
+    my $chome=homeserver($cnum,$cdomain);
+    if ($chome ne 'no_host') {
+       my $rep=reply("dump:$cdomain:$cnum:environment",$chome);
+       if ($rep ne 'con_lost') {
+	   my %cachehash=();
+           my %returnhash=('home'   => $chome, 
+                           'domain' => $cdomain,
+                           'num'    => $cnum);
+           map {
+               my ($name,$value)=split(/\=/,$_);
+               $name=&unescape($name);
+               $value=&unescape($value);
+               $returnhash{$name}=$value;
+               if ($name eq 'description') {
+		   $cachehash{$courseid}=$value;
+               }
+           } split(/\&/,$rep);
+           $returnhash{'url'}='/res/'.declutter($returnhash{'url'});
+           $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.
+	       $ENV{'user.name.'}.'_'.$cdomain.'_'.$cnum;
+	   put ('coursedescriptions',%cachehash);
+           return %returnhash;
+       }
+    }
+    return ();
+}
+
 # -------------------------------------------------------- Get user priviledges
 
 sub rolesinit {
@@ -525,7 +589,7 @@ sub get {
    my %returnhash=();
    map {
       my ($key,$value)=split(/=/,$_);
-      $returnhash{unespace($key)}=unescape($value);
+      $returnhash{unescape($key)}=unescape($value);
    } @pairs;
    return %returnhash;
 }
@@ -553,7 +617,7 @@ sub dump {
    my %returnhash=();
    map {
       my ($key,$value)=split(/=/,$_);
-      $returnhash{unespace($key)}=unescape($value);
+      $returnhash{unescape($key)}=unescape($value);
    } @pairs;
    return %returnhash;
 }
@@ -586,7 +650,7 @@ sub eget {
    my %returnhash=();
    map {
       my ($key,$value)=split(/=/,$_);
-      $returnhash{unespace($key)}=unescape($value);
+      $returnhash{unescape($key)}=unescape($value);
    } @pairs;
    return %returnhash;
 }
@@ -597,9 +661,15 @@ sub allowed {
     my ($priv,$uri)=@_;
     $uri=~s/^\/res//;
     $uri=~s/^\///;
-    if ($uri=~/^adm\//) {
+
+# Free bre access to adm resources
+
+    if (($uri=~/^adm\//) && ($priv eq 'bre')) {
 	return 'F';
     }
+
+# Gather priviledges over system and domain
+
     my $thisallowed='';
     if ($ENV{'user.priv./'}=~/$priv\&([^\:]*)/) {
        $thisallowed.=$1;
@@ -607,12 +677,95 @@ sub allowed {
     if ($ENV{'user.priv./'.(split(/\//,$uri))[0].'/'}=~/$priv\&([^\:]*)/) {
        $thisallowed.=$1;
     }
-    if ($ENV{'user.priv./'.$uri}=~/$priv\&([^\:]*)/) {
-       $thisallowed.=$1;
+
+# Full access at system or domain level? Exit.
+
+    if ($thisallowed=~/F/) {
+	return 'F';
+    }
+
+# The user does not have full access at system or domain level
+# Course level access control
+
+# uri itself refering to a course?
+    
+    if ($uri=~/\.course$/) {
+       if ($ENV{'user.priv./'.$uri}=~/$priv\&([^\:]*)/) {
+          $thisallowed.=$1;
+       }
+# Full access on course level? Exit.
+       if ($thisallowed=~/F/) {
+	  return 'F';
+       }
+
+# uri is refering to an individual resource; user needs to be in a course
+
+   } else {
+
+       unless(defined($ENV{'request.course.id'})) {
+	   return '1';
+       }
+
+# Get access priviledges for course
+
+       if ($ENV{'user.priv./'.$ENV{'request.course.id'}}=~/$priv\&([^\:]*)/) {
+          $thisallowed.=$1;
+       }
+
+# See if resource or referer is part of this course
+          
+       my @uriparts=split(/\//,$uri);
+       my $urifile=$uriparts[$#uriparts];
+       $urifile=~/\.(\w+)$/;
+       my $uritype=$1;
+       $#uriparts--;
+       my $uripath=join('/',@uriparts);
+       my $uricond=-1;
+       if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$uripath}=~
+	   /\&$urifile\:(\d+)\&/) {
+	   $uricond=$1;
+       } elsif (($fe{$uritype} eq 'emb') || ($fe{$uritype} eq 'img')) {
+	  my $refuri=$ENV{'HTTP_REFERER'};
+          $refuri=~s/^\/res//;
+          $refuri=~s/^\///;
+          @uriparts=split(/\//,$refuri);
+          $urifile=$uriparts[$#uriparts];
+          $#uriparts--;
+          $uripath=join('/',@uriparts);
+          if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$uripath}=~
+	     /\&$urifile\:(\d+)\&/) {
+	     $uricond=$1;
+	  }
+       }
+
+       if ($uricond>=0) {
+
+# The resource is part of the course
+# If user had full access on course level, go ahead
+
+           if ($thisallowed=~/F/) {
+	       return 'F';
+           }
+
+# Restricted by state?
+
+           if ($thisallowed=~/X/) {
+	      if (&condval($uricond)>1) {
+	         return '2';
+              } else {
+                 return '';
+              }
+	   }
+       }
     }
     return $thisallowed;
 }
 
+# ---------------------------------------------------------- Refresh State Info
+
+sub refreshstate {
+}
+
 # ----------------------------------------------------------------- Define Role
 
 sub definerole {
@@ -681,6 +834,7 @@ sub filedecription {
 sub assignrole {
     my ($udom,$uname,$url,$role,$end,$start)=@_;
     my $mrole;
+    $url=declutter($url);
     if ($role =~ /^cr\//) {
         unless ($url=~/\.course$/) { return 'invalid'; }
 	unless (allowed('ccr',$url)) { return 'refused'; }
@@ -782,8 +936,8 @@ sub dirlist {
 sub condval {
     my $condidx=shift;
     my $result=0;
-    if ($ENV{'request.course'}) {
-       if (defined($ENV{'acc.cond.'.$ENV{'request.course'}.'.'.$condidx})) {
+    if ($ENV{'request.course.id'}) {
+       if (defined($ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$condidx})) {
           my $operand='|';
 	  my @stack;
           map {
@@ -800,14 +954,14 @@ sub condval {
                   $operand=$_;
               } else {
                   my $new=
-                       substr($ENV{'user.state.'.$ENV{'request.course'}},$_,1);
+                    substr($ENV{'user.state.'.$ENV{'request.course.id'}},$_,1);
                   if ($operand eq '&') {
                      $result=$result>$new?$new:$result;
                   } else {
                      $result=$result>$new?$result:$new;
                   }                  
               }
-          } ($ENV{'acc.cond.'.$ENV{'request.course'}.'.'.$condidx}=~
+          } ($ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$condidx}=~
              /(\d+|\(|\)|\&|\|)/g);
        }
     }
@@ -820,6 +974,12 @@ sub varval {
     my ($realm,$space,@components)=split(/\./,shift);
     my $value='';
     if ($realm eq 'user') {
+	if ($space=~/^resource/) {
+	    $space=~s/^resource\[//;
+            $space=~s/\]$//;
+
+        } else {
+        }
     } elsif ($realm eq 'course') {
     } elsif ($realm eq 'session') {
     } elsif ($realm eq 'system') {
@@ -827,6 +987,90 @@ sub varval {
     return $value;
 }
 
+# ------------------------------------------------- Update symbolic store links
+
+sub symblist {
+    my ($mapname,%newhash)=@_;
+    $mapname=declutter($mapname);
+    my %hash;
+    if (($ENV{'request.course.fn'}) && (%newhash)) {
+        if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',
+                      &GDBM_WRCREAT,0640)) {
+	    map {
+                $hash{declutter($_)}=$mapname.'___'.$newhash{$_};
+            } keys %newhash;
+            if (untie(%hash)) {
+		return 'ok';
+            }
+        }
+    }
+    return 'error';
+}
+
+# ------------------------------------------------------ Return symb list entry
+
+sub symbread {
+    my %hash;
+    my $syval;
+    if (($ENV{'request.course.fn'}) && ($ENV{'request.filename'})) {
+        if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',
+                      &GDBM_READER,0640)) {
+            my $thisfn=declutter($ENV{'request.filename'});
+	    $syval=$hash{$thisfn};
+            if (untie(%hash)) {
+                unless ($syval=~/\_\d+$/) {
+		   unless ($ENV{'form.request.prefix'}=~/\.(\d+)\_$/) {
+                      return '';
+                   }    
+                   $syval.=$1;
+	        }
+                $syval.='___'.$thisfn;
+		return $syval;
+            }
+        }
+    }
+    return '';
+}
+
+# ---------------------------------------------------------- Return random seed
+
+sub numval {
+    my $txt=shift;
+    $txt=~tr/A-J/0-9/;
+    $txt=~tr/a-j/0-9/;
+    $txt=~tr/K-T/0-9/;
+    $txt=~tr/k-t/0-9/;
+    $txt=~tr/U-Z/0-5/;
+    $txt=~tr/u-z/0-5/;
+    $txt=~s/\D//g;
+    return int($txt);
+}    
+
+sub rndseed {
+    my $symb;
+    unless ($symb=&symbread()) { return ''; }
+    my $symbchck=unpack("%32C*",$symb);
+    my $symbseed=numval($symb)%$symbchck;
+    my $namechck=unpack("%32C*",$ENV{'user.name'});
+    my $nameseed=numval($ENV{'user.name'})%$namechck;
+    return int( $symbseed
+	       .$nameseed
+               .unpack("%32C*",$ENV{'user.domain'})
+               .unpack("%32C*",$ENV{'request.course.id'})
+               .$namechck
+               .$symbchck);
+}
+
+# ------------------------------------------------------------- Declutters URLs
+
+sub declutter {
+    my $thisfn=shift;
+    $thisfn=~s/^$perlvar{'lonDocRoot'}//;
+    $thisfn=~s/^\///;
+    $thisfn=~s/^res\///;
+    return $thisfn;
+}
+
 # -------------------------------------------------------- Escape Special Chars
 
 sub escape {