Diff for /loncom/lonnet/perl/lonnet.pm between versions 1.215 and 1.301

version 1.215, 2002/05/08 15:21:04 version 1.301, 2002/11/18 15:16:35
Line 77  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 %iphost %spareid %hostdom 
    %libserv %pr %prp %metacache %packagetab      %libserv %pr %prp %metacache %packagetab %titlecache 
    %courselogs %accesshash $processmarker $dumpcount      %courselogs %accesshash $processmarker $dumpcount 
    %coursedombuf %coursehombuf %courseresdatacache);     %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::LCParser;  use HTML::LCParser;
 use Fcntl qw(:flock);  use Fcntl qw(:flock);
   use Apache::loncoursedata;
   
 my $readit;  my $readit;
   
 # --------------------------------------------------------------------- Logging  # --------------------------------------------------------------------- Logging
Line 140  sub reply { Line 142  sub reply {
     unless (defined($hostname{$server})) { return 'no_such_host'; }      unless (defined($hostname{$server})) { return 'no_such_host'; }
     my $answer=subreply($cmd,$server);      my $answer=subreply($cmd,$server);
     if ($answer eq 'con_lost') {      if ($answer eq 'con_lost') {
        sleep 5;          #sleep 5; 
        $answer=subreply($cmd,$server);         #$answer=subreply($cmd,$server);
        if ($answer eq 'con_lost') {         #if ($answer eq 'con_lost') {
    &logthis("Second attempt con_lost on $server");   #   &logthis("Second attempt con_lost on $server");
            my $peerfile="$perlvar{'lonSockDir'}/$server";          #   my $peerfile="$perlvar{'lonSockDir'}/$server";
            my $client=IO::Socket::UNIX->new(Peer    =>"$peerfile",          #   my $client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
                                             Type    => SOCK_STREAM,          #                                    Type    => SOCK_STREAM,
                                             Timeout => 10)          #                                    Timeout => 10)
                       or return "con_lost";          #              or return "con_lost";
            &logthis("Killing socket");          #   &logthis("Killing socket");
            print $client "close_connection_exit\n";          #   print $client "close_connection_exit\n";
            sleep 5;             #sleep 5;
            $answer=subreply($cmd,$server);                 #   $answer=subreply($cmd,$server);       
        }            #}   
     }      }
     if (($answer=~/^refused/) || ($answer=~/^rejected/)) {      if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
        &logthis("<font color=blue>WARNING:".         &logthis("<font color=blue>WARNING:".
Line 348  sub delenv { Line 350  sub delenv {
     return 'ok';      return 'ok';
 }  }
   
   # ------------------------------------------ Fight off request when overloaded
   
   sub overloaderror {
       my ($r,$checkserver)=@_;
       unless ($checkserver) { $checkserver=$perlvar{'lonHostID'}; }
       my $loadavg;
       if ($checkserver eq $perlvar{'lonHostID'}) {
          my $loadfile=Apache::File->new('/proc/loadavg');
          $loadavg=<$loadfile>;
          $loadavg =~ s/\s.*//g;
          $loadavg = 100*$loadavg/$perlvar{'lonLoadLim'};
       } else {
          $loadavg=&reply('load',$checkserver);
       }
       my $overload=$loadavg-100;
       if ($overload>0) {
    $r->err_headers_out->{'Retry-After'}=$overload;
           $r->log_error('Overload of '.$overload.' on '.$checkserver);
           return 413;
       }    
       return '';
   }
   
 # ------------------------------ Find server with least workload from spare.tab  # ------------------------------ Find server with least workload from spare.tab
   
 sub spareserver {  sub spareserver {
       my $loadpercent = shift;
     my $tryserver;      my $tryserver;
     my $spareserver='';      my $spareserver='';
     my $lowestserver=100;      my $lowestserver=$loadpercent; 
     foreach $tryserver (keys %spareid) {      foreach $tryserver (keys %spareid) {
        my $answer=reply('load',$tryserver);         my $answer=reply('load',$tryserver);
        if (($answer =~ /\d/) && ($answer<$lowestserver)) {         if (($answer =~ /\d/) && ($answer<$lowestserver)) {
Line 481  sub authenticate { Line 507  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 563  sub idput { Line 593  sub idput {
   
 # ------------------------------------- Find the section of student in a course  # ------------------------------------- Find the section of student in a course
   
   sub getsection {
       my ($udom,$unam,$courseid)=@_;
       $courseid=~s/\_/\//g;
       $courseid=~s/^(\w)/\/$1/;
       my %Pending; 
       my %Expired;
       #
       # Each role can either have not started yet (pending), be active, 
       #    or have expired.
       #
       # If there is an active role, we are done.
       #
       # If there is more than one role which has not started yet, 
       #     choose the one which will start sooner
       # If there is one role which has not started yet, return it.
       #
       # If there is more than one expired role, choose the one which ended last.
       # If there is a role which has expired, return it.
       #
       foreach (split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles',
                           &homeserver($unam,$udom)))) {
           my ($key,$value)=split(/\=/,$_);
           $key=&unescape($key);
           next if ($key !~/^$courseid(?:\/)*(\w+)*\_st$/);
           my $section=$1;
           if ($key eq $courseid.'_st') { $section=''; }
           my ($dummy,$end,$start)=split(/\_/,&unescape($value));
           my $now=time;
           if (defined($end) && ($now > $end)) {
               $Expired{$end}=$section;
               next;
           }
           if (defined($start) && ($now < $start)) {
               $Pending{$start}=$section;
               next;
           }
           return $section;
       }
       #
       # Presumedly there will be few matching roles from the above
       # loop and the sorting time will be negligible.
       if (scalar(keys(%Pending))) {
           my ($time) = sort {$a <=> $b} keys(%Pending);
           return $Pending{$time};
       } 
       if (scalar(keys(%Expired))) {
           my @sorted = sort {$a <=> $b} keys(%Expired);
           my $time = pop(@sorted);
           return $Expired{$time};
       }
       return '-1';
   }
   
 sub usection {  sub usection {
     my ($udom,$unam,$courseid)=@_;      my ($udom,$unam,$courseid)=@_;
     $courseid=~s/\_/\//g;      $courseid=~s/\_/\//g;
Line 604  sub userenvironment { Line 687  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);
   }
   
   # ------------------------------------------ Find current version of a resource
   
   sub getversion {
       my $fname=&clutter(shift);
       unless ($fname=~/^\/res\//) { return -1; }
       return &currentversion(&filelocation('',$fname));
   }
   
   sub currentversion {
       my $fname=shift;
       my $author=$fname;
       $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
       my ($udom,$uname)=split(/\//,$author);
       my $home=homeserver($uname,$udom);
       if ($home eq 'no_host') { 
           return -1; 
       }
       my $answer=reply("currentversion:$fname",$home);
       if (($answer eq 'con_lost') || ($answer eq 'rejected')) {
    return -1;
       }
       return $answer;
   }
   
 # ----------------------------- Subscribe to a resource, return URL if possible  # ----------------------------- Subscribe to a resource, return URL if possible
   
 sub subscribe {  sub subscribe {
Line 612  sub subscribe { Line 731  sub subscribe {
     $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;      $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
     my ($udom,$uname)=split(/\//,$author);      my ($udom,$uname)=split(/\//,$author);
     my $home=homeserver($uname,$udom);      my $home=homeserver($uname,$udom);
     if (($home eq 'no_host') || ($home eq $perlvar{'lonHostID'})) {       if ($home eq 'no_host') { 
         return 'not_found';           return 'not_found'; 
     }      }
     my $answer=reply("sub:$fname",$home);      my $answer=reply("sub:$fname",$home);
Line 643  sub repcopy { Line 762  sub repcopy {
     } elsif ($remoteurl eq 'directory') {      } elsif ($remoteurl eq 'directory') {
            return OK;             return OK;
     } else {      } else {
           my $author=$filename;
           $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
           my ($udom,$uname)=split(/\//,$author);
           my $home=homeserver($uname,$udom);
           unless ($home eq $perlvar{'lonHostID'}) {
            my @parts=split(/\//,$filename);             my @parts=split(/\//,$filename);
            my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";             my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
            if ($path ne "$perlvar{'lonDocRoot'}/res") {             if ($path ne "$perlvar{'lonDocRoot'}/res") {
Line 678  sub repcopy { Line 802  sub repcopy {
                rename($transname,$filename);                 rename($transname,$filename);
                return OK;                 return OK;
            }             }
          }
     }      }
 }  }
   
Line 704  sub ssi { Line 829  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'};
       }
       return 
           &finishuserfileupload($docuname,$docudom,$docuhome,$formname,$fname);
   }
   
   sub finishuserfileupload {
       my ($docuname,$docudom,$docuhome,$formname,$fname)=@_;
       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
   #
       
       my $fetchresult= 
    &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$fname,$docuhome);
       if ($fetchresult eq 'ok') {
   #
   # Return the URL to it
           return '/uploaded/'.$path.$fname;
       } else {
           &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$fname.
            ' to host '.$docuhome.': '.$fetchresult);
           return '/adm/notfound.html';
       }    
   }
   
 # ------------------------------------------------------------------------- Log  # ------------------------------------------------------------------------- Log
   
 sub log {  sub log {
Line 735  sub flushcourselogs { Line 938  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 780  sub countacc { Line 983  sub countacc {
     my $url=&declutter(shift);      my $url=&declutter(shift);
     unless ($ENV{'request.course.id'}) { return ''; }      unless ($ENV{'request.course.id'}) { return ''; }
     $accesshash{$ENV{'request.course.id'}.'___'.$url.'___course'}=1;      $accesshash{$ENV{'request.course.id'}.'___'.$url.'___course'}=1;
     my $key=$processmarker.'_'.$dumpcount.'___'.$url.'___count';      my $key=$$.$processmarker.'_'.$dumpcount.'___'.$url.'___count';
     if (defined($accesshash{$key})) {      if (defined($accesshash{$key})) {
  $accesshash{$key}++;   $accesshash{$key}++;
     } else {      } else {
Line 795  sub checkout { Line 998  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 844  sub checkin { Line 1048  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 899  sub devalidate { Line 1103  sub devalidate {
     if ($cid) {      if ($cid) {
  my $key=$ENV{'user.name'}.':'.$ENV{'user.domain'}.':';   my $key=$ENV{'user.name'}.':'.$ENV{'user.domain'}.':';
         my $status=          my $status=
     &del('nohist_calculatedsheet',      &del('nohist_calculatedsheets',
  [$key.'studentcalc'],   [$key.'studentcalc'],
  $ENV{'course.'.$cid.'.domain'},   $ENV{'course.'.$cid.'.domain'},
  $ENV{'course.'.$cid.'.num'})   $ENV{'course.'.$cid.'.num'})
Line 914  sub devalidate { Line 1118  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 {  sub arrayref2str {
   my ($arrayref) = @_;    my ($arrayref) = @_;
   my $result='_ARRAY_REF__';    my $result='__ARRAY_REF__';
   foreach my $elem (@$arrayref) {    foreach my $elem (@$arrayref) {
     if (ref($elem) eq 'ARRAY') {      if(ref($elem) eq 'ARRAY') {
       $result.=&escape(&arrayref2str($elem)).'&';        $result.=&arrayref2str($elem).'&';
     } elsif (ref($elem) eq 'HASH') {      } elsif(ref($elem) eq 'HASH') {
       $result.=&escape(&hashref2str($elem)).'&';        $result.=&hashref2str($elem).'&';
     } elsif (ref($elem)) {      } elsif(ref($elem)) {
       &logthis("Got a ref of ".(ref($elem))." skipping.");        #print("Got a ref of ".(ref($elem))." skipping.");
     } else {      } else {
       $result.=&escape($elem).'&';        $result.=&escape($elem).'&';
     }      }
   }    }
   $result=~s/\&$//;    $result=~s/\&$//;
     $result .= '__END_ARRAY_REF__';
   return $result;    return $result;
 }  }
   
 sub hash2str {  sub hash2str {
   my (%hash) = @_;    my (%hash) = @_;
   my $result=&hashref2str(\%hash);    my $result=&hashref2str(\%hash);
   $result=~s/^_HASH_REF__//;    $result=~s/^__HASH_REF__//;
     $result=~s/__END_HASH_REF__$//;
   return $result;    return $result;
 }  }
   
 sub hashref2str {  sub hashref2str {
   my ($hashref)=@_;    my ($hashref)=@_;
   my $result='_HASH_REF__';    my $result='__HASH_REF__';
   foreach (keys(%$hashref)) {    foreach (keys(%$hashref)) {
     if (ref($_) eq 'ARRAY') {      if (ref($_) eq 'ARRAY') {
       $result.=&escape(&arrayref2str($_)).'=';        $result.=&arrayref2str($_).'=';
     } elsif (ref($_) eq 'HASH') {      } elsif (ref($_) eq 'HASH') {
       $result.=&escape(&hashref2str($_)).'=';        $result.=&hashref2str($_).'=';
     } elsif (ref($_)) {      } elsif (ref($_)) {
       &logthis("Got a ref of ".(ref($_))." skipping.");        $result.='=';
         #print("Got a ref of ".(ref($_))." skipping.");
     } else {      } else {
       $result.=&escape($_).'=';   if ($_) {$result.=&escape($_).'=';} else { last; }
     }      }
   
     if (ref($$hashref{$_}) eq 'ARRAY') {      if(ref($hashref->{$_}) eq 'ARRAY') {
       $result.=&escape(&arrayref2str($$hashref{$_})).'&';        $result.=&arrayref2str($hashref->{$_}).'&';
     } elsif (ref($$hashref{$_}) eq 'HASH') {      } elsif(ref($hashref->{$_}) eq 'HASH') {
       $result.=&escape(&hashref2str($$hashref{$_})).'&';        $result.=&hashref2str($hashref->{$_}).'&';
     } elsif (ref($$hashref{$_})) {      } elsif(ref($hashref->{$_})) {
       &logthis("Got a ref of ".(ref($$hashref{$_}))." skipping.");         $result.='&';
         #print("Got a ref of ".(ref($hashref->{$_}))." skipping.");
     } else {      } else {
       $result.=&escape($$hashref{$_}).'&';        $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(/\=/,$_);  
     $name=&unescape($name);    if($string !~ /^__HASH_REF__/) {
     $value=&unescape($value);        if (! ($string eq '' || !defined($string))) {
     if ($value =~ /^_HASH_REF__/) {    $hash{'error'}='Not hash reference';
       $value =~ s/^_HASH_REF__//;        }
       my %hash=&str2hash($value);        return (\%hash, $string);
       $value=\%hash;  
     } elsif ($value =~ /^_ARRAY_REF__/) {  
       $value =~ s/^_ARRAY_REF__//;  
       my @array=&str2array($value);  
       $value=\@array;  
     }  
     $returnhash{$name}=$value;  
   }    }
   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 {  sub str2array {
       my ($string)=@_;
       my ($array)=&str2arrayref('__ARRAY_REF__'.$string.'__END_ARRAY_REF__');
       return @$array;
   }
   
   sub str2arrayref {
   my ($string) = @_;    my ($string) = @_;
   my @returnarray;    my @array;
   foreach my $value (split(/\&/,$string)) {  
     $value=&unescape($value);    if($string !~ /^__ARRAY_REF__/) {
     if ($value =~ /^_HASH_REF__/) {        if (! ($string eq '' || !defined($string))) {
       $value =~ s/^_HASH_REF__//;    $array[0]='Array reference error';
       my %hash=&str2hash($value);        }
       $value=\%hash;        return (\@array, $string);
     } elsif ($value =~ /^_ARRAY_REF__/) {  
       $value =~ s/^_ARRAY_REF__//;  
       my @array=&str2array($value);  
       $value=\@array;  
     }  
     push(@returnarray,$value);  
   }    }
   return (@returnarray);  
     $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 1028  sub tmpreset { Line 1330  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 1064  sub tmpstore { Line 1366  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 1108  sub tmprestore { Line 1410  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 1251  sub coursedescription { Line 1553  sub coursedescription {
            while (my ($name,$value) = each %returnhash) {             while (my ($name,$value) = each %returnhash) {
                $envhash{'course.'.$normalid.'.'.$name}=$value;                 $envhash{'course.'.$normalid.'.'.$name}=$value;
            }             }
            $returnhash{'url'}='/res/'.declutter($returnhash{'url'});             $returnhash{'url'}=&clutter($returnhash{'url'});
            $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.             $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.
        $ENV{'user.name'}.'_'.$cdomain.'_'.$cnum;         $ENV{'user.name'}.'_'.$cdomain.'_'.$cnum;
            $envhash{'course.'.$normalid.'.last_cache'}=time;             $envhash{'course.'.$normalid.'.last_cache'}=time;
Line 1379  sub get { Line 1681  sub get {
   
    my $rep=&reply("get:$udomain:$uname:$namespace:$items",$uhome);     my $rep=&reply("get:$udomain:$uname:$namespace:$items",$uhome);
    my @pairs=split(/\&/,$rep);     my @pairs=split(/\&/,$rep);
      if ( $#pairs==0 && $pairs[0] =~ /^(con_lost|error|no_such_host)/i) {
        return @pairs;
      }
    my %returnhash=();     my %returnhash=();
    my $i=0;     my $i=0;
    foreach (@$storearr) {     foreach (@$storearr) {
Line 1496  sub allowed { Line 1801  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') && (!$ENV{'request.course.id'})) { 
              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 1544  sub allowed { Line 1876  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 1566  sub allowed { Line 1895  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 1580  sub allowed { Line 1908  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 1647  sub allowed { Line 1972  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 1658  sub allowed { Line 1983  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 1686  sub allowed { Line 2011  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 1693  sub allowed { Line 2019  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 1716  sub allowed { Line 2050  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 1729  sub allowed { Line 2069  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|/\Q$filename\E$||;
       #Trying to find the conditional for the file
       my $match=($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
          /\&\Q$filename\E\:([\d\|]+)\&/);
       if ($match) {
    return (1,$1);
       } else {
    return (0,0);
       }
   }
   
 # ----------------------------------------------------------------- Define Role  # ----------------------------------------------------------------- Define Role
   
 sub definerole {  sub definerole {
Line 1774  sub definerole { Line 2132  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 1791  sub metadata_query { Line 2151  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 1843  sub modifyuserauth { Line 2261  sub modifyuserauth {
     my $uhome=&homeserver($uname,$udom);      my $uhome=&homeserver($uname,$udom);
     unless (&allowed('mau',$udom)) { return 'refused'; }      unless (&allowed('mau',$udom)) { return 'refused'; }
     &logthis('Call to modify user authentication '.$udom.', '.$uname.', '.      &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'}.
                ' in domain '.$ENV{'request.role.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'},      &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.home'},
Line 1874  sub modifyuser { Line 2293  sub modifyuser {
      $last.', '.$gene.'(forceid: '.$forceid.')'.       $last.', '.$gene.'(forceid: '.$forceid.')'.
              (defined($desiredhome) ? ' desiredhome = '.$desiredhome :               (defined($desiredhome) ? ' desiredhome = '.$desiredhome :
                                      ' desiredhome not specified').                                        ' desiredhome not specified'). 
              ' by '.$ENV{'user.name'}.' at '.$ENV{'user.domain'});               ' by '.$ENV{'user.name'}.' at '.$ENV{'user.domain'}.
     my $uhome=&homeserver($uname,$udom);               ' in domain '.$ENV{'request.role.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='';
Line 1905  sub modifyuser { Line 2325  sub modifyuser {
  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';
         }          }
Line 1955  sub modifystudent { Line 2375  sub modifystudent {
  ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$forceid,   ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$forceid,
          $desiredhome);           $desiredhome);
     unless ($reply eq 'ok') { return $reply; }      unless ($reply eq 'ok') { return $reply; }
       # This will cause &modify_student_enrollment to get the uid from the
       # students environment
       $uid = undef if (!$forceid);
       $reply = &modify_student_enrollment($udom,$uname,$uid,$first,$middle,
                                           $last,$gene,$usec,$end,$start);
       return $reply;
   }
   
   sub modify_student_enrollment {
       my ($udom,$uname,$uid,$first,$middle,$last,$gene,$usec,$end,$start) = @_;
       # Get the course id from the environment
       my $cid='';
       unless ($cid=$ENV{'request.course.id'}) {
    return 'not_in_class';
       }
       # Make sure the user exists
     my $uhome=&homeserver($uname,$udom);      my $uhome=&homeserver($uname,$udom);
     if (($uhome eq '') || ($uhome eq 'no_host')) {       if (($uhome eq '') || ($uhome eq 'no_host')) { 
  return 'error: no such user';   return 'error: no such user';
     }      }
 # -------------------------------------------------- Add student to course list      #
     $reply=critical('put:'.$ENV{'course.'.$cid.'.domain'}.':'.      # Get student data if we were not given enough information
       if (!defined($first)  || $first  eq '' || 
           !defined($last)   || $last   eq '' || 
           !defined($uid)    || $uid    eq '' || 
           !defined($middle) || $middle eq '' || 
           !defined($gene)   || $gene   eq '') {
           # They did not supply us with enough data to enroll the student, so
           # we need to pick up more information.
           my %tmp = &get('environment',
                          ['firstname','middlename','lastname', 'generation','id']
                          ,$udom,$uname);
   
           foreach (keys(%tmp)) {
               &logthis("key $_ = ".$tmp{$_});
           }
           $first  = $tmp{'firstname'}  if (!defined($first)  || $first  eq '');
           $middle = $tmp{'middlename'} if (!defined($middle) || $middle eq '');
           $last   = $tmp{'lastname'}   if (!defined($last)   || $last eq '');
           $gene   = $tmp{'generation'} if (!defined($gene)   || $gene eq '');
           $uid    = $tmp{'id'}         if (!defined($uid)    || $uid  eq '');
       }
       my $fullname = &Apache::loncoursedata::ProcessFullName($last,$gene,
                                                              $first,$middle);
       my $reply=critical('put:'.$ENV{'course.'.$cid.'.domain'}.':'.
               $ENV{'course.'.$cid.'.num'}.':classlist:'.                $ENV{'course.'.$cid.'.num'}.':classlist:'.
                       &escape($uname.':'.$udom).'='.                        &escape($uname.':'.$udom).'='.
                       &escape($end.':'.$start),                        &escape(join(':',$end,$start,$uid,$usec,$fullname)),
               $ENV{'course.'.$cid.'.home'});                $ENV{'course.'.$cid.'.home'});
     unless (($reply eq 'ok') || ($reply eq 'delayed')) {      unless (($reply eq 'ok') || ($reply eq 'delayed')) {
  return 'error: '.$reply;   return 'error: '.$reply;
     }      }
 # ---------------------------------------------------- Add student role to user      # Add student role to user
     my $uurl='/'.$cid;      my $uurl='/'.$cid;
     $uurl=~s/\_/\//g;      $uurl=~s/\_/\//g;
     if ($usec) {      if ($usec) {
Line 1999  sub writecoursepref { Line 2458  sub writecoursepref {
 # ---------------------------------------------------------- Make/modify course  # ---------------------------------------------------------- Make/modify course
   
 sub createcourse {  sub createcourse {
     my ($udom,$description,$url)=@_;      my ($udom,$description,$url,$course_server,$nonstandard)=@_;
     $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';
     }      }
   # ----------------------------------------------------------------- Course made
       my $topurl=$url;
       unless ($nonstandard) {
   # ------------------------------------------ For standard courses, make top url
           my $mapurl=&clutter($url);
           if ($mapurl eq '/res/') { $mapurl=''; }
           $ENV{'form.initmap'}=(<<ENDINITMAP);
   <map>
   <resource id="1" type="start"></resource>
   <resource id="2" src="$mapurl"></resource>
   <resource id="3" type="finish"></resource>
   <link index="1" from="1" to="2"></link>
   <link index="2" from="2" to="3"></link>
   </map>
   ENDINITMAP
           $topurl=&declutter(
           &finishuserfileupload($uname,$udom,$uhome,'initmap','default.sequence')
                             );
       }
   # ----------------------------------------------------------- Write preferences
     &writecoursepref($udom.'_'.$uname,      &writecoursepref($udom.'_'.$uname,
                      ('description' => $description,                       ('description' => $description,
                       'url'         => $url));                        'url'         => $topurl));
     return '/'.$udom.'/'.$uname;      return '/'.$udom.'/'.$uname;
 }  }
   
Line 2062  sub revokecustomrole { Line 2543  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);
       }
   }
   
   # --------------------------------------------- GetFileTimestamp
   # This function utilizes dirlist and returns the date stamp for
   # when it was last modified.  It will also return an error of -1
   # if an error occurs
   
   sub GetFileTimestamp {
       my ($studentDomain,$studentName,$filename,$root)=@_;
       $studentDomain=~s/\W//g;
       $studentName=~s/\W//g;
       my $subdir=$studentName.'__';
       $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
       my $proname="$studentDomain/$subdir/$studentName";
       $proname .= '/'.$filename;
       my @dir = &Apache::lonnet::dirlist($proname, $studentDomain, $studentName,
                                          $root);
       my $fileStat = $dir[0];
       my @stats = split('&', $fileStat);
       if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') {
           return $stats[9];
       } else {
           return -1;
       }
 }  }
   
 # -------------------------------------------------------- Value of a Condition  # -------------------------------------------------------- Value of a Condition
Line 2161  sub condval { Line 2689  sub condval {
     return $result;      return $result;
 }  }
   
   # ---------------------------------------------------- Devalidate courseresdata
   
   sub devalidatecourseresdata {
       my ($coursenum,$coursedomain)=@_;
       my $hashid=$coursenum.':'.$coursedomain;
       delete $courseresdatacache{$hashid.'.time'};
   }
   
 # --------------------------------------------------- Course Resourcedata Query  # --------------------------------------------------- Course Resourcedata Query
   
 sub courseresdata {  sub courseresdata {
     my ($coursenum,$coursedomain,@which)=@_;      my ($coursenum,$coursedomain,@which)=@_;
     my $coursehom=&homeserver($coursenum,$coursedomain);      my $coursehom=&homeserver($coursenum,$coursedomain);
     my $hashid=$coursenum.':'.$coursedomain;      my $hashid=$coursenum.':'.$coursedomain;
     unless (defined($courseresdatacache{$hashid.'.time'})) {      my $dodump=0;
  unless (time-$courseresdatacache{$hashid.'.time'}<300) {      if (!defined($courseresdatacache{$hashid.'.time'})) {
            my $coursehom=&homeserver($coursenum,$coursedomain);   $dodump=1;
            if ($coursehom) {      } else {
               my $dumpreply=&reply('dump:'.$coursedomain.':'.$coursenum.   if (time-$courseresdatacache{$hashid.'.time'}>300) { $dodump=1; }
      ':resourcedata:.',$coursehom);  
       unless ($dumpreply=~/^error\:/) {  
          $courseresdatacache{$hashid.'.time'}=time;  
                  $courseresdatacache{$hashid}=$dumpreply;  
      }  
   }  
        }  
     }      }
    my @pairs=split(/\&/,$courseresdatacache{$hashid});      if ($dodump) {
    my %returnhash=();   my %dumpreply=&dump('resourcedata',$coursedomain,$coursenum);
    foreach (@pairs) {   my ($tmp) = keys(%dumpreply);
       my ($key,$value)=split(/=/,$_);   if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
       $returnhash{unescape($key)}=unescape($value);      $courseresdatacache{$hashid.'.time'}=time;
    }      $courseresdatacache{$hashid}=\%dumpreply;
     my $item;   }
    foreach $item (@which) {      }
        if ($returnhash{$item}) { return $returnhash{$item}; }      foreach my $item (@which) {
    }   if (defined($courseresdatacache{$hashid}->{$item})) {
    return '';      return $courseresdatacache{$hashid}->{$item};
    }
       }
       return undef;
 }  }
   
 # --------------------------------------------------------- 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 2212  sub EXT { Line 2755  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 2233  sub EXT { Line 2785  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 2253  sub EXT { Line 2809  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 $coursereply=&courseresdata(      my $coursereply=&courseresdata($ENV{'course.'.$courseid.'.num'},
                         $ENV{'course.'.$ENV{'request.course.id'}.'.num'},    $ENV{'course.'.$courseid.'.domain'},
                         $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},    ($seclevelr,$seclevelm,$seclevel,
                         ($seclevelr,$seclevelm,$seclevel,     $courselevelr,$courselevelm,
                          $courselevelr,$courselevelm,$courselevel));     $courselevel));
         if ($coursereply) { return $coursereply; }      if (defined($coursereply)) { return $coursereply; }
   
 # ------------------------------------------------------ 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 $filename;
       if ($metadata) { return $metadata; }   if (!$symbparm) { $symbparm=&symbread(); }
       $metadata=&metadata($ENV{'request.filename'},   if ($symbparm) {
                                          'parameter_'.$spacequalifierrest);      $filename=(split(/\_\_\_/,$symbparm))[2];
       if ($metadata) { return $metadata; }   } else {
       $filename=$ENV{'request.filename'};
    }
    my $metadata=&metadata($filename,$spacequalifierrest);
    if (defined($metadata)) { return $metadata; }
    $metadata=&metadata($filename,'parameter_'.$spacequalifierrest);
    if (defined($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 (defined($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 (defined($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 2374  sub metadata { Line 2936  sub metadata {
     my ($uri,$what,$liburi,$prefix,$depthcount)=@_;      my ($uri,$what,$liburi,$prefix,$depthcount)=@_;
   
     $uri=&declutter($uri);      $uri=&declutter($uri);
       # if it is a non metadata possible uri return quickly
       if (($uri eq '') || (($uri =~ m|^/*adm/|) && ($uri !~ m|^adm/includes|)) ||
           ($uri =~ m|/$|) || ($uri =~ m|/.meta$|)) {
    return '';
       }
     my $filename=$uri;      my $filename=$uri;
     $uri=~s/\.meta$//;      $uri=~s/\.meta$//;
 #  #
Line 2381  sub metadata { Line 2948  sub metadata {
 # Look at timestamp of caching  # Look at timestamp of caching
 # Everything is cached by the main uri, libraries are never directly cached  # Everything is cached by the main uri, libraries are never directly cached
 #  #
     unless (abs($metacache{$uri.':cachedtimestamp'}-time)<600) {      unless (abs($metacache{$uri.':cachedtimestamp'}-time)<600 && !defined($liburi)) {
 #  #
 # Is this a recursive call for a library?  # Is this a recursive call for a library?
 #  #
Line 2404  sub metadata { Line 2971  sub metadata {
       my $package=$token->[2]->{'package'};        my $package=$token->[2]->{'package'};
       my $keyroot='';        my $keyroot='';
               if ($prefix) {                if ($prefix) {
   $keyroot.='_'.$prefix;    $keyroot.=$prefix;
               } else {                } else {
                 if (defined($token->[2]->{'part'})) {                   if (defined($token->[2]->{'part'})) { 
                    $keyroot.='_'.$token->[2]->{'part'};                      $keyroot.='_'.$token->[2]->{'part'}; 
Line 2462  sub metadata { Line 3029  sub metadata {
 #  #
 # Importing a library here  # Importing a library here
 #                  #                
  if (defined($depthcount)) { $depthcount++; } else   
                                            { $depthcount=0; }  
                  if ($depthcount<20) {                   if ($depthcount<20) {
      foreach (split(/\,/,&metadata($uri,'keys',       my $location=$parser->get_text('/import');
                                   $parser->get_text('/import'),$unikey,       my $dir=$filename;
                                   $depthcount))) {       $dir=~s|[^/]*$||;
        $location=&filelocation($dir,$location);
        foreach (sort(split(/\,/,&metadata($uri,'keys',
    $location,$unikey,
    $depthcount+1)))) {
                          $metathesekeys{$_}=1;                           $metathesekeys{$_}=1;
      }       }
  }   }
Line 2493  sub metadata { Line 3062  sub metadata {
  }   }
        }         }
        $metacache{$uri.':keys'}=join(',',keys %metathesekeys);         $metacache{$uri.':keys'}=join(',',keys %metathesekeys);
    &metadata_generate_part0(\%metathesekeys,\%metacache,$uri);
          $metacache{$uri.':allpossiblekeys'}=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
     }      }
     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;
       }
   }
   
   # ------------------------------------------------- Get the title of a resource
   
   sub gettitle {
       my $urlsymb=shift;
       my $symb=&symbread($urlsymb);
       unless ($symb) {
    unless ($urlsymb) { $urlsymb=$ENV{'request.filename'}; }
           return &metadata($urlsymb,'title'); 
       }
       if ($titlecache{$symb}) { return $titlecache{$symb}; }
       my ($map,$resid,$url)=split(/\_\_\_/,$symb);
       my $title='';
       my %bighash;
       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
                               &GDBM_READER(),0640)) {
           my $mapid=$bighash{'map_pc_'.&clutter($map)};
           $title=$bighash{'title_'.$mapid.'.'.$resid};
           untie %bighash;
       }
       if ($title) {
           $titlecache{$symb}=$title;
           return $title;
       } else {
    return &metadata($urlsymb,'title');
       }
   }
       
 # ------------------------------------------------- Update symbolic store links  # ------------------------------------------------- Update symbolic store links
   
 sub symblist {  sub symblist {
Line 2507  sub symblist { Line 3133  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 2524  sub symblist { Line 3150  sub symblist {
 sub symbverify {  sub symbverify {
     my ($symb,$thisfn)=@_;      my ($symb,$thisfn)=@_;
     $thisfn=&declutter($thisfn);      $thisfn=&declutter($thisfn);
   
     &logthis("Symb verify: $symb $thisfn");  
   
 # direct jump to resource in page or to a sequence - will construct own symbs  # direct jump to resource in page or to a sequence - will construct own symbs
     if ($thisfn=~/\.(page|sequence)$/) { return 1; }      if ($thisfn=~/\.(page|sequence)$/) { return 1; }
 # check URL part  # check URL part
     my ($map,$resid,$url)=split(/\_\_\_/,$symb);      my ($map,$resid,$url)=split(/\_\_\_/,$symb);
     unless (&symbclean($url) eq &symbclean($thisfn)) { return 0; }      unless (&symbclean($url) eq &symbclean($thisfn)) { return 0; }
   
 # FIXME: done for now      $symb=&symbclean($symb);
     return 1;  
   
     my %bighash;      my %bighash;
     my $okay=0;      my $okay=0;
     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)) {
                   my $ids=$bighash{'ids_'.&clutter($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);   untie(%bighash);
     }      }
     return $okay;      return $okay;
Line 2563  sub symbclean { Line 3199  sub symbclean {
 # ------------------------------------------------------ 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 &symbclean($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 2590  sub symbread { Line 3231  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_'.&clutter($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 2603  sub symbread { Line 3248  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 2618  sub symbread { Line 3263  sub symbread {
  }   }
                      }                       }
      if ($realpossible!=1) { $syval=''; }       if ($realpossible!=1) { $syval=''; }
                    } else {
                        $syval='';
                  }                   }
       }        }
               untie(%bighash)                untie(%bighash)
Line 2686  sub ireceipt { Line 3333  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 {
   my $file=shift;   my $file=shift;
    if ($file=~/^\/*uploaded\//) { # user file
       my $ua=new LWP::UserAgent;
       my $request=new HTTP::Request('GET',&tokenwrapper($file));
       my $response=$ua->request($request);
       if ($response->is_success()) {
          return $response->content;
       } else { 
          return -1; 
       }
    } else { # normal file from res space
   &repcopy($file);    &repcopy($file);
   if (! -e $file ) { return -1; };    if (! -e $file ) { return -1; };
   my $fh=Apache::File->new($file);    my $fh=Apache::File->new($file);
   my $a='';    my $a='';
   while (<$fh>) { $a .=$_; }    while (<$fh>) { $a .=$_; }
   return $a    return $a;
    }
 }  }
   
 sub filelocation {  sub filelocation {
Line 2709  sub filelocation { Line 3367  sub filelocation {
   if ($file=~m:^/~:) { # is a contruction space reference    if ($file=~m:^/~:) { # is a contruction space reference
     $location = $file;      $location = $file;
     $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:;      $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:;
     } elsif ($file=~/^\/*uploaded/) { # is an uploaded file
       $location=$file;
   } else {    } else {
     $file=~s/^$perlvar{'lonDocRoot'}//;      $file=~s/^$perlvar{'lonDocRoot'}//;
     $file=~s:^/*res::;      $file=~s:^/*res::;
Line 2728  sub hreflocation { Line 3388  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 2741  sub declutter { Line 3402  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|userfiles|ext|raw|priv)\//) { 
          $thisfn='/res'.$thisfn; 
       }
     return $thisfn;      return $thisfn;
 }  }
   
Line 2769  sub goodbye { Line 3441  sub goodbye {
 }  }
   
 BEGIN {  BEGIN {
 # ------------------------------------------------------------ Read access.conf  # ----------------------------------- Read loncapa.conf and loncapa_apache.conf
     unless ($readit) {      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>) {
           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>) {      while (my $configline=<$config>) {
         if ($configline =~ /PerlSetVar/) {          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 2789  BEGIN { Line 3472  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;
    $iphost{$ip}=$id;
    if ($domdescr) { $domaindescription{$domain}=$domdescr; }
    if ($role eq 'library') { $libserv{$id}=$name; }
          } else {
    if ($configline) {
      &logthis("Skipping hosts.tab line -$configline-");
    }
          }
     }      }
 }  }
   
Line 2803  BEGIN { Line 3494  BEGIN {
   
     while (my $configline=<$config>) {      while (my $configline=<$config>) {
        chomp($configline);         chomp($configline);
        if (($configline) && ($configline ne $perlvar{'lonHostID'})) {         if ($configline) {
           $spareid{$configline}=1;            $spareid{$configline}=1;
        }         }
     }      }
Line 2851  BEGIN { Line 3542  BEGIN {
   
 %metacache=();  %metacache=();
   
 $processmarker=$$.'_'.time.'_'.$perlvar{'lonHostID'};  $processmarker='_'.time.'_'.$perlvar{'lonHostID'};
 $dumpcount=0;  $dumpcount=0;
   
 &logtouch();  &logtouch();
Line 2863  $readit=1; Line 3554  $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 2941  authentication scheme Line 3662  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 2964  idput($udom,%ids) : store away a list of Line 3688  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 *
   
   usection($udom,$uname,$cname) : finds the section of student in the
   course $cname, return section name/number or '' for "not in course"
   and '-1' for "no section"
   
 =item *  =item *
   
 userenvironment($domain,$user,$what) : puts out any environment parameter   userenvironment($udom,$uname,@what) : gets the values of the keys
 for a user  passed in @what from the requested user's environment, returns a hash
   
   =back
   
   =head2 User Roles
   
   =over 4
   
 =item *  =item *
   
 subscribe($fname) : subscribe to a resource, return URL if possible  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 *
   
 repcopy($filename) : replicate file  definerole($rolename,$sysrole,$domrole,$courole) : define role; define a custom
   role rolename set privileges in format of lonTabs/roles.tab for system, domain,
   and course level
   
 =item *  =item *
   
 ssi($url,%hash) : server side include, does a complete request cycle on url to  plaintext($short) : return value in %prp hash (rolesplain.tab); plain text
 localhost, posts hash  explanation of a user role term
   
   =back
   
   =head2 User Modification
   
   =over 4
   
 =item *  =item *
   
 log($domain,$name,$home,$message) : write to permanent log for user; use  assignrole($udom,$uname,$url,$role,$end,$start) : assign role; give a role to a
 critical subroutine  user for the level given by URL.  Optional start and end dates (leave empty
   string or zero for "no date")
   
 =item *  =item *
   
 flushcourselogs() : flush (save) buffer logs and access logs  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 *
   
 courselog($what) : save message for course in hash  modifyuserauth($udom,$uname,$umode,$upass) : modify user authentication
   
 =item *  =item *
   
 courseacclog($what) : save message for course using &courselog().  Perform  modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene) : 
 special processing for specific resource types (problems, exams, quizzes, etc).  modify user
   
 =item *  =item *
   
 countacc($url) : count the number of accesses to a given URL  modifystudent
   
   modify a students enrollment and identification information.
   The course id is resolved based on the current users environment.  
   This means the envoking user must be a course coordinator or otherwise
   associated with a course.
   
   This call is essentially a wrapper for lonnet::modifyuser and
   lonnet::modify_student_enrollment
   
   Inputs: 
   
   =over 4
   
   =item B<$udom> Students loncapa domain
   
   =item B<$uname> Students loncapa login name
   
   =item B<$uid> Students id/student number
   
   =item B<$umode> Students authentication mode
   
   =item B<$upass> Students password
   
   =item B<$first> Students first name
   
   =item B<$middle> Students middle name
   
   =item B<$last> Students last name
   
   =item B<$gene> Students generation
   
   =item B<$usec> Students section in course
   
   =item B<$end> Unix time of the roles expiration
   
   =item B<$start> Unix time of the roles start date
   
   =item B<$forceid> If defined, allow $uid to be changed
   
   =item B<$desiredhome> server to use as home server for student
   
   =back
   
 =item *  =item *
   
 sub checkout($symb,$tuname,$tudom,$tcrsid) : check out an item  modify_student_enrollment
   
   Change a students enrollment status in a class.  The environment variable
   'role.request.course' must be defined for this function to proceed.
   
   Inputs:
   
   =over 4
   
   =item $udom, students domain
   
   =item $uname, students name
   
   =item $uid, students user id
   
   =item $first, students first name
   
   =item $middle
   
   =item $last
   
   =item $gene
   
   =item $usec
   
   =item $end
   
   =item $start
   
   =back
   
   
 =item *  =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, supports elements that are arrayrefs and hashrefs  
   
 =item *  =item *
   
 hashref2str($hashref) : convert a hashref into a string complete with  courseresdata($coursenum,$coursedomain,@which) : request for current
 escaping and '=' and '&' separators, supports elements that are  parameter setting for a specific course, @what should be a list of
 arrayrefs and hashrefs  parameters to ask about. This routine caches answers for 5 minutes.
   
   =back
   
   =head2 Course Modification
   
   =over 4
   
 =item *  =item *
   
 arrayref2str($arrayref) : convert an arrayref into a string complete  writecoursepref($courseid,%prefs) : write preferences (environment
 with escaping and '&' separators, supports elements that are arrayrefs  database) for a course
 and hashrefs  
   
 =item *  =item *
   
 str2hash($string) : convert string to hash using unescaping and  createcourse($udom,$description,$url) : make/modify course
 splitting on '=' and '&', supports elements that are arrayrefs and  
 hashrefs  =back
   
   =head2 Resource Subroutines
   
   =over 4
   
 =item *  =item *
   
 str2array($string) : convert string to hash using unescaping and  subscribe($fname) : subscribe to a resource, returns URL if possible (probably should use repcopy instead)
 splitting on '&', supports elements that are arrayrefs and hashrefs  
   
 =item *  =item *
   
 tmpreset($symb,$namespace,$domain,$stuname) : temporary storage  repcopy($filename) : subscribes to the requested file, and attempts to
   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 *
   
 tmprestore($symb,$namespace,$domain,$stuname) : temporary restore  EXT($varname,$symb,$udom,$uname) : evaluates and returns the value of
   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 *
   
 store($storehash,$symb,$namespace,$domain,$stuname) : stores hash permanently  directcondval($number) : get current value of a condition; reads from a state
 for this url; hashref needs to be given and should be a \%hashname; the  string
 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  condval($condidx) : value of condition index based on state
 uses critical subroutine  
   
 =item *  =item *
   
 restore($symb,$namespace,$domain,$stuname) : returns hash for this symb;  metadata($uri,$what,$liburi,$prefix,$depthcount) : request a
 all args 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 *
   
 coursedescription($courseid) : course description  metadata_query($query,$custom,$customshow) : make a metadata query against the
   network of library servers; returns file handle of where SQL and regex results
   will be stored for query
   
 =item *  =item *
   
 rolesinit($domain,$username,$authhost) : get user privileges  symbread($filename) : return symbolic list entry (filename argument optional);
   returns the data handle
   
 =item *  =item *
   
 get($namespace,$storearr,$udomain,$uname) : returns hash with keys from array  symbverify($symb,$thisfn) : verifies that $symb actually exists and is
 reference filled in from namesp ($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 *
   
 del($namespace,$storearr,$udomain,$uname) : deletes keys out of array from  symbclean($symb) : removes versions numbers from a symb, returns the
 namesp ($udomain and $uname are optional)  cleaned symb
   
 =item *  =item *
   
 dump($namespace,$udomain,$uname,$regexp) :   is_on_map($uri) : checks if the $uri is somewhere on the current
 dumps the complete (or key matching regexp) namespace into a hash  course map, user must be in a course for it to work.
 ($udomain, $uname and $regexp are optional)  
   
 =item *  =item *
   
 put($namespace,$storehash,$udomain,$uname) : stores hash in namesp  numval($salt) : return random seed value (addend for rndseed)
 ($udomain and $uname are optional)  
   
 =item *  =item *
   
 cput($namespace,$storehash,$udomain,$uname) : critical put  rndseed($symb,$courseid,$udom,$uname) : create a random sum; returns
 ($udomain and $uname are optional)  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 *
   
 eget($namespace,$storearr,$udomain,$uname) : returns hash with keys from array  ireceipt($funame,$fudom,$fucourseid,$fusymb) : return unique,
 reference filled in from namesp (encrypts the return communication)  unfakeable, receipt
 ($udomain and $uname are optional)  
   
 =item *  =item *
   
 allowed($priv,$uri) : check for a user privilege; returns codes for allowed  receipt() : API to ireceipt working off of ENV values; given out to users
 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  countacc($url) : count the number of accesses to a given URL
 role rolename set privileges in format of lonTabs/roles.tab for system, domain,  
 and course level  
   
 =item *  =item *
   
 metadata_query($query,$custom,$customshow) : make a metadata query against the  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
 network of library servers; returns file handle of where SQL and regex results  
 will be stored for query  
   
 =item *  =item *
   
 plaintext($short) : return value in %prp hash (rolesplain.tab); plain text  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)
 explanation of a user role term  
   
 =item *  =item *
   
 assignrole($udom,$uname,$url,$role,$end,$start) : assign role; give a role to a  expirespread($uname,$udom,$stype,$usymb) : set expire date for spreadsheet
 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  devalidate($symb) : devalidate temporary spreadsheet calculations,
   forcing spreadsheet to reevaluate the resource scores next time.
   
   =back
   
   =head2 Storing/Retreiving Data
   
   =over 4
   
 =item *  =item *
   
 modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene) :   store($storehash,$symb,$namespace,$udom,$uname) : stores hash permanently
 modify user  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 *
   
 modifystudent($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec,  cstore($storehash,$symb,$namespace,$udom,$uname) : same as store but
 $end,$start) : modify student  uses critical subroutine
   
 =item *  =item *
   
 writecoursepref($courseid,%prefs) : write preferences for a course  restore($symb,$namespace,$udom,$uname) : returns hash for this symb;
   all args are optional
   
 =item *  =item *
   
 createcourse($udom,$description,$url) : make/modify course  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 *  =item *
   
 assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start) : assign  tmprestore($symb,$namespace,$udom,$uname) : storage that works very
 custom role; give a custom role to a user for the level given by URL.  Specify  similar to restore, but all data is stored in a temporary location and
 name and domain of role author, and role name  can be reset using tmpreset. Returns a hash of values on success,
   error string otherwise.
   
 =item *  =item *
   
 revokerole($udom,$uname,$url,$role) : revoke a role for url  tmpreset($symb,$namespace,$udom,$uname) : temporary storage reset,
   deltes all keys for $symb form the temporary storage hash.
   
 =item *  =item *
   
 revokecustomrole($udom,$uname,$url,$role) : revoke a custom role  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 3191  dirlist($uri) : return directory list ba Line 4097  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 3255  filelocation except for hrefs Line 4195  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 3263  escape() : unpack non-word characters in Line 4209  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 3270  as a PerlChildExitHandler Line 4262  as a PerlChildExitHandler
   
 =back  =back
   
   =head2 Other
   
   =over 4
   
   =item *
   
   symblist($mapname,%newhash) : update symbolic storage links
   
   =back
   
 =cut  =cut

Removed from v.1.215  
changed lines
  Added in v.1.301


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