--- loncom/lonnet/perl/lonnet.pm	2002/05/16 20:25:12	1.221
+++ loncom/lonnet/perl/lonnet.pm	2002/06/24 14:16:58	1.242
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.221 2002/05/16 20:25:12 matthew Exp $
+# $Id: lonnet.pm,v 1.242 2002/06/24 14:16:58 www Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -77,7 +77,7 @@ use Apache::File;
 use LWP::UserAgent();
 use HTTP::Headers;
 use vars 
-qw(%perlvar %hostname %homecache %badhomecache %hostip %spareid %hostdom 
+qw(%perlvar %hostname %homecache %badServerCache %hostip %spareid %hostdom 
    %libserv %pr %prp %metacache %packagetab 
    %courselogs %accesshash $processmarker $dumpcount 
    %coursedombuf %coursehombuf %courseresdatacache);
@@ -140,20 +140,20 @@ sub reply {
     unless (defined($hostname{$server})) { return 'no_such_host'; }
     my $answer=subreply($cmd,$server);
     if ($answer eq 'con_lost') {
-       sleep 5; 
-       $answer=subreply($cmd,$server);
-       if ($answer eq 'con_lost') {
-	   &logthis("Second attempt con_lost on $server");
-           my $peerfile="$perlvar{'lonSockDir'}/$server";
-           my $client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
-                                            Type    => SOCK_STREAM,
-                                            Timeout => 10)
-                      or return "con_lost";
-           &logthis("Killing socket");
-           print $client "close_connection_exit\n";
-           sleep 5;
-           $answer=subreply($cmd,$server);       
-       }   
+       #sleep 5; 
+       #$answer=subreply($cmd,$server);
+       #if ($answer eq 'con_lost') {
+	#   &logthis("Second attempt con_lost on $server");
+        #   my $peerfile="$perlvar{'lonSockDir'}/$server";
+        #   my $client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
+        #                                    Type    => SOCK_STREAM,
+        #                                    Timeout => 10)
+        #              or return "con_lost";
+        #   &logthis("Killing socket");
+        #   print $client "close_connection_exit\n";
+           #sleep 5;
+        #   $answer=subreply($cmd,$server);       
+       #}   
     }
     if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
        &logthis("<font color=blue>WARNING:".
@@ -481,25 +481,23 @@ sub authenticate {
 # ---------------------- Find the homebase for a user from domain's lib servers
 
 sub homeserver {
-    my ($uname,$udom)=@_;
+    my ($uname,$udom,$ignoreBadCache)=@_;
     my $index="$uname:$udom";
     if ($homecache{$index}) { 
         return "$homecache{$index}"; 
     }
     my $tryserver;
     foreach $tryserver (keys %libserv) {
-        next if (exists($badhomecache{$index}->{$tryserver}));
+        next if ($ignoreBadCache ne 'true' && 
+		 exists($badServerCache{$tryserver}));
 	if ($hostdom{$tryserver} eq $udom) {
            my $answer=reply("home:$udom:$uname",$tryserver);
-           my @tmp =keys(%{$badhomecache{$index}}); 
            if ($answer eq 'found') { 
               $homecache{$index}=$tryserver;
               return $tryserver; 
-           } else {
-               $badhomecache{$index}->{$tryserver}=1;
+           } elsif ($answer eq 'no_host') {
+	       $badServerCache{$tryserver}=1;
            }
-       } else {
-           $badhomecache{$index}->{$tryserver}=1;
        }
     }    
     return 'no_host';
@@ -801,6 +799,7 @@ sub checkout {
     my $now=time;
     my $lonhost=$perlvar{'lonHostID'};
     my $infostr=&escape(
+                 'CHECKOUTTOKEN&'.
                  $tuname.'&'.
                  $tudom.'&'.
                  $tcrsid.'&'.
@@ -850,7 +849,7 @@ sub checkin {
     $lonhost=~tr/A-Z/a-z/;
     my $dtoken=$ta.'_'.$hostip{$lonhost}.'_'.$tb;
     $dtoken=~s/\W/\_/g;
-    my ($tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=
+    my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=
                  split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));
 
     unless (($tuname) && ($tudom)) {
@@ -1502,7 +1501,21 @@ sub allowed {
 # Free bre to public access
 
     if ($priv eq 'bre') {
-	if (&metadata($uri,'copyright') eq 'public') { return 'F'; }
+        my $copyright=&metadata($uri,'copyright');
+	if ($copyright eq 'public') { return 'F'; }
+        if ($copyright eq 'priv') {
+            $uri=~/([^\/]+)\/([^\/]+)\//;
+	    unless (($ENV{'user.name'} eq $2) && ($ENV{'user.domain'} eq $1)) {
+		return '';
+            }
+        }
+        if ($copyright eq 'domain') {
+            $uri=~/([^\/]+)\/([^\/]+)\//;
+	    unless (($ENV{'user.domain'} eq $1) ||
+                 ($ENV{'course.'.$ENV{'request.course.id'}.'.domain'} eq $1)) {
+		return '';
+            }
+        }
     }
 
     my $thisallowed='';
@@ -1550,19 +1563,16 @@ sub allowed {
 # the course
 
     if ($ENV{'request.course.id'}) {
+
        $courseprivid=$ENV{'request.course.id'};
        if ($ENV{'request.course.sec'}) {
           $courseprivid.='/'.$ENV{'request.course.sec'};
        }
        $courseprivid=~s/\_/\//;
        my $checkreferer=1;
-       my @uriparts=split(/\//,$uri);
-       my $filename=$uriparts[$#uriparts];
-       my $pathname=$uri;
-       $pathname=~s/\/$filename$//;
-       if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
-           /\&$filename\:([\d\|]+)\&/) {
-           $statecond=$1;
+       my ($match,$cond)=&is_on_map($uri);
+       if ($match) {
+           $statecond=$cond;
            if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
                =~/$priv\&([^\:]*)/) {
                $thisallowed.=$1;
@@ -1572,7 +1582,6 @@ sub allowed {
        
        if ($checkreferer) {
 	  my $refuri=$ENV{'httpref.'.$orguri};
-
             unless ($refuri) {
                 foreach (keys %ENV) {
 		    if ($_=~/^httpref\..*\*/) {
@@ -1586,15 +1595,12 @@ sub allowed {
                     }
                 }
             }
+
          if ($refuri) { 
 	  $refuri=&declutter($refuri);
-          my @uriparts=split(/\//,$refuri);
-          my $filename=$uriparts[$#uriparts];
-          my $pathname=$refuri;
-          $pathname=~s/\/$filename$//;
-            if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
-              /\&$filename\:([\d\|]+)\&/) {
-              my $refstatecond=$1;
+          my ($match,$cond)=&is_on_map($refuri);
+            if ($match) {
+              my $refstatecond=$cond;
               if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
                   =~/$priv\&([^\:]*)/) {
                   $thisallowed.=$1;
@@ -1653,7 +1659,7 @@ sub allowed {
                 || ($ENV{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {
 		   if ($ENV{$prefix.'res.'.$uri.'.lock.expire'}>time) {
                        &log($ENV{'user.domain'},$ENV{'user.name'},
-                            $ENV{'user.host'},
+                            $ENV{'user.home'},
                             'Locked by res: '.$priv.' for '.$uri.' due to '.
                             $cdom.'/'.$cnum.'/'.$csec.' expire '.
                             $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
@@ -1664,7 +1670,7 @@ sub allowed {
                 || ($ENV{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {
 		   if ($ENV{'priv.'.$priv.'.lock.expire'}>time) {
                        &log($ENV{'user.domain'},$ENV{'user.name'},
-                            $ENV{'user.host'},
+                            $ENV{'user.home'},
                             'Locked by priv: '.$priv.' for '.$uri.' due to '.
                             $cdom.'/'.$cnum.'/'.$csec.' expire '.
                             $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
@@ -1692,6 +1698,7 @@ sub allowed {
 
    if ($thisallowed=~/C/) {
        my $rolecode=(split(/\./,$ENV{'request.role'}))[0];
+       my $unamedom=$ENV{'user.name'}.':'.$ENV{'user.domain'};
        if ($ENV{'course.'.$ENV{'request.course.id'}.'.'.$priv.'.roles.denied'}
 	   =~/$rolecode/) {
            &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},
@@ -1699,6 +1706,14 @@ sub allowed {
                 $ENV{'request.course.id'});
            return '';
        }
+
+       if ($ENV{'course.'.$ENV{'request.course.id'}.'.'.$priv.'.users.denied'}
+	   =~/$unamedom/) {
+           &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},
+                'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
+                $ENV{'request.course.id'});
+           return '';
+       }
    }
 
 # Resource preferences
@@ -1735,6 +1750,23 @@ sub allowed {
    return 'F';
 }
 
+# --------------------------------------------------- Is a resource on the map?
+
+sub is_on_map {
+    my $uri=&declutter(shift);
+    my @uriparts=split(/\//,$uri);
+    my $filename=$uriparts[$#uriparts];
+    my $pathname=$uri;
+    $pathname=~s/\/$filename$//;
+    my $match=($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
+	       /\&$filename\:([\d\|]+)\&/);
+    if ($match) {
+       return (1,$1);
+   } else {
+       return (0,0);
+   }
+}
+
 # ----------------------------------------------------------------- Define Role
 
 sub definerole {
@@ -1797,6 +1829,64 @@ sub metadata_query {
     return \%rhash;
 }
 
+# ----------------------------------------- Send log queries and wait for reply
+
+sub log_query {
+    my ($uname,$udom,$query,%filters)=@_;
+    my $uhome=&homeserver($uname,$udom);
+    if ($uhome eq 'no_host') { return 'error: no_host'; }
+    my $uhost=$hostname{$uhome};
+    my $command=&escape(join(':',map{$_.'='.$filters{$_}} keys %filters));
+    my $queryid=&reply("querysend:".$query.':'.$udom.':'.$uname.':'.$command,
+                       $uhome);
+    unless ($queryid=~/^$uhost\_/) { return 'error: '.$queryid; }
+    return get_query_reply($queryid);
+}
+
+sub get_query_reply {
+    my $queryid=shift;
+    my $replyfile=$perlvar{'lonDaemons'}.'/tmp/'.$queryid;
+    my $reply='';
+    for (1..100) {
+	sleep 2;
+        if (-e $replyfile.'.end') {
+	    if (my $fh=Apache::File->new($replyfile)) {
+               $reply.=<$fh>;
+               $fh->close;
+	   } else { return 'error: reply_file_error'; }
+           return &unescape($reply);
+	}
+    }
+    return 'timeout:'.$queryid;
+}
+
+sub courselog_query {
+#
+# possible filters:
+# url: url or symb
+# username
+# domain
+# action: view, submit, grade
+# start: timestamp
+# end: timestamp
+#
+    my (%filters)=@_;
+    unless ($ENV{'request.course.id'}) { return 'no_course'; }
+    if ($filters{'url'}) {
+	$filters{'url'}=&symbclean(&declutter($filters{'url'}));
+        $filters{'url'}=~s/\.(\w+)$/(\\.\\d+)*\\.$1/;
+        $filters{'url'}=~s/\.(\w+)\_\_\_/(\\.\\d+)*\\.$1/;
+    }
+    my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+    my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
+    return &log_query($cname,$cdom,'courselog',%filters);
+}
+
+sub userlog_query {
+    my ($uname,$udom,%filters)=@_;
+    return &log_query($uname,$udom,'userlog',%filters);
+}
+
 # ------------------------------------------------------------------ Plain Text
 
 sub plaintext {
@@ -1881,7 +1971,7 @@ sub modifyuser {
              (defined($desiredhome) ? ' desiredhome = '.$desiredhome :
                                      ' desiredhome not specified'). 
              ' by '.$ENV{'user.name'}.' at '.$ENV{'user.domain'});
-    my $uhome=&homeserver($uname,$udom);
+    my $uhome=&homeserver($uname,$udom,'true');
 # ----------------------------------------------------------------- Create User
     if (($uhome eq 'no_host') && ($umode) && ($upass)) {
         my $unhome='';
@@ -1911,7 +2001,7 @@ sub modifyuser {
 	unless ($reply eq 'ok') {
             return 'error: '.$reply;
         }   
-        $uhome=&homeserver($uname,$udom);
+        $uhome=&homeserver($uname,$udom,'true');
         if (($uhome eq '') || ($uhome eq 'no_host') || ($uhome ne $unhome)) {
 	    return 'error: verify home';
         }
@@ -2018,11 +2108,11 @@ sub createcourse {
    my $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).
        unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'};
 # ----------------------------------------------- Make sure that does not exist
-   my $uhome=&homeserver($uname,$udom);
+   my $uhome=&homeserver($uname,$udom,'true');
    unless (($uhome eq '') || ($uhome eq 'no_host')) {
        $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).
         unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'};
-       $uhome=&homeserver($uname,$udom);       
+       $uhome=&homeserver($uname,$udom,'true');       
        unless (($uhome eq '') || ($uhome eq 'no_host')) {
            return 'error: unable to generate unique course-ID';
        } 
@@ -2031,7 +2121,7 @@ sub createcourse {
     my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::',
                       $ENV{'user.home'});
     unless ($reply eq 'ok') { return 'error: '.$reply; }
-    $uhome=&homeserver($uname,$udom);
+    $uhome=&homeserver($uname,$udom,'true');
     if (($uhome eq '') || ($uhome eq 'no_host')) { 
 	return 'error: no such course';
     }
@@ -2269,7 +2359,11 @@ sub EXT {
             my %reply=&get($space,[$item]);
             return $reply{$item};
         }
-    } elsif ($realm eq 'request') {
+    } elsif ($realm eq 'query') {
+# ---------------------------------------------- pull stuff out of query string
+        &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},[$space]);
+	return $ENV{'form.'.$space}; 
+   } elsif ($realm eq 'request') {
 # ------------------------------------------------------------- request.browser
         if ($space eq 'browser') {
 	    return $ENV{'browser.'.$qualifier};
@@ -2600,10 +2694,15 @@ sub symbclean {
 
 sub symbread {
     my $thisfn=shift;
+# no filename provided? try from environment
     unless ($thisfn) {
         if ($ENV{'request.symb'}) { return &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)) { return &symbclean($thisfn); }
+    }
     $thisfn=declutter($thisfn);
     my %hash;
     my %bighash;
@@ -2632,6 +2731,10 @@ sub symbread {
               unless ($ids) { 
                  $ids=$bighash{'ids_/'.$thisfn};
               }
+              unless ($ids) {
+# alias?
+		  $ids=$bighash{'mapalias_'.$thisfn};
+              }
               if ($ids) {
 # ------------------------------------------------------------------- Has ID(s)
                  my @possibilities=split(/\,/,$ids);
@@ -2764,6 +2867,7 @@ sub hreflocation {
     unless (($file=~/^http:\/\//i) || ($file=~/^\//)) {
        my $finalpath=filelocation($dir,$file);
        $finalpath=~s/^\/home\/httpd\/html//;
+       $finalpath=~s-/home/(\w+)/public_html/-/~$1/-;
        return $finalpath;
     } else {
        return $file;
@@ -2777,6 +2881,7 @@ sub declutter {
     $thisfn=~s/^$perlvar{'lonDocRoot'}//;
     $thisfn=~s/^\///;
     $thisfn=~s/^res\///;
+    $thisfn=~s/\?.+$//;
     return $thisfn;
 }
 
@@ -2805,21 +2910,8 @@ sub goodbye {
 }
 
 BEGIN {
-# ------------------------------------------- Read access.conf and loncapa.conf
-# (eventually access.conf will become deprecated)
+# ----------------------------------- Read loncapa.conf and loncapa_apache.conf
     unless ($readit) {
-
-{
-    my $config=Apache::File->new("/etc/httpd/conf/access.conf");
-
-    while (my $configline=<$config>) {
-        if ($configline =~ /^[^\#]*PerlSetVar/) {
-	   my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
-           chomp($varvalue);
-           $perlvar{$varname}=$varvalue;
-        }
-    }
-}
 {
     my $config=Apache::File->new("/etc/httpd/conf/loncapa.conf");
 
@@ -3304,7 +3396,7 @@ 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
+meant to be "fairly clean" absolute reference, $dir is a directory that relative $file lookups are to looked in ($dir of /a/dir and a file of ../bob will become /a/bob)
 
 =item *