--- loncom/lonnet/perl/lonnet.pm	2003/03/26 04:57:04	1.353
+++ loncom/lonnet/perl/lonnet.pm	2003/05/02 15:26:36	1.367
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.353 2003/03/26 04:57:04 www Exp $
+# $Id: lonnet.pm,v 1.367 2003/05/02 15:26:36 www Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -591,7 +591,11 @@ sub idput {
 # --------------------------------------------------- Assign a key to a student
 
 sub assign_access_key {
-    my ($ckey,$cdom,$cnum,$udom,$uname)=@_;
+#
+# a valid key looks like uname:udom#comments
+# comments are being appended
+#
+    my ($ckey,$cdom,$cnum,$udom,$uname,$logentry)=@_;
     $cdom=
    $ENV{'course.'.$ENV{'request.course.id'}.'.domain'} unless (defined($cdom));
     $cnum=
@@ -599,13 +603,16 @@ sub assign_access_key {
     $udom=$ENV{'user.name'} unless (defined($udom));
     $uname=$ENV{'user.domain'} unless (defined($uname));
     my %existing=&get('accesskeys',[$ckey],$cdom,$cnum);
-    if (($existing{$ckey}=~/^\d+$/) || # has time - new key
-        ($existing{$ckey} eq $uname.':'.$udom)) { # this should not happen,
+    if (($existing{$ckey}=~/^\#(.*)$/) || # - new key
+        ($existing{$ckey}=~/^$uname\:$udom\#(.*)$/)) { 
+                                                  # assigned to this person
+                                                  # - this should not happen,
                                                   # unless something went wrong
                                                   # the first time around
 # ready to assign
-    } elsif (!$existing{$ckey}) {
-        if (&put('accesskey',{$ckey=>$uname.':'.$udom},$cdom,$cnum) eq 'ok') {
+        $logentry=$1.'; '.$logentry;
+        if (&put('accesskey',{$ckey=>$uname.':'.$udom.'#'.$logentry},
+                                                 $cdom,$cnum) eq 'ok') {
 # key now belongs to user
 	    my $envkey='key.'.$cdom.'_'.$cnum;
             if (&put('environment',{$envkey => $ckey}) eq 'ok') {
@@ -618,6 +625,7 @@ sub assign_access_key {
         } else {
             return 'error: Could not assign key, try again later.';
         }
+    } elsif (!$existing{$ckey}) {
 # the key does not exist
 	return 'error: The key does not exist';
     } else {
@@ -626,15 +634,43 @@ sub assign_access_key {
     }
 }
 
+# ------------------------------------------ put an additional comment on a key
+
+sub comment_access_key {
+#
+# a valid key looks like uname:udom#comments
+# comments are being appended
+#
+    my ($ckey,$cdom,$cnum,$logentry)=@_;
+    $cdom=
+   $ENV{'course.'.$ENV{'request.course.id'}.'.domain'} unless (defined($cdom));
+    $cnum=
+   $ENV{'course.'.$ENV{'request.course.id'}.'.num'} unless (defined($cnum));
+    my %existing=&get('accesskeys',[$ckey],$cdom,$cnum);
+    if ($existing{$ckey}) {
+        $existing{$ckey}.='; '.$logentry;
+# ready to assign
+        if (&put('accesskeys',{$ckey=>$existing{$ckey}},
+                                                 $cdom,$cnum) eq 'ok') {
+	    return 'ok';
+        } else {
+	    return 'error: Count not store comment.';
+        }
+    } else {
+# the key does not exist
+	return 'error: The key does not exist';
+    }
+}
+
 # ------------------------------------------------------ Generate a set of keys
 
 sub generate_access_keys {
-    my ($number,$cdom,$cnum)=@_;
+    my ($number,$cdom,$cnum,$logentry)=@_;
     $cdom=
    $ENV{'course.'.$ENV{'request.course.id'}.'.domain'} unless (defined($cdom));
     $cnum=
    $ENV{'course.'.$ENV{'request.course.id'}.'.num'} unless (defined($cnum));
-    unless (&allowed('ccc',$cdom)) { return 0; }
+    unless (&allowed('mky',$cdom)) { return 0; }
     unless (($cdom) && ($cnum)) { return 0; }
     if ($number>10000) { return 0; }
     sleep(2); # make sure don't get same seed twice
@@ -650,7 +686,11 @@ sub generate_access_keys {
        if ($existing{$newkey}) {
            $i--;
        } else {
-	  if (&put('accesskeys',{ $newkey => time() },$cdom,$cnum) eq 'ok') {
+	  if (&put('accesskeys',
+              { $newkey => '# generated '.localtime().
+                           ' by '.$ENV{'user.name'}.'@'.$ENV{'user.domain'}.
+                           '; '.$logentry },
+		   $cdom,$cnum) eq 'ok') {
               $total++;
 	  }
        }
@@ -671,7 +711,7 @@ sub validate_access_key {
     $udom=$ENV{'user.name'} unless (defined($udom));
     $uname=$ENV{'user.domain'} unless (defined($uname));
     my %existing=&get('accesskeys',[$ckey],$cdom,$cnum);
-    return ($existing{$ckey} eq $uname.':'.$udom);
+    return ($existing{$ckey}=~/^$uname\:$udom\#/);
 }
 
 # ------------------------------------- Find the section of student in a course
@@ -1149,6 +1189,14 @@ sub countacc {
     }
 }
 
+sub linklog {
+    my ($from,$to)=@_;
+    $from=&declutter($from);
+    $to=&declutter($to);
+    $accesshash{$from.'___'.$to.'___comefrom'}=1;
+    $accesshash{$to.'___'.$from.'___goto'}=1;
+}
+  
 sub userrolelog {
     my ($trole,$username,$domain,$area,$tstart,$tend)=@_;
     if (($trole=~/^ca/) || ($trole=~/^in/) || 
@@ -1198,11 +1246,13 @@ sub courseidput {
 sub courseiddump {
     my ($domfilter,$descfilter,$sincefilter)=@_;
     my %returnhash=();
+    unless ($domfilter) { $domfilter=''; }
     foreach my $tryserver (keys %libserv) {
-	if ($hostdom{$tryserver}=~/$domfilter/) {
+	if ((!$domfilter) || ($hostdom{$tryserver} eq $domfilter)) {
 	    foreach (
              split(/\&/,&reply('courseiddump:'.$hostdom{$tryserver}.':'.
-			       $sincefilter.':'.&escape($descfilter)))) {
+			       $sincefilter.':'.&escape($descfilter),
+                               $tryserver))) {
 		my ($key,$value)=split(/\=/,$_);
                 if (($key) && ($value)) {
 		    $returnhash{&unescape($key)}=&unescape($value);
@@ -2548,7 +2598,7 @@ sub plaintext {
 # ----------------------------------------------------------------- Assign Role
 
 sub assignrole {
-    my ($udom,$uname,$url,$role,$end,$start)=@_;
+    my ($udom,$uname,$url,$role,$end,$start,$deleteflag)=@_;
     my $mrole;
     if ($role =~ /^cr\//) {
 	unless (&allowed('ccr',$url)) {
@@ -2579,7 +2629,20 @@ sub assignrole {
            $command.='_0_'.$start;
         }
     }
+# actually delete
+    if ($deleteflag) {
+	if (&allowed('dro',$udom)) {
+# modify command to delete the role
+           $command="encrypt:rolesdel:$ENV{'user.domain'}:$ENV{'user.name'}:".
+                "$udom:$uname:$url".'_'."$mrole";
+# set start and finish to negative values for userrolelog
+           $start=-1;
+           $end=-1;
+        }
+    }
+# send command
     my $answer=&reply($command,&homeserver($uname,$udom));
+# log new user role if status is ok
     if ($answer eq 'ok') {
 	&userrolelog($mrole,$uname,$udom,$url,$start,$end);
     }
@@ -2829,6 +2892,11 @@ sub createcourse {
 	return 'error: no such course';
     }
 # ----------------------------------------------------------------- Course made
+# log existance
+    &courseidput($udom,&escape($udom.'_'.$uname).'='.&escape($description),
+                 $uhome);
+    &flushcourselogs();
+# set toplevel url
     my $topurl=$url;
     unless ($nonstandard) {
 # ------------------------------------------ For standard courses, make top url
@@ -2857,25 +2925,26 @@ ENDINITMAP
 # ---------------------------------------------------------- Assign Custom Role
 
 sub assigncustomrole {
-    my ($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start)=@_;
+    my ($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start,$deleteflag)=@_;
     return &assignrole($udom,$uname,$url,'cr/'.$rdom.'/'.$rnam.'/'.$rolename,
-                       $end,$start);
+                       $end,$start,$deleteflag);
 }
 
 # ----------------------------------------------------------------- Revoke Role
 
 sub revokerole {
-    my ($udom,$uname,$url,$role)=@_;
+    my ($udom,$uname,$url,$role,$deleteflag)=@_;
     my $now=time;
-    return &assignrole($udom,$uname,$url,$role,$now);
+    return &assignrole($udom,$uname,$url,$role,$now,$deleteflag);
 }
 
 # ---------------------------------------------------------- Revoke Custom Role
 
 sub revokecustomrole {
-    my ($udom,$uname,$url,$rdom,$rnam,$rolename)=@_;
+    my ($udom,$uname,$url,$rdom,$rnam,$rolename,$deleteflag)=@_;
     my $now=time;
-    return &assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$now);
+    return &assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$now,
+           $deleteflag);
 }
 
 # ------------------------------------------------------------ Directory lister
@@ -3073,8 +3142,10 @@ sub EXT {
     unless ($varname) { return ''; }
     #get real user name/domain, courseid and symb
     my $courseid;
+    my $publicuser;
     if (!($uname && $udom)) {
-      (my $cursymb,$courseid,$udom,$uname)=&Apache::lonxml::whichuser();
+      (my $cursymb,$courseid,$udom,$uname,$publicuser)=
+	  &Apache::lonxml::whichuser();
       if (!$symbparm) {	$symbparm=$cursymb; }
     } else {
 	$courseid=$ENV{'request.course.id'};
@@ -3097,7 +3168,12 @@ sub EXT {
 	    if (defined($Apache::lonhomework::parsing_a_problem)) {
 		return $Apache::lonhomework::history{$qualifierrest};
 	    } else {
-		my %restored=&restore($symbparm,$courseid,$udom,$uname);
+		my %restored;
+		if ($publicuser || $ENV{'request.state'} eq 'construct') {
+		    %restored=&tmprestore($symbparm,$courseid,$udom,$uname);
+		} else {
+		    %restored=&restore($symbparm,$courseid,$udom,$uname);
+		}
 		return $restored{$qualifierrest};
 	    }
 # ----------------------------------------------------------------- user.access
@@ -3110,7 +3186,11 @@ sub EXT {
 		($udom eq $ENV{'user.domain'})) {
 		return $ENV{join('.',('environment',$qualifierrest))};
 	    } else {
-		my %returnhash=&userenvironment($udom,$uname,$qualifierrest);
+		my %returnhash;
+		if (!$publicuser) {
+		    %returnhash=&userenvironment($udom,$uname,
+						 $qualifierrest);
+		}
 		return $returnhash{$qualifierrest};
 	    }
 # ----------------------------------------------------------------- user.course
@@ -3134,8 +3214,11 @@ sub EXT {
             return $uname;
 # ---------------------------------------------------- Any other user namespace
         } else {
-            my %reply=&get($space,[$qualifierrest],$udom,$uname);
-            return $reply{$qualifierrest};
+	    my %reply;
+	    if (!$publicuser) {
+		%reply=&get($space,[$qualifierrest],$udom,$uname);
+	    }
+	    return $reply{$qualifierrest};
         }
     } elsif ($realm eq 'query') {
 # ---------------------------------------------- pull stuff out of query string
@@ -3154,7 +3237,7 @@ sub EXT {
         return $ENV{'course.'.$courseid.'.'.$spacequalifierrest};
     } elsif ($realm eq 'resource') {
 
-	if ($courseid eq $ENV{'request.course.id'}) {
+	if (defined($courseid) && $courseid eq $ENV{'request.course.id'}) {
 
 	    #print '<br>'.$space.' - '.$qualifier.' - '.$spacequalifierrest;
 
@@ -3328,6 +3411,7 @@ sub metadata {
         my $parser=HTML::LCParser->new(\$metastring);
         my $token;
         undef %metathesekeys;
+	delete($metacache{$uri.':packages'});
         while ($token=$parser->get_token) {
 	    if ($token->[0] eq 'S') {
 		if (defined($token->[2]->{'package'})) {
@@ -3354,16 +3438,20 @@ sub metadata {
 				$value.=' [Part: '.$part.']';
 			    }
 			    my $unikey='parameter'.$keyroot.'_'.$name;
-			    if ($subp eq 'default') { $unikey='parameter_0_'.$name; }
-			    $metathesekeys{$unikey}=1;
-			    $metacache{$uri.':'.$unikey.'.part'}=$part;
+			    if ($subp eq 'default') {
+				$unikey='parameter_0_'.$name;
+				$metacache{$uri.':'.$unikey.'.part'}='0';
+			    } else {
+				$metacache{$uri.':'.$unikey.'.part'}=$part;
+				$metathesekeys{$unikey}=1;
+			    }
 			    unless (defined($metacache{$uri.':'.$unikey.'.'.$subp})) {
 				$metacache{$uri.':'.$unikey.'.'.$subp}=$value;
 			    }
 			    if (defined($metacache{$uri.':'.$unikey.'.default'})) {
 				$metacache{$uri.':'.$unikey}=
-				    $metacache{$uri.':'.$unikey.'.default'}
-				}
+				    $metacache{$uri.':'.$unikey.'.default'};
+			    }
 			}
 		    }
 		} else {
@@ -3459,7 +3547,7 @@ sub metadata_generate_part0 {
 	if ($metakey=~/^parameter\_(.*)/) {
 	  my $part=$$metacache{$uri.':'.$metakey.'.part'};
 	  my $name=$$metacache{$uri.':'.$metakey.'.name'};
-	  if (! exists($$metadata{'parameter_0_'.$name})) {
+	  if (! exists($$metadata{'parameter_0_'.$name.'.name'})) {
 	    $allnames{$name}=$part;
 	  }
 	}
@@ -3499,6 +3587,7 @@ sub gettitle {
         $title=$bighash{'title_'.$mapid.'.'.$resid};
         untie %bighash;
     }
+    $title=~s/\&colon\;/\:/gs;
     if ($title) {
         $titlecache{$symb}=$title;
         return $title;
@@ -3672,29 +3761,88 @@ sub numval {
     $txt=~tr/u-z/0-5/;
     $txt=~s/\D//g;
     return int($txt);
-}    
+}
 
 sub rndseed {
     my ($symb,$courseid,$domain,$username)=@_;
+
+    my ($wsymb,$wcourseid,$wdomain,$wusername)=&Apache::lonxml::whichuser();
     if (!$symb) {
-      unless ($symb=&symbread()) { return time; }
+	unless ($symb=$wsymb) { return time; }
+    }
+    if (!$courseid) { $courseid=$wcourseid; }
+    if (!$domain) { $domain=$wdomain; }
+    if (!$username) { $username=$wusername }
+    my $which=$ENV{"course.$courseid.rndseed"};
+    my $CODE=$ENV{'scantron.CODE'};
+    if (defined($CODE)) {
+	&rndseed_CODE_64bit($symb,$courseid,$domain,$username);
+    } elsif ($which eq '64bit') {
+	return &rndseed_64bit($symb,$courseid,$domain,$username);
+    }
+    return &rndseed_32bit($symb,$courseid,$domain,$username);
+}
+
+sub rndseed_32bit {
+    my ($symb,$courseid,$domain,$username)=@_;
+    {
+	use integer;
+	my $symbchck=unpack("%32C*",$symb) << 27;
+	my $symbseed=numval($symb) << 22;
+	my $namechck=unpack("%32C*",$username) << 17;
+	my $nameseed=numval($username) << 12;
+	my $domainseed=unpack("%32C*",$domain) << 7;
+	my $courseseed=unpack("%32C*",$courseid);
+	my $num=$symbseed+$nameseed+$domainseed+$courseseed+$namechck+$symbchck;
+	#&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
+	#&Apache::lonxml::debug("rndseed :$num:$symb");
+	return $num;
     }
-    if (!$courseid) { $courseid=$ENV{'request.course.id'};}
-    if (!$domain) {$domain=$ENV{'user.domain'};}
-    if (!$username) {$username=$ENV{'user.name'};}
+}
+
+sub rndseed_64bit {
+    my ($symb,$courseid,$domain,$username)=@_;
     {
-      use integer;
-      my $symbchck=unpack("%32C*",$symb) << 27;
-      my $symbseed=numval($symb) << 22;
-      my $namechck=unpack("%32C*",$username) << 17;
-      my $nameseed=numval($username) << 12;
-      my $domainseed=unpack("%32C*",$domain) << 7;
-      my $courseseed=unpack("%32C*",$courseid);
-      my $num=$symbseed+$nameseed+$domainseed+$courseseed+$namechck+$symbchck;
-      #uncommenting these lines can break things!
-      #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
-      #&Apache::lonxml::debug("rndseed :$num:$symb");
-      return $num;
+	use integer;
+	my $symbchck=unpack("%32S*",$symb) << 21;
+	my $symbseed=numval($symb) << 10;
+	my $namechck=unpack("%32S*",$username);
+	
+	my $nameseed=numval($username) << 21;
+	my $domainseed=unpack("%32S*",$domain) << 10;
+	my $courseseed=unpack("%32S*",$courseid);
+	
+	my $num1=$symbchck+$symbseed+$namechck;
+	my $num2=$nameseed+$domainseed+$courseseed;
+	#&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
+	#&Apache::lonxml::debug("rndseed :$num:$symb");
+	return "$num1,$num2";
+    }
+}
+
+sub rndseed_CODE_64bit {
+    my ($symb,$courseid,$domain,$username)=@_;
+    {
+	use integer;
+	my $symbchck=unpack("%32S*",$symb) << 16;
+	my $symbseed=numval($symb);
+	my $CODEseed=numval($ENV{'scantron.CODE'}) << 16;
+	my $courseseed=unpack("%32S*",$courseid);
+	my $num1=$symbseed+$CODEseed;
+	my $num2=$courseseed+$symbchck;
+	#&Apache::lonxml::debug("$symbseed:$CODEseed|$courseseed:$symbchck");
+	#&Apache::lonxml::debug("rndseed :$num1:$num2:$symb");
+	return "$num1,$num2";
+    }
+}
+
+sub setup_random_from_rndseed {
+    my ($rndseed)=@_;
+    if ($rndseed =~/,/) {
+	my ($num1,$num2)=split(/,/,$rndseed);
+	&Math::Random::random_set_seed(abs($num1),abs($num2));
+    } else {
+	&Math::Random::random_set_seed_from_phrase($rndseed);
     }
 }
 
@@ -3820,6 +3968,7 @@ sub goodbye {
    &logthis("Starting Shut down");
    &flushcourselogs();
    &logthis("Shutting down");
+   return DONE;
 }
 
 BEGIN {