--- loncom/lonnet/perl/lonnet.pm	2004/09/15 20:08:34	1.541
+++ loncom/lonnet/perl/lonnet.pm	2004/10/06 09:48:39	1.550
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.541 2004/09/15 20:08:34 albertel Exp $
+# $Id: lonnet.pm,v 1.550 2004/10/06 09:48:39 foxr Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -52,6 +52,7 @@ use Apache::lonlocal;
 use Storable qw(lock_store lock_nstore lock_retrieve freeze thaw);
 use Time::HiRes qw( gettimeofday tv_interval );
 my $readit;
+my $max_connection_retries = 10;     # Or some such value.
 
 =pod
 
@@ -116,14 +117,40 @@ sub logperm {
 sub subreply {
     my ($cmd,$server)=@_;
     my $peerfile="$perlvar{'lonSockDir'}/$server";
-    my $client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
-                                     Type    => SOCK_STREAM,
-                                     Timeout => 10)
-       or return "con_lost";
-    print $client "$cmd\n";
-    my $answer=<$client>;
-    if (!$answer) { $answer="con_lost"; }
-    chomp($answer);
+    #
+    #  With loncnew process trimming, there's a timing hole between lonc server
+    #  process exit and the master server picking up the listen on the AF_UNIX
+    #  socket.  In that time interval, a lock file will exist:
+
+    my $lockfile=$peerfile.".lock";
+    while (-e $lockfile) {	# Need to wait for the lockfile to disappear.
+	sleep(1);
+    }
+    # At this point, either a loncnew parent is listening or an old lonc
+    # or loncnew child is listening so we can connect or everything's dead.
+    #
+    #   We'll give the connection a few tries before abandoning it.  If
+    #   connection is not possible, we'll con_lost back to the client.
+    #   
+    my $client;
+    for (my $retries = 0; $retries < $max_connection_retries; $retries++) {
+	$client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
+				      Type    => SOCK_STREAM,
+				      Timeout => 10);
+	if($client) {
+	    last;		# Connected!
+	}
+	sleep(1);		# Try again later if failed connection.
+    }
+    my $answer;
+    if ($client) {
+	print $client "$cmd\n";
+	$answer=<$client>;
+	if (!$answer) { $answer="con_lost"; }
+	chomp($answer);
+    } else {
+	$answer = 'con_lost';	# Failed connection.
+    }
     return $answer;
 }
 
@@ -795,11 +822,11 @@ sub getsection {
         if ($key eq $courseid.'_st') { $section=''; }
         my ($dummy,$end,$start)=split(/\_/,&unescape($value));
         my $now=time;
-        if (defined($end) && ($now > $end)) {
+        if (defined($end) && $end && ($now > $end)) {
             $Expired{$end}=$section;
             next;
         }
-        if (defined($start) && ($now < $start)) {
+        if (defined($start) && $start && ($now < $start)) {
             $Pending{$start}=$section;
             next;
         }
@@ -826,6 +853,7 @@ my $disk_caching_disabled=0;
 sub devalidate_cache {
     my ($cache,$id,$name) = @_;
     delete $$cache{$id.'.time'};
+    delete $$cache{$id.'.file'};
     delete $$cache{$id};
     if (1 || $disk_caching_disabled) { return; }
     my $filename=$perlvar{'lonDaemons'}.'/tmp/lonnet_internal_cache_'.$name.".db";
@@ -857,16 +885,32 @@ sub is_cached {
     my ($cache,$id,$name,$time) = @_;
     if (!$time) { $time=300; }
     if (!exists($$cache{$id.'.time'})) {
-	&load_cache_item($cache,$name,$id);
+	&load_cache_item($cache,$name,$id,$time);
     }
     if (!exists($$cache{$id.'.time'})) {
 #	&logthis("Didn't find $id");
 	return (undef,undef);
     } else {
 	if (time-($$cache{$id.'.time'})>$time) {
-#	    &logthis("Devalidating $id - ".time-($$cache{$id.'.time'}));
-	    &devalidate_cache($cache,$id,$name);
-	    return (undef,undef);
+	    if (exists($$cache{$id.'.file'})) {
+		foreach my $filename (@{ $$cache{$id.'.file'} }) {
+		    my $mtime=(stat($filename))[9];
+		    #+1 is to take care of edge effects
+		    if ($mtime && (($mtime+1) < ($$cache{$id.'.time'}))) {
+#			&logthis("Upping $mtime - ".$$cache{$id.'.time'}.
+#				 "$id because of $filename");
+		    } else {
+			&logthis("Devalidating $filename $id - ".(time-($$cache{$id.'.time'})));
+			&devalidate_cache($cache,$id,$name);
+			return (undef,undef);
+		    }
+		}
+		$$cache{$id.'.time'}=time;
+	    } else {
+#		&logthis("Devalidating $id - ".time-($$cache{$id.'.time'}));
+		&devalidate_cache($cache,$id,$name);
+		return (undef,undef);
+	    }
 	}
     }
     return ($$cache{$id},1);
@@ -910,6 +954,9 @@ sub save_cache {
 		eval <<'EVALBLOCK';
 		$hash{$id.'.time'}=$$cache{$id.'.time'};
 		$hash{$id}=freeze({'item'=>$$cache{$id}});
+		if (exists($$cache{$id.'.file'})) {
+		    $hash{$id.'.file'}=freeze({'item'=>$$cache{$id.'.file'}});
+		}
 EVALBLOCK
                 if ($@) {
 		    &logthis("<font color='red'>save_cache blew up :$@:$name</font>");
@@ -934,7 +981,7 @@ EVALBLOCK
 }
 
 sub load_cache_item {
-    my ($cache,$name,$id)=@_;
+    my ($cache,$name,$id,$time)=@_;
     if ($disk_caching_disabled) { return; }
     my $starttime=&Time::HiRes::time();
 #    &logthis("Before Loading $name  for $id size is ".scalar(%$cache));
@@ -958,9 +1005,17 @@ sub load_cache_item {
 		}
 #	    &logthis("Initial load: $count");
 	    } else {
-		my $hashref=thaw($hash{$id});
-		$$cache{$id}=$hashref->{'item'};
-		$$cache{$id.'.time'}=$hash{$id.'.time'};
+		if (($$cache{$id.'.time'}+$time) < time) {
+		    $$cache{$id.'.time'}=$hash{$id.'.time'};
+		    {
+			my $hashref=thaw($hash{$id});
+			$$cache{$id}=$hashref->{'item'};
+		    }
+		    if (exists($hash{$id.'.file'})) {
+			my $hashref=thaw($hash{$id.'.file'});
+			$$cache{$id.'.file'}=$hashref->{'item'};
+		    }
+		}
 	    }
 EVALBLOCK
         if ($@) {
@@ -2746,7 +2801,9 @@ sub allowed {
     $uri=&deversion($uri);
     my $orguri=$uri;
     $uri=&declutter($uri);
-
+    
+    
+    
     if (defined($ENV{'allowed.'.$priv})) { return $ENV{'allowed.'.$priv}; }
 # Free bre access to adm and meta resources
     if (((($uri=~/^adm\//) && ($uri !~ m|/bulletinboard$|)) 
@@ -2754,6 +2811,13 @@ sub allowed {
 	return 'F';
     }
 
+# Free bre access to user's own portfolio contents
+    my ($space,$domain,$name,$dir)=split('/',$uri);
+    if (('uploaded' eq $space) && ($ENV{'user.name'} eq $name) && 
+	($ENV{'user.domain'} eq $domain) && ('portfolio' eq $dir)) {
+        return 'F';
+    }
+
 # Free bre to public access
 
     if ($priv eq 'bre') {
@@ -3155,8 +3219,10 @@ sub log_query {
 sub fetch_enrollment_query {
     my ($context,$affiliatesref,$replyref,$dom,$cnum) = @_;
     my $homeserver;
+    my $maxtries = 1;
     if ($context eq 'automated') {
         $homeserver = $perlvar{'lonHostID'};
+        $maxtries = 10; # will wait for up to 2000s for retrieval of classlist data before timeout
     } else {
         $homeserver = &homeserver($cnum,$dom);
     }
@@ -3174,8 +3240,13 @@ sub fetch_enrollment_query {
         return 'error: '.$queryid;
     }
     my $reply = &get_query_reply($queryid);
+    my $tries = 1;
+    while (($reply=~/^timeout/) && ($tries < $maxtries)) {
+        $reply = &get_query_reply($queryid);
+        $tries ++;
+    }
     if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) {
-        &logthis('fetch_enrollment_query error: '.$reply.' for '.$dom.' '.$ENV{'user.name'}.' for '.$queryid.' context: '.$context.' '.$cnum);
+        &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;
         if ($homeserver eq $perlvar{'lonHostID'}) {
@@ -4260,7 +4331,9 @@ sub metadata {
         unless ($filename=~/\.meta$/) { $filename.='.meta'; }
 	my $metastring;
 	if ($uri !~ m|^uploaded/|) {
-	    $metastring=&getfile(&filelocation('',&clutter($filename)));
+	    my $file=&filelocation('',&clutter($filename));
+	    push(@{$metacache{$uri.'.file'}},$file);
+	    $metastring=&getfile($file);
 	}
         my $parser=HTML::LCParser->new(\$metastring);
         my $token;
@@ -4625,22 +4698,19 @@ sub deversion {
 
 sub symbread {
     my ($thisfn,$donotrecurse)=@_;
-    if (defined($ENV{'request.symbread.cached'})) {
-	return $ENV{'request.symbread.cached'};
-    }
+    my $cache_str='request.symbread.cached.'.$thisfn;
+    if (defined($ENV{$cache_str})) { return $ENV{$cache_str}; }
 # no filename provided? try from environment
     unless ($thisfn) {
         if ($ENV{'request.symb'}) {
-	    $ENV{'request.symbread.cached'}=&symbclean($ENV{'request.symb'});
-	    return $ENV{'request.symbread.cached'};
+	    return $ENV{$cache_str}=&symbclean($ENV{'request.symb'});
 	}
 	$thisfn=$ENV{'request.filename'};
     }
 # is that filename actually a symb? Verify, clean, and return
     if ($thisfn=~/\_\_\_\d+\_\_\_(.*)$/) {
 	if (&symbverify($thisfn,$1)) {
-	    $ENV{'request.symbread.cached'}=&symbclean($thisfn);
-	    return $ENV{'request.symbread.cached'};
+	    return $ENV{$cache_str}=&symbclean($thisfn);
 	}
     }
     $thisfn=declutter($thisfn);
@@ -4662,8 +4732,7 @@ sub symbread {
            unless ($syval=~/\_\d+$/) {
 	       unless ($ENV{'form.request.prefix'}=~/\.(\d+)\_$/) {
                   &appenv('request.ambiguous' => $thisfn);
-		  $ENV{'request.symbread.cached'}='';
-                  return '';
+		  return $ENV{$cache_str}='';
                }    
                $syval.=$1;
 	   }
@@ -4710,13 +4779,11 @@ sub symbread {
            }
         }
         if ($syval) {
-	    $ENV{'request.symbread.cached'}=&symbclean($syval.'___'.$thisfn);
-	    return $ENV{'request.symbread.cached'};
+	    return $ENV{$cache_str}=&symbclean($syval.'___'.$thisfn);
         }
     }
     &appenv('request.ambiguous' => $thisfn);
-    $ENV{'request.symbread.cached'}='';
-    return '';
+    return $ENV{$cache_str}='';
 }
 
 # ---------------------------------------------------------- Return random seed