--- loncom/lonnet/perl/lonnet.pm	2002/10/07 20:24:38	1.293
+++ loncom/lonnet/perl/lonnet.pm	2002/12/05 23:51:05	1.309
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.293 2002/10/07 20:24:38 matthew Exp $
+# $Id: lonnet.pm,v 1.309 2002/12/05 23:51:05 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -77,15 +77,18 @@ use Apache::File;
 use LWP::UserAgent();
 use HTTP::Headers;
 use vars 
-qw(%perlvar %hostname %homecache %badServerCache %hostip %spareid %hostdom 
-   %libserv %pr %prp %metacache %packagetab 
+qw(%perlvar %hostname %homecache %badServerCache %hostip %iphost %spareid %hostdom 
+   %libserv %pr %prp %metacache %packagetab %titlecache 
    %courselogs %accesshash $processmarker $dumpcount 
-   %coursedombuf %coursehombuf %courseresdatacache %domaindescription);
+   %coursedombuf %coursehombuf %courseresdatacache 
+   %domaindescription);
 use IO::Socket;
 use GDBM_File;
 use Apache::Constants qw(:common :http);
 use HTML::LCParser;
 use Fcntl qw(:flock);
+use Apache::loncoursedata;
+
 my $readit;
 
 # --------------------------------------------------------------------- Logging
@@ -200,6 +203,7 @@ sub critical {
                " Critical message to unknown server ($server)</font>");
         return 'no_such_host';
     }
+    sleep 2;
     my $answer=reply($cmd,$server);
     if ($answer eq 'con_lost') {
         my $pingreply=reply('ping',$server);
@@ -213,7 +217,8 @@ sub critical {
             $middlename=substr($middlename,0,16);
             $middlename=~s/\W//g;
             my $dfilename=
-             "$perlvar{'lonSockDir'}/delayed/$now.$middlename.$server";
+      "$perlvar{'lonSockDir'}/delayed/$now.$dumpcount.$$.$middlename.$server";
+            $dumpcount++;
             {
              my $dfh;
              if ($dfh=Apache::File->new(">$dfilename")) {
@@ -591,6 +596,59 @@ sub idput {
 
 # ------------------------------------- Find the section of student in a course
 
+sub getsection {
+    my ($udom,$unam,$courseid)=@_;
+    $courseid=~s/\_/\//g;
+    $courseid=~s/^(\w)/\/$1/;
+    my %Pending; 
+    my %Expired;
+    #
+    # Each role can either have not started yet (pending), be active, 
+    #    or have expired.
+    #
+    # If there is an active role, we are done.
+    #
+    # If there is more than one role which has not started yet, 
+    #     choose the one which will start sooner
+    # If there is one role which has not started yet, return it.
+    #
+    # If there is more than one expired role, choose the one which ended last.
+    # If there is a role which has expired, return it.
+    #
+    foreach (split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles',
+                        &homeserver($unam,$udom)))) {
+        my ($key,$value)=split(/\=/,$_);
+        $key=&unescape($key);
+        next if ($key !~/^$courseid(?:\/)*(\w+)*\_st$/);
+        my $section=$1;
+        if ($key eq $courseid.'_st') { $section=''; }
+        my ($dummy,$end,$start)=split(/\_/,&unescape($value));
+        my $now=time;
+        if (defined($end) && ($now > $end)) {
+            $Expired{$end}=$section;
+            next;
+        }
+        if (defined($start) && ($now < $start)) {
+            $Pending{$start}=$section;
+            next;
+        }
+        return $section;
+    }
+    #
+    # Presumedly there will be few matching roles from the above
+    # loop and the sorting time will be negligible.
+    if (scalar(keys(%Pending))) {
+        my ($time) = sort {$a <=> $b} keys(%Pending);
+        return $Pending{$time};
+    } 
+    if (scalar(keys(%Expired))) {
+        my @sorted = sort {$a <=> $b} keys(%Expired);
+        my $time = pop(@sorted);
+        return $Expired{$time};
+    }
+    return '-1';
+}
+
 sub usection {
     my ($udom,$unam,$courseid)=@_;
     $courseid=~s/\_/\//g;
@@ -785,7 +843,8 @@ sub tokenwrapper {
     if ($uri=~/^uploaded\/([^\/]+)\/([^\/]+)\/([^\/]+)(\?\.*)*$/) {
 	&appenv('userfile.'.$1.'/'.$2.'/'.$3 => $ENV{'request.course.id'});
         return 'http://'.$hostname{ &homeserver($2,$1)}.'/'.$uri.
-               (($uri=~/\?/)?'&':'?').'token='.$token;
+               (($uri=~/\?/)?'&':'?').'token='.$token.
+                               '&tokenissued='.$perlvar{'lonHostID'};
     } else {
 	return '/adm/notfound.html';
     }
@@ -838,13 +897,16 @@ sub finishuserfileupload {
     }
 # Notify homeserver to grep it
 #
-    if 
-(&reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$fname,$docuhome) eq 'ok') 
-    {
+    
+    my $fetchresult= 
+ &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$fname,$docuhome);
+    if ($fetchresult eq 'ok') {
 #
 # Return the URL to it
         return '/uploaded/'.$path.$fname;
     } else {
+        &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$fname.
+         ' to host '.$docuhome.': '.$fetchresult);
         return '/adm/notfound.html';
     }    
 }
@@ -1045,7 +1107,7 @@ sub devalidate {
     if ($cid) {
 	my $key=$ENV{'user.name'}.':'.$ENV{'user.domain'}.':';
         my $status=
-	    &del('nohist_calculatedsheet',
+	    &del('nohist_calculatedsheets',
 		 [$key.'studentcalc'],
 		 $ENV{'course.'.$cid.'.domain'},
 		 $ENV{'course.'.$cid.'.num'})
@@ -1484,11 +1546,15 @@ sub coursedescription {
     $courseid=~s/\_/\//g;
     my ($cdomain,$cnum)=split(/\//,$courseid);
     my $chome=&homeserver($cnum,$cdomain);
+    my $normalid=$cdomain.'_'.$cnum;
+    # need to always cache even if we get errors otherwise we keep 
+    # trying and trying and trying to get the course description.
+    my %envhash=();
+    my %returnhash=();
+    $envhash{'course.'.$normalid.'.last_cache'}=time;
     if ($chome ne 'no_host') {
-       my %returnhash=&dump('environment',$cdomain,$cnum);
+       %returnhash=&dump('environment',$cdomain,$cnum);
        if (!exists($returnhash{'con_lost'})) {
-           my $normalid=$cdomain.'_'.$cnum;
-           my %envhash=();
            $returnhash{'home'}= $chome;
 	   $returnhash{'domain'} = $cdomain;
 	   $returnhash{'num'} = $cnum;
@@ -1498,15 +1564,13 @@ sub coursedescription {
            $returnhash{'url'}=&clutter($returnhash{'url'});
            $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.
 	       $ENV{'user.name'}.'_'.$cdomain.'_'.$cnum;
-           $envhash{'course.'.$normalid.'.last_cache'}=time;
            $envhash{'course.'.$normalid.'.home'}=$chome;
            $envhash{'course.'.$normalid.'.domain'}=$cdomain;
            $envhash{'course.'.$normalid.'.num'}=$cnum;
-           &appenv(%envhash);
-           return %returnhash;
        }
     }
-    return ();
+    &appenv(%envhash);
+    return %returnhash;
 }
 
 # -------------------------------------------------------- Get user privileges
@@ -1744,7 +1808,9 @@ sub allowed {
 
     if ($priv eq 'bre') {
         my $copyright=&metadata($uri,'copyright');
-	if ($copyright eq 'public') { return 'F'; }
+	if (($copyright eq 'public') && (!$ENV{'request.course.id'})) { 
+           return 'F'; 
+        }
         if ($copyright eq 'priv') {
             $uri=~/([^\/]+)\/([^\/]+)\//;
 	    unless (($ENV{'user.name'} eq $2) && ($ENV{'user.domain'} eq $1)) {
@@ -2315,20 +2381,59 @@ sub modifystudent {
 	($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$forceid,
          $desiredhome);
     unless ($reply eq 'ok') { return $reply; }
+    # This will cause &modify_student_enrollment to get the uid from the
+    # students environment
+    $uid = undef if (!$forceid);
+    $reply = &modify_student_enrollment($udom,$uname,$uid,$first,$middle,
+                                        $last,$gene,$usec,$end,$start);
+    return $reply;
+}
+
+sub modify_student_enrollment {
+    my ($udom,$uname,$uid,$first,$middle,$last,$gene,$usec,$end,$start) = @_;
+    # Get the course id from the environment
+    my $cid='';
+    unless ($cid=$ENV{'request.course.id'}) {
+	return 'not_in_class';
+    }
+    # Make sure the user exists
     my $uhome=&homeserver($uname,$udom);
     if (($uhome eq '') || ($uhome eq 'no_host')) { 
 	return 'error: no such user';
     }
-# -------------------------------------------------- Add student to course list
-    $reply=critical('put:'.$ENV{'course.'.$cid.'.domain'}.':'.
+    #
+    # Get student data if we were not given enough information
+    if (!defined($first)  || $first  eq '' || 
+        !defined($last)   || $last   eq '' || 
+        !defined($uid)    || $uid    eq '' || 
+        !defined($middle) || $middle eq '' || 
+        !defined($gene)   || $gene   eq '') {
+        # They did not supply us with enough data to enroll the student, so
+        # we need to pick up more information.
+        my %tmp = &get('environment',
+                       ['firstname','middlename','lastname', 'generation','id']
+                       ,$udom,$uname);
+
+        foreach (keys(%tmp)) {
+            &logthis("key $_ = ".$tmp{$_});
+        }
+        $first  = $tmp{'firstname'}  if (!defined($first)  || $first  eq '');
+        $middle = $tmp{'middlename'} if (!defined($middle) || $middle eq '');
+        $last   = $tmp{'lastname'}   if (!defined($last)   || $last eq '');
+        $gene   = $tmp{'generation'} if (!defined($gene)   || $gene eq '');
+        $uid    = $tmp{'id'}         if (!defined($uid)    || $uid  eq '');
+    }
+    my $fullname = &Apache::loncoursedata::ProcessFullName($last,$gene,
+                                                           $first,$middle);
+    my $reply=critical('put:'.$ENV{'course.'.$cid.'.domain'}.':'.
 	              $ENV{'course.'.$cid.'.num'}.':classlist:'.
                       &escape($uname.':'.$udom).'='.
-                      &escape($end.':'.$start),
+                      &escape(join(':',$end,$start,$uid,$usec,$fullname)),
 	              $ENV{'course.'.$cid.'.home'});
     unless (($reply eq 'ok') || ($reply eq 'delayed')) {
 	return 'error: '.$reply;
     }
-# ---------------------------------------------------- Add student role to user
+    # Add student role to user
     my $uurl='/'.$cid;
     $uurl=~s/\_/\//g;
     if ($usec) {
@@ -2616,6 +2721,8 @@ sub courseresdata {
 	if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
 	    $courseresdatacache{$hashid.'.time'}=time;
 	    $courseresdatacache{$hashid}=\%dumpreply;
+	} elsif ($tmp =~ /^(con_lost|no_such_host)/) {
+	    return $tmp;
 	}
     }
     foreach my $item (@which) {
@@ -2742,24 +2849,35 @@ sub EXT {
 	    my $courselevelm=$courseid.'.'.$mapparm;
 
 # ----------------------------------------------------------- first, check user
-	    my %resourcedata=&get('resourcedata',
-				  [$courselevelr,$courselevelm,$courselevel],
-				 $udom,$uname);
-	    if (($resourcedata{$courselevelr}!~/^error\:/) &&
-		($resourcedata{$courselevelr}!~/^con_lost/)) {
-
-		if ($resourcedata{$courselevelr}) {
-		    return $resourcedata{$courselevelr}; }
-		if ($resourcedata{$courselevelm}) {
-		    return $resourcedata{$courselevelm}; }
-		if ($resourcedata{$courselevel}) {
-		    return $resourcedata{$courselevel}; }
-	    } else {
-		if ($resourcedata{$courselevelr}!~/No such file/) {
-		    &logthis("<font color=blue>WARNING:".
-			     " Trying to get resource data for ".
-			     $uname." at ".$udom.": ".
-			     $resourcedata{$courselevelr}."</font>");
+	    #most student don't have any data set, check if there is some data
+            #every thirty minutes
+	    if (!
+		(exists($ENV{'cache.studentresdata'})
+		    && (($ENV{'cache.studentresdata'}+1800) > time))) {
+		my %resourcedata=&get('resourcedata',
+				      [$courselevelr,$courselevelm,$courselevel],
+				      $udom,$uname);
+		my ($tmp)=keys(%resourcedata);
+		if (($tmp!~/^error\:/) && ($tmp!~/^con_lost/)) {
+		    if ($resourcedata{$courselevelr}) {
+			return $resourcedata{$courselevelr}; }
+		    if ($resourcedata{$courselevelm}) {
+			return $resourcedata{$courselevelm}; }
+		    if ($resourcedata{$courselevel}) {
+			return $resourcedata{$courselevel}; }
+		} else {
+		    if ($tmp!~/No such file/) {
+			&logthis("<font color=blue>WARNING:".
+				 " Trying to get resource data for ".
+				 $uname." at ".$udom.": ".
+				 $tmp."</font>");
+		    } elsif ($tmp=~/error:No such file/) {
+			$ENV{'cache.studentresdata'}=time;
+			&appenv(('cache.studentresdata'=>
+				 $ENV{'cache.studentresdata'}));
+		    } elsif ($tmp =~ /^(con_lost|no_such_host)/) {
+			return $tmp;
+		    }
 		}
 	    }
 
@@ -2999,6 +3117,33 @@ sub metadata_generate_part0 {
     }
 }
 
+# ------------------------------------------------- Get the title of a resource
+
+sub gettitle {
+    my $urlsymb=shift;
+    my $symb=&symbread($urlsymb);
+    unless ($symb) {
+	unless ($urlsymb) { $urlsymb=$ENV{'request.filename'}; }
+        return &metadata($urlsymb,'title'); 
+    }
+    if ($titlecache{$symb}) { return $titlecache{$symb}; }
+    my ($map,$resid,$url)=split(/\_\_\_/,$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 ($title) {
+        $titlecache{$symb}=$title;
+        return $title;
+    } else {
+	return &metadata($urlsymb,'title');
+    }
+}
+    
 # ------------------------------------------------- Update symbolic store links
 
 sub symblist {
@@ -3345,12 +3490,14 @@ BEGIN {
     my $config=Apache::File->new("$perlvar{'lonTabDir'}/hosts.tab");
 
     while (my $configline=<$config>) {
+       next if ($configline =~ /^(\#|\s*$)/);
        chomp($configline);
        my ($id,$domain,$role,$name,$ip,$domdescr)=split(/:/,$configline);
        if ($id && $domain && $role && $name && $ip) {
 	 $hostname{$id}=$name;
 	 $hostdom{$id}=$domain;
 	 $hostip{$id}=$ip;
+	 $iphost{$ip}=$id;
 	 if ($domdescr) { $domaindescription{$domain}=$domdescr; }
 	 if ($role eq 'library') { $libserv{$id}=$name; }
        } else {
@@ -3638,7 +3785,8 @@ The course id is resolved based on the c
 This means the envoking user must be a course coordinator or otherwise
 associated with a course.
 
-This call is essentially a wrapper for lonnet::modifyuser
+This call is essentially a wrapper for lonnet::modifyuser and
+lonnet::modify_student_enrollment
 
 Inputs: 
 
@@ -3676,6 +3824,40 @@ Inputs:
 
 =item *
 
+modify_student_enrollment
+
+Change a students enrollment status in a class.  The environment variable
+'role.request.course' must be defined for this function to proceed.
+
+Inputs:
+
+=over 4
+
+=item $udom, students domain
+
+=item $uname, students name
+
+=item $uid, students user id
+
+=item $first, students first name
+
+=item $middle
+
+=item $last
+
+=item $gene
+
+=item $usec
+
+=item $end
+
+=item $start
+
+=back
+
+
+=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