Diff for /loncom/lonnet/perl/lonnet.pm between versions 1.194 and 1.268

version 1.194, 2001/12/27 17:00:30 version 1.268, 2002/08/17 18:23:27
Line 64 Line 64
 # 12/5 Guy Albertelli  # 12/5 Guy Albertelli
 # 12/6,12/7,12/12 Gerd Kortemeyer  # 12/6,12/7,12/12 Gerd Kortemeyer
 # 12/18 Scott Harrison  # 12/18 Scott Harrison
 # 12/21,12/22,12/27 Gerd Kortemeyer  # 12/21,12/22,12/27,12/28 Gerd Kortemeyer
   # YEAR=2002
   # 1/4,2/4,2/7 Gerd Kortemeyer
 #  #
 ###  ###
   
Line 75  use Apache::File; Line 77  use Apache::File;
 use LWP::UserAgent();  use LWP::UserAgent();
 use HTTP::Headers;  use HTTP::Headers;
 use vars   use vars 
 qw(%perlvar %hostname %homecache %hostip %spareid %hostdom   qw(%perlvar %hostname %homecache %badServerCache %hostip %spareid %hostdom 
    %libserv %pr %prp %metacache %packagetab      %libserv %pr %prp %metacache %packagetab 
    %courselogs %accesshash $processmarker $dumpcount      %courselogs %accesshash $processmarker $dumpcount 
    %coursedombuf %coursehombuf);     %coursedombuf %coursehombuf %courseresdatacache %domaindescription);
 use IO::Socket;  use IO::Socket;
 use GDBM_File;  use GDBM_File;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use HTML::TokeParser;  use HTML::LCParser;
 use Fcntl qw(:flock);  use Fcntl qw(:flock);
   my $readit;
   
 # --------------------------------------------------------------------- Logging  # --------------------------------------------------------------------- Logging
   
Line 134  sub subreply { Line 137  sub subreply {
   
 sub reply {  sub reply {
     my ($cmd,$server)=@_;      my ($cmd,$server)=@_;
       unless (defined($hostname{$server})) { return 'no_such_host'; }
     my $answer=subreply($cmd,$server);      my $answer=subreply($cmd,$server);
     if ($answer eq 'con_lost') { $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);       
          #}   
       }
     if (($answer=~/^refused/) || ($answer=~/^rejected/)) {      if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
        &logthis("<font color=blue>WARNING:".         &logthis("<font color=blue>WARNING:".
                 " $cmd to $server returned $answer</font>");                  " $cmd to $server returned $answer</font>");
Line 345  sub spareserver { Line 364  sub spareserver {
     return $spareserver;      return $spareserver;
 }  }
   
   # --------------------------------------------- Try to change a user's password
   
   sub changepass {
       my ($uname,$udom,$currentpass,$newpass,$server)=@_;
       $currentpass = &escape($currentpass);
       $newpass     = &escape($newpass);
       my $answer = reply("encrypt:passwd:$udom:$uname:$currentpass:$newpass",
          $server);
       if (! $answer) {
    &logthis("No reply on password change request to $server ".
    "by $uname in domain $udom.");
       } elsif ($answer =~ "^ok") {
           &logthis("$uname in $udom successfully changed their password ".
    "on $server.");
       } elsif ($answer =~ "^pwchange_failure") {
    &logthis("$uname in $udom was unable to change their password ".
    "on $server.  The action was blocked by either lcpasswd ".
    "or pwchange");
       } elsif ($answer =~ "^non_authorized") {
           &logthis("$uname in $udom did not get their password correct when ".
    "attempting to change it on $server.");
       } elsif ($answer =~ "^auth_mode_error") {
           &logthis("$uname in $udom attempted to change their password despite ".
    "not being locally or internally authenticated on $server.");
       } elsif ($answer =~ "^unknown_user") {
           &logthis("$uname in $udom attempted to change their password ".
    "on $server but were unable to because $server is not ".
    "their home server.");
       } elsif ($answer =~ "^refused") {
    &logthis("$server refused to change $uname in $udom password because ".
    "it was sent an unencrypted request to change the password.");
       }
       return $answer;
   }
   
 # ----------------------- Try to determine user's current authentication scheme  # ----------------------- Try to determine user's current authentication scheme
   
 sub queryauthenticate {  sub queryauthenticate {
Line 388  sub queryauthenticate { Line 442  sub queryauthenticate {
 sub authenticate {  sub authenticate {
     my ($uname,$upass,$udom)=@_;      my ($uname,$upass,$udom)=@_;
     $upass=escape($upass);      $upass=escape($upass);
       $uname=~s/\W//g;
     if (($perlvar{'lonRole'} eq 'library') &&       if (($perlvar{'lonRole'} eq 'library') && 
         ($udom eq $perlvar{'lonDefDomain'})) {          ($udom eq $perlvar{'lonDefDomain'})) {
     my $answer=reply("encrypt:auth:$udom:$uname:$upass",$perlvar{'lonHostID'});      my $answer=reply("encrypt:auth:$udom:$uname:$upass",$perlvar{'lonHostID'});
Line 426  sub authenticate { Line 481  sub authenticate {
 # ---------------------- Find the homebase for a user from domain's lib servers  # ---------------------- Find the homebase for a user from domain's lib servers
   
 sub homeserver {  sub homeserver {
     my ($uname,$udom)=@_;      my ($uname,$udom,$ignoreBadCache)=@_;
   
     my $index="$uname:$udom";      my $index="$uname:$udom";
     if ($homecache{$index}) { return "$homecache{$index}"; }      if ($homecache{$index}) { 
           return "$homecache{$index}"; 
       }
     my $tryserver;      my $tryserver;
     foreach $tryserver (keys %libserv) {      foreach $tryserver (keys %libserv) {
           next if ($ignoreBadCache ne 'true' && 
    exists($badServerCache{$tryserver}));
  if ($hostdom{$tryserver} eq $udom) {   if ($hostdom{$tryserver} eq $udom) {
            my $answer=reply("home:$udom:$uname",$tryserver);             my $answer=reply("home:$udom:$uname",$tryserver);
            if ($answer eq 'found') {              if ($answer eq 'found') { 
       $homecache{$index}=$tryserver;                $homecache{$index}=$tryserver;
               return $tryserver;                 return $tryserver; 
    }             } elsif ($answer eq 'no_host') {
          $badServerCache{$tryserver}=1;
              }
        }         }
     }          }    
     return 'no_host';      return 'no_host';
Line 549  sub userenvironment { Line 608  sub userenvironment {
     return %returnhash;      return %returnhash;
 }  }
   
   # -------------------------------------------------------------------- New chat
   
   sub chatsend {
       my ($newentry,$anon)=@_;
       my $cnum=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
       my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
       my $chome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'};
       &reply('chatsend:'.$cdom.':'.$cnum.':'.
      &escape($ENV{'user.domain'}.':'.$ENV{'user.name'}.':'.$anon.':'.
      &escape($newentry)),$chome);
   }
   
 # ----------------------------- Subscribe to a resource, return URL if possible  # ----------------------------- Subscribe to a resource, return URL if possible
   
 sub subscribe {  sub subscribe {
Line 572  sub subscribe { Line 643  sub subscribe {
 sub repcopy {  sub repcopy {
     my $filename=shift;      my $filename=shift;
     $filename=~s/\/+/\//g;      $filename=~s/\/+/\//g;
       if ($filename=~/^\/home\/httpd\/html\/adm\//) { return OK; }
     my $transname="$filename.in.transfer";      my $transname="$filename.in.transfer";
     if ((-e $filename) || (-e $transname)) { return OK; }      if ((-e $filename) || (-e $transname)) { return OK; }
     my $remoteurl=subscribe($filename);      my $remoteurl=subscribe($filename);
Line 637  sub ssi { Line 709  sub ssi {
           
     if (%form) {      if (%form) {
       $request=new HTTP::Request('POST',"http://".$ENV{'HTTP_HOST'}.$fn);        $request=new HTTP::Request('POST',"http://".$ENV{'HTTP_HOST'}.$fn);
       $request->content(join '&', map { "$_=$form{$_}" } keys %form);        $request->content(join('&',map { &escape($_).'='.&escape($form{$_}) } keys %form));
     } else {      } else {
       $request=new HTTP::Request('GET',"http://".$ENV{'HTTP_HOST'}.$fn);        $request=new HTTP::Request('GET',"http://".$ENV{'HTTP_HOST'}.$fn);
     }      }
Line 648  sub ssi { Line 720  sub ssi {
     return $response->content;      return $response->content;
 }  }
   
   # ------- Add a token to a remote URI's query string to vouch for access rights
   
   sub tokenwrapper {
       my $uri=shift;
       $uri=~s/^http\:\/\/([^\/]+)//;
       $uri=~s/^\///;
       $ENV{'user.environment'}=~/\/([^\/]+)\.id/;
       my $token=$1;
       if ($uri=~/^uploaded\/([^\/]+)\/([^\/]+)\/([^\/]+)(\?\.*)*$/) {
    &appenv('userfile.'.$1.'/'.$2.'/'.$3 => $ENV{'request.course.id'});
           return 'http://'.$hostname{ &homeserver($2,$1)}.'/'.$uri.
                  (($uri=~/\?/)?'&':'?').'token='.$token;
       } else {
    return '/adm/notfound.html';
       }
   }
       
   # --------------- Take an uploaded file and put it into the userfiles directory
   # input: name of form element, coursedoc=1 means this is for the course
   # output: url of file in userspace
   
   sub userfileupload {
       my ($formname,$coursedoc)=@_;
       my $fname=$ENV{'form.'.$formname.'.filename'};
       $fname=~s/\\/\//g;
       $fname=~s/^.*\/([^\/]+)$/$1/;
       unless ($fname) { return 'error: no uploaded file'; }
       chop($ENV{'form.'.$formname});
   # Create the directory if not present
       my $docuname='';
       my $docudom='';
       my $docuhome='';
       if ($coursedoc) {
    $docuname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
    $docudom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
    $docuhome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'};
       } else {
           $docuname=$ENV{'user.name'};
           $docudom=$ENV{'user.domain'};
           $docuhome=$ENV{'user.home'};
       }
       my $path=$docudom.'/'.$docuname.'/';
       my $filepath=$perlvar{'lonDocRoot'};
       my @parts=split(/\//,$filepath.'/userfiles/'.$path);
       my $count;
       for ($count=4;$count<=$#parts;$count++) {
           $filepath.="/$parts[$count]";
           if ((-e $filepath)!=1) {
       mkdir($filepath,0777);
           }
       }
   # Save the file
       {
          my $fh=Apache::File->new('>'.$filepath.'/'.$fname);
          print $fh $ENV{'form.'.$formname};
       }
   # Notify homeserver to grep it
   #
       if 
   (&reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$fname,$docuhome) eq 'ok') 
       {
   #
   # Return the URL to it
           return '/uploaded/'.$path.$fname;
       } else {
           return '/adm/notfound.html';
       }    
   }
   
 # ------------------------------------------------------------------------- Log  # ------------------------------------------------------------------------- Log
   
 sub log {  sub log {
Line 679  sub flushcourselogs { Line 820  sub flushcourselogs {
         my $entry=$_;          my $entry=$_;
         $entry=~/\_\_\_(\w+)\/(\w+)\/(.*)\_\_\_(\w+)$/;          $entry=~/\_\_\_(\w+)\/(\w+)\/(.*)\_\_\_(\w+)$/;
         my %temphash=($entry => $accesshash{$entry});          my %temphash=($entry => $accesshash{$entry});
         if (&Apache::lonnet::put('resevaldata',\%temphash,$1,$2) eq 'ok') {          if (&Apache::lonnet::put('nohist_resevaldata',\%temphash,$1,$2) eq 'ok') {
     delete $accesshash{$entry};      delete $accesshash{$entry};
         }          }
     }      }
Line 739  sub checkout { Line 880  sub checkout {
     my $now=time;      my $now=time;
     my $lonhost=$perlvar{'lonHostID'};      my $lonhost=$perlvar{'lonHostID'};
     my $infostr=&escape(      my $infostr=&escape(
                    'CHECKOUTTOKEN&'.
                  $tuname.'&'.                   $tuname.'&'.
                  $tudom.'&'.                   $tudom.'&'.
                  $tcrsid.'&'.                   $tcrsid.'&'.
Line 788  sub checkin { Line 930  sub checkin {
     $lonhost=~tr/A-Z/a-z/;      $lonhost=~tr/A-Z/a-z/;
     my $dtoken=$ta.'_'.$hostip{$lonhost}.'_'.$tb;      my $dtoken=$ta.'_'.$hostip{$lonhost}.'_'.$tb;
     $dtoken=~s/\W/\_/g;      $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)));                   split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));
   
     unless (($tuname) && ($tudom)) {      unless (($tuname) && ($tudom)) {
Line 858  sub devalidate { Line 1000  sub devalidate {
     }      }
 }  }
   
   sub get_scalar {
       my ($string,$end) = @_;
       my $value;
       if ($$string =~ s/^([^&]*?)($end)/$2/) {
    $value = $1;
       } elsif ($$string =~ s/^([^&]*?)&//) {
    $value = $1;
       }
       return &unescape($value);
   }
   
   sub array2str {
     my (@array) = @_;
     my $result=&arrayref2str(\@array);
     $result=~s/^__ARRAY_REF__//;
     $result=~s/__END_ARRAY_REF__$//;
     return $result;
   }
   
   sub arrayref2str {
     my ($arrayref) = @_;
     my $result='__ARRAY_REF__';
     foreach my $elem (@$arrayref) {
       if(ref($elem) eq 'ARRAY') {
         $result.=&arrayref2str($elem).'&';
       } elsif(ref($elem) eq 'HASH') {
         $result.=&hashref2str($elem).'&';
       } elsif(ref($elem)) {
         #print("Got a ref of ".(ref($elem))." skipping.");
       } else {
         $result.=&escape($elem).'&';
       }
     }
     $result=~s/\&$//;
     $result .= '__END_ARRAY_REF__';
     return $result;
   }
   
 sub hash2str {  sub hash2str {
   my (%hash)=@_;    my (%hash) = @_;
   my $result='';    my $result=&hashref2str(\%hash);
   foreach (keys %hash) { $result.=escape($_).'='.escape($hash{$_}).'&'; }    $result=~s/^__HASH_REF__//;
     $result=~s/__END_HASH_REF__$//;
     return $result;
   }
   
   sub hashref2str {
     my ($hashref)=@_;
     my $result='__HASH_REF__';
     foreach (keys(%$hashref)) {
       if (ref($_) eq 'ARRAY') {
         $result.=&arrayref2str($_).'=';
       } elsif (ref($_) eq 'HASH') {
         $result.=&hashref2str($_).'=';
       } elsif (ref($_)) {
         $result.='=';
         #print("Got a ref of ".(ref($_))." skipping.");
       } else {
    if ($_) {$result.=&escape($_).'=';} else { last; }
       }
   
       if(ref($hashref->{$_}) eq 'ARRAY') {
         $result.=&arrayref2str($hashref->{$_}).'&';
       } elsif(ref($hashref->{$_}) eq 'HASH') {
         $result.=&hashref2str($hashref->{$_}).'&';
       } elsif(ref($hashref->{$_})) {
          $result.='&';
         #print("Got a ref of ".(ref($hashref->{$_}))." skipping.");
       } else {
         $result.=&escape($hashref->{$_}).'&';
       }
     }
   $result=~s/\&$//;    $result=~s/\&$//;
     $result .= '__END_HASH_REF__';
   return $result;    return $result;
 }  }
   
 sub str2hash {  sub str2hash {
       my ($string)=@_;
       my ($hash)=&str2hashref('__HASH_REF__'.$string.'__END_HASH_REF__');
       return %$hash;
   }
   
   sub str2hashref {
   my ($string) = @_;    my ($string) = @_;
   my %returnhash;  
   foreach (split(/\&/,$string)) {    my %hash;
     my ($name,$value)=split(/\=/,$_);  
     $returnhash{&unescape($name)}=&unescape($value);    if($string !~ /^__HASH_REF__/) {
         if (! ($string eq '' || !defined($string))) {
     $hash{'error'}='Not hash reference';
         }
         return (\%hash, $string);
   }    }
   return %returnhash;  
     $string =~ s/^__HASH_REF__//;
   
     while($string !~ /^__END_HASH_REF__/) {
         #key
         my $key='';
         if($string =~ /^__HASH_REF__/) {
             ($key, $string)=&str2hashref($string);
             if(defined($key->{'error'})) {
                 $hash{'error'}='Bad data';
                 return (\%hash, $string);
             }
         } elsif($string =~ /^__ARRAY_REF__/) {
             ($key, $string)=&str2arrayref($string);
             if($key->[0] eq 'Array reference error') {
                 $hash{'error'}='Bad data';
                 return (\%hash, $string);
             }
         } else {
             $string =~ s/^(.*?)=//;
     $key=&unescape($1);
         }
         $string =~ s/^=//;
   
         #value
         my $value='';
         if($string =~ /^__HASH_REF__/) {
             ($value, $string)=&str2hashref($string);
             if(defined($value->{'error'})) {
                 $hash{'error'}='Bad data';
                 return (\%hash, $string);
             }
         } elsif($string =~ /^__ARRAY_REF__/) {
             ($value, $string)=&str2arrayref($string);
             if($value->[0] eq 'Array reference error') {
                 $hash{'error'}='Bad data';
                 return (\%hash, $string);
             }
         } else {
     $value=&get_scalar(\$string,'__END_HASH_REF__');
         }
         $string =~ s/^&//;
   
         $hash{$key}=$value;
     }
   
     $string =~ s/^__END_HASH_REF__//;
   
     return (\%hash, $string);
   }
   
   sub str2array {
       my ($string)=@_;
       my ($array)=&str2arrayref('__ARRAY_REF__'.$string.'__END_ARRAY_REF__');
       return @$array;
   }
   
   sub str2arrayref {
     my ($string) = @_;
     my @array;
   
     if($string !~ /^__ARRAY_REF__/) {
         if (! ($string eq '' || !defined($string))) {
     $array[0]='Array reference error';
         }
         return (\@array, $string);
     }
   
     $string =~ s/^__ARRAY_REF__//;
   
     while($string !~ /^__END_ARRAY_REF__/) {
         my $value='';
         if($string =~ /^__HASH_REF__/) {
             ($value, $string)=&str2hashref($string);
             if(defined($value->{'error'})) {
                 $array[0] ='Array reference error';
                 return (\@array, $string);
             }
         } elsif($string =~ /^__ARRAY_REF__/) {
             ($value, $string)=&str2arrayref($string);
             if($value->[0] eq 'Array reference error') {
                 $array[0] ='Array reference error';
                 return (\@array, $string);
             }
         } else {
     $value=&get_scalar(\$string,'__END_ARRAY_REF__');
         }
         $string =~ s/^&//;
   
         push(@array, $value);
     }
   
     $string =~ s/^__END_ARRAY_REF__//;
   
     return (\@array, $string);
 }  }
   
 # -------------------------------------------------------------------Temp Store  # -------------------------------------------------------------------Temp Store
Line 897  sub tmpreset { Line 1212  sub tmpreset {
   my %hash;    my %hash;
   if (tie(%hash,'GDBM_File',    if (tie(%hash,'GDBM_File',
   $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',    $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
   &GDBM_WRCREAT,0640)) {    &GDBM_WRCREAT(),0640)) {
     foreach my $key (keys %hash) {      foreach my $key (keys %hash) {
       if ($key=~ /:$symb/) {        if ($key=~ /:$symb/) {
  delete($hash{$key});   delete($hash{$key});
Line 933  sub tmpstore { Line 1248  sub tmpstore {
   my $path=$perlvar{'lonDaemons'}.'/tmp';    my $path=$perlvar{'lonDaemons'}.'/tmp';
   if (tie(%hash,'GDBM_File',    if (tie(%hash,'GDBM_File',
   $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',    $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
   &GDBM_WRCREAT,0640)) {    &GDBM_WRCREAT(),0640)) {
     $hash{"version:$symb"}++;      $hash{"version:$symb"}++;
     my $version=$hash{"version:$symb"};      my $version=$hash{"version:$symb"};
     my $allkeys='';       my $allkeys=''; 
Line 977  sub tmprestore { Line 1292  sub tmprestore {
   my $path=$perlvar{'lonDaemons'}.'/tmp';    my $path=$perlvar{'lonDaemons'}.'/tmp';
   if (tie(%hash,'GDBM_File',    if (tie(%hash,'GDBM_File',
   $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',    $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
   &GDBM_READER,0640)) {    &GDBM_READER(),0640)) {
     my $version=$hash{"version:$symb"};      my $version=$hash{"version:$symb"};
     $returnhash{'version'}=$version;      $returnhash{'version'}=$version;
     my $scope;      my $scope;
Line 1008  sub store { Line 1323  sub store {
   
     if ($stuname) { $home=&homeserver($stuname,$domain); }      if ($stuname) { $home=&homeserver($stuname,$domain); }
   
       $symb=&symbclean($symb);
     if (!$symb) { unless ($symb=&symbread()) { return ''; } }      if (!$symb) { unless ($symb=&symbread()) { return ''; } }
   
     &devalidate($symb);      &devalidate($symb);
Line 1038  sub cstore { Line 1354  sub cstore {
   
     if ($stuname) { $home=&homeserver($stuname,$domain); }      if ($stuname) { $home=&homeserver($stuname,$domain); }
   
       $symb=&symbclean($symb);
     if (!$symb) { unless ($symb=&symbread()) { return ''; } }      if (!$symb) { unless ($symb=&symbread()) { return ''; } }
   
     &devalidate($symb);      &devalidate($symb);
Line 1073  sub restore { Line 1390  sub restore {
     if (!$symb) {      if (!$symb) {
       unless ($symb=escape(&symbread())) { return ''; }        unless ($symb=escape(&symbread())) { return ''; }
     } else {      } else {
       $symb=&escape($symb);        $symb=&escape(&symbclean($symb));
     }      }
     if (!$namespace) {       if (!$namespace) { 
        unless ($namespace=$ENV{'request.course.id'}) {          unless ($namespace=$ENV{'request.course.id'}) { 
Line 1363  sub allowed { Line 1680  sub allowed {
 # Free bre to public access  # Free bre to public access
   
     if ($priv eq 'bre') {      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 '';
               }
           }
           if ($ENV{'request.role'}=~ /li\.\//) {
               # Library role, so allow browsing of resources in this domain.
               return 'F';
           }
       }
       # Domain coordinator is trying to create a course
       if (($priv eq 'ccc') && ($ENV{'request.role'} =~ /^dc\./)) {
           # uri is the requested domain in this case.
           # comparison to 'request.role.domain' shows if the user has selected
           # a role of dc for the domain in question. 
           return 'F' if ($uri eq $ENV{'request.role.domain'});
     }      }
   
     my $thisallowed='';      my $thisallowed='';
Line 1411  sub allowed { Line 1753  sub allowed {
 # the course  # the course
   
     if ($ENV{'request.course.id'}) {      if ($ENV{'request.course.id'}) {
   
        $courseprivid=$ENV{'request.course.id'};         $courseprivid=$ENV{'request.course.id'};
        if ($ENV{'request.course.sec'}) {         if ($ENV{'request.course.sec'}) {
           $courseprivid.='/'.$ENV{'request.course.sec'};            $courseprivid.='/'.$ENV{'request.course.sec'};
        }         }
        $courseprivid=~s/\_/\//;         $courseprivid=~s/\_/\//;
        my $checkreferer=1;         my $checkreferer=1;
        my @uriparts=split(/\//,$uri);         my ($match,$cond)=&is_on_map($uri);
        my $filename=$uriparts[$#uriparts];         if ($match) {
        my $pathname=$uri;             $statecond=$cond;
        $pathname=~s/\/$filename$//;  
        if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~  
            /\&$filename\:([\d\|]+)\&/) {  
            $statecond=$1;  
            if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}             if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
                =~/$priv\&([^\:]*)/) {                 =~/$priv\&([^\:]*)/) {
                $thisallowed.=$1;                 $thisallowed.=$1;
Line 1433  sub allowed { Line 1772  sub allowed {
                 
        if ($checkreferer) {         if ($checkreferer) {
   my $refuri=$ENV{'httpref.'.$orguri};    my $refuri=$ENV{'httpref.'.$orguri};
   
             unless ($refuri) {              unless ($refuri) {
                 foreach (keys %ENV) {                  foreach (keys %ENV) {
     if ($_=~/^httpref\..*\*/) {      if ($_=~/^httpref\..*\*/) {
Line 1447  sub allowed { Line 1785  sub allowed {
                     }                      }
                 }                  }
             }              }
   
          if ($refuri) {            if ($refuri) { 
   $refuri=&declutter($refuri);    $refuri=&declutter($refuri);
           my @uriparts=split(/\//,$refuri);            my ($match,$cond)=&is_on_map($refuri);
           my $filename=$uriparts[$#uriparts];              if ($match) {
           my $pathname=$refuri;                my $refstatecond=$cond;
           $pathname=~s/\/$filename$//;  
             if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~  
               /\&$filename\:([\d\|]+)\&/) {  
               my $refstatecond=$1;  
               if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}                if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
                   =~/$priv\&([^\:]*)/) {                    =~/$priv\&([^\:]*)/) {
                   $thisallowed.=$1;                    $thisallowed.=$1;
Line 1514  sub allowed { Line 1849  sub allowed {
                 || ($ENV{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {                  || ($ENV{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {
    if ($ENV{$prefix.'res.'.$uri.'.lock.expire'}>time) {     if ($ENV{$prefix.'res.'.$uri.'.lock.expire'}>time) {
                        &log($ENV{'user.domain'},$ENV{'user.name'},                         &log($ENV{'user.domain'},$ENV{'user.name'},
                             $ENV{'user.host'},                              $ENV{'user.home'},
                             'Locked by res: '.$priv.' for '.$uri.' due to '.                              'Locked by res: '.$priv.' for '.$uri.' due to '.
                             $cdom.'/'.$cnum.'/'.$csec.' expire '.                              $cdom.'/'.$cnum.'/'.$csec.' expire '.
                             $ENV{$prefix.'priv.'.$priv.'.lock.expire'});                              $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
Line 1525  sub allowed { Line 1860  sub allowed {
                 || ($ENV{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {                  || ($ENV{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {
    if ($ENV{'priv.'.$priv.'.lock.expire'}>time) {     if ($ENV{'priv.'.$priv.'.lock.expire'}>time) {
                        &log($ENV{'user.domain'},$ENV{'user.name'},                         &log($ENV{'user.domain'},$ENV{'user.name'},
                             $ENV{'user.host'},                              $ENV{'user.home'},
                             'Locked by priv: '.$priv.' for '.$uri.' due to '.                              'Locked by priv: '.$priv.' for '.$uri.' due to '.
                             $cdom.'/'.$cnum.'/'.$csec.' expire '.                              $cdom.'/'.$cnum.'/'.$csec.' expire '.
                             $ENV{$prefix.'priv.'.$priv.'.lock.expire'});                              $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
Line 1553  sub allowed { Line 1888  sub allowed {
   
    if ($thisallowed=~/C/) {     if ($thisallowed=~/C/) {
        my $rolecode=(split(/\./,$ENV{'request.role'}))[0];         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'}         if ($ENV{'course.'.$ENV{'request.course.id'}.'.'.$priv.'.roles.denied'}
    =~/$rolecode/) {     =~/$rolecode/) {
            &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},             &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},
Line 1560  sub allowed { Line 1896  sub allowed {
                 $ENV{'request.course.id'});                  $ENV{'request.course.id'});
            return '';             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  # Resource preferences
Line 1583  sub allowed { Line 1927  sub allowed {
        }         }
    }     }
   
 # Restricted by state?  # Restricted by state or randomout?
   
    if ($thisallowed=~/X/) {     if ($thisallowed=~/X/) {
         if ($ENV{'acc.randomout'}) {
            my $symb=&symbread($uri,1);
            if (($symb) && ($ENV{'acc.randomout'}=~/\&$symb\&/)) { 
               return ''; 
            }
         }
       if (&condval($statecond)) {        if (&condval($statecond)) {
  return '2';   return '2';
       } else {        } else {
Line 1596  sub allowed { Line 1946  sub allowed {
    return 'F';     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  # ----------------------------------------------------------------- Define Role
   
 sub definerole {  sub definerole {
Line 1641  sub definerole { Line 2008  sub definerole {
 # ---------------- Make a metadata query against the network of library servers  # ---------------- Make a metadata query against the network of library servers
   
 sub metadata_query {  sub metadata_query {
     my ($query,$custom,$customshow)=@_;      my ($query,$custom,$customshow,$server_array)=@_;
     my %rhash;      my %rhash;
     for my $server (keys %libserv) {      my @server_list = (defined($server_array) ? @$server_array
                                                 : keys(%libserv) );
       for my $server (@server_list) {
  unless ($custom or $customshow) {   unless ($custom or $customshow) {
     my $reply=&reply("querysend:".&escape($query),$server);      my $reply=&reply("querysend:".&escape($query),$server);
     $rhash{$server}=$reply;      $rhash{$server}=$reply;
Line 1658  sub metadata_query { Line 2027  sub metadata_query {
     return \%rhash;      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  # ------------------------------------------------------------------ Plain Text
   
 sub plaintext {  sub plaintext {
Line 1703  sub assignrole { Line 2130  sub assignrole {
 }  }
   
 # -------------------------------------------------- Modify user authentication  # -------------------------------------------------- Modify user authentication
   # Overrides without validation
   
 sub modifyuserauth {  sub modifyuserauth {
     my ($udom,$uname,$umode,$upass)=@_;      my ($udom,$uname,$umode,$upass)=@_;
     my $uhome=&homeserver($uname,$udom);      my $uhome=&homeserver($uname,$udom);
     &logthis('Call to modify user authentication'.$udom.', '.$uname.', '.      unless (&allowed('mau',$udom)) { return 'refused'; }
       &logthis('Call to modify user authentication '.$udom.', '.$uname.', '.
              $umode.' by '.$ENV{'user.name'}.' at '.$ENV{'user.domain'});                 $umode.' by '.$ENV{'user.name'}.' at '.$ENV{'user.domain'});  
     my $reply=&reply('encrypt:changeuserauth:'.$udom.':'.$uname.':'.$umode.':'.      my $reply=&reply('encrypt:changeuserauth:'.$udom.':'.$uname.':'.$umode.':'.
      &escape($upass),$uhome);       &escape($upass),$uhome);
       &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.home'},
           'Authentication changed for '.$udom.', '.$uname.', '.$umode.
            '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply);
       &log($udom,,$uname,$uhome,
           'Authentication changed by '.$ENV{'user.domain'}.', '.
                                        $ENV{'user.name'}.', '.$umode.
            '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply);
     unless ($reply eq 'ok') {      unless ($reply eq 'ok') {
           &logthis('Authentication mode error: '.$reply);
  return 'error: '.$reply;   return 'error: '.$reply;
     }         }   
     return 'ok';      return 'ok';
Line 1718  sub modifyuserauth { Line 2156  sub modifyuserauth {
   
 # --------------------------------------------------------------- Modify a user  # --------------------------------------------------------------- Modify a user
   
   
 sub modifyuser {  sub modifyuser {
     my ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene)=@_;      my ($udom,    $uname, $uid,
           $umode,   $upass, $first,
           $middle,  $last,  $gene,
           $forceid, $desiredhome)=@_;
       $udom=~s/\W//g;
       $uname=~s/\W//g;
     &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.      &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.
              $umode.', '.$first.', '.$middle.', '.               $umode.', '.$first.', '.$middle.', '.
      $last.', '.$gene.' by '.       $last.', '.$gene.'(forceid: '.$forceid.')'.
              $ENV{'user.name'}.' at '.$ENV{'user.domain'});                 (defined($desiredhome) ? ' desiredhome = '.$desiredhome :
     my $uhome=&homeserver($uname,$udom);                                       ' desiredhome not specified'). 
                ' by '.$ENV{'user.name'}.' at '.$ENV{'user.domain'});
       my $uhome=&homeserver($uname,$udom,'true');
 # ----------------------------------------------------------------- Create User  # ----------------------------------------------------------------- Create User
     if (($uhome eq 'no_host') && ($umode) && ($upass)) {      if (($uhome eq 'no_host') && ($umode) && ($upass)) {
         my $unhome='';          my $unhome='';
  if ($ENV{'course.'.$ENV{'request.course.id'}.'.domain'} eq $udom) {          if (defined($desiredhome) && $hostdom{$desiredhome} eq $udom) { 
               $unhome = $desiredhome;
    } elsif($ENV{'course.'.$ENV{'request.course.id'}.'.domain'} eq $udom) {
     $unhome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'};      $unhome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'};
         } else {          } else { # load balancing routine for determining $unhome
             my $tryserver;              my $tryserver;
             my $loadm=10000000;              my $loadm=10000000;
             foreach $tryserver (keys %libserv) {              foreach $tryserver (keys %libserv) {
Line 1745  sub modifyuser { Line 2191  sub modifyuser {
     }      }
         }          }
         if (($unhome eq '') || ($unhome eq 'no_host')) {          if (($unhome eq '') || ($unhome eq 'no_host')) {
     return 'error: find home';      return 'error: unable to find a home server for '.$uname.
                      ' in domain '.$udom;
         }          }
         my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':'.$umode.':'.          my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':'.$umode.':'.
                          &escape($upass),$unhome);                           &escape($upass),$unhome);
  unless ($reply eq 'ok') {   unless ($reply eq 'ok') {
             return 'error: '.$reply;              return 'error: '.$reply;
         }             }   
         $uhome=&homeserver($uname,$udom);          $uhome=&homeserver($uname,$udom,'true');
         if (($uhome eq '') || ($uhome eq 'no_host') || ($uhome ne $unhome)) {          if (($uhome eq '') || ($uhome eq 'no_host') || ($uhome ne $unhome)) {
     return 'error: verify home';      return 'error: verify home';
         }          }
     }      }   # End of creation of new user
 # ---------------------------------------------------------------------- Add ID  # ---------------------------------------------------------------------- Add ID
     if ($uid) {      if ($uid) {
        $uid=~tr/A-Z/a-z/;         $uid=~tr/A-Z/a-z/;
        my %uidhash=&idrget($udom,$uname);         my %uidhash=&idrget($udom,$uname);
        if (($uidhash{$uname}) && ($uidhash{$uname}!~/error\:/)) {         if (($uidhash{$uname}) && ($uidhash{$uname}!~/error\:/) 
            && (!$forceid)) {
   unless ($uid eq $uidhash{$uname}) {    unless ($uid eq $uidhash{$uname}) {
       return 'error: mismatch '.$uidhash{$uname}.' versus '.$uid;        return 'error: mismatch '.$uidhash{$uname}.' versus '.$uid;
           }            }
Line 1773  sub modifyuser { Line 2221  sub modifyuser {
     my %names=&get('environment',      my %names=&get('environment',
    ['firstname','middlename','lastname','generation'],     ['firstname','middlename','lastname','generation'],
    $udom,$uname);     $udom,$uname);
       if ($names{'firstname'} =~ m/^error:.*/) { %names=(); }
     if ($first)  { $names{'firstname'}  = $first; }      if ($first)  { $names{'firstname'}  = $first; }
     if ($middle) { $names{'middlename'} = $middle; }      if ($middle) { $names{'middlename'} = $middle; }
     if ($last)   { $names{'lastname'}   = $last; }      if ($last)   { $names{'lastname'}   = $last; }
Line 1790  sub modifyuser { Line 2239  sub modifyuser {
   
 sub modifystudent {  sub modifystudent {
     my ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec,      my ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec,
         $end,$start)=@_;          $end,$start,$forceid,$desiredhome)=@_;
     my $cid='';      my $cid='';
     unless ($cid=$ENV{'request.course.id'}) {      unless ($cid=$ENV{'request.course.id'}) {
  return 'not_in_class';   return 'not_in_class';
     }      }
 # --------------------------------------------------------------- Make the user  # --------------------------------------------------------------- Make the user
     my $reply=&modifyuser      my $reply=&modifyuser
  ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene);   ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$forceid,
            $desiredhome);
     unless ($reply eq 'ok') { return $reply; }      unless ($reply eq 'ok') { return $reply; }
     my $uhome=&homeserver($uname,$udom);      my $uhome=&homeserver($uname,$udom);
     if (($uhome eq '') || ($uhome eq 'no_host')) {       if (($uhome eq '') || ($uhome eq 'no_host')) { 
Line 1843  sub writecoursepref { Line 2293  sub writecoursepref {
 # ---------------------------------------------------------- Make/modify course  # ---------------------------------------------------------- Make/modify course
   
 sub createcourse {  sub createcourse {
     my ($udom,$description,$url)=@_;      my ($udom,$description,$url,$course_server)=@_;
     $url=&declutter($url);      $url=&declutter($url);
     my $cid='';      my $cid='';
     unless (&allowed('ccc',$ENV{'user.domain'})) {      unless (&allowed('ccc',$udom)) {
         return 'refused';  
     }  
     unless ($udom eq $ENV{'user.domain'}) {  
         return 'refused';          return 'refused';
     }      }
 # ------------------------------------------------------------------- Create ID  # ------------------------------------------------------------------- Create ID
    my $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).     my $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).
        unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'};         unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'};
 # ----------------------------------------------- Make sure that does not exist  # ----------------------------------------------- Make sure that does not exist
    my $uhome=&homeserver($uname,$udom);     my $uhome=&homeserver($uname,$udom,'true');
    unless (($uhome eq '') || ($uhome eq 'no_host')) {     unless (($uhome eq '') || ($uhome eq 'no_host')) {
        $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).         $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).
         unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'};          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')) {         unless (($uhome eq '') || ($uhome eq 'no_host')) {
            return 'error: unable to generate unique course-ID';             return 'error: unable to generate unique course-ID';
        }          } 
    }     }
   # ------------------------------------------------ Check supplied server name
       $course_server = $ENV{'user.homeserver'} if (! defined($course_server));
       if (! exists($libserv{$course_server})) {
           return 'error:bad server name '.$course_server;
       }
 # ------------------------------------------------------------- Make the course  # ------------------------------------------------------------- Make the course
     my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::',      my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::',
                       $ENV{'user.home'});                        $course_server);
     unless ($reply eq 'ok') { return 'error: '.$reply; }      unless ($reply eq 'ok') { return 'error: '.$reply; }
     $uhome=&homeserver($uname,$udom);      $uhome=&homeserver($uname,$udom,'true');
     if (($uhome eq '') || ($uhome eq 'no_host')) {       if (($uhome eq '') || ($uhome eq 'no_host')) { 
  return 'error: no such course';   return 'error: no such course';
     }      }
Line 1906  sub revokecustomrole { Line 2358  sub revokecustomrole {
 # ------------------------------------------------------------ Directory lister  # ------------------------------------------------------------ Directory lister
   
 sub dirlist {  sub dirlist {
     my $uri=shift;      my ($uri,$userdomain,$username,$alternateDirectoryRoot)=@_;
   
     $uri=~s/^\///;      $uri=~s/^\///;
     $uri=~s/\/$//;      $uri=~s/\/$//;
     my ($res,$udom,$uname,@rest)=split(/\//,$uri);      my ($udom, $uname);
     if ($udom) {      (undef,$udom,$uname)=split(/\//,$uri);
      if ($uname) {      if(defined($userdomain)) {
        my $listing=reply('ls:'.$perlvar{'lonDocRoot'}.'/'.$uri,          $udom = $userdomain;
                       homeserver($uname,$udom));      }
        return split(/:/,$listing);      if(defined($username)) {
      } else {          $uname = $username;
        my $tryserver;      }
        my %allusers=();  
        foreach $tryserver (keys %libserv) {      my $dirRoot = $perlvar{'lonDocRoot'};
   if ($hostdom{$tryserver} eq $udom) {      if(defined($alternateDirectoryRoot)) {
              my $listing=reply('ls:'.$perlvar{'lonDocRoot'}.'/res/'.$udom,          $dirRoot = $alternateDirectoryRoot;
        $tryserver);          $dirRoot =~ s/\/$//;
              if (($listing ne 'no_such_dir') && ($listing ne 'empty')      }
               && ($listing ne 'con_lost')) {  
                 foreach (split(/:/,$listing)) {      if($udom) {
                   my ($entry,@stat)=split(/&/,$_);          if($uname) {
                   $allusers{$entry}=1;              my $listing=reply('ls:'.$dirRoot.'/'.$uri,
                                 homeserver($uname,$udom));
               return split(/:/,$listing);
           } elsif(!defined($alternateDirectoryRoot)) {
               my $tryserver;
               my %allusers=();
               foreach $tryserver (keys %libserv) {
                   if($hostdom{$tryserver} eq $udom) {
                       my $listing=reply('ls:'.$perlvar{'lonDocRoot'}.'/res/'.
                                         $udom, $tryserver);
                       if (($listing ne 'no_such_dir') && ($listing ne 'empty')
                           && ($listing ne 'con_lost')) {
                           foreach (split(/:/,$listing)) {
                               my ($entry,@stat)=split(/&/,$_);
                               $allusers{$entry}=1;
                           }
                       }
                 }                  }
              }              }
   }              my $alluserstr='';
        }              foreach (sort keys %allusers) {
        my $alluserstr='';                  $alluserstr.=$_.'&user:';
        foreach (sort keys %allusers) {              }
            $alluserstr.=$_.'&user:';              $alluserstr=~s/:$//;
        }              return split(/:/,$alluserstr);
        $alluserstr=~s/:$//;          } else {
        return split(/:/,$alluserstr);              my @emptyResults = ();
      }               push(@emptyResults, 'missing user name');
    } else {              return split(':',@emptyResults);
        my $tryserver;          }
        my %alldom=();      } elsif(!defined($alternateDirectoryRoot)) {
        foreach $tryserver (keys %libserv) {          my $tryserver;
    $alldom{$hostdom{$tryserver}}=1;          my %alldom=();
        }          foreach $tryserver (keys %libserv) {
        my $alldomstr='';              $alldom{$hostdom{$tryserver}}=1;
        foreach (sort keys %alldom) {          }
           $alldomstr.=$perlvar{'lonDocRoot'}.'/res/'.$_.'&domain:';          my $alldomstr='';
        }          foreach (sort keys %alldom) {
        $alldomstr=~s/:$//;              $alldomstr.=$perlvar{'lonDocRoot'}.'/res/'.$_.'&domain:';
        return split(/:/,$alldomstr);                 }
    }          $alldomstr=~s/:$//;
           return split(/:/,$alldomstr);       
       } else {
           my @emptyResults = ();
           push(@emptyResults, 'missing domain');
           return split(':',@emptyResults);
       }
 }  }
   
 # -------------------------------------------------------- Value of a Condition  # -------------------------------------------------------- Value of a Condition
Line 2005  sub condval { Line 2480  sub condval {
     return $result;      return $result;
 }  }
   
   # --------------------------------------------------- Course Resourcedata Query
   
   sub courseresdata {
       my ($coursenum,$coursedomain,@which)=@_;
       my $coursehom=&homeserver($coursenum,$coursedomain);
       my $hashid=$coursenum.':'.$coursedomain;
       my $dodump=0;
       if (!defined($courseresdatacache{$hashid.'.time'})) {
    $dodump=1;
       } else {
    if (time-$courseresdatacache{$hashid.'.time'}>300) { $dodump=1; }
       }
       if ($dodump) {
    my %dumpreply=&dump('resourcedata',$coursedomain,$coursenum);
    my ($tmp) = keys(%dumpreply);
    if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
       $courseresdatacache{$hashid.'.time'}=time;
       $courseresdatacache{$hashid}=\%dumpreply;
    }
       }
       foreach my $item (@which) {
    if ($courseresdatacache{$hashid}->{$item}) {
       return $courseresdatacache{$hashid}->{$item};
    }
       }
       return '';
   }
   
 # --------------------------------------------------------- Value of a Variable  # --------------------------------------------------------- Value of a Variable
   
 sub EXT {  sub EXT {
     my ($varname,$symbparm)=@_;      my ($varname,$symbparm,$udom,$uname)=@_;
   
     unless ($varname) { return ''; }      unless ($varname) { return ''; }
   
       #get real user name/domain, courseid and symb
       my $courseid;
       if (!($uname && $udom)) {
         (my $cursymb,$courseid,$udom,$uname)=&Apache::lonxml::whichuser();
         if (!$symbparm) { $symbparm=$cursymb; }
       } else {
    $courseid=$ENV{'request.course.id'};
       }
   
     my ($realm,$space,$qualifier,@therest)=split(/\./,$varname);      my ($realm,$space,$qualifier,@therest)=split(/\./,$varname);
     my $rest;      my $rest;
     if ($therest[0]) {      if ($therest[0]) {
Line 2024  sub EXT { Line 2538  sub EXT {
     if ($realm eq 'user') {      if ($realm eq 'user') {
 # --------------------------------------------------------------- user.resource  # --------------------------------------------------------------- user.resource
  if ($space eq 'resource') {   if ($space eq 'resource') {
     my %restored=&restore();      my %restored=&restore(undef,undef,$udom,$uname);
             return $restored{$qualifierrest};              return $restored{$qualifierrest};
 # ----------------------------------------------------------------- user.access  # ----------------------------------------------------------------- user.access
         } elsif ($space eq 'access') {          } elsif ($space eq 'access') {
       # FIXME - not supporting calls for a specific user
             return &allowed($qualifier,$rest);              return &allowed($qualifier,$rest);
 # ------------------------------------------ user.preferences, user.environment  # ------------------------------------------ user.preferences, user.environment
         } elsif (($space eq 'preferences') || ($space eq 'environment')) {          } elsif (($space eq 'preferences') || ($space eq 'environment')) {
             return $ENV{join('.',('environment',$qualifierrest))};      if (($uname eq $ENV{'user.name'}) &&
    ($udom eq $ENV{'user.domain'})) {
    return $ENV{join('.',('environment',$qualifierrest))};
       } else {
    my %returnhash=&userenvironment($udom,$uname,$qualifierrest);
    return $returnhash{$qualifierrest};
       }
 # ----------------------------------------------------------------- user.course  # ----------------------------------------------------------------- user.course
         } elsif ($space eq 'course') {          } elsif ($space eq 'course') {
       # FIXME - not supporting calls for a specific user
             return $ENV{join('.',('request.course',$qualifier))};              return $ENV{join('.',('request.course',$qualifier))};
 # ------------------------------------------------------------------- user.role  # ------------------------------------------------------------------- user.role
         } elsif ($space eq 'role') {          } elsif ($space eq 'role') {
       # FIXME - not supporting calls for a specific user
             my ($role,$where)=split(/\./,$ENV{'request.role'});              my ($role,$where)=split(/\./,$ENV{'request.role'});
             if ($qualifier eq 'value') {              if ($qualifier eq 'value') {
  return $role;   return $role;
Line 2045  sub EXT { Line 2568  sub EXT {
             }              }
 # ----------------------------------------------------------------- user.domain  # ----------------------------------------------------------------- user.domain
         } elsif ($space eq 'domain') {          } elsif ($space eq 'domain') {
             return $ENV{'user.domain'};              return $udom;
 # ------------------------------------------------------------------- user.name  # ------------------------------------------------------------------- user.name
         } elsif ($space eq 'name') {          } elsif ($space eq 'name') {
             return $ENV{'user.name'};              return $uname;
 # ---------------------------------------------------- Any other user namespace  # ---------------------------------------------------- Any other user namespace
         } else {          } else {
             my $item=($rest)?$qualifier.'.'.$rest:$qualifier;              my $item=($rest)?$qualifier.'.'.$rest:$qualifier;
             my %reply=&get($space,[$item]);              my %reply=&get($space,[$item]);
             return $reply{$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  # ------------------------------------------------------------- request.browser
         if ($space eq 'browser') {          if ($space eq 'browser') {
     return $ENV{'browser.'.$qualifier};      return $ENV{'browser.'.$qualifier};
Line 2065  sub EXT { Line 2592  sub EXT {
         }          }
     } elsif ($realm eq 'course') {      } elsif ($realm eq 'course') {
 # ---------------------------------------------------------- course.description  # ---------------------------------------------------------- course.description
         return $ENV{'course.'.$ENV{'request.course.id'}.'.'.          return $ENV{'course.'.$courseid.'.'.$spacequalifierrest};
                               $spacequalifierrest};  
     } elsif ($realm eq 'resource') {      } elsif ($realm eq 'resource') {
        if ($ENV{'request.course.id'}) {  
   
 #   print '<br>'.$space.' - '.$qualifier.' - '.$spacequalifierrest;   if ($courseid eq $ENV{'request.course.id'}) {
   
       #print '<br>'.$space.' - '.$qualifier.' - '.$spacequalifierrest;
   
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
          my $symbp;      if (!$symbparm) { $symbparm=&symbread(); }
          if ($symbparm) {      my $symbp=$symbparm;
             $symbp=$symbparm;      my $mapp=(split(/\_\_\_/,$symbp))[0];
  } else {  
             $symbp=&symbread();      my $symbparm=$symbp.'.'.$spacequalifierrest;
          }                  my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
          my $mapp=(split(/\_\_\_/,$symbp))[0];  
       my $section;
          my $symbparm=$symbp.'.'.$spacequalifierrest;      if (($ENV{'user.name'} eq $uname) &&
          my $mapparm=$mapp.'___(all).'.$spacequalifierrest;   ($ENV{'user.domain'} eq $udom)) {
    $section=$ENV{'request.course.sec'};
          my $seclevel=      } else {
             $ENV{'request.course.id'}.'.['.   $section=&usection($udom,$uname,$courseid);
  $ENV{'request.course.sec'}.'].'.$spacequalifierrest;      }
          my $seclevelr=  
             $ENV{'request.course.id'}.'.['.  
  $ENV{'request.course.sec'}.'].'.$symbparm;  
          my $seclevelm=  
             $ENV{'request.course.id'}.'.['.  
  $ENV{'request.course.sec'}.'].'.$mapparm;  
   
          my $courselevel=  
             $ENV{'request.course.id'}.'.'.$spacequalifierrest;  
          my $courselevelr=  
             $ENV{'request.course.id'}.'.'.$symbparm;  
          my $courselevelm=  
             $ENV{'request.course.id'}.'.'.$mapparm;  
   
 # ----------------------------------------------------------- first, check user      my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest;
          my %resourcedata=get('resourcedata',      my $seclevelr=$courseid.'.['.$section.'].'.$symbparm;
                            [$courselevelr,$courselevelm,$courselevel]);      my $seclevelm=$courseid.'.['.$section.'].'.$mapparm;
          if (($resourcedata{$courselevelr}!~/^error\:/) &&  
              ($resourcedata{$courselevelr}!~/^con_lost/)) {      my $courselevel=$courseid.'.'.$spacequalifierrest;
       my $courselevelr=$courseid.'.'.$symbparm;
          if ($resourcedata{$courselevelr}) {       my $courselevelm=$courseid.'.'.$mapparm;
             return $resourcedata{$courselevelr}; }  
          if ($resourcedata{$courselevelm}) {   
             return $resourcedata{$courselevelm}; }  
          if ($resourcedata{$courselevel}) { return $resourcedata{$courselevel}; }  
   
       } else {  # ----------------------------------------------------------- first, check user
   if ($resourcedata{$courselevelr}!~/No such file/) {      my %resourcedata=&get('resourcedata',
     &logthis("<font color=blue>WARNING:".    [$courselevelr,$courselevelm,$courselevel],
    " Trying to get resource data for ".$ENV{'user.name'}." at "   $udom,$uname);
                    .$ENV{'user.domain'}.": ".$resourcedata{$courselevelr}.      if (($resourcedata{$courselevelr}!~/^error\:/) &&
                  "</font>");   ($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>");
    }
       }
   
 # -------------------------------------------------------- second, check course  # -------------------------------------------------------- second, check course
   
         my $reply=&reply('get:'.      my $coursereply=&courseresdata($ENV{'course.'.$courseid.'.num'},
               $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'.    $ENV{'course.'.$courseid.'.domain'},
               $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.    ($seclevelr,$seclevelm,$seclevel,
       ':resourcedata:'.     $courselevelr,$courselevelm,
    &escape($seclevelr).'&'.&escape($seclevelm).'&'.&escape($seclevel).'&'.     $courselevel));
    &escape($courselevelr).'&'.&escape($courselevelm).'&'.&escape($courselevel),      if ($coursereply) { return $coursereply; }
    $ENV{'course.'.$ENV{'request.course.id'}.'.home'});  
       if ($reply!~/^error\:/) {  
   foreach (split(/\&/,$reply)) {  
       if ($_) { return &unescape($_); }  
           }  
       }  
       if (($reply=~/^con_lost/) || ($reply=~/^error\:/)) {  
   &logthis("<font color=blue>WARNING:".  
                 " Getting ".$reply." asking for ".$varname." for ".  
                 $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.  
                 ' at '.  
                 $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.  
                 ' from '.  
                 $ENV{'course.'.$ENV{'request.course.id'}.'.home'}.  
                  "</font>");  
       }  
 # ------------------------------------------------------ third, check map parms  # ------------------------------------------------------ third, check map parms
        my %parmhash=();      my %parmhash=();
        my $thisparm='';             my $thisparm='';
        if (tie(%parmhash,'GDBM_File',      if (tie(%parmhash,'GDBM_File',
           $ENV{'request.course.fn'}.'_parms.db',&GDBM_READER,0640)) {      $ENV{'request.course.fn'}.'_parms.db',
            $thisparm=$parmhash{$symbparm};      &GDBM_READER(),0640)) {
    untie(%parmhash);   $thisparm=$parmhash{$symbparm};
        }   untie(%parmhash);
        if ($thisparm) { return $thisparm; }      }
      }      if ($thisparm) { return $thisparm; }
         }
 # --------------------------------------------- last, look in resource metadata  # --------------------------------------------- last, look in resource metadata
   
       $spacequalifierrest=~s/\./\_/;   $spacequalifierrest=~s/\./\_/;
       my $metadata=&metadata($ENV{'request.filename'},$spacequalifierrest);   my $metadata=&metadata($ENV{'request.filename'},$spacequalifierrest);
       if ($metadata) { return $metadata; }   if ($metadata) { return $metadata; }
       $metadata=&metadata($ENV{'request.filename'},   $metadata=&metadata($ENV{'request.filename'},
                                          'parameter_'.$spacequalifierrest);      'parameter_'.$spacequalifierrest);
       if ($metadata) { return $metadata; }   if ($metadata) { return $metadata; }
   
 # ------------------------------------------------------------------ Cascade up  # ------------------------------------------------------------------ Cascade up
    unless ($space eq '0') {
       unless ($space eq '0') {      my ($part,$id)=split(/\_/,$space);
           my ($part,$id)=split(/\_/,$space);      if ($id) {
           if ($id) {   my $partgeneral=&EXT('resource.'.$part.'.'.$qualifierrest,
       my $partgeneral=&EXT('resource.'.$part.'.'.$qualifierrest,       $symbparm,$udom,$uname);
                                    $symbparm);   if ($partgeneral) { return $partgeneral; }
               if ($partgeneral) { return $partgeneral; }      } else {
           } else {   my $resourcegeneral=&EXT('resource.0.'.$qualifierrest,
               my $resourcegeneral=&EXT('resource.0.'.$qualifierrest,   $symbparm,$udom,$uname);
                                        $symbparm);   if ($resourcegeneral) { return $resourcegeneral; }
               if ($resourcegeneral) { return $resourcegeneral; }      }
           }   }
       }  
   
 # ---------------------------------------------------- Any other user namespace  # ---------------------------------------------------- Any other user namespace
     } elsif ($realm eq 'environment') {      } elsif ($realm eq 'environment') {
 # ----------------------------------------------------------------- environment  # ----------------------------------------------------------------- environment
         return $ENV{'environment.'.$spacequalifierrest};   if (($uname eq $ENV{'user.name'})&&($udom eq $ENV{'user.domain'})) {
       return $ENV{'environment.'.$spacequalifierrest};
    } else {
       my %returnhash=&userenvironment($udom,$uname,
       $spacequalifierrest);
       return $returnhash{$spacequalifierrest};
    }
     } elsif ($realm eq 'system') {      } elsif ($realm eq 'system') {
 # ----------------------------------------------------------------- system.time  # ----------------------------------------------------------------- system.time
  if ($space eq 'time') {   if ($space eq 'time') {
Line 2219  sub metadata { Line 2731  sub metadata {
         my %metathesekeys=();          my %metathesekeys=();
         unless ($filename=~/\.meta$/) { $filename.='.meta'; }          unless ($filename=~/\.meta$/) { $filename.='.meta'; }
  my $metastring=&getfile($perlvar{'lonDocRoot'}.'/res/'.$filename);   my $metastring=&getfile($perlvar{'lonDocRoot'}.'/res/'.$filename);
         my $parser=HTML::TokeParser->new(\$metastring);          my $parser=HTML::LCParser->new(\$metastring);
         my $token;          my $token;
         undef %metathesekeys;          undef %metathesekeys;
         while ($token=$parser->get_token) {          while ($token=$parser->get_token) {
Line 2308  sub metadata { Line 2820  sub metadata {
   $metacache{$uri.':'.$unikey.'.'.$_}=$token->[2]->{$_};    $metacache{$uri.':'.$unikey.'.'.$_}=$token->[2]->{$_};
               }                }
               unless (                unless (
                  $metacache{$uri.':'.$unikey}=$parser->get_text('/'.$entry)                   $metacache{$uri.':'.$unikey}=&HTML::Entities::decode($parser->get_text('/'.$entry))
       ) { $metacache{$uri.':'.$unikey}=        ) { $metacache{$uri.':'.$unikey}=
       $metacache{$uri.':'.$unikey.'.default'};        $metacache{$uri.':'.$unikey.'.default'};
       }        }
Line 2319  sub metadata { Line 2831  sub metadata {
 # the next is the end of "start tag"  # the next is the end of "start tag"
  }   }
        }         }
    &metadata_generate_part0(\%metathesekeys,\%metacache,$uri);
        $metacache{$uri.':keys'}=join(',',keys %metathesekeys);         $metacache{$uri.':keys'}=join(',',keys %metathesekeys);
        $metacache{$uri.':cachedtimestamp'}=time;         $metacache{$uri.':cachedtimestamp'}=time;
 # this is the end of "was not already recently cached  # this is the end of "was not already recently cached
Line 2326  sub metadata { Line 2839  sub metadata {
     return $metacache{$uri.':'.$what};      return $metacache{$uri.':'.$what};
 }  }
   
   sub metadata_generate_part0 {
       my ($metadata,$metacache,$uri) = @_;
       my %allnames;
       foreach my $metakey (sort keys %$metadata) {
    if ($metakey=~/^parameter\_(.*)/) {
     my $part=$$metacache{$uri.':'.$metakey.'.part'};
     my $name=$$metacache{$uri.':'.$metakey.'.name'};
     if (! exists($$metadata{'parameter_0_'.$name})) {
       $allnames{$name}=$part;
     }
    }
       }
       foreach my $name (keys(%allnames)) {
         $$metadata{"parameter_0_$name"}=1;
         my $key="$uri:parameter_0_$name";
         $$metacache{"$key.part"}='0';
         $$metacache{"$key.name"}=$name;
         $$metacache{"$key.type"}=$$metacache{$uri.':parameter_'.
      $allnames{$name}.'_'.$name.
      '.type'};
         my $olddis=$$metacache{$uri.':parameter_'.$allnames{$name}.'_'.$name.
        '.display'};
         my $expr='\\[Part: '.$allnames{$name}.'\\]';
         $olddis=~s/$expr/\[Part: 0\]/;
         $$metacache{"$key.display"}=$olddis;
       }
   }
   
 # ------------------------------------------------- Update symbolic store links  # ------------------------------------------------- Update symbolic store links
   
 sub symblist {  sub symblist {
Line 2334  sub symblist { Line 2875  sub symblist {
     my %hash;      my %hash;
     if (($ENV{'request.course.fn'}) && (%newhash)) {      if (($ENV{'request.course.fn'}) && (%newhash)) {
         if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',          if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',
                       &GDBM_WRCREAT,0640)) {                        &GDBM_WRCREAT(),0640)) {
     foreach (keys %newhash) {      foreach (keys %newhash) {
                 $hash{declutter($_)}=$mapname.'___'.$newhash{$_};                  $hash{declutter($_)}=$mapname.'___'.$newhash{$_};
             }              }
Line 2346  sub symblist { Line 2887  sub symblist {
     return 'error';      return 'error';
 }  }
   
   # --------------------------------------------------------------- Verify a symb
   
   sub symbverify {
       my ($symb,$thisfn)=@_;
       $thisfn=&declutter($thisfn);
   # direct jump to resource in page or to a sequence - will construct own symbs
       if ($thisfn=~/\.(page|sequence)$/) { return 1; }
   # check URL part
       my ($map,$resid,$url)=split(/\_\_\_/,$symb);
       unless (&symbclean($url) eq &symbclean($thisfn)) { return 0; }
   
       $symb=&symbclean($symb);
   
       my %bighash;
       my $okay=0;
       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
                               &GDBM_READER(),0640)) {
           my $ids=$bighash{'ids_/res/'.$thisfn};
           unless ($ids) { 
              $ids=$bighash{'ids_/'.$thisfn};
           }
           if ($ids) {
   # ------------------------------------------------------------------- Has ID(s)
       foreach (split(/\,/,$ids)) {
                  my ($mapid,$resid)=split(/\./,$_);
                  if (
     &symbclean(&declutter($bighash{'map_id_'.$mapid}).'___'.$resid.'___'.$thisfn)
      eq $symb) { 
                     $okay=1; 
                  }
      }
           }
    untie(%bighash);
       }
       return $okay;
   }
   
   # --------------------------------------------------------------- Clean-up symb
   
   sub symbclean {
       my $symb=shift;
   
   # remove version from map
       $symb=~s/\.(\d+)\.(\w+)\_\_\_/\.$2\_\_\_/;
   
   # remove version from URL
       $symb=~s/\.(\d+)\.(\w+)$/\.$2/;
   
       return $symb;
   }
   
 # ------------------------------------------------------ Return symb list entry  # ------------------------------------------------------ Return symb list entry
   
 sub symbread {  sub symbread {
     my $thisfn=shift;      my ($thisfn,$donotrecurse)=@_;
   # no filename provided? try from environment
     unless ($thisfn) {      unless ($thisfn) {
         if ($ENV{'request.symb'}) { return $ENV{'request.symb'}; }          if ($ENV{'request.symb'}) { return &symbclean($ENV{'request.symb'}); }
  $thisfn=$ENV{'request.filename'};   $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);      $thisfn=declutter($thisfn);
     my %hash;      my %hash;
     my %bighash;      my %bighash;
     my $syval='';      my $syval='';
     if (($ENV{'request.course.fn'}) && ($thisfn)) {      if (($ENV{'request.course.fn'}) && ($thisfn)) {
         if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',          if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',
                       &GDBM_READER,0640)) {                        &GDBM_READER(),0640)) {
     $syval=$hash{$thisfn};      $syval=$hash{$thisfn};
             untie(%hash);              untie(%hash);
         }          }
Line 2376  sub symbread { Line 2973  sub symbread {
         } else {          } else {
 # ------------------------------------------------------- Was not in symb table  # ------------------------------------------------------- Was not in symb table
            if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',             if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
                             &GDBM_READER,0640)) {                              &GDBM_READER(),0640)) {
 # ---------------------------------------------- Get ID(s) for current resource  # ---------------------------------------------- Get ID(s) for current resource
               my $ids=$bighash{'ids_/res/'.$thisfn};                my $ids=$bighash{'ids_/res/'.$thisfn};
               unless ($ids) {                 unless ($ids) { 
                  $ids=$bighash{'ids_/'.$thisfn};                   $ids=$bighash{'ids_/'.$thisfn};
               }                }
                 unless ($ids) {
   # alias?
     $ids=$bighash{'mapalias_'.$thisfn};
                 }
               if ($ids) {                if ($ids) {
 # ------------------------------------------------------------------- Has ID(s)  # ------------------------------------------------------------------- Has ID(s)
                  my @possibilities=split(/\,/,$ids);                   my @possibilities=split(/\,/,$ids);
Line 2389  sub symbread { Line 2990  sub symbread {
 # ----------------------------------------------- There is only one possibility  # ----------------------------------------------- There is only one possibility
      my ($mapid,$resid)=split(/\./,$ids);       my ($mapid,$resid)=split(/\./,$ids);
                      $syval=declutter($bighash{'map_id_'.$mapid}).'___'.$resid;                       $syval=declutter($bighash{'map_id_'.$mapid}).'___'.$resid;
                  } else {                   } elsif (!$donotrecurse) {
 # ------------------------------------------ There is more than one possibility  # ------------------------------------------ There is more than one possibility
                      my $realpossible=0;                       my $realpossible=0;
                      foreach (@possibilities) {                       foreach (@possibilities) {
Line 2404  sub symbread { Line 3005  sub symbread {
  }   }
                      }                       }
      if ($realpossible!=1) { $syval=''; }       if ($realpossible!=1) { $syval=''; }
                    } else {
                        $syval='';
                  }                   }
       }        }
               untie(%bighash)                untie(%bighash)
            }              } 
         }          }
         if ($syval) {          if ($syval) {
            return $syval.'___'.$thisfn;              return &symbclean($syval.'___'.$thisfn); 
         }          }
     }      }
     &appenv('request.ambiguous' => $thisfn);      &appenv('request.ambiguous' => $thisfn);
Line 2472  sub ireceipt { Line 3075  sub ireceipt {
 }  }
   
 sub receipt {  sub receipt {
     return &ireceipt($ENV{'user.name'},$ENV{'user.domain'},    my ($symb,$courseid,$domain,$name) = &Apache::lonxml::whichuser();
                      $ENV{'request.course.id'},&symbread());    return &ireceipt($name,$domain,$courseid,$symb);
 }  }
     
 # ------------------------------------------------------------ Serves up a file  # ------------------------------------------------------------ Serves up a file
 # returns either the contents of the file or a -1  # returns either the contents of the file or a -1
 sub getfile {  sub getfile {
Line 2514  sub hreflocation { Line 3117  sub hreflocation {
     unless (($file=~/^http:\/\//i) || ($file=~/^\//)) {      unless (($file=~/^http:\/\//i) || ($file=~/^\//)) {
        my $finalpath=filelocation($dir,$file);         my $finalpath=filelocation($dir,$file);
        $finalpath=~s/^\/home\/httpd\/html//;         $finalpath=~s/^\/home\/httpd\/html//;
          $finalpath=~s-/home/(\w+)/public_html/-/~$1/-;
        return $finalpath;         return $finalpath;
     } else {      } else {
        return $file;         return $file;
Line 2527  sub declutter { Line 3131  sub declutter {
     $thisfn=~s/^$perlvar{'lonDocRoot'}//;      $thisfn=~s/^$perlvar{'lonDocRoot'}//;
     $thisfn=~s/^\///;      $thisfn=~s/^\///;
     $thisfn=~s/^res\///;      $thisfn=~s/^res\///;
       $thisfn=~s/\?.+$//;
       return $thisfn;
   }
   
   # ------------------------------------------------------------- Clutter up URLs
   
   sub clutter {
       my $thisfn='/'.&declutter(shift);
       unless ($thisfn=~/^\/(uploaded|adm)\//) { $thisfn='/res'.$thisfn; }
     return $thisfn;      return $thisfn;
 }  }
   
Line 2549  sub unescape { Line 3162  sub unescape {
 # ================================================================ Main Program  # ================================================================ Main Program
   
 sub goodbye {  sub goodbye {
      &logthis("Starting Shut down");
    &flushcourselogs();     &flushcourselogs();
    &logthis("Shutting down");     &logthis("Shutting down");
 }  }
   
 BEGIN {  BEGIN {
 # ------------------------------------------------------------ Read access.conf  # ----------------------------------- Read loncapa.conf and loncapa_apache.conf
       unless ($readit) {
 {  {
     my $config=Apache::File->new("/etc/httpd/conf/access.conf");      my $config=Apache::File->new("/etc/httpd/conf/loncapa.conf");
   
     while (my $configline=<$config>) {      while (my $configline=<$config>) {
         if ($configline =~ /PerlSetVar/) {          if ($configline =~ /^[^\#]*PerlSetVar/) {
      my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
              chomp($varvalue);
              $perlvar{$varname}=$varvalue;
           }
       }
   }
   {
       my $config=Apache::File->new("/etc/httpd/conf/loncapa_apache.conf");
   
       while (my $configline=<$config>) {
           if ($configline =~ /^[^\#]*PerlSetVar/) {
    my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);     my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
            chomp($varvalue);             chomp($varvalue);
            $perlvar{$varname}=$varvalue;             $perlvar{$varname}=$varvalue;
Line 2573  BEGIN { Line 3199  BEGIN {
   
     while (my $configline=<$config>) {      while (my $configline=<$config>) {
        chomp($configline);         chomp($configline);
        my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);         my ($id,$domain,$role,$name,$ip,$domdescr)=split(/:/,$configline);
        $hostname{$id}=$name;         if ($id && $domain && $role && $name && $ip) {
        $hostdom{$id}=$domain;   $hostname{$id}=$name;
        $hostip{$id}=$ip;   $hostdom{$id}=$domain;
        if ($role eq 'library') { $libserv{$id}=$name; }   $hostip{$id}=$ip;
    if ($domdescr) { $domaindescription{$domain}=$domdescr; }
    if ($role eq 'library') { $libserv{$id}=$name; }
          } else {
    if ($configline) {
      &logthis("Skipping hosts.tab line -$configline-");
    }
          }
     }      }
 }  }
   
Line 2640  $dumpcount=0; Line 3273  $dumpcount=0;
   
 &logtouch();  &logtouch();
 &logthis('<font color=yellow>INFO: Read configuration</font>');  &logthis('<font color=yellow>INFO: Read configuration</font>');
   $readit=1;
   }
 }  }
   
 1;  1;
 __END__  __END__
   
   =pod
   
 =head1 NAME  =head1 NAME
   
 Apache::lonnet - TCP networking package  Apache::lonnet - Subroutines to ask questions about things in the network.
   
 =head1 SYNOPSIS  =head1 SYNOPSIS
   
 Invoked by other LON-CAPA modules.  Invoked by other LON-CAPA modules, when they need to talk to or about objects in the network.
   
  &Apache::lonnet::SUBROUTINENAME(ARGUMENTS);   &Apache::lonnet::SUBROUTINENAME(ARGUMENTS);
   
   Common parameters:
   
   =over 4
   
   =item *
   
   $uname : an internal username (if $cname expecting a course Id specifically)
   
   =item *
   
   $udom : a domain (if $cdom expecting a course's domain specifically)
   
   =item *
   
   $symb : a resource instance identifier
   
   =item *
   
   $namespace : the name of a .db file that contains the data needed or
   being set.
   
   =back
   
 =head1 INTRODUCTION  =head1 INTRODUCTION
   
 This module provides subroutines which interact with the  This module provides subroutines which interact with the
 lonc/lond (TCP) network layer of LON-CAPA.  lonc/lond (TCP) network layer of LON-CAPA. And Can be used to ask about 
   - classes
   - users 
   - resources
   
   For many of these objects you can also use this to store data about
   them or modify them in various ways.
   
 This is part of the LearningOnline Network with CAPA project  This is part of the LearningOnline Network with CAPA project
 described at http://www.lon-capa.org.  described at http://www.lon-capa.org.
   
 =head1 HANDLER SUBROUTINE  =head1 RETURN MESSAGES
   
 There is no handler routine for this module.  
   
 =head1 OTHER SUBROUTINES  
   
 =over 4  =over 4
   
 =item *  =item *
   
 logtouch() : make sure the logfile, lonnet.log, exists  con_lost : unable to contact remote host
   
 =item *  =item *
   
 logthis() : append message to lonnet.log  con_delayed : unable to contact remote host, message will be delivered
   when the connection is brought back up
   
 =item *  =item *
   
 logperm() : append a permanent message to lonnet.perm.log  con_failed : unable to contact remote host and unable to save message
   for later delivery
   
 =item *  =item *
   
 subreply() : non-critical communication, called by &reply  error: : an error a occured, a description of the error follows the :
   
 =item *  =item *
   
 reply() : makes two attempts to pass message; logs refusals and rejections  no_such_host : unable to fund a host associated with the user/domain
   that was requested
   
 =item *  =back
   
 reconlonc() : tries to reconnect lonc client processes.  =head1 PUBLIC SUBROUTINES
   
 =item *  =head2 Session Environment Functions
   
 critical() : passes a critical message to another server; if cannot get  =over 4
 through then place message in connection buffer  
   
 =item *  =item *
   
 appenv(%hash) : read in current user environment, append new environment  appenv(%hash) : the value of %hash is written to the user envirnoment
 values to make new user environment  file, and will be restored for each access this user makes during this
   session, also modifies the %ENV for the current process
   
 =item *  =item *
   
 delenv($varname) : read in current user environment, remove all values  delenv($regexp) : removes all items from the session environment file that matches the regular expression in $regexp. The values are also delted from the current processes %ENV.
 beginning with $varname, write new user environment (note: flock is used  
 to prevent conflicting shared read/writes with file)  
   
 =item *  =back
   
 spareserver() : find server with least workload from spare.tab  =head2 User Information
   
   =over 4
   
 =item *  =item *
   
Line 2723  authentication scheme Line 3388  authentication scheme
 =item *  =item *
   
 authenticate($uname,$upass,$udom) : try to authenticate user from domain's lib  authenticate($uname,$upass,$udom) : try to authenticate user from domain's lib
 servers (first use the current one)  servers (first use the current one), $upass should be the users password
   
 =item *  =item *
   
 homeserver($uname,$udom) : find the homebase for a user from domain's lib  homeserver($uname,$udom) : find the server which has the user's
 servers  directory and files (there must be only one), this caches the answer,
   and also caches if there is a borken connection.
   
 =item *  =item *
   
 idget($udom,@ids) : find the usernames behind a list of IDs (returns hash:  idget($udom,@ids) : find the usernames behind a list of IDs (IDs are a
   unique resource in a domain, there must be only 1 ID per username, and
   only 1 username per ID in a specific domain) (returns hash:
 id=>name,id=>name)  id=>name,id=>name)
   
 =item *  =item *
Line 2746  idput($udom,%ids) : store away a list of Line 3414  idput($udom,%ids) : store away a list of
   
 =item *  =item *
   
 usection($domain,$user,$courseid) : output of section name/number or '' for  rolesinit($udom,$username,$authhost) : get user privileges
 "not in course" and '-1' for "no section"  
   
 =item *  =item *
   
 userenvironment($domain,$user,$what) : puts out any environment parameter   usection($udom,$uname,$cname) : finds the section of student in the
 for a user  course $cname, return section name/number or '' for "not in course"
   and '-1' for "no section"
   
 =item *  =item *
   
 subscribe($fname) : subscribe to a resource, return URL if possible  userenvironment($udom,$uname,@what) : gets the values of the keys
   passed in @what from the requested user's environment, returns a hash
   
   =back
   
   =head2 User Roles
   
   =over 4
   
 =item *  =item *
   
 repcopy($filename) : replicate file  allowed($priv,$uri) : check for a user privilege; 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
   
 =item *  =item *
   
 ssi($url,%hash) : server side include, does a complete request cycle on url to  definerole($rolename,$sysrole,$domrole,$courole) : define role; define a custom
 localhost, posts hash  role rolename set privileges in format of lonTabs/roles.tab for system, domain,
   and course level
   
 =item *  =item *
   
 log($domain,$name,$home,$message) : write to permanent log for user; use  plaintext($short) : return value in %prp hash (rolesplain.tab); plain text
 critical subroutine  explanation of a user role term
   
   =back
   
   =head2 User Modification
   
   =over 4
   
 =item *  =item *
   
 flushcourselogs() : flush (save) buffer logs and access logs  assignrole($udom,$uname,$url,$role,$end,$start) : assign role; give a role to a
   user for the level given by URL.  Optional start and end dates (leave empty
   string or zero for "no date")
   
 =item *  =item *
   
 courselog($what) : save message for course in hash  changepass($uname,$udom,$currentpass,$newpass,$server) : attempts to
   change a users, password, possible return values are: ok,
   pwchange_failure, non_authorized, auth_mode_error, unknown_user,
   refused
   
 =item *  =item *
   
 courseacclog($what) : save message for course using &courselog().  Perform  modifyuserauth($udom,$uname,$umode,$upass) : modify user authentication
 special processing for specific resource types (problems, exams, quizzes, etc).  
   
 =item *  =item *
   
 countacc($url) : count the number of accesses to a given URL  modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene) : 
   modify user
   
 =item *  =item *
   
 sub checkout($symb,$tuname,$tudom,$tcrsid) : check out an item  modifystudent($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec,$end,$start) : modify student
   
 =item *  =item *
   
 sub checkin($token) : check in an 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
   
 =item *  =item *
   
 sub expirespread($uname,$udom,$stype,$usymb) : set expire date for spreadsheet  revokerole($udom,$uname,$url,$role) : revoke a role for url
   
 =item *  =item *
   
 devalidate($symb) : devalidate spreadsheets  revokecustomrole($udom,$uname,$url,$role) : revoke a custom role
   
   =back
   
   =head2 Course Infomation
   
   =over 4
   
 =item *  =item *
   
 hash2str(%hash) : convert a hash into a string complete with escaping and '='  coursedescription($courseid) : course description
 and '&' separators  
   
 =item *  =item *
   
 str2hash($string) : convert string to hash using unescaping and splitting on  courseresdata($coursenum,$coursedomain,@which) : request for current
 '=' and '&'  parameter setting for a specific course, @what should be a list of
   parameters to ask about. This routine caches answers for 5 minutes.
   
   =back
   
   =head2 Course Modification
   
   =over 4
   
 =item *  =item *
   
 tmpreset($symb,$namespace,$domain,$stuname) : temporary storage  writecoursepref($courseid,%prefs) : write preferences (environment
   database) for a course
   
 =item *  =item *
   
 tmprestore($symb,$namespace,$domain,$stuname) : temporary restore  createcourse($udom,$description,$url) : make/modify course
   
   =back
   
   =head2 Resource Subroutines
   
   =over 4
   
 =item *  =item *
   
 store($storehash,$symb,$namespace,$domain,$stuname) : stores hash permanently  subscribe($fname) : subscribe to a resource, returns URL if possible (probably should use repcopy instead)
 for this url; hashref needs to be given and should be a \%hashname; the  
 remaining args aren't required and if they aren't passed or are '' they will  
 be derived from the ENV  
   
 =item *  =item *
   
 cstore($storehash,$symb,$namespace,$domain,$stuname) : same as store but  repcopy($filename) : subscribes to the requested file, and attempts to
 uses critical subroutine  replicate from the owning library server, Might return
   HTTP_SERVICE_UNAVAILABLE, HTTP_NOT_FOUND, FORBIDDEN, OK, or
   HTTP_BAD_REQUEST, also attempts to grab the metadata for the
   resource. Expects the local filesystem pathname
   (/home/httpd/html/res/....)
   
   =back
   
   =head2 Resource Information
   
   =over 4
   
 =item *  =item *
   
 restore($symb,$namespace,$domain,$stuname) : returns hash for this symb;  EXT($varname,$symb,$udom,$uname) : evaluates and returns the value of
 all args are optional  a vairety of different possible values, $varname should be a request
   string, and the other parameters can be used to specify who and what
   one is asking about.
   
   Possible values for $varname are environment.lastname (or other item
   from the envirnment hash), user.name (or someother aspect about the
   user), resource.0.maxtries (or some other part and parameter of a
   resource)
   
 =item *  =item *
   
 coursedescription($courseid) : course description  directcondval($number) : get current value of a condition; reads from a state
   string
   
 =item *  =item *
   
 rolesinit($domain,$username,$authhost) : get user privileges  condval($condidx) : value of condition index based on state
   
 =item *  =item *
   
 get($namespace,$storearr,$udomain,$uname) : returns hash with keys from array  metadata($uri,$what,$liburi,$prefix,$depthcount) : request a
 reference filled in from namesp ($udomain and $uname are optional)  resource's metadata, $what should be either a specific key, or either
   'keys' (to get a list of possible keys) or 'packages' to get a list of
   packages that this resource currently uses, the last 3 arguments are only used internally for recursive metadata.
   
   this function automatically caches all requests
   
 =item *  =item *
   
 del($namespace,$storearr,$udomain,$uname) : deletes keys out of array from  metadata_query($query,$custom,$customshow) : make a metadata query against the
 namesp ($udomain and $uname are optional)  network of library servers; returns file handle of where SQL and regex results
   will be stored for query
   
 =item *  =item *
   
 dump($namespace,$udomain,$uname,$regexp) :   symbread($filename) : return symbolic list entry (filename argument optional);
 dumps the complete (or key matching regexp) namespace into a hash  returns the data handle
 ($udomain, $uname and $regexp are optional)  
   
 =item *  =item *
   
 put($namespace,$storehash,$udomain,$uname) : stores hash in namesp  symbverify($symb,$thisfn) : verifies that $symb actually exists and is
 ($udomain and $uname are optional)  a possible symb for the URL in $thisfn, returns a 1 on success, 0 on
   failure, user must be in a course, as it assumes the existance of the
   course initi hash, and uses $ENV('request.course.id'}
   
   
 =item *  =item *
   
 cput($namespace,$storehash,$udomain,$uname) : critical put  symbclean($symb) : removes versions numbers from a symb, returns the
 ($udomain and $uname are optional)  cleaned symb
   
 =item *  =item *
   
 eget($namespace,$storearr,$udomain,$uname) : returns hash with keys from array  is_on_map($uri) : checks if the $uri is somewhere on the current
 reference filled in from namesp (encrypts the return communication)  course map, user must be in a course for it to work.
 ($udomain and $uname are optional)  
   
 =item *  =item *
   
 allowed($priv,$uri) : check for a user privilege; returns codes for allowed  numval($salt) : return random seed value (addend for rndseed)
 actions  
  F: full access  
  U,I,K: authentication modes (cxx only)  
  '': forbidden  
  1: user needs to choose course  
  2: browse allowed  
   
 =item *  =item *
   
 definerole($rolename,$sysrole,$domrole,$courole) : define role; define a custom  rndseed($symb,$courseid,$udom,$uname) : create a random sum; returns
 role rolename set privileges in format of lonTabs/roles.tab for system, domain,  a random seed, all arguments are optional, if they aren't sent it uses the
 and course level  environment to derive them. Note: if symb isn't sent and it can't get one
   from &symbread it will use the current time as its return value
   
 =item *  =item *
   
 metadata_query($query,$custom,$customshow) : make a metadata query against the  ireceipt($funame,$fudom,$fucourseid,$fusymb) : return unique,
 network of library servers; returns file handle of where SQL and regex results  unfakeable, receipt
 will be stored for query  
   
 =item *  =item *
   
 plaintext($short) : return value in %prp hash (rolesplain.tab); plain text  receipt() : API to ireceipt working off of ENV values; given out to users
 explanation of a user role term  
   
 =item *  =item *
   
 assignrole($udom,$uname,$url,$role,$end,$start) : assign role; give a role to a  countacc($url) : count the number of accesses to a given URL
 user for the level given by URL.  Optional start and end dates (leave empty  
 string or zero for "no date")  
   
 =item *  =item *
   
 modifyuserauth($udom,$uname,$umode,$upass) : modify user authentication  checkout($symb,$tuname,$tudom,$tcrsid) :  creates a record of a user having looked at an item, most likely printed out or otherwise using a resource
   
 =item *  =item *
   
 modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene) :   checkin($token) : updates that a resource has beeen returned (a hard copy version for instance) and returns the data that $token was Checkout with ($symb, $tuname, $tudom, and $tcrsid)
 modify user  
   
 =item *  =item *
   
 modifystudent($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec,  expirespread($uname,$udom,$stype,$usymb) : set expire date for spreadsheet
 $end,$start) : modify student  
   
 =item *  =item *
   
 writecoursepref($courseid,%prefs) : write preferences for a course  devalidate($symb) : devalidate temporary spreadsheet calculations,
   forcing spreadsheet to reevaluate the resource scores next time.
   
   =back
   
   =head2 Storing/Retreiving Data
   
   =over 4
   
 =item *  =item *
   
 createcourse($udom,$description,$url) : make/modify course  store($storehash,$symb,$namespace,$udom,$uname) : stores hash permanently
   for this url; hashref needs to be given and should be a \%hashname; the
   remaining args aren't required and if they aren't passed or are '' they will
   be derived from the ENV
   
 =item *  =item *
   
 assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start) : assign  cstore($storehash,$symb,$namespace,$udom,$uname) : same as store but
 custom role; give a custom role to a user for the level given by URL.  Specify  uses critical subroutine
 name and domain of role author, and role name  
   
 =item *  =item *
   
 revokerole($udom,$uname,$url,$role) : revoke a role for url  restore($symb,$namespace,$udom,$uname) : returns hash for this symb;
   all args are optional
   
 =item *  =item *
   
 revokecustomrole($udom,$uname,$url,$role) : revoke a custom role  tmpstore($storehash,$symb,$namespace,$udom,$uname) : storage that
   works very similar to store/cstore, but all data is stored in a
   temporary location and can be reset using tmpreset, $storehash should
   be a hash reference, returns nothing on success
   
   =item *
   
   tmprestore($symb,$namespace,$udom,$uname) : storage that works very
   similar to restore, but all data is stored in a temporary location and
   can be reset using tmpreset. Returns a hash of values on success,
   error string otherwise.
   
   =item *
   
   tmpreset($symb,$namespace,$udom,$uname) : temporary storage reset,
   deltes all keys for $symb form the temporary storage hash.
   
   =item *
   
   get($namespace,$storearr,$udom,$uname) : returns hash with keys from array
   reference filled in from namesp ($udom and $uname are optional)
   
   =item *
   
   del($namespace,$storearr,$udom,$uname) : deletes keys out of array from
   namesp ($udom and $uname are optional)
   
   =item *
   
   dump($namespace,$udom,$uname,$regexp) : 
   dumps the complete (or key matching regexp) namespace into a hash
   ($udom, $uname and $regexp are optional)
   
   =item *
   
   put($namespace,$storehash,$udom,$uname) : stores hash in namesp
   ($udom and $uname are optional)
   
   =item *
   
   cput($namespace,$storehash,$udom,$uname) : critical put
   ($udom and $uname are optional)
   
   =item *
   
   eget($namespace,$storearr,$udom,$uname) : returns hash with keys from array
   reference filled in from namesp (encrypts the return communication)
   ($udom and $uname are optional)
   
   =item *
   
   log($udom,$name,$home,$message) : write to permanent log for user; use
   critical subroutine
   
   =back
   
   =head2 Network Status Functions
   
   =over 4
   
 =item *  =item *
   
Line 2955  dirlist($uri) : return directory list ba Line 3747  dirlist($uri) : return directory list ba
   
 =item *  =item *
   
 directcondval($number) : get current value of a condition; reads from a state  spareserver() : find server with least workload from spare.tab
 string  
   =back
   
   =head2 Apache Request
   
   =over 4
   
 =item *  =item *
   
 condval($condidx) : value of condition index based on state  ssi($url,%hash) : server side include, does a complete request cycle on url to
   localhost, posts hash
   
   =back
   
   =head2 Data to String to Data
   
   =over 4
   
 =item *  =item *
   
 EXT($varname,$symbparm) : value of a variable  hash2str(%hash) : convert a hash into a string complete with escaping and '='
   and '&' separators, supports elements that are arrayrefs and hashrefs
   
 =item *  =item *
   
 metadata($uri,$what,$liburi,$prefix,$depthcount) : get metadata; returns the  hashref2str($hashref) : convert a hashref into a string complete with
 metadata entry for a file; entry='keys', returns a comma separated list of keys  escaping and '=' and '&' separators, supports elements that are
   arrayrefs and hashrefs
   
 =item *  =item *
   
 symblist($mapname,%newhash) : update symbolic storage links  arrayref2str($arrayref) : convert an arrayref into a string complete
   with escaping and '&' separators, supports elements that are arrayrefs
   and hashrefs
   
 =item *  =item *
   
 symbread($filename) : return symbolic list entry (filename argument optional);  str2hash($string) : convert string to hash using unescaping and
 returns the data handle  splitting on '=' and '&', supports elements that are arrayrefs and
   hashrefs
   
 =item *  =item *
   
 numval($salt) : return random seed value (addend for rndseed)  str2array($string) : convert string to hash using unescaping and
   splitting on '&', supports elements that are arrayrefs and hashrefs
   
   =back
   
   =head2 Logging Routines
   
   =over 4
   
   These routines allow one to make log messages in the lonnet.log and
   lonnet.perm logfiles.
   
 =item *  =item *
   
 rndseed($symb,$courseid,$domain,$username) : create a random sum; returns  logtouch() : make sure the logfile, lonnet.log, exists
 a random seed, all arguments are optional, if they aren't sent it uses the  
 environment to derive them. Note: if symb isn't sent and it can't get one  
 from &symbread it will use the current time as its return value  
   
 =item *  =item *
   
 ireceipt($funame,$fudom,$fucourseid,$fusymb) : return unique,  logthis() : append message to the normal lonnet.log file, it gets
 unfakeable, receipt  preiodically rolled over and deleted.
   
 =item *  =item *
   
 receipt() : API to ireceipt working off of ENV values; given out to users  logperm() : append a permanent message to lonnet.perm.log, this log
   file never gets deleted by any automated portion of the system, only
   messages of critical importance should go in here.
   
   =back
   
   =head2 General File Helper Routines
   
   =over 4
   
 =item *  =item *
   
 getfile($file) : serves up a file, returns the contents of a file or -1;  getfile($file) : returns the entire contents of a file or -1; it
 replicates and subscribes to the file  properly subscribes to and replicates the file if neccessary.
   
 =item *  =item *
   
 filelocation($dir,$file) : returns file system location of a file based on URI;  filelocation($dir,$file) : returns file system location of a file
 meant to be "fairly clean" absolute reference  based on URI; 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 *  =item *
   
Line 3019  filelocation except for hrefs Line 3845  filelocation except for hrefs
   
 declutter() : declutters URLs (remove docroot, beginning slashes, 'res' etc)  declutter() : declutters URLs (remove docroot, beginning slashes, 'res' etc)
   
   =back
   
   =head2 HTTP Helper Routines
   
   =over 4
   
 =item *  =item *
   
 escape() : unpack non-word characters into CGI-compatible hex codes  escape() : unpack non-word characters into CGI-compatible hex codes
Line 3027  escape() : unpack non-word characters in Line 3859  escape() : unpack non-word characters in
   
 unescape() : pack CGI-compatible hex codes into actual non-word ASCII character  unescape() : pack CGI-compatible hex codes into actual non-word ASCII character
   
   =back
   
   =head1 PRIVATE SUBROUTINES
   
   =head2 Underlying communication routines (Shouldn't call)
   
   =over 4
   
   =item *
   
   subreply() : tries to pass a message to lonc, returns con_lost if incapable
   
   =item *
   
   reply() : uses subreply to send a message to remote machine, logs all failures
   
   =item *
   
   critical() : passes a critical message to another server; if cannot
   get through then place message in connection buffer directory and
   returns con_delayed, if incapable of saving message, returns
   con_failed
   
   =item *
   
   reconlonc() : tries to reconnect lonc client processes.
   
   =back
   
   =head2 Resource Access Logging
   
   =over 4
   
   =item *
   
   flushcourselogs() : flush (save) buffer logs and access logs
   
   =item *
   
   courselog($what) : save message for course in hash
   
   =item *
   
   courseacclog($what) : save message for course using &courselog().  Perform
   special processing for specific resource types (problems, exams, quizzes, etc).
   
 =item *  =item *
   
 goodbye() : flush course logs and log shutting down; it is called in srm.conf  goodbye() : flush course logs and log shutting down; it is called in srm.conf
Line 3034  as a PerlChildExitHandler Line 3912  as a PerlChildExitHandler
   
 =back  =back
   
   =head2 Other
   
   =over 4
   
   =item *
   
   symblist($mapname,%newhash) : update symbolic storage links
   
   =back
   
 =cut  =cut

Removed from v.1.194  
changed lines
  Added in v.1.268


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>