Diff for /loncom/lonnet/perl/lonnet.pm between versions 1.1172.2.118.2.11 and 1.1429

version 1.1172.2.118.2.11, 2020/10/26 01:32:59 version 1.1429, 2020/10/15 19:17:40
Line 71  delayed. Line 71  delayed.
 package Apache::lonnet;  package Apache::lonnet;
   
 use strict;  use strict;
 use LWP::UserAgent();  
 use HTTP::Date;  use HTTP::Date;
 use Image::Magick;  use Image::Magick;
 use CGI::Cookie;  use CGI::Cookie;
   
   use Encode;
   
 use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir $deftex  use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir $deftex
             $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease              $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease
             %managerstab $passwdmin);              %managerstab $passwdmin);
Line 99  use LONCAPA qw(:DEFAULT :match); Line 100  use LONCAPA qw(:DEFAULT :match);
 use LONCAPA::Configuration;  use LONCAPA::Configuration;
 use LONCAPA::lonmetadata;  use LONCAPA::lonmetadata;
 use LONCAPA::Lond;  use LONCAPA::Lond;
   use LONCAPA::LWPReq;
 use LONCAPA::transliterate;  use LONCAPA::transliterate;
   
 use File::Copy;  use File::Copy;
Line 111  require Exporter; Line 113  require Exporter;
 our @ISA = qw (Exporter);  our @ISA = qw (Exporter);
 our @EXPORT = qw(%env);  our @EXPORT = qw(%env);
   
   
 # ------------------------------------ Logging (parameters, docs, slots, roles)  # ------------------------------------ Logging (parameters, docs, slots, roles)
 {  {
     my $logid;      my $logid;
Line 125  our @EXPORT = qw(%env); Line 128  our @EXPORT = qw(%env);
  $logid ++;   $logid ++;
         my $now = time();          my $now = time();
  my $id=$now.'00000'.$$.'00000'.$logid;   my $id=$now.'00000'.$$.'00000'.$logid;
         my $logentry = {          my $logentry = { 
                          $id => {                            $id => {
                                   'exe_uname' => $env{'user.name'},                                     'exe_uname' => $env{'user.name'},
                                   'exe_udom'  => $env{'user.domain'},                                     'exe_udom'  => $env{'user.domain'},
                                   'exe_time'  => $now,                                     'exe_time'  => $now,
                                   'exe_ip'    => $ENV{'REMOTE_ADDR'},                                     'exe_ip'    => $ENV{'REMOTE_ADDR'},
                                   'delflag'   => $delflag,                                     'delflag'   => $delflag,
                                   'logentry'  => $storehash,                                     'logentry'  => $storehash,
                                   'uname'     => $uname,                                     'uname'     => $uname,
                                   'udom'      => $udom,                                     'udom'      => $udom,
                                 }                                    }
                        };                         };
         return &put('nohist_'.$hash_name,$logentry,$cdom,$cnum);   return &put('nohist_'.$hash_name,$logentry,$cdom,$cnum);
     }      }
 }  }
   
Line 182  sub create_connection { Line 185  sub create_connection {
      Type    => SOCK_STREAM,       Type    => SOCK_STREAM,
      Timeout => 10);       Timeout => 10);
     return 0 if (!$client);      return 0 if (!$client);
     print $client (join(':',$hostname,$lonid,&machine_ids($hostname))."\n");      print $client (join(':',$hostname,$lonid,&machine_ids($hostname),$loncaparevs{$lonid})."\n");
     my $result = <$client>;      my $result = <$client>;
     chomp($result);      chomp($result);
     return 1 if ($result eq 'done');      return 1 if ($result eq 'done');
Line 227  sub get_server_distarch { Line 230  sub get_server_distarch {
     return;      return;
 }  }
   
   sub get_servercerts_info {
       my ($lonhost,$hostname,$context) = @_;
       return if ($lonhost eq '');
       if ($hostname eq '') {
           $hostname = &hostname($lonhost);
       }
       return if ($hostname eq '');
       my ($rep,$uselocal);
       if ($context eq 'install') {
           $uselocal = 1;
       } elsif (grep { $_ eq $lonhost } &current_machine_ids()) {
           $uselocal = 1;
       }
       if (($context ne 'cgi') && ($context ne 'install') && ($uselocal)) {
           my $distro = (split(/\:/,&get_server_distarch($lonhost)))[0];
           if ($distro eq '') {
               $uselocal = 0;
           } elsif ($distro =~ /^(?:centos|redhat|scientific)(\d+)$/) {
               if ($1 < 6) {
                   $uselocal = 0;
               }
           }  elsif ($distro =~ /^(?:sles)(\d+)$/) {
               if ($1 < 12) {
                   $uselocal = 0;
               }
           }
       }
       if ($uselocal) {
           $rep = LONCAPA::Lond::server_certs(\%perlvar,$lonhost,$hostname);
       } else {
           $rep=&reply('servercerts',$lonhost);
       }
       my ($result,%returnhash);
       if (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') ||
           ($rep eq 'unknown_cmd')) {
           $result = $rep;
       } else {
           $result = 'ok';
           my @pairs=split(/\&/,$rep);
           foreach my $item (@pairs) {
               my ($key,$value)=split(/=/,$item,2);
               my $what = &unescape($key);
               $returnhash{$what}=&thaw_unescape($value);
           }
       }
       return ($result,\%returnhash);
   }
   
 sub get_server_loncaparev {  sub get_server_loncaparev {
     my ($dom,$lonhost,$ignore_cache,$caller) = @_;      my ($dom,$lonhost,$ignore_cache,$caller) = @_;
     if (defined($lonhost)) {      if (defined($lonhost)) {
Line 261  sub get_server_loncaparev { Line 312  sub get_server_loncaparev {
             $answer = &reply('serverloncaparev',$lonhost);              $answer = &reply('serverloncaparev',$lonhost);
             if (($answer eq 'unknown_cmd') || ($answer eq 'con_lost')) {              if (($answer eq 'unknown_cmd') || ($answer eq 'con_lost')) {
                 if ($caller eq 'loncron') {                  if ($caller eq 'loncron') {
                     my $ua=new LWP::UserAgent;  
                     $ua->timeout(4);  
                     my $hostname = &hostname($lonhost);                      my $hostname = &hostname($lonhost);
                     my $protocol = $protocol{$lonhost};                      my $protocol = $protocol{$lonhost};
                     $protocol = 'http' if ($protocol ne 'https');                      $protocol = 'http' if ($protocol ne 'https');
                     my $url = $protocol.'://'.$hostname.'/adm/about.html';                      my $url = $protocol.'://'.$hostname.'/adm/about.html';
                     my $request=new HTTP::Request('GET',$url);                      my $request=new HTTP::Request('GET',$url);
                     my $response=$ua->request($request);                      my $response=&LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,4,1);
                     unless ($response->is_error()) {                      unless ($response->is_error()) {
                         my $content = $response->content;                          my $content = $response->content;
                         if ($content =~ /<p>VERSION\:\s*([\w.\-]+)<\/p>/) {                          if ($content =~ /<p>VERSION\:\s*([\w.\-]+)<\/p>/) {
Line 359  sub remote_devalidate_cache { Line 408  sub remote_devalidate_cache {
     my $items;      my $items;
     return unless (ref($cachekeys) eq 'ARRAY');      return unless (ref($cachekeys) eq 'ARRAY');
     my $cachestr = join('&',@{$cachekeys});      my $cachestr = join('&',@{$cachekeys});
     return &reply('devalidatecache:'.&escape($cachestr),$lonhost);      my $response = &reply('devalidatecache:'.&escape($cachestr),$lonhost);
       return $response;
 }  }
   
 # -------------------------------------------------- Non-critical communication  # -------------------------------------------------- Non-critical communication
Line 391  sub subreply { Line 441  sub subreply {
  } else {   } else {
     &create_connection(&hostname($server),$server);      &create_connection(&hostname($server),$server);
  }   }
         sleep(0.1); # Try again later if failed connection.          sleep(0.1); # Try again later if failed connection.
     }      }
     my $answer;      my $answer;
     if ($client) {      if ($client) {
Line 464  sub reconlonc { Line 514  sub reconlonc {
     &logthis("lonc at pid $loncpid responding, sending USR1");      &logthis("lonc at pid $loncpid responding, sending USR1");
             kill USR1 => $loncpid;              kill USR1 => $loncpid;
             sleep 1;              sleep 1;
          } else {          } else {
     &logthis(      &logthis(
                "<font color=\"blue\">WARNING:".                 "<font color=\"blue\">WARNING:".
                " lonc at pid $loncpid not responding, giving up</font>");                 " lonc at pid $loncpid not responding, giving up</font>");
Line 659  sub check_for_valid_session { Line 709  sub check_for_valid_session {
         }          }
     }      }
     if (!-e "$lonidsdir/$handle.id") {      if (!-e "$lonidsdir/$handle.id") {
         if ((ref($domref)) && ($name eq 'lonID') &&          if ((ref($domref)) && ($name eq 'lonID') && 
             ($handle =~ /^($match_username)\_\d+\_($match_domain)\_(.+)$/)) {              ($handle =~ /^($match_username)\_\d+\_($match_domain)\_(.+)$/)) {
             my ($possuname,$possudom,$possuhome) = ($1,$2,$3);              my ($possuname,$possudom,$possuhome) = ($1,$2,$3);
             if ((&domain($possudom) ne '') && (&homeserver($possuname,$possudom) eq $possuhome)) {              if ((&domain($possudom) ne '') && (&homeserver($possuname,$possudom) eq $possuhome)) {
Line 688  sub check_for_valid_session { Line 738  sub check_for_valid_session {
     if (ref($userhashref) eq 'HASH') {      if (ref($userhashref) eq 'HASH') {
         $userhashref->{'name'} = $disk_env{'user.name'};          $userhashref->{'name'} = $disk_env{'user.name'};
         $userhashref->{'domain'} = $disk_env{'user.domain'};          $userhashref->{'domain'} = $disk_env{'user.domain'};
           $userhashref->{'lti'} = $disk_env{'request.lti.login'};
           if ($userhashref->{'lti'}) {
               $userhashref->{'ltitarget'} = $disk_env{'request.lti.target'};
               $userhashref->{'ltiuri'} = $disk_env{'request.lti.uri'};
           }
     }      }
     untie(%disk_env);      untie(%disk_env);
   
Line 1027  sub find_existing_session { Line 1082  sub find_existing_session {
     return;      return;
 }  }
   
   sub delusersession {
       my ($lonid,$udom,$uname) = @_;
       my $uprimary_id = &domain($udom,'primary');
       my $uintdom = &internet_dom($uprimary_id);
       my $intdom = &internet_dom($lonid);
       my $serverhomedom = &host_domain($lonid);
       if (($uintdom ne '') && ($uintdom eq $intdom)) {
           return &reply(join(':','delusersession',
                               map {&escape($_)} ($udom,$uname)),$lonid);
       }
       return;
   }
   
 # check if user's browser sent load balancer cookie and server still has session  # check if user's browser sent load balancer cookie and server still has session
 # and is not overloaded.  # and is not overloaded.
 sub check_for_balancer_cookie {  sub check_for_balancer_cookie {
Line 1082  sub check_for_balancer_cookie { Line 1150  sub check_for_balancer_cookie {
     return ($otherserver,$cookie);      return ($otherserver,$cookie);
 }  }
   
 sub updatebalcookie {  
     my ($cookie,$balancer,$lastentry)=@_;  
     if ($cookie =~ /^($match_domain)\_($match_username)\_[a-f0-9]{32}$/) {  
         my ($udom,$uname) = ($1,$2);  
         my $uprimary_id = &domain($udom,'primary');  
         my $uintdom = &internet_dom($uprimary_id);  
         my $intdom = &internet_dom($balancer);  
         my $serverhomedom = &host_domain($balancer);  
         if (($uintdom ne '') && ($uintdom eq $intdom)) {  
             return &reply('updatebalcookie:'.&escape($cookie).':'.&escape($lastentry),$balancer);  
         }  
     }  
     return;  
 }  
   
 sub delbalcookie {  sub delbalcookie {
     my ($cookie,$balancer) =@_;      my ($cookie,$balancer) =@_;
     if ($cookie =~ /^($match_domain)\_($match_username)\_[a-f0-9]{32}$/) {      if ($cookie =~ /^($match_domain)\_($match_username)\_[a-f0-9]{32}$/) {
Line 1106  sub delbalcookie { Line 1159  sub delbalcookie {
         my $intdom = &internet_dom($balancer);          my $intdom = &internet_dom($balancer);
         my $serverhomedom = &host_domain($balancer);          my $serverhomedom = &host_domain($balancer);
         if (($uintdom ne '') && ($uintdom eq $intdom)) {          if (($uintdom ne '') && ($uintdom eq $intdom)) {
             return &reply('delbalcookie:'.&escape($cookie),$balancer);              return &reply("delbalcookie:$cookie",$balancer);
         }          }
     }      }
 }  }
Line 1142  sub choose_server { Line 1195  sub choose_server {
         }          }
     }      }
     foreach my $lonhost (keys(%servers)) {      foreach my $lonhost (keys(%servers)) {
         my $loginvia;  
         if ($skiploadbal) {          if ($skiploadbal) {
             if (ref($balancers) eq 'HASH') {              if (ref($balancers) eq 'HASH') {
                 next if (exists($balancers->{$lonhost}));                  next if (exists($balancers->{$lonhost}));
             }              }
         }          }
           my $loginvia;
         if ($checkloginvia) {          if ($checkloginvia) {
             $loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost};              $loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost};
             if ($loginvia) {              if ($loginvia) {
Line 1473  sub get_lonbalancer_config { Line 1526  sub get_lonbalancer_config {
 sub check_loadbalancing {  sub check_loadbalancing {
     my ($uname,$udom,$caller) = @_;      my ($uname,$udom,$caller) = @_;
     my ($is_balancer,$currtargets,$currrules,$dom_in_use,$homeintdom,      my ($is_balancer,$currtargets,$currrules,$dom_in_use,$homeintdom,
         $rule_in_effect,$offloadto,$otherserver,$setcookie);          $rule_in_effect,$offloadto,$otherserver,$setcookie,$dom_balancers);
     my $lonhost = $perlvar{'lonHostID'};      my $lonhost = $perlvar{'lonHostID'};
     my @hosts = &current_machine_ids();      my @hosts = &current_machine_ids();
     my $uprimary_id = &Apache::lonnet::domain($udom,'primary');      my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
     my $uintdom = &Apache::lonnet::internet_dom($uprimary_id);      my $uintdom = &Apache::lonnet::internet_dom($uprimary_id);
     my $intdom = &Apache::lonnet::internet_dom($lonhost);      my $intdom = &Apache::lonnet::internet_dom($lonhost);
     my $serverhomedom = &host_domain($lonhost);      my $serverhomedom = &host_domain($lonhost);
     my $domneedscache;       my $domneedscache;
     my $cachetime = 60*60*24;      my $cachetime = 60*60*24;
   
     if (($uintdom ne '') && ($uintdom eq $intdom)) {      if (($uintdom ne '') && ($uintdom eq $intdom)) {
Line 1500  sub check_loadbalancing { Line 1553  sub check_loadbalancing {
         }          }
     }      }
     if (ref($result) eq 'HASH') {      if (ref($result) eq 'HASH') {
         ($is_balancer,$currtargets,$currrules,$setcookie) =          ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers) =
             &check_balancer_result($result,@hosts);              &check_balancer_result($result,@hosts);
         if ($is_balancer) {          if ($is_balancer) {
             if (ref($currrules) eq 'HASH') {              if (ref($currrules) eq 'HASH') {
Line 1561  sub check_loadbalancing { Line 1614  sub check_loadbalancing {
             }              }
         }          }
         if (ref($result) eq 'HASH') {          if (ref($result) eq 'HASH') {
             ($is_balancer,$currtargets,$currrules,$setcookie) =              ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers) =
                 &check_balancer_result($result,@hosts);                  &check_balancer_result($result,@hosts);
             if ($is_balancer) {              if ($is_balancer) {
                 if (ref($currrules) eq 'HASH') {                  if (ref($currrules) eq 'HASH') {
Line 1593  sub check_loadbalancing { Line 1646  sub check_loadbalancing {
     if ($domneedscache) {      if ($domneedscache) {
         &do_cache_new('loadbalancing',$domneedscache,$is_balancer,$cachetime);          &do_cache_new('loadbalancing',$domneedscache,$is_balancer,$cachetime);
     }      }
     if (($is_balancer) && ($caller ne 'switchserver')) {      if ($is_balancer) {
         my $lowest_load = 30000;          my $lowest_load = 30000;
         if (ref($offloadto) eq 'HASH') {          if (ref($offloadto) eq 'HASH') {
             if (ref($offloadto->{'primary'}) eq 'ARRAY') {              if (ref($offloadto->{'primary'}) eq 'ARRAY') {
Line 1633  sub check_loadbalancing { Line 1686  sub check_loadbalancing {
                 }                  }
             }              }
         }          }
           unless ($homeintdom) {
               undef($setcookie);
           }
     }      }
     if (($is_balancer) && (!$homeintdom)) {      return ($is_balancer,$otherserver,$setcookie,$offloadto,$dom_balancers);
         undef($setcookie);  
     }  
     return ($is_balancer,$otherserver,$setcookie);  
 }  }
   
 sub check_balancer_result {  sub check_balancer_result {
     my ($result,@hosts) = @_;      my ($result,@hosts) = @_;
     my ($is_balancer,$currtargets,$currrules,$setcookie);      my ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers);
     if (ref($result) eq 'HASH') {      if (ref($result) eq 'HASH') {
         if ($result->{'lonhost'} ne '') {          if ($result->{'lonhost'} ne '') {
             my $currbalancer = $result->{'lonhost'};              my $currbalancer = $result->{'lonhost'};
Line 1651  sub check_balancer_result { Line 1704  sub check_balancer_result {
                 $currtargets = $result->{'targets'};                  $currtargets = $result->{'targets'};
                 $currrules = $result->{'rules'};                  $currrules = $result->{'rules'};
             }              }
               $dom_balancers = $currbalancer;
         } else {          } else {
             foreach my $key (keys(%{$result})) {              if (keys(%{$result})) {
                 if (($key ne '') && (grep(/^\Q$key\E$/,@hosts)) &&                  foreach my $key (keys(%{$result})) {
                     (ref($result->{$key}) eq 'HASH')) {                      if (($key ne '') && (grep(/^\Q$key\E$/,@hosts)) &&
                     $is_balancer = 1;                          (ref($result->{$key}) eq 'HASH')) {
                     $currrules = $result->{$key}{'rules'};                          $is_balancer = 1;
                     $currtargets = $result->{$key}{'targets'};                          $currrules = $result->{$key}{'rules'};
                     $setcookie = $result->{$key}{'cookie'};                          $currtargets = $result->{$key}{'targets'};
                     last;                          $setcookie = $result->{$key}{'cookie'};
                           last;
                       }
                 }                  }
                   $dom_balancers = join(',',sort(keys(%{$result})));
             }              }
         }          }
     }      }
     return ($is_balancer,$currtargets,$currrules,$setcookie);      return ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers);
 }  }
   
 sub get_loadbalancer_targets {  sub get_loadbalancer_targets {
Line 1736  sub internet_dom_servers { Line 1793  sub internet_dom_servers {
     return %uniqservers;      return %uniqservers;
 }  }
   
   sub trusted_domains {
       my ($cmdtype,$calldom) = @_;
       my ($trusted,$untrusted);
       if (&domain($calldom) eq '') {
           return ($trusted,$untrusted);
       }
       unless ($cmdtype =~ /^(content|shared|enroll|coaurem|othcoau|domroles|catalog|reqcrs|msg)$/) {
           return ($trusted,$untrusted);
       }
       my $callprimary = &domain($calldom,'primary');
       my $intcalldom = &Apache::lonnet::internet_dom($callprimary);
       if ($intcalldom eq '') {
           return ($trusted,$untrusted);
       }
   
       my ($trustconfig,$cached)=&Apache::lonnet::is_cached_new('trust',$calldom);
       unless (defined($cached)) {
           my %domconfig = &Apache::lonnet::get_dom('configuration',['trust'],$calldom);
           &Apache::lonnet::do_cache_new('trust',$calldom,$domconfig{'trust'},3600);
           $trustconfig = $domconfig{'trust'};
       }
       if (ref($trustconfig)) {
           my (%possexc,%possinc,@allexc,@allinc); 
           if (ref($trustconfig->{$cmdtype}) eq 'HASH') {
               if (ref($trustconfig->{$cmdtype}->{'exc'}) eq 'ARRAY') {
                   map { $possexc{$_} = 1; } @{$trustconfig->{$cmdtype}->{'exc'}}; 
               }
               if (ref($trustconfig->{$cmdtype}->{'inc'}) eq 'ARRAY') {
                   $possinc{$intcalldom} = 1;
                   map { $possinc{$_} = 1; } @{$trustconfig->{$cmdtype}->{'inc'}};
               }
           }
           if (keys(%possexc)) {
               if (keys(%possinc)) {
                   foreach my $key (sort(keys(%possexc))) {
                       next if ($key eq $intcalldom);
                       unless ($possinc{$key}) {
                           push(@allexc,$key);
                       }
                   }
               } else {
                   @allexc = sort(keys(%possexc));
               }
           }
           if (keys(%possinc)) {
               $possinc{$intcalldom} = 1;
               @allinc = sort(keys(%possinc));
           }
           if ((@allexc > 0) || (@allinc > 0)) {
               my %doms_by_intdom;
               my %allintdoms = &all_host_intdom();
               my %alldoms = &all_host_domain();
               foreach my $key (%allintdoms) {
                   if (ref($doms_by_intdom{$allintdoms{$key}}) eq 'ARRAY') {
                       unless (grep(/^\Q$alldoms{$key}\E$/,@{$doms_by_intdom{$allintdoms{$key}}})) {
                           push(@{$doms_by_intdom{$allintdoms{$key}}},$alldoms{$key});
                       }
                   } else {
                       $doms_by_intdom{$allintdoms{$key}} = [$alldoms{$key}]; 
                   }
               }
               foreach my $exc (@allexc) {
                   if (ref($doms_by_intdom{$exc}) eq 'ARRAY') {
                       push(@{$untrusted},@{$doms_by_intdom{$exc}});
                   }
               }
               foreach my $inc (@allinc) {
                   if (ref($doms_by_intdom{$inc}) eq 'ARRAY') {
                       push(@{$trusted},@{$doms_by_intdom{$inc}});
                   }
               }
           }
       }
       return ($trusted,$untrusted);
   }
   
   sub will_trust {
       my ($cmdtype,$domain,$possdom) = @_;
       return 1 if ($domain eq $possdom);
       my ($trustedref,$untrustedref) = &trusted_domains($cmdtype,$possdom);
       my $willtrust; 
       if ((ref($trustedref) eq 'ARRAY') && (@{$trustedref} > 0)) {
           if (grep(/^\Q$domain\E$/,@{$trustedref})) {
               $willtrust = 1;
           }
       } elsif ((ref($untrustedref) eq 'ARRAY') && (@{$untrustedref} > 0)) {
           unless (grep(/^\Q$domain\E$/,@{$untrustedref})) {
               $willtrust = 1;
           }
       } else {
           $willtrust = 1;
       }
       return $willtrust;
   }
   
 # ---------------------- Find the homebase for a user from domain's lib servers  # ---------------------- Find the homebase for a user from domain's lib servers
   
 my %homecache;  my %homecache;
Line 1761  sub homeserver { Line 1913  sub homeserver {
     return 'no_host';      return 'no_host';
 }  }
   
 # ------------------------------------- Find the usernames behind a list of IDs  # ----- Find the usernames behind a list of student/employee IDs or clicker IDs
   
 sub idget {  sub idget {
     my ($udom,@ids)=@_;      my ($udom,$idsref,$namespace)=@_;
     my %returnhash=();      my %returnhash=();
       my @ids=(); 
       if (ref($idsref) eq 'ARRAY') {
           @ids = @{$idsref};
       } else {
           return %returnhash; 
       }
       if ($namespace eq '') {
           $namespace = 'ids';
       }
           
     my %servers = &get_servers($udom,'library');      my %servers = &get_servers($udom,'library');
     foreach my $tryserver (keys(%servers)) {      foreach my $tryserver (keys(%servers)) {
  my $idlist=join('&', map { &escape($_); } @ids);   my $idlist=join('&', map { &escape($_); } @ids);
  $idlist=~tr/A-Z/a-z/;    if ($namespace eq 'ids') {
  my $reply=&reply("idget:$udom:".$idlist,$tryserver);      $idlist=~tr/A-Z/a-z/;
    }
    my $reply;
    if ($namespace eq 'ids') {
       $reply=&reply("idget:$udom:".$idlist,$tryserver);
    } else {
       $reply=&reply("getdom:$udom:$namespace:$idlist",$tryserver);
    }
  my @answer=();   my @answer=();
  if (($reply ne 'con_lost') && ($reply!~/^error\:/)) {   if (($reply ne 'con_lost') && ($reply!~/^error\:/)) {
     @answer=split(/\&/,$reply);      @answer=split(/\&/,$reply);
Line 1780  sub idget { Line 1948  sub idget {
  for ($i=0;$i<=$#ids;$i++) {   for ($i=0;$i<=$#ids;$i++) {
     if ($answer[$i]) {      if ($answer[$i]) {
  $returnhash{$ids[$i]}=&unescape($answer[$i]);   $returnhash{$ids[$i]}=&unescape($answer[$i]);
     }       }
  }   }
     }       }
     return %returnhash;      return %returnhash;
 }  }
   
Line 1797  sub idrget { Line 1965  sub idrget {
     return %returnhash;      return %returnhash;
 }  }
   
 # ------------------------------- Store away a list of names and associated IDs  # Store away a list of names and associated student/employee IDs or clicker IDs
   
 sub idput {  sub idput {
     my ($udom,%ids)=@_;      my ($udom,$idsref,$uhom,$namespace)=@_;
     my %servers=();      my %servers=();
       my %ids=();
       my %byid = ();
       if (ref($idsref) eq 'HASH') {
           %ids=%{$idsref};
       }
       if ($namespace eq '') {
           $namespace = 'ids'; 
       }
     foreach my $uname (keys(%ids)) {      foreach my $uname (keys(%ids)) {
  &cput('environment',{'id'=>$ids{$uname}},$udom,$uname);   &cput('environment',{'id'=>$ids{$uname}},$udom,$uname);
         my $uhom=&homeserver($uname,$udom);          if ($uhom eq '') {
               $uhom=&homeserver($uname,$udom);
           }
         if ($uhom ne 'no_host') {          if ($uhom ne 'no_host') {
             my $id=&escape($ids{$uname});  
             $id=~tr/A-Z/a-z/;  
             my $esc_unam=&escape($uname);              my $esc_unam=&escape($uname);
     if ($servers{$uhom}) {              if ($namespace eq 'ids') {
  $servers{$uhom}.='&'.$id.'='.$esc_unam;                  my $id=&escape($ids{$uname});
                   $id=~tr/A-Z/a-z/;
                   my $esc_unam=&escape($uname);
                   $servers{$uhom}.=$id.'='.$esc_unam.'&';
             } else {              } else {
                 $servers{$uhom}=$id.'='.$esc_unam;                  my @currids = split(/,/,$ids{$uname});
                   foreach my $id (@currids) {
                       $byid{$uhom}{$id} .= $uname.',';
                   }
               }
           }
       }
       if ($namespace eq 'clickers') {
           foreach my $server (keys(%byid)) {
               if (ref($byid{$server}) eq 'HASH') {
                   foreach my $id (keys(%{$byid{$server}})) {
                       $byid{$server} =~ s/,$//;
                       $servers{$uhom}.=&escape($id).'='.&escape($byid{$server}).'&'; 
                   }
             }              }
         }          }
     }      }
     foreach my $server (keys(%servers)) {      foreach my $server (keys(%servers)) {
         &critical('idput:'.$udom.':'.$servers{$server},$server);          $servers{$server} =~ s/\&$//;
           if ($namespace eq 'ids') {     
               &critical('idput:'.$udom.':'.$servers{$server},$server);
           } else {
               &critical('updateclickers:'.$udom.':add:'.$servers{$server},$server);
           }
     }      }
 }  }
   
 # ---------------------------------------- Delete unwanted IDs from ids.db file  # ------------- Delete unwanted student/employee IDs or clicker IDs from domain
   
 sub iddel {  sub iddel {
     my ($udom,$idshashref,$uhome)=@_;      my ($udom,$idshashref,$uhome,$namespace)=@_;
     my %result=();      my %result=();
     unless (ref($idshashref) eq 'HASH') {      my %ids=();
       my %byid = ();
       if (ref($idshashref) eq 'HASH') {
           %ids=%{$idshashref};
       } else {
         return %result;          return %result;
     }      }
       if ($namespace eq '') {
           $namespace = 'ids';
       }
     my %servers=();      my %servers=();
     while (my ($id,$uname) = each(%{$idshashref})) {      while (my ($id,$unamestr) = each(%ids)) {
         my $uhom;          if ($namespace eq 'ids') {
         if ($uhome) {              my $uhom = $uhome;
             $uhom = $uhome;              if ($uhom eq '') { 
         } else {                  $uhom=&homeserver($unamestr,$udom);
             $uhom=&homeserver($uname,$udom);              }
         }              if ($uhom ne 'no_host') {
         if ($uhom ne 'no_host') {  
             if ($servers{$uhom}) {  
                 $servers{$uhom}.='&'.&escape($id);                  $servers{$uhom}.='&'.&escape($id);
             } else {              }
                 $servers{$uhom}=&escape($id);           } else {
               my @curritems = split(/,/,$ids{$id});
               foreach my $uname (@curritems) {
                   my $uhom = $uhome;
                   if ($uhom eq '') {
                       $uhom=&homeserver($uname,$udom);
                   }
                   if ($uhom ne 'no_host') { 
                       $byid{$uhom}{$id} .= $uname.',';
                   }
               }
           }
       }
       if ($namespace eq 'clickers') {
           foreach my $server (keys(%byid)) {
               if (ref($byid{$server}) eq 'HASH') {
                   foreach my $id (keys(%{$byid{$server}})) {
                       $byid{$server}{$id} =~ s/,$//;
                       $servers{$server}.=&escape($id).'='.&escape($byid{$server}{$id}).'&';
                   }
             }              }
         }          }
     }      }
     foreach my $server (keys(%servers)) {      foreach my $server (keys(%servers)) {
         $result{$server} = &critical('iddel:'.$udom.':'.$servers{$server},$uhome);          $servers{$server} =~ s/\&$//;
           if ($namespace eq 'ids') {
               $result{$server} = &critical('iddel:'.$udom.':'.$servers{$server},$uhome);
           } elsif ($namespace eq 'clickers') {
               $result{$server} = &critical('updateclickers:'.$udom.':del:'.$servers{$server},$server);
           }
     }      }
     return %result;      return %result;
 }  }
   
   # ----- Update clicker ID-to-username look-ups in clickers.db on library server 
   
   sub updateclickers {
       my ($udom,$action,$idshashref,$uhome,$critical) = @_;
       my %clickers;
       if (ref($idshashref) eq 'HASH') {
           %clickers=%{$idshashref};
       } else {
           return;
       }
       my $items='';
       foreach my $item (keys(%clickers)) {
           $items.=&escape($item).'='.&escape($clickers{$item}).'&';
       }
       $items=~s/\&$//;
       my $request = "updateclickers:$udom:$action:$items";
       if ($critical) {
           return &critical($request,$uhome);
       } else {
           return &reply($request,$uhome);
       }
   }
   
 # ------------------------------dump from db file owned by domainconfig user  # ------------------------------dump from db file owned by domainconfig user
 sub dump_dom {  sub dump_dom {
     my ($namespace, $udom, $regexp) = @_;      my ($namespace, $udom, $regexp) = @_;
Line 2035  sub inst_directory_query { Line 2284  sub inst_directory_query {
         unless ($homeserver eq $perlvar{'lonHostID'}) {          unless ($homeserver eq $perlvar{'lonHostID'}) {
             if ($srch->{'srchby'} eq 'email') {              if ($srch->{'srchby'} eq 'email') {
                 my $lcrev = &get_server_loncaparev($udom,$homeserver);                  my $lcrev = &get_server_loncaparev($udom,$homeserver);
                 my ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.(\d+)[\w.\-]+\'?$/);                  my ($major,$minor) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                 if (($major eq '' && $minor eq '') || ($major < 2) ||                  if (($major eq '' && $minor eq '') || ($major < 2) ||
                     (($major == 2) && ($minor < 11)) ||                      (($major == 2) && ($minor < 12))) {
                     (($major == 2) && ($minor == 11) && ($subver < 3))) {  
                     return;                      return;
                 }                  }
             }              }
Line 2087  sub usersearch { Line 2335  sub usersearch {
             unless ($tryserver eq $perlvar{'lonHostID'}) {              unless ($tryserver eq $perlvar{'lonHostID'}) {
                 if ($srch->{'srchby'} eq 'email') {                  if ($srch->{'srchby'} eq 'email') {
                     my $lcrev = &get_server_loncaparev($dom,$tryserver);                      my $lcrev = &get_server_loncaparev($dom,$tryserver);
                     my ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.(\d+)[\w.\-]+\'?$/);                      my ($major,$minor) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                     next if (($major eq '' && $minor eq '') || ($major < 2) ||                      next if (($major eq '' && $minor eq '') || ($major < 2) ||
                              (($major == 2) && ($minor < 11)) ||                               (($major == 2) && ($minor < 12)));
                              (($major == 2) && ($minor == 11) && ($subver < 3)));  
                 }                  }
             }              }
             my $host=&hostname($tryserver);              my $host=&hostname($tryserver);
Line 2182  sub get_multiple_instusers { Line 2429  sub get_multiple_instusers {
     my ($udom,$users,$caller) = @_;      my ($udom,$users,$caller) = @_;
     my ($outcome,$results);      my ($outcome,$results);
     if (ref($users) eq 'HASH') {      if (ref($users) eq 'HASH') {
         my $count = keys(%{$users});          my $count = keys(%{$users}); 
         my $requested = &freeze_escape($users);          my $requested = &freeze_escape($users);
         my $homeserver = &domain($udom,'primary');          my $homeserver = &domain($udom,'primary');
         if ($homeserver ne '') {          if ($homeserver ne '') {
Line 2226  sub get_multiple_instusers { Line 2473  sub get_multiple_instusers {
                 } else {                  } else {
                     ($outcome,my $userdata) = split(/=/,$response,2);                      ($outcome,my $userdata) = split(/=/,$response,2);
                     if ($outcome eq 'ok') {                      if ($outcome eq 'ok') {
                         $results = &thaw_unescape($userdata);                          $results = &thaw_unescape($userdata); 
                     }                      }
                 }                  }
             }              }
Line 2331  sub get_domain_defaults { Line 2578  sub get_domain_defaults {
                                   'requestcourses','inststatus',                                    'requestcourses','inststatus',
                                   'coursedefaults','usersessions',                                    'coursedefaults','usersessions',
                                   'requestauthor','selfenrollment',                                    'requestauthor','selfenrollment',
                                   'coursecategories','autoenroll',                                    'coursecategories','ssl','autoenroll',
                                   'helpsettings'],$domain);                                    'trust','helpsettings'],$domain);
     my @coursetypes = ('official','unofficial','community','textbook');      my @coursetypes = ('official','unofficial','community','textbook','placement');
     if (ref($domconfig{'defaults'}) eq 'HASH') {      if (ref($domconfig{'defaults'}) eq 'HASH') {
         $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'};           $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; 
         $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'};          $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'};
Line 2366  sub get_domain_defaults { Line 2613  sub get_domain_defaults {
         }          }
     }      }
     if (ref($domconfig{'requestcourses'}) eq 'HASH') {      if (ref($domconfig{'requestcourses'}) eq 'HASH') {
         foreach my $item ('official','unofficial','community','textbook') {          foreach my $item ('official','unofficial','community','textbook','placement') {
             $domdefaults{$item} = $domconfig{'requestcourses'}{$item};              $domdefaults{$item} = $domconfig{'requestcourses'}{$item};
         }          }
     }      }
Line 2379  sub get_domain_defaults { Line 2626  sub get_domain_defaults {
         }          }
     }      }
     if (ref($domconfig{'coursedefaults'}) eq 'HASH') {      if (ref($domconfig{'coursedefaults'}) eq 'HASH') {
           $domdefaults{'canuse_pdfforms'} = $domconfig{'coursedefaults'}{'canuse_pdfforms'};
         $domdefaults{'usejsme'} = $domconfig{'coursedefaults'}{'usejsme'};          $domdefaults{'usejsme'} = $domconfig{'coursedefaults'}{'usejsme'};
         $domdefaults{'uselcmath'} = $domconfig{'coursedefaults'}{'uselcmath'};          $domdefaults{'uselcmath'} = $domconfig{'coursedefaults'}{'uselcmath'};
         if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') {          if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') {
Line 2395  sub get_domain_defaults { Line 2643  sub get_domain_defaults {
             }              }
             if ($domdefaults{'postsubmit'} eq 'on') {              if ($domdefaults{'postsubmit'} eq 'on') {
                 if (ref($domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') {                  if (ref($domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') {
                     $domdefaults{$type.'postsubtimeout'} =                      $domdefaults{$type.'postsubtimeout'} = 
                         $domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}{$type};                          $domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}{$type}; 
                 }                  }
             }              }
         }          }
Line 2412  sub get_domain_defaults { Line 2660  sub get_domain_defaults {
         }          }
         if ($domconfig{'coursedefaults'}{'texengine'}) {          if ($domconfig{'coursedefaults'}{'texengine'}) {
             $domdefaults{'texengine'} = $domconfig{'coursedefaults'}{'texengine'};              $domdefaults{'texengine'} = $domconfig{'coursedefaults'}{'texengine'};
         }          } 
     }      }
     if (ref($domconfig{'usersessions'}) eq 'HASH') {      if (ref($domconfig{'usersessions'}) eq 'HASH') {
         if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {          if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {
Line 2463  sub get_domain_defaults { Line 2711  sub get_domain_defaults {
             $domdefaults{'catunauth'} = $domconfig{'coursecategories'}{'unauth'};              $domdefaults{'catunauth'} = $domconfig{'coursecategories'}{'unauth'};
         }          }
     }      }
       if (ref($domconfig{'ssl'}) eq 'HASH') {
           if (ref($domconfig{'ssl'}{'replication'}) eq 'HASH') {
               $domdefaults{'replication'} = $domconfig{'ssl'}{'replication'};
           }
           if (ref($domconfig{'ssl'}{'connto'}) eq 'HASH') {
               $domdefaults{'connect'} = $domconfig{'ssl'}{'connto'};
           }
           if (ref($domconfig{'ssl'}{'connfrom'}) eq 'HASH') {
               $domdefaults{'connect'} = $domconfig{'ssl'}{'connfrom'};
           }
       }
       if (ref($domconfig{'trust'}) eq 'HASH') {
           my @prefixes = qw(content shared enroll othcoau coaurem domroles catalog reqcrs msg);
           foreach my $prefix (@prefixes) {
               if (ref($domconfig{'trust'}{$prefix}) eq 'HASH') {
                   $domdefaults{'trust'.$prefix} = $domconfig{'trust'}{$prefix};
               }
           }
       }
     if (ref($domconfig{'autoenroll'}) eq 'HASH') {      if (ref($domconfig{'autoenroll'}) eq 'HASH') {
         $domdefaults{'autofailsafe'} = $domconfig{'autoenroll'}{'autofailsafe'};          $domdefaults{'autofailsafe'} = $domconfig{'autoenroll'}{'autofailsafe'};
     }      }
Line 2535  sub retrieve_instcodes { Line 2802  sub retrieve_instcodes {
     return $totcodes;      return $totcodes;
 }  }
   
   sub course_portal_url {
       my ($cnum,$cdom) = @_;
       my $chome = &homeserver($cnum,$cdom);
       my $hostname = &hostname($chome);
       my $protocol = $protocol{$chome};
       $protocol = 'http' if ($protocol ne 'https');
       my %domdefaults = &get_domain_defaults($cdom);
       my $firsturl;
       if ($domdefaults{'portal_def'}) {
           $firsturl = $domdefaults{'portal_def'};
       } else {
           $firsturl = $protocol.'://'.$hostname;
       }
       return $firsturl;
   }
   
 # --------------------------------------------- Get domain config for passwords  # --------------------------------------------- Get domain config for passwords
   
 sub get_passwdconf {  sub get_passwdconf {
Line 2558  sub get_passwdconf { Line 2841  sub get_passwdconf {
     return %passwdconf;      return %passwdconf;
 }  }
   
 sub course_portal_url {  
     my ($cnum,$cdom) = @_;  
     my $chome = &homeserver($cnum,$cdom);  
     my $hostname = &hostname($chome);  
     my $protocol = $protocol{$chome};  
     $protocol = 'http' if ($protocol ne 'https');  
     my %domdefaults = &get_domain_defaults($cdom);  
     my $firsturl;  
     if ($domdefaults{'portal_def'}) {  
         $firsturl = $domdefaults{'portal_def'};  
     } else {  
         $firsturl = $protocol.'://'.$hostname;  
     }  
     return $firsturl;  
 }  
   
 # --------------------------------------------------- Assign a key to a student  # --------------------------------------------------- Assign a key to a student
   
 sub assign_access_key {  sub assign_access_key {
Line 2819  sub devalidate_cache_new { Line 3086  sub devalidate_cache_new {
   
 sub is_cached_new {  sub is_cached_new {
     my ($name,$id,$debug) = @_;      my ($name,$id,$debug) = @_;
     my $remembered_id=$name.':'.$id; # this is to avoid make_key (which is slow) for       my $remembered_id=$name.':'.$id; # this is to avoid make_key (which is slow) whenever possible
                                      # keys in %remembered hash, which persists for  
                                      # duration of request (no restriction on key length).  
     if (exists($remembered{$remembered_id})) {      if (exists($remembered{$remembered_id})) {
  if ($debug) { &Apache::lonnet::logthis("Early return $remembered_id of $remembered{$remembered_id} "); }   if ($debug) { &Apache::lonnet::logthis("Early return $remembered_id of $remembered{$remembered_id} "); }
  $accessed{$remembered_id}=[&gettimeofday()];   $accessed{$remembered_id}=[&gettimeofday()];
Line 3083  sub repcopy { Line 3348  sub repcopy {
    mkdir($path,0777);     mkdir($path,0777);
                }                 }
            }             }
            my $ua=new LWP::UserAgent;  
            my $request=new HTTP::Request('GET',"$remoteurl");             my $request=new HTTP::Request('GET',"$remoteurl");
            my $response=$ua->request($request,$transname);             my $response;
              if ($remoteurl =~ m{/raw/}) {
                  $response=&LONCAPA::LWPReq::makerequest($home,$request,$transname,\%perlvar,'',0,1);
              } else {
                  $response=&LONCAPA::LWPReq::makerequest($home,$request,$transname,\%perlvar,'',1);
              }
            if ($response->is_error()) {             if ($response->is_error()) {
        unlink($transname);         unlink($transname);
                my $message=$response->status_line;                 my $message=$response->status_line;
Line 3095  sub repcopy { Line 3364  sub repcopy {
            } else {             } else {
        if ($remoteurl!~/\.meta$/) {         if ($remoteurl!~/\.meta$/) {
                   my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta');                    my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta');
                   my $mresponse=$ua->request($mrequest,$filename.'.meta');                    my $mresponse;
                     if ($remoteurl =~ m{/raw/}) {
                         $mresponse = &LONCAPA::LWPReq::makerequest($home,$mrequest,$filename.'.meta',\%perlvar,'',0,1);
                     } else {
                         $mresponse = &LONCAPA::LWPReq::makerequest($home,$mrequest,$filename.'.meta',\%perlvar,'',1);
                     }
                   if ($mresponse->is_error()) {                    if ($mresponse->is_error()) {
       unlink($filename.'.meta');        unlink($filename.'.meta');
                       &logthis(                        &logthis(
Line 3125  sub unsubscribe { Line 3399  sub unsubscribe {
     } elsif (grep { $_ eq $home } &current_machine_ids()) {      } elsif (grep { $_ eq $home } &current_machine_ids()) {
         $answer = 'home';          $answer = 'home';
     } else {      } else {
         $answer = reply("unsub:$fname",$home);          my $defdom = $perlvar{'lonDefDomain'};
           if (&will_trust('content',$defdom,$udom)) {
               $answer = reply("unsub:$fname",$home);
           } else {
               $answer = 'untrusted';
           }
     }      }
     return $answer;      return $answer;
 }  }
Line 3179  sub absolute_url { Line 3458  sub absolute_url {
 sub ssi {  sub ssi {
   
     my ($fn,%form)=@_;      my ($fn,%form)=@_;
     my ($request,$response);      my $request;
   
     $form{'no_update_last_known'}=1;      $form{'no_update_last_known'}=1;
     &Apache::lonenc::check_encrypt(\$fn);      &Apache::lonenc::check_encrypt(\$fn);
     if (%form) {      if (%form) {
       $request=new HTTP::Request('POST',&absolute_url().$fn);        $request=new HTTP::Request('POST',&absolute_url().$fn);
       $request->content(join('&',map {        $request->content(join('&',map { 
             my $name = escape($_);              my $name = escape($_);
             "$name=" . ( ref($form{$_}) eq 'ARRAY'              "$name=" . ( ref($form{$_}) eq 'ARRAY' 
             ? join("&$name=", map {escape($_) } @{$form{$_}})              ? join("&$name=", map {escape($_) } @{$form{$_}}) 
             : &escape($form{$_}) );              : &escape($form{$_}) );    
         } keys(%form)));          } keys(%form)));
     } else {      } else {
       $request=new HTTP::Request('GET',&absolute_url().$fn);        $request=new HTTP::Request('GET',&absolute_url().$fn);
     }      }
   
     $request->header(Cookie => $ENV{'HTTP_COOKIE'});      $request->header(Cookie => $ENV{'HTTP_COOKIE'});
       my $lonhost = $perlvar{'lonHostID'};
       my $islocal;
     if (($env{'request.course.id'}) &&      if (($env{'request.course.id'}) &&
         ($form{'grade_courseid'} eq $env{'request.course.id'}) &&          ($form{'grade_courseid'} eq $env{'request.course.id'}) &&
         ($form{'grade_username'} ne '') && ($form{'grade_domain'} ne '') &&          ($form{'grade_username'} ne '') && ($form{'grade_domain'} ne '') &&
         ($form{'grade_symb'} ne '') &&          ($form{'grade_symb'} ne '') &&
         (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}.          (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}.
                                  ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {                                   ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {
         if (LWP::UserAgent->VERSION >= 5.834) {          $islocal = 1;
             my $ua=new LWP::UserAgent;  
             $ua->local_address('127.0.0.1');  
             $response = $ua->request($request);  
         } else {  
             {  
                 require LWP::Protocol::http;  
                 local @LWP::Protocol::http::EXTRA_SOCK_OPTS = (LocalAddr => '127.0.0.1');  
                 my $ua=new LWP::UserAgent;  
                 $response = $ua->request($request);  
                 @LWP::Protocol::http::EXTRA_SOCK_OPTS = ();  
             }  
         }  
     } else {  
         my $ua=new LWP::UserAgent;  
         $response = $ua->request($request);  
     }      }
       my $response= &LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,
                                                   '','','',$islocal);
   
     if (wantarray) {      if (wantarray) {
  return ($response->content, $response);   return ($response->content, $response);
     } else {      } else {
Line 3229  sub ssi { Line 3497  sub ssi {
   
 sub externalssi {  sub externalssi {
     my ($url)=@_;      my ($url)=@_;
     my $ua=new LWP::UserAgent;  
     my $request=new HTTP::Request('GET',$url);      my $request=new HTTP::Request('GET',$url);
     my $response=$ua->request($request);      my $response = &LONCAPA::LWPReq::makerequest('',$request,'',\%perlvar);
     if (wantarray) {      if (wantarray) {
         return ($response->content, $response);          return ($response->content, $response);
     } else {      } else {
Line 3239  sub externalssi { Line 3506  sub externalssi {
     }      }
 }  }
   
 # If the local copy of a replicated resource is outdated, trigger a  
 # connection from the homeserver to flush the delayed queue. If no update  # If the local copy of a replicated resource is outdated, trigger a  
   # connection from the homeserver to flush the delayed queue. If no update 
 # happens, remove local copies of outdated resource (and corresponding  # happens, remove local copies of outdated resource (and corresponding
 # metadata file).  # metadata file).
   
Line 3260  sub remove_stale_resfile { Line 3528  sub remove_stale_resfile {
                     if ($hostname) {                      if ($hostname) {
                         my $protocol = $protocol{$homeserver};                          my $protocol = $protocol{$homeserver};
                         $protocol = 'http' if ($protocol ne 'https');                          $protocol = 'http' if ($protocol ne 'https');
                         my $uri = $protocol.'://'.$hostname.'/raw/'.&declutter($url);                          my $uri = &declutter($url);
                         my $ua=new LWP::UserAgent;                          my $request=new HTTP::Request('HEAD',$protocol.'://'.$hostname.'/raw/'.$uri);
                         $ua->timeout(5);                          my $response = &LONCAPA::LWPReq::makerequest($homeserver,$request,'',\%perlvar,5,0,1);
                         my $request=new HTTP::Request('HEAD',$uri);  
                         my $response=$ua->request($request);  
                         if ($response->is_success()) {                          if ($response->is_success()) {
                             my $remmodtime = &HTTP::Date::str2time( $response->header('Last-modified') );                              my $remmodtime = &HTTP::Date::str2time( $response->header('Last-modified') );
                             my $locmodtime = (stat($fname))[9];                              my $locmodtime = (stat($fname))[9];
Line 3327  sub allowuploaded { Line 3593  sub allowuploaded {
 #  #
 # Determine if the current user should be able to edit a particular resource,  # Determine if the current user should be able to edit a particular resource,
 # when viewing in course context.  # when viewing in course context.
 # (a) When viewing resource used to determine if "Edit" item is included in  # (a) When viewing resource used to determine if "Edit" item is included in 
 #     Functions.  #     Functions.
 # (b) When displaying folder contents in course editor, used to determine if  # (b) When displaying folder contents in course editor, used to determine if
 #     "Edit" link will be displayed alongside resource.  #     "Edit" link will be displayed alongside resource.
Line 3335  sub allowuploaded { Line 3601  sub allowuploaded {
 #  input: six args -- filename (decluttered), course number, course domain,  #  input: six args -- filename (decluttered), course number, course domain,
 #                   url, symb (if registered) and group (if this is a group  #                   url, symb (if registered) and group (if this is a group
 #                   item -- e.g., bulletin board, group page etc.).  #                   item -- e.g., bulletin board, group page etc.).
 #  output: array of five scalars --  #  output: array of five scalars -- 
 #          $cfile -- url for file editing if editable on current server  #          $cfile -- url for file editing if editable on current server
 #          $home -- homeserver of resource (i.e., for author if published,  #          $home -- homeserver of resource (i.e., for author if published,
 #                                           or course if uploaded.).  #                                           or course if uploaded.).
 #          $switchserver --  1 if server switch will be needed.  #          $switchserver --  1 if server switch will be needed.
 #          $forceedit -- 1 if icon/link should be to go to edit mode  #          $forceedit -- 1 if icon/link should be to go to edit mode 
 #          $forceview -- 1 if icon/link should be to go to view mode  #          $forceview -- 1 if icon/link should be to go to view mode
 #  #
   
Line 3429  sub can_edit_resource { Line 3695  sub can_edit_resource {
                     $forceedit = 1;                      $forceedit = 1;
                 }                  }
                 $cfile = $resurl;                  $cfile = $resurl;
             } elsif (($resurl ne '') && (&is_on_map($resurl))) {              } elsif (($resurl ne '') && (&is_on_map($resurl))) { 
                 if ($resurl =~ m{^/adm/$match_domain/$match_username/\d+/smppg|bulletinboard$}) {                  if ($resurl =~ m{^/adm/$match_domain/$match_username/\d+/smppg|bulletinboard$}) {
                     $incourse = 1;                      $incourse = 1;
                     if ($env{'form.forceedit'}) {                      if ($env{'form.forceedit'}) {
Line 3480  sub can_edit_resource { Line 3746  sub can_edit_resource {
                 }                  }
             } elsif ($resurl eq '/res/lib/templates/simpleproblem.problem/smpedit') {              } elsif ($resurl eq '/res/lib/templates/simpleproblem.problem/smpedit') {
                 my $template = '/res/lib/templates/simpleproblem.problem';                  my $template = '/res/lib/templates/simpleproblem.problem';
                 if (&is_on_map($template)) {                  if (&is_on_map($template)) { 
                     $incourse = 1;                      $incourse = 1;
                     $forceview = 1;                      $forceview = 1;
                     $cfile = $template;                      $cfile = $template;
Line 3540  sub can_edit_resource { Line 3806  sub can_edit_resource {
                 $cfile=$file;                  $cfile=$file;
             }              }
         }          }
         if (($cfile ne '') && (!$incourse || $uploaded) &&          if (($cfile ne '') && (!$incourse || $uploaded) && 
             (($home ne '') && ($home ne 'no_host'))) {              (($home ne '') && ($home ne 'no_host'))) {
             my @ids=&current_machine_ids();              my @ids=&current_machine_ids();
             unless (grep(/^\Q$home\E$/,@ids)) {              unless (grep(/^\Q$home\E$/,@ids)) {
Line 3567  sub in_course { Line 3833  sub in_course {
     if ($hideprivileged) {      if ($hideprivileged) {
         my $skipuser;          my $skipuser;
         my %coursehash = &coursedescription($cdom.'_'.$cnum);          my %coursehash = &coursedescription($cdom.'_'.$cnum);
         my @possdoms = ($cdom);          my @possdoms = ($cdom);  
         if ($coursehash{'checkforpriv'}) {          if ($coursehash{'checkforpriv'}) { 
             push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));              push(@possdoms,split(/,/,$coursehash{'checkforpriv'})); 
         }          }
         if (&privileged($uname,$udom,\@possdoms)) {          if (&privileged($uname,$udom,\@possdoms)) {
             $skipuser = 1;              $skipuser = 1;
Line 3808  sub resizeImage { Line 4074  sub resizeImage {
 # input: $formname - the contents of the file are in $env{"form.$formname"}  # input: $formname - the contents of the file are in $env{"form.$formname"}
 #                    the desired filename is in $env{"form.$formname.filename"}  #                    the desired filename is in $env{"form.$formname.filename"}
 #        $context - possible values: coursedoc, existingfile, overwrite,   #        $context - possible values: coursedoc, existingfile, overwrite, 
 #                                    canceloverwrite, scantron or ''.   #                                    canceloverwrite, scantron or ''.
 #                   if 'coursedoc': upload to the current course  #                   if 'coursedoc': upload to the current course
 #                   if 'existingfile': write file to tmp/overwrites directory   #                   if 'existingfile': write file to tmp/overwrites directory 
 #                   if 'canceloverwrite': delete file written to tmp/overwrites directory  #                   if 'canceloverwrite': delete file written to tmp/overwrites directory
Line 3816  sub resizeImage { Line 4082  sub resizeImage {
 #        $subdir - directory in userfile to store the file into  #        $subdir - directory in userfile to store the file into
 #        $parser - instruction to parse file for objects ($parser = parse) or  #        $parser - instruction to parse file for objects ($parser = parse) or
 #                  if context is 'scantron', $parser is hashref of csv column mapping  #                  if context is 'scantron', $parser is hashref of csv column mapping
 #                  (e.g.,{ PaperID => 0, LastName => 1, FirstName => 2, ID => 3,  #                  (e.g.,{ PaperID => 0, LastName => 1, FirstName => 2, ID => 3, 
 #                          Section => 4, CODE => 5, FirstQuestion => 9 }).  #                          Section => 4, CODE => 5, FirstQuestion => 9 }).
 #        $allfiles - reference to hash for embedded objects  #        $allfiles - reference to hash for embedded objects
 #        $codebase - reference to hash for codebase of java objects  #        $codebase - reference to hash for codebase of java objects
Line 3866  sub userfileupload { Line 4132  sub userfileupload {
             } else {              } else {
                 $docudom = $env{'user.domain'};                  $docudom = $env{'user.domain'};
             }              }
             if ($destuname =~ /^$match_username$/) {               if ($destuname =~ /^$match_username$/) {
                 $docuname = $destuname;                  $docuname = $destuname;
             } else {              } else {
                 $docuname = $env{'user.name'};                  $docuname = $env{'user.name'};
Line 4092  sub extract_embedded_items { Line 4358  sub extract_embedded_items {
     }      }
     if (lc($tagname) eq 'a') {      if (lc($tagname) eq 'a') {
                 unless (($attr->{'href'} =~ /^#/) || ($attr->{'href'} eq '')) {                  unless (($attr->{'href'} =~ /^#/) || ($attr->{'href'} eq '')) {
     &add_filetype($allfiles,$attr->{'href'},'href');                      &add_filetype($allfiles,$attr->{'href'},'href');
                 }                  }
     }      }
             if (lc($tagname) eq 'script') {              if (lc($tagname) eq 'script') {
Line 4273  sub bubblesheet_converter { Line 4539  sub bubblesheet_converter {
         ($fullpath =~ m{^\Q$perlvar{'lonDocRoot'}/userfiles/$cdom/\E$match_courseid/scantron_orig}) &&          ($fullpath =~ m{^\Q$perlvar{'lonDocRoot'}/userfiles/$cdom/\E$match_courseid/scantron_orig}) &&
         (-e $fullpath) && (ref($config) eq 'HASH') && ($format ne '')) {          (-e $fullpath) && (ref($config) eq 'HASH') && ($format ne '')) {
         my (%csvcols,%csvoptions);          my (%csvcols,%csvoptions);
         if (ref($config->{'fields'}) eq 'HASH') {          if (ref($config->{'fields'}) eq 'HASH') {  
             %csvcols = %{$config->{'fields'}};              %csvcols = %{$config->{'fields'}};
         }          }
         if (ref($config->{'options'}) eq 'HASH') {          if (ref($config->{'options'}) eq 'HASH') {
Line 4672  sub flushcourselogs { Line 4938  sub flushcourselogs {
         delete $domainrolehash{$entry};          delete $domainrolehash{$entry};
     }      }
     foreach my $dom (keys(%domrolebuffer)) {      foreach my $dom (keys(%domrolebuffer)) {
         my %servers;   my %servers;
         if (defined(&domain($dom,'primary'))) {   if (defined(&domain($dom,'primary'))) {
             my $primary=&domain($dom,'primary');      my $primary=&domain($dom,'primary');
             my $hostname=&hostname($primary);      my $hostname=&hostname($primary);
             $servers{$primary} = $hostname;      $servers{$primary} = $hostname;
         } else {   } else { 
             %servers = &get_servers($dom,'library');      %servers = &get_servers($dom,'library');
         }   }
  foreach my $tryserver (keys(%servers)) {   foreach my $tryserver (keys(%servers)) {
     if (&reply('domroleput:'.$dom.':'.      if (&reply('domroleput:'.$dom.':'.
                $domrolebuffer{$dom},$tryserver) eq 'ok') {         $domrolebuffer{$dom},$tryserver) eq 'ok') {
         last;   last;
     } else {      } else {  
  &logthis('Put of domain roles failed for '.$dom.' and  '.$tryserver);   &logthis('Put of domain roles failed for '.$dom.' and  '.$tryserver);
     }      }
         }          }
Line 5012  sub get_my_roles { Line 5278  sub get_my_roles {
             } else {              } else {
                 my $possdoms = [$domain];                  my $possdoms = [$domain];
                 if (ref($roledoms) eq 'ARRAY') {                  if (ref($roledoms) eq 'ARRAY') {
                    push(@{$possdoms},@{$roledoms});                     push(@{$possdoms},@{$roledoms}); 
                 }                  }
                 if (&privileged($username,$domain,$possdoms,\@privroles)) {                  if (&privileged($username,$domain,$possdoms,\@privroles)) {
                     if (!$nothide{$username.':'.$domain}) {                      if (!$nothide{$username.':'.$domain}) {
Line 5309  sub courseiddump { Line 5575  sub courseiddump {
     if (($domfilter eq '') ||      if (($domfilter eq '') ||
  (&host_domain($tryserver) eq $domfilter)) {   (&host_domain($tryserver) eq $domfilter)) {
                 my $rep;                  my $rep;
                 if (grep { $_ eq $tryserver } &current_machine_ids()) {                  if (grep { $_ eq $tryserver } current_machine_ids()) {
                     $rep = &LONCAPA::Lond::dump_course_id_handler(                      $rep = LONCAPA::Lond::dump_course_id_handler(
                         join(":", (&host_domain($tryserver), $sincefilter,                          join(":", (&host_domain($tryserver), $sincefilter, 
                                 &escape($descfilter), &escape($instcodefilter),                                  &escape($descfilter), &escape($instcodefilter), 
                                 &escape($ownerfilter), &escape($coursefilter),                                  &escape($ownerfilter), &escape($coursefilter),
                                 &escape($typefilter), &escape($regexp_ok),                                  &escape($typefilter), &escape($regexp_ok), 
                                 $as_hash, &escape($selfenrollonly),                                  $as_hash, &escape($selfenrollonly), 
                                 &escape($catfilter), $showhidden, $caller,                                  &escape($catfilter), $showhidden, $caller, 
                                 &escape($cloner), &escape($cc_clone), $cloneonly,                                  &escape($cloner), &escape($cc_clone), $cloneonly, 
                                 &escape($createdbefore), &escape($createdafter),                                  &escape($createdbefore), &escape($createdafter), 
                                 &escape($creationcontext),$domcloner,$hasuniquecode,                                  &escape($creationcontext),$domcloner,$hasuniquecode,
                                 $reqcrsdom,&escape($reqinstcode))));                                  $reqcrsdom,&escape($reqinstcode))));
                 } else {                  } else {
Line 5334  sub courseiddump { Line 5600  sub courseiddump {
                              &escape($creationcontext).':'.$domcloner.':'.$hasuniquecode.                               &escape($creationcontext).':'.$domcloner.':'.$hasuniquecode.
                              ':'.$reqcrsdom.':'.&escape($reqinstcode),$tryserver);                               ':'.$reqcrsdom.':'.&escape($reqinstcode),$tryserver);
                 }                  }
                        
                 my @pairs=split(/\&/,$rep);                  my @pairs=split(/\&/,$rep);
                 foreach my $item (@pairs) {                  foreach my $item (@pairs) {
                     my ($key,$value)=split(/\=/,$item,2);                      my ($key,$value)=split(/\=/,$item,2);
Line 5561  sub set_first_access { Line 5827  sub set_first_access {
 }  }
 }  }
   
 sub checkout {  
     my ($symb,$tuname,$tudom,$tcrsid)=@_;  
     my $now=time;  
     my $lonhost=$perlvar{'lonHostID'};  
     my $infostr=&escape(  
                  'CHECKOUTTOKEN&'.  
                  $tuname.'&'.  
                  $tudom.'&'.  
                  $tcrsid.'&'.  
                  $symb.'&'.  
                  $now.'&'.$ENV{'REMOTE_ADDR'});  
     my $token=&reply('tmpput:'.$infostr,$lonhost);  
     if ($token=~/^error\:/) {  
         &logthis("<font color=\"blue\">WARNING: ".  
                 "Checkout tmpput failed ".$tudom.' - '.$tuname.' - '.$symb.  
                  "</font>");  
         return '';  
     }  
   
     $token=~s/^(\d+)\_.*\_(\d+)$/$1\*$2\*$lonhost/;  
     $token=~tr/a-z/A-Z/;  
   
     my %infohash=('resource.0.outtoken' => $token,  
                   'resource.0.checkouttime' => $now,  
                   'resource.0.outremote' => $ENV{'REMOTE_ADDR'});  
   
     unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {  
        return '';  
     } else {  
         &logthis("<font color=\"blue\">WARNING: ".  
                 "Checkout cstore failed ".$tudom.' - '.$tuname.' - '.$symb.  
                  "</font>");  
     }  
   
     if (&log($tudom,$tuname,&homeserver($tuname,$tudom),  
                          &escape('Checkout '.$infostr.' - '.  
                                                  $token)) ne 'ok') {  
         return '';  
     } else {  
         &logthis("<font color=\"blue\">WARNING: ".  
                 "Checkout log failed ".$tudom.' - '.$tuname.' - '.$symb.  
                  "</font>");  
     }  
     return $token;  
 }  
   
 # ------------------------------------------------------------ Check in an item  
   
 sub checkin {  
     my $token=shift;  
     my $now=time;  
     my ($ta,$tb,$lonhost)=split(/\*/,$token);  
     $lonhost=~tr/A-Z/a-z/;  
     my $dtoken=$ta.'_'.&hostname($lonhost).'_'.$tb;  
     $dtoken=~s/\W/\_/g;  
     my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=  
                  split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));  
   
     unless (($tuname) && ($tudom)) {  
         &logthis('Check in '.$token.' ('.$dtoken.') failed');  
         return '';  
     }  
   
     unless (&allowed('mgr',$tcrsid)) {  
         &logthis('Check in '.$token.' ('.$dtoken.') unauthorized: '.  
                  $env{'user.name'}.' - '.$env{'user.domain'});  
         return '';  
     }  
   
     my %infohash=('resource.0.intoken' => $token,  
                   'resource.0.checkintime' => $now,  
                   'resource.0.inremote' => $ENV{'REMOTE_ADDR'});  
   
     unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {  
        return '';  
     }  
   
     if (&log($tudom,$tuname,&homeserver($tuname,$tudom),  
                          &escape('Checkin - '.$token)) ne 'ok') {  
         return '';  
     }  
   
     return ($symb,$tuname,$tudom,$tcrsid);  
 }  
   
 # --------------------------------------------- Set Expire Date for Spreadsheet  # --------------------------------------------- Set Expire Date for Spreadsheet
   
 sub expirespread {  sub expirespread {
Line 6239  sub privileged { Line 6420  sub privileged {
     my $now = time;      my $now = time;
     my $roles;      my $roles;
     if (ref($possroles) eq 'ARRAY') {      if (ref($possroles) eq 'ARRAY') {
         $roles = $possroles;          $roles = $possroles; 
     } else {      } else {
         $roles = ['dc','su'];          $roles = ['dc','su'];
     }      }
Line 6266  sub privileged { Line 6447  sub privileged {
         for my $role (@rolesdump{grep { ! /^rolesdef_/ } keys(%rolesdump)}) {          for my $role (@rolesdump{grep { ! /^rolesdef_/ } keys(%rolesdump)}) {
             my ($trole, $tend, $tstart) = split(/_/, $role);              my ($trole, $tend, $tstart) = split(/_/, $role);
             if (grep(/^\Q$trole\E$/,@{$roles})) {              if (grep(/^\Q$trole\E$/,@{$roles})) {
                 return 1 unless ($tend && $tend < $now)                  return 1 unless ($tend && $tend < $now) 
                         or ($tstart && $tstart > $now);                          or ($tstart && $tstart > $now);
             }              }
         }          }
Line 6304  sub privileged_by_domain { Line 6485  sub privileged_by_domain {
                         my ($trole,$uname,$udom,$rest) = split(/:/,$item,4);                          my ($trole,$uname,$udom,$rest) = split(/:/,$item,4);
                         my ($end,$start) = split(/:/,$dompersonnel{$server}{$item});                          my ($end,$start) = split(/:/,$dompersonnel{$server}{$item});
                         next if ($end && $end < $now);                          next if ($end && $end < $now);
                         $privileged{$dom}{$trole}{$uname.':'.$udom} =                          $privileged{$dom}{$trole}{$uname.':'.$udom} = 
                             $dompersonnel{$server}{$item};                              $dompersonnel{$server}{$item};
                     }                      }
                 }                  }
Line 6737  sub check_adhoc_privs { Line 6918  sub check_adhoc_privs {
     my $cckey = 'user.role.'.$checkrole.'./'.$cdom.'/'.$cnum;      my $cckey = 'user.role.'.$checkrole.'./'.$cdom.'/'.$cnum;
     if ($sec) {      if ($sec) {
         $cckey .= '/'.$sec;          $cckey .= '/'.$sec;
     }      } 
     my $setprivs;      my $setprivs;
     if ($env{$cckey}) {      if ($env{$cckey}) {
         my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend);          my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend);
Line 6779  sub set_adhoc_privileges { Line 6960  sub set_adhoc_privileges {
     my ($author,$adv,$rar)= &set_userprivs(\%userroles,\%rolehash);      my ($author,$adv,$rar)= &set_userprivs(\%userroles,\%rolehash);
     &appenv(\%userroles,[$role,'cm']);      &appenv(\%userroles,[$role,'cm']);
     &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$spec);      &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$spec);
     unless ($caller eq 'constructaccess' && $env{'request.course.id'}) {      unless (($caller eq 'constructaccess' && $env{'request.course.id'}) ||
               ($caller eq 'tiny')) {
         &appenv( {'request.role'        => $spec,          &appenv( {'request.role'        => $spec,
                   'request.role.domain' => $dcdom,                    'request.role.domain' => $dcdom,
                   'request.course.sec'  => $sec,                     'request.course.sec'  => $sec,
                  }                   }
                );                 );
         my $tadv=0;          my $tadv=0;
Line 6842  sub unserialize { Line 7024  sub unserialize {
     return {} if $rep =~ /^error/;      return {} if $rep =~ /^error/;
   
     my %returnhash=();      my %returnhash=();
     foreach my $item (split(/\&/,$rep)) {   foreach my $item (split(/\&/,$rep)) {
         my ($key, $value) = split(/=/, $item, 2);      my ($key, $value) = split(/=/, $item, 2);
         $key = unescape($key) unless $escapedkeys;      $key = unescape($key) unless $escapedkeys;
         next if $key =~ /^error: 2 /;      next if $key =~ /^error: 2 /;
         $returnhash{$key} = &thaw_unescape($value);      $returnhash{$key} = &thaw_unescape($value);
     }   }
       #return %returnhash;
     return \%returnhash;      return \%returnhash;
 }  }        
   
 # see Lond::dump_with_regexp  # see Lond::dump_with_regexp
 # if $escapedkeys hash keys won't get unescaped.  # if $escapedkeys hash keys won't get unescaped.
Line 6864  sub dump { Line 7047  sub dump {
     } else {      } else {
         $regexp='.';          $regexp='.';
     }      }
     if (grep { $_ eq $uhome } &current_machine_ids()) {      if (grep { $_ eq $uhome } current_machine_ids()) {
         # user is hosted on this machine          # user is hosted on this machine
         my $reply = LONCAPA::Lond::dump_with_regexp(join(':', ($udomain,          my $reply = LONCAPA::Lond::dump_with_regexp(join(":", ($udomain,
                     $uname, $namespace, $regexp, $range)), $perlvar{'lonVersion'});                      $uname, $namespace, $regexp, $range)), $perlvar{'lonVersion'});
         return %{&unserialize($reply, $escapedkeys)};          return %{unserialize($reply, $escapedkeys)};
     }      }
     my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);      my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
     my @pairs=split(/\&/,$rep);      my @pairs=split(/\&/,$rep);
Line 6876  sub dump { Line 7059  sub dump {
     if (!($rep =~ /^error/ )) {      if (!($rep =~ /^error/ )) {
  foreach my $item (@pairs) {   foreach my $item (@pairs) {
     my ($key,$value)=split(/=/,$item,2);      my ($key,$value)=split(/=/,$item,2);
             $key = &unescape($key) unless ($escapedkeys);          $key = unescape($key) unless $escapedkeys;
           #$key = &unescape($key);
     next if ($key =~ /^error: 2 /);      next if ($key =~ /^error: 2 /);
     $returnhash{$key}=&thaw_unescape($value);      $returnhash{$key}=&thaw_unescape($value);
  }   }
Line 6920  sub currentdump { Line 7104  sub currentdump {
    my $rep;     my $rep;
   
    if (grep { $_ eq $uhome } current_machine_ids()) {     if (grep { $_ eq $uhome } current_machine_ids()) {
        $rep = LONCAPA::Lond::dump_profile_database(join(":", ($sdom, $sname,         $rep = LONCAPA::Lond::dump_profile_database(join(":", ($sdom, $sname, 
                    $courseid)));                     $courseid)));
    } else {     } else {
        $rep = reply('currentdump:'.$sdom.':'.$sname.':'.$courseid,$uhome);         $rep = reply('currentdump:'.$sdom.':'.$sname.':'.$courseid,$uhome);
Line 7181  sub tmpdel { Line 7365  sub tmpdel {
     return &reply("tmpdel:$token",$server);      return &reply("tmpdel:$token",$server);
 }  }
   
 # ------------------------------------------------------------ get_timebased_id  # ------------------------------------------------------------ get_timebased_id 
   
 sub get_timebased_id {  sub get_timebased_id {
     my ($prefix,$keyid,$namespace,$cdom,$cnum,$idtype,$who,$locktries,      my ($prefix,$keyid,$namespace,$cdom,$cnum,$idtype,$who,$locktries,
         $maxtries) = @_;          $maxtries) = @_;
     my ($newid,$error,$dellock);      my ($newid,$error,$dellock);
     unless (($prefix =~ /^\w+$/) && ($keyid =~ /^\w+$/) && ($namespace ne '')) {      unless (($prefix =~ /^\w+$/) && ($keyid =~ /^\w+$/) && ($namespace ne '')) {  
         return ('','ok','invalid call to get suffix');          return ('','ok','invalid call to get suffix');
     }      }
   
Line 7201  sub get_timebased_id { Line 7385  sub get_timebased_id {
     if (!$maxtries) {      if (!$maxtries) {
         $maxtries = 10;          $maxtries = 10;
     }      }
       
     if (($cdom eq '') || ($cnum eq '')) {      if (($cdom eq '') || ($cnum eq '')) {
         if ($env{'request.course.id'}) {          if ($env{'request.course.id'}) {
             $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};              $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
Line 7356  sub get_portfolio_access { Line 7540  sub get_portfolio_access {
                 if (ref($access_hash->{$ipkey}{'ip'}) eq 'ARRAY') {                  if (ref($access_hash->{$ipkey}{'ip'}) eq 'ARRAY') {
                     if (&Apache::loncommon::check_ip_acc(join(',',@{$access_hash->{$ipkey}{'ip'}}),$clientip)) {                      if (&Apache::loncommon::check_ip_acc(join(',',@{$access_hash->{$ipkey}{'ip'}}),$clientip)) {
                         $allowed = 1;                          $allowed = 1;
                         last;                          last; 
                     }                      }
                 }                  }
             }              }
Line 7550  sub usertools_access { Line 7734  sub usertools_access {
                       unofficial => 1,                        unofficial => 1,
                       community  => 1,                        community  => 1,
                       textbook   => 1,                        textbook   => 1,
                         placement  => 1,
                         lti        => 1,
                  );                   );
     } elsif ($context eq 'requestauthor') {      } elsif ($context eq 'requestauthor') {
         %tools = (          %tools = (
Line 7584  sub usertools_access { Line 7770  sub usertools_access {
   
     my ($toolstatus,$inststatus,$envkey);      my ($toolstatus,$inststatus,$envkey);
     if ($context eq 'requestauthor') {      if ($context eq 'requestauthor') {
         $envkey = $context;          $envkey = $context; 
     } else {      } else {
         $envkey = $context.'.'.$tool;          $envkey = $context.'.'.$tool;
     }      }
Line 7746  sub is_advanced_user { Line 7932  sub is_advanced_user {
 }  }
   
 sub check_can_request {  sub check_can_request {
     my ($dom,$can_request,$request_domains) = @_;      my ($dom,$can_request,$request_domains,$uname,$udom) = @_;
     my $canreq = 0;      my $canreq = 0;
       if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) {
           $uname = $env{'user.name'};
           $udom = $env{'user.domain'};
       }
     my ($types,$typename) = &Apache::loncommon::course_types();      my ($types,$typename) = &Apache::loncommon::course_types();
     my @options = ('approval','validate','autolimit');      my @options = ('approval','validate','autolimit');
     my $optregex = join('|',@options);      my $optregex = join('|',@options);
     if ((ref($can_request) eq 'HASH') && (ref($types) eq 'ARRAY')) {      if ((ref($can_request) eq 'HASH') && (ref($types) eq 'ARRAY')) {
         foreach my $type (@{$types}) {          foreach my $type (@{$types}) {
             if (&usertools_access($env{'user.name'},              if (&usertools_access($uname,$udom,$type,undef,
                                   $env{'user.domain'},                                    'requestcourses')) {
                                   $type,undef,'requestcourses')) {  
                 $canreq ++;                  $canreq ++;
                 if (ref($request_domains) eq 'HASH') {                  if (ref($request_domains) eq 'HASH') {
                     push(@{$request_domains->{$type}},$env{'user.domain'});                      push(@{$request_domains->{$type}},$udom);
                 }                  }
                 if ($dom eq $env{'user.domain'}) {                  if ($dom eq $udom) {
                     $can_request->{$type} = 1;                      $can_request->{$type} = 1;
                 }                  }
             }              }
             if ($env{'environment.reqcrsotherdom.'.$type} ne '') {              if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '') &&
                   ($env{'environment.reqcrsotherdom.'.$type} ne '')) {
                 my @curr = split(',',$env{'environment.reqcrsotherdom.'.$type});                  my @curr = split(',',$env{'environment.reqcrsotherdom.'.$type});
                 if (@curr > 0) {                  if (@curr > 0) {
                     foreach my $item (@curr) {                      foreach my $item (@curr) {
Line 7781  sub check_can_request { Line 7971  sub check_can_request {
                             }                              }
                         }                          }
                     }                      }
                     unless($dom eq $env{'user.domain'}) {                      unless ($dom eq $env{'user.domain'}) {
                         $canreq ++;                          $canreq ++;
                         if (grep(/^\Q$dom\E:($optregex)(=?\d*)$/,@curr)) {                          if (grep(/^\Q$dom\E:($optregex)(=?\d*)$/,@curr)) {
                             $can_request->{$type} = 1;                              $can_request->{$type} = 1;
Line 7911  sub allowed { Line 8101  sub allowed {
 # Free bre to public access  # Free bre to public access
   
     if ($priv eq 'bre') {      if ($priv eq 'bre') {
         my $copyright=&metadata($uri,'copyright');          my $copyright;
           unless ($uri =~ /ext\.tool/) {
               $copyright=&metadata($uri,'copyright');
           }
  if (($copyright eq 'public') && (!$env{'request.course.id'})) {    if (($copyright eq 'public') && (!$env{'request.course.id'})) { 
            return 'F';              return 'F'; 
         }          }
Line 8068  sub allowed { Line 8261  sub allowed {
             if ($env{'user.priv.'.$env{'request.role'}.'./'}              if ($env{'user.priv.'.$env{'request.role'}.'./'}
                   =~/\Q$priv\E\&([^\:]*)/) {                    =~/\Q$priv\E\&([^\:]*)/) {
                 my $value = $1;                  my $value = $1;
                 if ($noblockcheck) {                  my $deeplinkblock = &deeplink_check($priv,$symb,$uri);
                   if ($deeplinkblock) {
                       $thisallowed='D';
                   } elsif ($noblockcheck) {
                     $thisallowed.=$value;                      $thisallowed.=$value;
                 } else {                  } else {
                     my @blockers = &has_comm_blocking($priv,$symb,$uri,$ignorecache);                      my @blockers = &has_comm_blocking($priv,$symb,$uri,$ignorecache);
Line 8088  sub allowed { Line 8284  sub allowed {
                     $refuri=&declutter($refuri);                      $refuri=&declutter($refuri);
                     my ($match) = &is_on_map($refuri);                      my ($match) = &is_on_map($refuri);
                     if ($match) {                      if ($match) {
                         if ($noblockcheck) {                          my $deeplinkblock = &deeplink_check($priv,$symb,$refuri);
                           if ($deeplinkblock) {
                               $thisallowed='D';
                           } elsif ($noblockcheck) {
                             $thisallowed='F';                              $thisallowed='F';
                         } else {                          } else {
                             my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);                              my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);
Line 8203  sub allowed { Line 8402  sub allowed {
                   =~/\Q$priv\E\&([^\:]*)/) {                    =~/\Q$priv\E\&([^\:]*)/) {
                   my $value = $1;                    my $value = $1;
                   if ($priv eq 'bre') {                    if ($priv eq 'bre') {
                       if ($noblockcheck) {                        my $deeplinkblock = &deeplink_check($priv,$symb,$refuri);
                         if ($deeplinkblock) {
                             $thisallowed = 'D';
                         } elsif ($noblockcheck) {
                           $thisallowed.=$value;                            $thisallowed.=$value;
                       } else {                        } else {
                           my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);                            my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);
Line 8319  sub allowed { Line 8521  sub allowed {
        my $unamedom=$env{'user.name'}.':'.$env{'user.domain'};         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'}
    =~/\Q$rolecode\E/) {     =~/\Q$rolecode\E/) {
    if (($priv ne 'pch') && ($priv ne 'plc')) {      if (($priv ne 'pch') && ($priv ne 'plc') && ($priv ne 'pac')) {
        &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.         &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
  'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.   'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
  $env{'request.course.id'});   $env{'request.course.id'});
Line 8329  sub allowed { Line 8531  sub allowed {
   
        if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}         if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}
    =~/\Q$unamedom\E/) {     =~/\Q$unamedom\E/) {
    if (($priv ne 'pch') && ($priv ne 'plc')) {      if (($priv ne 'pch') && ($priv ne 'plc') && ($priv ne 'pac')) {
        &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.         &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.
  'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.   'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
  $env{'request.course.id'});   $env{'request.course.id'});
Line 8371  sub allowed { Line 8573  sub allowed {
  return 'A';   return 'A';
     } elsif ($thisallowed eq 'B') {      } elsif ($thisallowed eq 'B') {
         return 'B';          return 'B';
       } elsif ($thisallowed eq 'D') {
           return 'D';
     }      }
    return 'F';     return 'F';
 }  }
Line 8387  sub constructaccess { Line 8591  sub constructaccess {
     my ($ownername,$ownerdomain,$ownerhome);      my ($ownername,$ownerdomain,$ownerhome);
   
     ($ownerdomain,$ownername) =      ($ownerdomain,$ownername) =
         ($url=~ m{^(?:\Q$perlvar{'lonDocRoot'}\E|)/priv/($match_domain)/($match_username)(?:/|$)});          ($url=~ m{^(?:\Q$perlvar{'lonDocRoot'}\E|)(?:/daxepage|/daxeopen)?/priv/($match_domain)/($match_username)(?:/|$)});
   
 # The URL does not really point to any authorspace, forget it  # The URL does not really point to any authorspace, forget it
     unless (($ownername) && ($ownerdomain)) { return ''; }      unless (($ownername) && ($ownerdomain)) { return ''; }
Line 8408  sub constructaccess { Line 8612  sub constructaccess {
             $ownerhome = &homeserver($ownername,$ownerdomain);              $ownerhome = &homeserver($ownername,$ownerdomain);
             return ($ownername,$ownerdomain,$ownerhome);              return ($ownername,$ownerdomain,$ownerhome);
         }          }
           if ($env{'request.course.id'}) {
               if (($ownername eq $env{'course.'.$env{'request.course.id'}.'.num'}) &&
                   ($ownerdomain eq $env{'course.'.$env{'request.course.id'}.'.domain'})) {
                   if (&allowed('mdc',$env{'request.course.id'})) {
                       $ownerhome = $env{'course.'.$env{'request.course.id'}.'.home'};
                       return ($ownername,$ownerdomain,$ownerhome);
                   }
               }
           }
     }      }
   
 # We don't have any access right now. If we are not possibly going to do anything about this,  # We don't have any access right now. If we are not possibly going to do anything about this,
Line 8452  sub constructaccess { Line 8665  sub constructaccess {
 my $cacheduser='';  my $cacheduser='';
 # Course for which data are being temporarily cached.  # Course for which data are being temporarily cached.
 my $cachedcid='';  my $cachedcid='';
 # Cached blockers for this user (a hash of blocking items).  # Cached blockers for this user (a hash of blocking items). 
 my %cachedblockers=();  my %cachedblockers=();
 # When the data were last cached.  # When the data were last cached.
 my $cachedlast='';  my $cachedlast='';
   
 sub load_all_blockers {  sub load_all_blockers {
     my ($uname,$udom)=@_;      my ($uname,$udom)=@_;
     if (($uname ne '') && ($udom ne '')) {      if (($uname ne '') && ($udom ne '')) { 
         if (($cacheduser eq $uname.':'.$udom) &&          if (($cacheduser eq $uname.':'.$udom) &&
             ($cachedcid eq $env{'request.course.id'}) &&              ($cachedcid eq $env{'request.course.id'}) &&
             (abs($cachedlast-time)<5)) {              (abs($cachedlast-time)<5)) {
Line 8504  sub get_commblock_resources { Line 8717  sub get_commblock_resources {
     } else {      } else {
         %commblocks = &get_comm_blocks();          %commblocks = &get_comm_blocks();
     }      }
     return %blockers unless (keys(%commblocks) > 0);      return %blockers unless (keys(%commblocks) > 0); 
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
     return %blockers unless (ref($navmap));      return %blockers unless (ref($navmap));
     my $now = time;      my $now = time;
Line 8516  sub get_commblock_resources { Line 8729  sub get_commblock_resources {
                     if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') {                      if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') {
                         if (ref($commblocks{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') {                          if (ref($commblocks{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') {
                             if (keys(%{$commblocks{$block}{'blocks'}{'docs'}{'maps'}})) {                              if (keys(%{$commblocks{$block}{'blocks'}{'docs'}{'maps'}})) {
                                 $blockers{$block}{maps} = $commblocks{$block}{'blocks'}{'docs'}{'maps'};                                  $blockers{$block}{maps} = $commblocks{$block}{'blocks'}{'docs'}{'maps'}; 
                             }                              }
                         }                          }
                         if (ref($commblocks{$block}{'blocks'}{'docs'}{'resources'}) eq 'HASH') {                          if (ref($commblocks{$block}{'blocks'}{'docs'}{'resources'}) eq 'HASH') {
Line 8541  sub get_commblock_resources { Line 8754  sub get_commblock_resources {
                         if ($item =~ /___\d+___/) {                          if ($item =~ /___\d+___/) {
                             $type = 'resource';                              $type = 'resource';
                             @interval=&EXT("resource.0.interval",$item);                              @interval=&EXT("resource.0.interval",$item);
                             if (ref($navmap)) {                              if (ref($navmap)) {                        
                                 my $res = $navmap->getBySymb($item);                                  my $res = $navmap->getBySymb($item); 
                                 push(@to_test,$res);                                  push(@to_test,$res);
                             }                              }
                         } else {                          } else {
Line 8637  sub has_comm_blocking { Line 8850  sub has_comm_blocking {
     }      }
     if ($symb) {      if ($symb) {
         @symbs = ($symb);          @symbs = ($symb);
     } elsif (keys(%possibles)) {      } elsif (keys(%possibles)) { 
         @symbs = keys(%possibles);          @symbs = keys(%possibles);
     }      }
     my $noblock;      my $noblock;
Line 8672  sub has_comm_blocking { Line 8885  sub has_comm_blocking {
             }              }
         }          }
     }      }
     unless ($noblock) {      unless ($noblock) { 
         return @blockers;          return @blockers;
     }      }
     return;      return;
 }  }
 }  }
   
 # -------------------------------- Deversion and split uri into path an filename  sub deeplink_check {
       my ($priv,$symb,$uri) = @_;
       return unless ($env{'request.course.id'});
       return unless ($priv eq 'bre');
       return if ($env{'request.state'} eq 'construct');
       return if ($env{'request.role.adv'});
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my (%possibles,@symbs);
       if (!$symb) {
           $symb = &symbread($uri,1,1,1,\%possibles);
       }
       if ($symb) {
           @symbs = ($symb);
       } elsif (keys(%possibles)) {
           @symbs = keys(%possibles);
       }
   
       my ($login,$switchrole,$allow);
       if ($env{'request.deeplink.login'} =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
           my $key = $1;
           my $tinyurl;
           my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
           if (defined($cached)) {
                $tinyurl = $result;
           } else {
                my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
                my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
                if ($currtiny{$key} ne '') {
                    $tinyurl = $currtiny{$key};
                    &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
                }
           }
           if ($tinyurl ne '') {
               my ($cnumreq,$posslogin) = split(/\&/,$tinyurl);
               if ($cnumreq eq $cnum) {
                   $login = $posslogin;
               } else {
                   $switchrole = 1;
               }
           }
       }
       foreach my $symb (@symbs) {
           last if ($allow);
           my $deeplink = &EXT("resource.0.deeplink",$symb);
           if ($deeplink eq '') {
               $allow = 1;
           } else {
               my ($listed,$scope,$access) = split(/,/,$deeplink);
               if ($access eq 'any') {
                   $allow = 1;
               } elsif ($login) {
                   if ($access eq 'only') {
                       if ($scope eq 'res') {
                           if ($symb eq $login) {
                               $allow = 1;
                           }
                       } elsif ($scope eq 'map') {
   #FIXME Compare map for $env{'request.deeplink.login'} with map for $symb
                       } elsif ($scope eq 'rec') {
   #FIXME Recurse up for $env{'request.deeplink.login'} with map for $symb
                       }
                   } else {
                       my ($acctype,$item) = split(/:/,$access);
                       if (($acctype eq 'lti') && ($env{'user.linkprotector'})) {
                           if (grep(/^\Q$item\E$/,split(/,/,$env{'user.linkprotector'}))) {
                               my %tinyurls = &get('tiny',[$symb],$cdom,$cnum);
                               if (grep(/\Q$tinyurls{$symb}\E$/,split(/,/,$env{'user.linkproturis'}))) {
                                   $allow = 1;
                               }
                           }
                       } elsif (($acctype eq 'key') && ($env{'user.deeplinkkey'})) {
                           if (grep(/^\Q$item\E$/,split(/,/,$env{'user.deeplinkkey'}))) {
                               my %tinyurls = &get('tiny',[$symb],$cdom,$cnum);
                               if (grep(/\Q$tinyurls{$symb}\E$/,split(/,/,$env{'user.keyedlinkuri'}))) {
                                   $allow = 1;
                               }
                           }
                       }
                   }
               }
           }
       }
       return if ($allow);
       return 1;
   }
   
   # -------------------------------- Deversion and split uri into path an filename   
   
 #  #
 #   Removes the version from a URI and  #   Removes the version from a URI and
Line 8805  sub metadata_query { Line 9105  sub metadata_query {
     my @server_list = (defined($server_array) ? @$server_array      my @server_list = (defined($server_array) ? @$server_array
                                               : keys(%libserv) );                                                : keys(%libserv) );
     for my $server (@server_list) {      for my $server (@server_list) {
         my $domains = '';          my $domains = ''; 
         if (ref($domains_hash) eq 'HASH') {          if (ref($domains_hash) eq 'HASH') {
             $domains = $domains_hash->{$server};                  $domains = $domains_hash->{$server}; 
         }          }
  unless ($custom or $customshow) {   unless ($custom or $customshow) {
     my $reply=&reply("querysend:".&escape($query).':::'.&escape($domains),$server);      my $reply=&reply("querysend:".&escape($query).':::'.&escape($domains),$server);
Line 8940  sub fetch_enrollment_query { Line 9240  sub fetch_enrollment_query {
 }  }
   
 sub get_query_reply {  sub get_query_reply {
     my ($queryid,$sleep,$loopmax) = @_;      my ($queryid,$sleep,$loopmax) = @_;;
     if (($sleep eq '') || ($sleep !~ /^\d+\.?\d*$/)) {      if (($sleep eq '') || ($sleep !~ /^\d+\.?\d*$/)) {
         $sleep = 0.2;          $sleep = 0.2;
     }      }
Line 9387  sub auto_crsreq_update { Line 9687  sub auto_crsreq_update {
                             ':'.&escape($action).':'.&escape($ownername).':'.                              ':'.&escape($action).':'.&escape($ownername).':'.
                             &escape($ownerdomain).':'.&escape($fullname).':'.                              &escape($ownerdomain).':'.&escape($fullname).':'.
                             &escape($title).':'.&escape($code).':'.                              &escape($title).':'.&escape($code).':'.
                             &escape($accessstart).':'.&escape($accessend).':'.$info,$homeserver);                              &escape($accessstart).':'.&escape($accessend).':'.$info,
                               $homeserver);
         unless ($response =~ /(con_lost|error|no_such_host|refused)/) {          unless ($response =~ /(con_lost|error|no_such_host|refused)/) {
             my @items = split(/&/,$response);              my @items = split(/&/,$response);
             foreach my $item (@items) {              foreach my $item (@items) {
Line 9647  sub plaintext { Line 9948  sub plaintext {
     my %rolenames = (      my %rolenames = (
                       Course    => 'std',                        Course    => 'std',
                       Community => 'alt1',                        Community => 'alt1',
                         Placement => 'std',
                     );                      );
     if ($cid ne '') {      if ($cid ne '') {
         if ($env{'course.'.$cid.'.'.$short.'.plaintext'} ne '') {          if ($env{'course.'.$cid.'.'.$short.'.plaintext'} ne '') {
Line 9728  sub assignrole { Line 10030  sub assignrole {
             }              }
             if ($refused) {              if ($refused) {
                 my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});                  my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});
                 if (!$selfenroll && $context eq 'course') {                  if (!$selfenroll && (($context eq 'course') || ($context eq 'ltienroll' && $env{'request.lti.login'}))) {
                     my %crsenv;                      my %crsenv;
                     if ($role eq 'cc' || $role eq 'co') {                      if ($role eq 'cc' || $role eq 'co') {
                         %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner'));                          %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner'));
Line 9748  sub assignrole { Line 10050  sub assignrole {
                             }                              }
                         }                          }
                     }                      }
                 } elsif (($selfenroll == 1) && ($role eq 'st') && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {                  } elsif (($selfenroll == 1) && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
                     $refused = '';                      if ($role eq 'st') {
                           $refused = '';
                       } elsif (($context eq 'ltienroll') && ($env{'request.lti.login'})) {
                           $refused = '';
                       }
                 } elsif ($context eq 'requestcourses') {                  } elsif ($context eq 'requestcourses') {
                     my @possroles = ('st','ta','ep','in','cc','co');                      my @possroles = ('st','ta','ep','in','cc','co');
                     if ((grep(/^\Q$role\E$/,@possroles)) && ($env{'user.name'} ne '' && $env{'user.domain'} ne '')) {                      if ((grep(/^\Q$role\E$/,@possroles)) && ($env{'user.name'} ne '' && $env{'user.domain'} ne '')) {
Line 9768  sub assignrole { Line 10074  sub assignrole {
                         }                          }
                     }                      }
                 } elsif ($context eq 'requestauthor') {                  } elsif ($context eq 'requestauthor') {
                     if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'}) &&                      if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'}) && 
                         ($url eq '/'.$udom.'/') && ($role eq 'au')) {                          ($url eq '/'.$udom.'/') && ($role eq 'au')) {
                         if ($env{'environment.requestauthor'} eq 'automatic') {                          if ($env{'environment.requestauthor'} eq 'automatic') {
                             $refused = '';                              $refused = '';
Line 9776  sub assignrole { Line 10082  sub assignrole {
                             my %domdefaults = &get_domain_defaults($udom);                              my %domdefaults = &get_domain_defaults($udom);
                             if (ref($domdefaults{'requestauthor'}) eq 'HASH') {                              if (ref($domdefaults{'requestauthor'}) eq 'HASH') {
                                 my $checkbystatus;                                  my $checkbystatus;
                                 if ($env{'user.adv'}) {                                  if ($env{'user.adv'}) { 
                                     my $disposition = $domdefaults{'requestauthor'}{'_LC_adv'};                                      my $disposition = $domdefaults{'requestauthor'}{'_LC_adv'};
                                     if ($disposition eq 'automatic') {                                      if ($disposition eq 'automatic') {
                                         $refused = '';                                          $refused = '';
                                     } elsif ($disposition eq '') {                                      } elsif ($disposition eq '') {
                                         $checkbystatus = 1;                                          $checkbystatus = 1;
                                     }                                      } 
                                 } else {                                  } else {
                                     $checkbystatus = 1;                                      $checkbystatus = 1;
                                 }                                  }
Line 9870  sub assignrole { Line 10176  sub assignrole {
                            $context);                             $context);
         } elsif (($role eq 'ca') || ($role eq 'aa')) {          } elsif (($role eq 'ca') || ($role eq 'aa')) {
             &coauthorrolelog($role,$uname,$udom,$url,$origstart,$origend,$delflag,              &coauthorrolelog($role,$uname,$udom,$url,$origstart,$origend,$delflag,
                              $context);                               $context); 
         }          }
         if ($role eq 'cc') {          if ($role eq 'cc') {
             &autoupdate_coowners($url,$end,$start,$uname,$udom);              &autoupdate_coowners($url,$end,$start,$uname,$udom);
Line 10030  sub modifyuser { Line 10336  sub modifyuser {
     my $newuser;      my $newuser;
     if ($uhome eq 'no_host') {      if ($uhome eq 'no_host') {
         $newuser = 1;          $newuser = 1;
           unless (($umode && ($upass ne '')) || ($umode eq 'localauth') ||
                   ($umode eq 'lti')) {
               return 'error: more information needed to create new user';
           }
     }      }
 # ----------------------------------------------------------------- Create User  # ----------------------------------------------------------------- Create User
     if (($uhome eq 'no_host') &&       if (($uhome eq 'no_host') && 
  (($umode && $upass) || ($umode eq 'localauth'))) {   (($umode && $upass) || ($umode eq 'localauth') || ($umode eq 'lti'))) {
         my $unhome='';          my $unhome='';
         if (defined($desiredhome) && &host_domain($desiredhome) eq $udom) {           if (defined($desiredhome) && &host_domain($desiredhome) eq $udom) { 
             $unhome = $desiredhome;              $unhome = $desiredhome;
Line 10075  sub modifyuser { Line 10385  sub modifyuser {
                   'current user id "'.$uidhash{$uname}.'".';                    'current user id "'.$uidhash{$uname}.'".';
           }            }
        } else {         } else {
   &idput($udom,($uname => $uid));    &idput($udom,{$uname => $uid},$uhome,'ids');
        }         }
     }      }
 # -------------------------------------------------------------- Add names, etc  # -------------------------------------------------------------- Add names, etc
Line 10200  sub modifystudent { Line 10510  sub modifystudent {
     # student's environment      # student's environment
     $uid = undef if (!$forceid);      $uid = undef if (!$forceid);
     $reply = &modify_student_enrollment($udom,$uname,$uid,$first,$middle,$last,      $reply = &modify_student_enrollment($udom,$uname,$uid,$first,$middle,$last,
  $gene,$usec,$end,$start,$type,$locktype,                                          $gene,$usec,$end,$start,$type,$locktype,
                                         $cid,$selfenroll,$context,$credits,$instsec);                                          $cid,$selfenroll,$context,$credits,$instsec);
     return $reply;      return $reply;
 }  }
Line 10482  sub generate_coursenum { Line 10792  sub generate_coursenum {
 sub is_course {  sub is_course {
     my ($cdom, $cnum) = scalar(@_) == 1 ?       my ($cdom, $cnum) = scalar(@_) == 1 ? 
          ($_[0] =~ /^($match_domain)_($match_courseid)$/)  :  @_;           ($_[0] =~ /^($match_domain)_($match_courseid)$/)  :  @_;
   
     return unless (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/));      return unless (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/));
     my $uhome=&homeserver($cnum,$cdom);      my $uhome=&homeserver($cnum,$cdom);
     my $iscourse;      my $iscourse;
Line 10500  sub is_course { Line 10811  sub is_course {
             &do_cache_new('iscourse',$hashid,$iscourse,3600);              &do_cache_new('iscourse',$hashid,$iscourse,3600);
         }          }
     }      }
     return unless($iscourse);      return unless ($iscourse);
     return wantarray ? ($cdom, $cnum) : $cdom.'_'.$cnum;      return wantarray ? ($cdom, $cnum) : $cdom.'_'.$cnum;
 }  }
   
Line 10699  sub files_not_in_path { Line 11010  sub files_not_in_path {
     return (@return_files);      return (@return_files);
 }  }
   
   #------------------------------Submitted/Handedback Portfolio Files Versioning
    
   sub portfiles_versioning {
       my ($symb,$domain,$stu_name,$portfiles,$versioned_portfiles) = @_;
       my $portfolio_root = '/userfiles/portfolio';
       return unless ((ref($portfiles) eq 'ARRAY') && (ref($versioned_portfiles) eq 'ARRAY'));
       foreach my $file (@{$portfiles}) {
           &unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);
           my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
           my ($answer_name,$answer_ver,$answer_ext) = &file_name_version_ext($answer_file);
           my $getpropath = 1;
           my ($dir_list,$listerror) = &dirlist($portfolio_root.$directory,$domain,
                                                $stu_name,$getpropath);
           my $version = &get_next_version($answer_name,$answer_ext,$dir_list);
           my $new_answer = 
               &version_selected_portfile($domain,$stu_name,$directory,$answer_file,$version);
           if ($new_answer ne 'problem getting file') {
               push(@{$versioned_portfiles}, $directory.$new_answer);
               &mark_as_readonly($domain,$stu_name,[$directory.$new_answer],
                                 [$symb,$env{'request.course.id'},'graded']);
           }
       }
   }
   
   sub get_next_version {
       my ($answer_name, $answer_ext, $dir_list) = @_;
       my $version;
       if (ref($dir_list) eq 'ARRAY') {
           foreach my $row (@{$dir_list}) {
               my ($file) = split(/\&/,$row,2);
               my ($file_name,$file_version,$file_ext) =
                   &file_name_version_ext($file);
               if (($file_name eq $answer_name) &&
                   ($file_ext eq $answer_ext)) {
                        # gets here if filename and extension match,
                        # regardless of version
                   if ($file_version ne '') {
                       # a versioned file is found  so save it for later
                       if ($file_version > $version) {
                           $version = $file_version;
                       }
                   }
               }
           }
       }
       $version ++;
       return($version);
   }
   
   sub version_selected_portfile {
       my ($domain,$stu_name,$directory,$file_name,$version) = @_;
       my ($answer_name,$answer_ver,$answer_ext) =
           &file_name_version_ext($file_name);
       my $new_answer;
       $env{'form.copy'} =
           &getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");
       if($env{'form.copy'} eq '-1') {
           $new_answer = 'problem getting file';
       } else {
           $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;
           my $copy_result = 
               &finishuserfileupload($stu_name,$domain,'copy',
                                     '/portfolio'.$directory.$new_answer);
       }
       undef($env{'form.copy'});
       return ($new_answer);
   }
   
   sub file_name_version_ext {
       my ($file)=@_;
       my @file_parts = split(/\./, $file);
       my ($name,$version,$ext);
       if (@file_parts > 1) {
           $ext=pop(@file_parts);
           if (@file_parts > 1 && $file_parts[-1] =~ /^\d+$/) {
               $version=pop(@file_parts);
           }
           $name=join('.',@file_parts);
       } else {
           $name=join('.',@file_parts);
       }
       return($name,$version,$ext);
   }
   
 #----------------------------------------------Get portfolio file permissions  #----------------------------------------------Get portfolio file permissions
   
 sub get_portfile_permissions {  sub get_portfile_permissions {
Line 10843  sub modify_access_controls { Line 11238  sub modify_access_controls {
 }  }
   
 sub make_public_indefinitely {  sub make_public_indefinitely {
     my ($requrl) = @_;      my (@requrl) = @_;
       return &automated_portfile_access('public',\@requrl);
   }
   
   sub automated_portfile_access {
       my ($accesstype,$addsref,$delsref,$info) = @_;
       unless (($accesstype eq 'public') || ($accesstype eq 'ip')) {
           return 'invalid';
       }
       my %urls;
       if (ref($addsref) eq 'ARRAY') {
           foreach my $requrl (@{$addsref}) {
               if (&is_portfolio_url($requrl)) {
                   unless (exists($urls{$requrl})) {
                       $urls{$requrl} = 'add';
                   }
               }
           }
       }
       if (ref($delsref) eq 'ARRAY') {
           foreach my $requrl (@{$delsref}) { 
               if (&is_portfolio_url($requrl)) {
                   unless (exists($urls{$requrl})) {
                       $urls{$requrl} = 'delete'; 
                   }
               }
           }
       }
       unless (keys(%urls)) {
           return 'invalid';
       }
       my $ip;
       if ($accesstype eq 'ip') {
           if (ref($info) eq 'HASH') {
               if ($info->{'ip'} ne '') {
                   $ip = $info->{'ip'};
               }
           }
           if ($ip eq '') {
               return 'invalid';
           }
       }
       my $errors;
     my $now = time;      my $now = time;
     my $action = 'activate';      my %current_perms;
     my $aclnum = 0;      foreach my $requrl (sort(keys(%urls))) {
     if (&is_portfolio_url($requrl)) {          my $action;
           if ($urls{$requrl} eq 'add') {
               $action = 'activate';
           } else {
               $action = 'none';
           }
           my $aclnum = 0;
         my (undef,$udom,$unum,$file_name,$group) =          my (undef,$udom,$unum,$file_name,$group) =
             &parse_portfolio_url($requrl);              &parse_portfolio_url($requrl);
         my $current_perms = &get_portfile_permissions($udom,$unum);          unless (exists($current_perms{$unum.':'.$udom})) {
         my %access_controls = &get_access_controls($current_perms,              $current_perms{$unum.':'.$udom} = &get_portfile_permissions($udom,$unum);
           }
           my %access_controls = &get_access_controls($current_perms{$unum.':'.$udom},
                                                    $group,$file_name);                                                     $group,$file_name);
         foreach my $key (keys(%{$access_controls{$file_name}})) {          foreach my $key (keys(%{$access_controls{$file_name}})) {
             my ($num,$scope,$end,$start) =               my ($num,$scope,$end,$start) = 
                 ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);                  ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
             if ($scope eq 'public') {              if ($scope eq $accesstype) {
                 if ($start <= $now && $end == 0) {                  if (($start <= $now) && ($end == 0)) {
                     $action = 'none';                      if ($accesstype eq 'ip') {
                 } else {                          if (ref($access_controls{$file_name}{$key}) eq 'HASH') {
                               if (ref($access_controls{$file_name}{$key}{'ip'}) eq 'ARRAY') {
                                   if (grep(/^\Q$ip\E$/,@{$access_controls{$file_name}{$key}{'ip'}})) {
                                       if ($urls{$requrl} eq 'add') {
                                           $action = 'none';
                                           last;
                                       } else {
                                           $action = 'delete';
                                           $aclnum = $num;
                                           last;
                                       }
                                   }
                               }
                           }
                       } elsif ($accesstype eq 'public') {
                           if ($urls{$requrl} eq 'add') {
                               $action = 'none';
                               last;
                           } else {
                               $action = 'delete';
                               $aclnum = $num;
                               last;
                           }
                       }
                   } elsif ($accesstype eq 'public') {
                     $action = 'update';                      $action = 'update';
                     $aclnum = $num;                      $aclnum = $num;
                       last;
                 }                  }
                 last;  
             }              }
         }          }
         if ($action eq 'none') {          if ($action eq 'none') {
              return 'ok';              next;
         } else {          } else {
             my %changes;              my %changes;
             my $newend = 0;              my $newend = 0;
             my $newstart = $now;              my $newstart = $now;
             my $newkey = $aclnum.':public_'.$newend.'_'.$newstart;              my $newkey = $aclnum.':'.$accesstype.'_'.$newend.'_'.$newstart;
             $changes{$action}{$newkey} = {              $changes{$action}{$newkey} = {
                 type => 'public',                  type => $accesstype,
                 time => {                  time => {
                     start => $newstart,                      start => $newstart,
                     end   => $newend,                      end   => $newend,
                 },                  },
             };              };
               if ($accesstype eq 'ip') {
                   $changes{$action}{$newkey}{'ip'} = [$ip];
               }
             my ($outcome,$deloutcome,$new_values,$translation) =              my ($outcome,$deloutcome,$new_values,$translation) =
                 &modify_access_controls($file_name,\%changes,$udom,$unum);                  &modify_access_controls($file_name,\%changes,$udom,$unum);
             return $outcome;              unless ($outcome eq 'ok') {
                   $errors .= $outcome.' ';
               }
         }          }
       }
       if ($errors) {
           $errors =~ s/\s$//;
           return $errors;
     } else {      } else {
         return 'invalid';          return 'ok';
     }      }
 }  }
   
Line 11090  sub dirlist { Line 11568  sub dirlist {
             foreach my $user (sort(keys(%allusers))) {              foreach my $user (sort(keys(%allusers))) {
                 push(@alluserslist,$user.'&user');                  push(@alluserslist,$user.'&user');
             }              }
   
             if (!%listerror) {              if (!%listerror) {
                 # no errors                  # no errors
                 return (\@alluserslist);                  return (\@alluserslist);
             } elsif (scalar(keys(%servers)) == 1) {              } elsif (scalar(keys(%servers)) == 1) {
                 # one library server, one error                  # one library server, one error 
                 my ($key) = keys(%listerror);                  my ($key) = keys(%listerror);
                 return (\@alluserslist, $listerror{$key});                  return (\@alluserslist, $listerror{$key});
             } elsif ( grep { $_ eq 'con_lost' } values(%listerror) ) {              } elsif ( grep { $_ eq 'con_lost' } values(%listerror) ) {
Line 11103  sub dirlist { Line 11582  sub dirlist {
                 return (\@alluserslist, 'con_lost');                  return (\@alluserslist, 'con_lost');
             } else {              } else {
                 # multiple library servers and no con_lost -> data should be                  # multiple library servers and no con_lost -> data should be
                 # complete.                  # complete. 
                 return (\@alluserslist);                  return (\@alluserslist);
             }              }
   
Line 11179  sub stat_file { Line 11658  sub stat_file {
     return ();      return ();
 }  }
   
   # --------------------------------------------------------- recursedirs
   # Recursive function to traverse either a specific user's Authoring Space
   # or corresponding Published Resource Space, and populate the hash ref:
   # $dirhashref with URLs of all directories, and if $filehashref hash
   # ref arg is provided, the URLs of any files, excluding versioned, .meta,
   # or .rights files in resource space, and .meta, .save, .log, and .bak
   # files in Authoring Space.
   #
   # Inputs:
   #
   # $is_home - true if current server is home server for user's space
   # $context - either: priv, or res respectively for Authoring or Resource Space.
   # $docroot - Document root (i.e., /home/httpd/html
   # $toppath - Top level directory (i.e., /res/$dom/$uname or /priv/$dom/$uname
   # $relpath - Current path (relative to top level).
   # $dirhashref - reference to hash to populate with URLs of directories (Required)
   # $filehashref - reference to hash to populate with URLs of files (Optional)
   #
   # Returns: nothing
   #
   # Side Effects: populates $dirhashref, and $filehashref (if provided).
   #
   # Currently used by interface/londocs.pm to create linked select boxes for
   # directory and filename to import a Course "Author" resource into a course, and
   # also to create linked select boxes for Authoring Space and Directory to choose
   # save location for creation of a new "standard" problem from the Course Editor.
   #
   
   sub recursedirs {
       my ($is_home,$context,$docroot,$toppath,$relpath,$dirhashref,$filehashref) = @_;
       return unless (ref($dirhashref) eq 'HASH');
       my $currpath = $docroot.$toppath;
       if ($relpath) {
           $currpath .= "/$relpath";
       }
       my $savefile;
       if (ref($filehashref)) {
           $savefile = 1;
       }
       if ($is_home) {
           if (opendir(my $dirh,$currpath)) {
               foreach my $item (sort { lc($a) cmp lc($b) } grep(!/^\.+$/,readdir($dirh))) {
                   next if ($item eq '');
                   if (-d "$currpath/$item") {
                       my $newpath;
                       if ($relpath) {
                           $newpath = "$relpath/$item";
                       } else {
                           $newpath = $item;
                       }
                       $dirhashref->{&Apache::lonlocal::js_escape($newpath)} = 1;
                       &recursedirs($is_home,$context,$docroot,$toppath,$newpath,$dirhashref,$filehashref);
                   } elsif ($savefile) {
                       if ($context eq 'priv') {
                           unless ($item =~ /\.(meta|save|log|bak|DS_Store)$/) {
                               $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = 1;
                           }
                       } else {
                           unless (($item =~ /\.meta$/) || ($item =~ /\.\d+\.\w+$/) || ($item =~ /\.rights$/)) {
                               $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = 1;
                           }
                       }
                   }
               }
               closedir($dirh);
           }
       } else {
           my ($dirlistref,$listerror) =
               &dirlist($toppath.$relpath);
           my @dir_lines;
           my $dirptr=16384;
           if (ref($dirlistref) eq 'ARRAY') {
               foreach my $dir_line (sort
                                 {
                                     my ($afile)=split('&',$a,2);
                                     my ($bfile)=split('&',$b,2);
                                     return (lc($afile) cmp lc($bfile));
                                 } (@{$dirlistref})) {
                   my ($item,$dom,undef,$testdir,undef,undef,undef,undef,$size,undef,$mtime,undef,undef,undef,$obs,undef) =
                       split(/\&/,$dir_line,16);
                   $item =~ s/\s+$//;
                   next if (($item =~ /^\.\.?$/) || ($obs));
                   if ($dirptr&$testdir) {
                       my $newpath;
                       if ($relpath) {
                           $newpath = "$relpath/$item";
                       } else {
                           $relpath = '/';
                           $newpath = $item;
                       }
                       $dirhashref->{&Apache::lonlocal::js_escape($newpath)} = 1;
                       &recursedirs($is_home,$context,$docroot,$toppath,$newpath,$dirhashref,$filehashref);
                   } elsif ($savefile) {
                       if ($context eq 'priv') {
                           unless ($item =~ /\.(meta|save|log|bak|DS_Store)$/) {
                               $filehashref->{$relpath}{$item} = 1;
                           }
                       } else {
                           unless (($item =~ /\.meta$/) || ($item =~ /\.\d+\.\w+$/)) {
                               $filehashref->{$relpath}{$item} = 1;
                           }
                       }
                   }
               }
           }
       }
       return;
   }
   
 # -------------------------------------------------------- Value of a Condition  # -------------------------------------------------------- Value of a Condition
   
 # gets the value of a specific preevaluated condition  # gets the value of a specific preevaluated condition
Line 11343  sub get_userresdata { Line 11931  sub get_userresdata {
 #     $name      - Course/user name.  #     $name      - Course/user name.
 #     $domain    - Name of the domain the user/course is registered on.  #     $domain    - Name of the domain the user/course is registered on.
 #     $type      - Type of thing $name is (must be 'course' or 'user')  #     $type      - Type of thing $name is (must be 'course' or 'user')
   #     $mapp      - decluttered URL of enclosing map  
   #     $recursed  - Ref to scalar -- set to 1, if nested maps have been recursed.
   #     $recurseup - Ref to array of map URLs, starting with map containing
   #                  $mapp up through hierarchy of nested maps to top level map.  
   #     $courseid  - CourseID (first part of param identifier).
   #     $modifier  - Middle part of param identifier.
   #     $what      - Last part of param identifier.
 #     @which     - Array of names of resources desired.  #     @which     - Array of names of resources desired.
 #  Returns:  #  Returns:
 #     The value of the first reasource in @which that is found in the  #     The value of the first reasource in @which that is found in the
Line 11352  sub get_userresdata { Line 11947  sub get_userresdata {
 #     'user', an undefined  reference is returned.  #     'user', an undefined  reference is returned.
 #     If none of the resources are found, an undef is returned  #     If none of the resources are found, an undef is returned
 sub resdata {  sub resdata {
     my ($name,$domain,$type,@which)=@_;      my ($name,$domain,$type,$mapp,$recursed,$recurseup,$courseid,
           $modifier,$what,@which)=@_;
     my $result;      my $result;
     if ($type eq 'course') {      if ($type eq 'course') {
  $result=&get_courseresdata($name,$domain);   $result=&get_courseresdata($name,$domain);
Line 11361  sub resdata { Line 11957  sub resdata {
     }      }
     if (!ref($result)) { return $result; }          if (!ref($result)) { return $result; }    
     foreach my $item (@which) {      foreach my $item (@which) {
  if (defined($result->{$item->[0]})) {          if ($item->[1] eq 'course') {
     return [$result->{$item->[0]},$item->[1]];              if ((ref($recurseup) eq 'ARRAY') && (ref($recursed) eq 'SCALAR')) {
                   unless ($$recursed) {
                       @{$recurseup} = &get_map_hierarchy($mapp,$courseid);
                       $$recursed = 1;
                   }
                   foreach my $item (@${recurseup}) {
                       my $norecursechk=$courseid.$modifier.$item.'___(all).'.$what;
                       last if (defined($result->{$norecursechk}));
                       my $recursechk=$courseid.$modifier.$item.'___(rec).'.$what;
                       if (defined($result->{$recursechk})) { return [$result->{$recursechk},'map']; }
                   }
               }
         }          }
           if (defined($result->{$item->[0]})) {
       return [$result->{$item->[0]},$item->[1]];
    }
     }      }
     return undef;      return undef;
 }  }
Line 11414  sub get_numsuppfiles { Line 12024  sub get_numsuppfiles {
     unless (defined($cached)) {      unless (defined($cached)) {
         my $chome=&homeserver($cnum,$cdom);          my $chome=&homeserver($cnum,$cdom);
         unless ($chome eq 'no_host') {          unless ($chome eq 'no_host') {
             ($suppcount,my $errors) = (0,0);              ($suppcount,my $supptools,my $errors) = (0,0,0);
             my $suppmap = 'supplemental.sequence';              my $suppmap = 'supplemental.sequence';
             ($suppcount,$errors) =              ($suppcount,$supptools,$errors) =
                 &Apache::loncommon::recurse_supplemental($cnum,$cdom,$suppmap,$suppcount,$errors);                  &Apache::loncommon::recurse_supplemental($cnum,$cdom,$suppmap,$suppcount,
                                                            $supptools,$errors);
         }          }
         &do_cache_new('suppcount',$hashid,$suppcount,600);          &do_cache_new('suppcount',$hashid,$suppcount,600);
     }      }
Line 11428  sub get_numsuppfiles { Line 12039  sub get_numsuppfiles {
 # EXT resource caching routines  # EXT resource caching routines
 #  #
   
   {
   # Cache (5 seconds) of map hierarchy for speedup of navmaps display
   #
   # The course for which we cache
   my $cachedmapkey='';
   # The cached recursive maps for this course
   my %cachedmaps=();
   # When this was last done
   my $cachedmaptime='';
   
 sub clear_EXT_cache_status {  sub clear_EXT_cache_status {
     &delenv('cache.EXT.');      &delenv('cache.EXT.');
 }  }
Line 11605  sub EXT { Line 12226  sub EXT {
             }              }
         }          }
   
  my ($section, $group, @groups);   my ($section, $group, @groups, @recurseup, $recursed);
  my ($courselevelm,$courselevel);   my ($courselevelm,$courseleveli,$courselevel,$mapp);
         if (($courseid eq '') && ($cid)) {          if (($courseid eq '') && ($cid)) {
             $courseid = $cid;              $courseid = $cid;
         }          }
  if (($symbparm && $courseid) &&    if (($symbparm && $courseid) && 
     (($courseid eq $env{'request.course.id'}) || ($courseid eq $cid))) {      (($courseid eq $env{'request.course.id'}) || ($courseid eq $cid)))  {
   
     #print '<br>'.$space.' - '.$qualifier.' - '.$spacequalifierrest;      #print '<br>'.$space.' - '.$qualifier.' - '.$spacequalifierrest;
   
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
     my $symbp=$symbparm;      my $symbp=$symbparm;
     my $mapp=&deversion((&decode_symb($symbp))[0]);      $mapp=&deversion((&decode_symb($symbp))[0]);
   
     my $symbparm=$symbp.'.'.$spacequalifierrest;      my $symbparm=$symbp.'.'.$spacequalifierrest;
               my $recurseparm=$mapp.'___(rec).'.$spacequalifierrest;
     my $mapparm=$mapp.'___(all).'.$spacequalifierrest;      my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
   
     if (($env{'user.name'} eq $uname) &&      if (($env{'user.name'} eq $uname) &&
  ($env{'user.domain'} eq $udom)) {   ($env{'user.domain'} eq $udom)) {
  $section=$env{'request.course.sec'};   $section=$env{'request.course.sec'};
Line 11638  sub EXT { Line 12258  sub EXT {
   
     my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest;      my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest;
     my $seclevelr=$courseid.'.['.$section.'].'.$symbparm;      my $seclevelr=$courseid.'.['.$section.'].'.$symbparm;
               my $secleveli=$courseid.'.['.$section.'].'.$recurseparm;
     my $seclevelm=$courseid.'.['.$section.'].'.$mapparm;      my $seclevelm=$courseid.'.['.$section.'].'.$mapparm;
   
     $courselevel=$courseid.'.'.$spacequalifierrest;      $courselevel=$courseid.'.'.$spacequalifierrest;
     my $courselevelr=$courseid.'.'.$symbparm;      my $courselevelr=$courseid.'.'.$symbparm;
               $courseleveli=$courseid.'.'.$recurseparm;
     $courselevelm=$courseid.'.'.$mapparm;      $courselevelm=$courseid.'.'.$mapparm;
   
 # ----------------------------------------------------------- first, check user  # ----------------------------------------------------------- first, check user
   
     my $userreply=&resdata($uname,$udom,'user',      my $userreply=&resdata($uname,$udom,'user',$mapp,\$recursed,
                                      \@recurseup,$courseid,'.',$spacequalifierrest, 
        ([$courselevelr,'resource'],         ([$courselevelr,'resource'],
  [$courselevelm,'map'     ],   [$courselevelm,'map'     ],
                                           [$courseleveli,'map'     ],
  [$courselevel, 'course'  ]));   [$courselevel, 'course'  ]));
     if (defined($userreply)) { return &get_reply($userreply); }      if (defined($userreply)) { return &get_reply($userreply); }
   
Line 11656  sub EXT { Line 12280  sub EXT {
             my $coursereply;              my $coursereply;
             if (@groups > 0) {              if (@groups > 0) {
                 $coursereply = &check_group_parms($courseid,\@groups,$symbparm,                  $coursereply = &check_group_parms($courseid,\@groups,$symbparm,
                                        $mapparm,$spacequalifierrest);                                         $recurseparm,$mapparm,$spacequalifierrest,
                 if (defined($coursereply)) { return &get_reply($coursereply); }                                         $mapp,\$recursed,\@recurseup);
                   if (defined($coursereply)) { return &get_reply($coursereply); } 
             }              }
   
     $coursereply=&resdata($env{'course.'.$courseid.'.num'},      $coursereply=&resdata($env{'course.'.$courseid.'.num'},
   $env{'course.'.$courseid.'.domain'},    $env{'course.'.$courseid.'.domain'},
   'course',    'course',$mapp,\$recursed,\@recurseup,
                                     $courseid,'.['.$section.'].',$spacequalifierrest,
   ([$seclevelr,   'resource'],    ([$seclevelr,   'resource'],
    [$seclevelm,   'map'     ],     [$seclevelm,   'map'     ],
                                      [$secleveli,   'map'     ],
    [$seclevel,    'course'  ],     [$seclevel,    'course'  ],
    [$courselevelr,'resource']));     [$courselevelr,'resource']));
     if (defined($coursereply)) { return &get_reply($coursereply); }      if (defined($coursereply)) { return &get_reply($coursereply); }
Line 11681  sub EXT { Line 12308  sub EXT {
     if ($thisparm) { return &get_reply([$thisparm,'resource']); }      if ($thisparm) { return &get_reply([$thisparm,'resource']); }
  }   }
 # ------------------------------------------ fourth, look in resource metadata  # ------------------------------------------ fourth, look in resource metadata
    
  $spacequalifierrest=~s/\./\_/;          my $what = $spacequalifierrest;
    $what=~s/\./\_/;
  my $filename;   my $filename;
  if (!$symbparm) { $symbparm=&symbread(); }   if (!$symbparm) { $symbparm=&symbread(); }
  if ($symbparm) {   if ($symbparm) {
Line 11690  sub EXT { Line 12318  sub EXT {
  } else {   } else {
     $filename=$env{'request.filename'};      $filename=$env{'request.filename'};
  }   }
  my $metadata=&metadata($filename,$spacequalifierrest);          my $toolsymb;
           if (($filename =~ /ext\.tool$/) && ($what ne '0_gradable')) {
               $toolsymb = $symbparm;
           }
    my $metadata=&metadata($filename,$what,$toolsymb);
  if (defined($metadata)) { return &get_reply([$metadata,'resource']); }   if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
  $metadata=&metadata($filename,'parameter_'.$spacequalifierrest);   $metadata=&metadata($filename,'parameter_'.$what,$toolsymb);
  if (defined($metadata)) { return &get_reply([$metadata,'resource']); }   if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
   
 # ---------------------------------------------- fourth, look in rest of course  # ----------------------------------------------- fifth, look in rest of course
  if ($symbparm && defined($courseid) &&    if ($symbparm && defined($courseid) && 
     $courseid eq $env{'request.course.id'}) {      $courseid eq $env{'request.course.id'}) {
     my $coursereply=&resdata($env{'course.'.$courseid.'.num'},      my $coursereply=&resdata($env{'course.'.$courseid.'.num'},
      $env{'course.'.$courseid.'.domain'},       $env{'course.'.$courseid.'.domain'},
      'course',       'course',$mapp,\$recursed,\@recurseup,
                                        $courseid,'.',$spacequalifierrest,
      ([$courselevelm,'map'   ],       ([$courselevelm,'map'   ],
                                         [$courseleveli,'map'   ],
       [$courselevel, 'course']));        [$courselevel, 'course']));
     if (defined($coursereply)) { return &get_reply($coursereply); }      if (defined($coursereply)) { return &get_reply($coursereply); }
  }   }
Line 11716  sub EXT { Line 12350  sub EXT {
     if (defined($partgeneral[0])) { return &get_reply(\@partgeneral); }      if (defined($partgeneral[0])) { return &get_reply(\@partgeneral); }
  }   }
  if ($recurse) { return undef; }   if ($recurse) { return undef; }
  my $pack_def=&packages_tab_default($filename,$varname);   my $pack_def=&packages_tab_default($filename,$varname,$toolsymb);
  if (defined($pack_def)) { return &get_reply([$pack_def,'resource']); }   if (defined($pack_def)) { return &get_reply([$pack_def,'resource']); }
 # ---------------------------------------------------- Any other user namespace  # ---------------------------------------------------- Any other user namespace
     } elsif ($realm eq 'environment') {      } elsif ($realm eq 'environment') {
Line 11741  sub EXT { Line 12375  sub EXT {
  if ($space eq 'name') {   if ($space eq 'name') {
     return $ENV{'SERVER_NAME'};      return $ENV{'SERVER_NAME'};
         }          }
       } elsif ($realm eq 'client') {
           if ($space eq 'remote_addr') {
               return $ENV{'REMOTE_ADDR'};
           }
     }      }
     return '';      return '';
 }  }
Line 11758  sub get_reply { Line 12396  sub get_reply {
 }  }
   
 sub check_group_parms {  sub check_group_parms {
     my ($courseid,$groups,$symbparm,$mapparm,$what) = @_;      my ($courseid,$groups,$symbparm,$recurseparm,$mapparm,$what,$mapp,
     my @groupitems = ();          $recursed,$recurseupref) = @_;
     my $resultitem;      my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$recurseparm,'map'],
     my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$what,'course']);                    [$what,'course']);
       my $coursereply;
     foreach my $group (@{$groups}) {      foreach my $group (@{$groups}) {
           my @groupitems = ();
         foreach my $level (@levels) {          foreach my $level (@levels) {
              my $item = $courseid.'.['.$group.'].'.$level->[0];               my $item = $courseid.'.['.$group.'].'.$level->[0];
              push(@groupitems,[$item,$level->[1]]);               push(@groupitems,[$item,$level->[1]]);
         }          }
           my $coursereply = &resdata($env{'course.'.$courseid.'.num'},
                                      $env{'course.'.$courseid.'.domain'},
                                      'course',$mapp,$recursed,$recurseupref,
                                      $courseid,'.['.$group.'].',$what,
                                      @groupitems);
           last if (defined($coursereply));
     }      }
     my $coursereply = &resdata($env{'course.'.$courseid.'.num'},  
                             $env{'course.'.$courseid.'.domain'},  
                                      'course',@groupitems);  
     return $coursereply;      return $coursereply;
 }  }
   
   sub get_map_hierarchy {
       my ($mapname,$courseid) = @_;
       my @recurseup = ();
       if ($mapname) {
           if (($cachedmapkey eq $courseid) &&
               (abs($cachedmaptime-time)<5)) {
               if (ref($cachedmaps{$mapname}) eq 'ARRAY') {
                   return @{$cachedmaps{$mapname}};
               }
           }
           my $navmap = Apache::lonnavmaps::navmap->new();
           if (ref($navmap)) {
               @recurseup = $navmap->recurseup_maps($mapname);
               undef($navmap);
               $cachedmaps{$mapname} = \@recurseup;
               $cachedmaptime=time;
               $cachedmapkey=$courseid;
           }
       }
       return @recurseup;
   }
   
   }
   
 sub sort_course_groups { # Sort groups based on defined rankings. Default is sort().  sub sort_course_groups { # Sort groups based on defined rankings. Default is sort().
     my ($courseid,@groups) = @_;      my ($courseid,@groups) = @_;
     @groups = sort(@groups);      @groups = sort(@groups);
Line 11781  sub sort_course_groups { # Sort groups b Line 12448  sub sort_course_groups { # Sort groups b
 }  }
   
 sub packages_tab_default {  sub packages_tab_default {
     my ($uri,$varname)=@_;      my ($uri,$varname,$toolsymb)=@_;
     my (undef,$part,$name)=split(/\./,$varname);      my (undef,$part,$name)=split(/\./,$varname);
   
     my (@extension,@specifics,$do_default);      my (@extension,@specifics,$do_default);
     foreach my $package (split(/,/,&metadata($uri,'packages'))) {      foreach my $package (split(/,/,&metadata($uri,'packages',$toolsymb))) {
  my ($pack_type,$pack_part)=split(/_/,$package,2);   my ($pack_type,$pack_part)=split(/_/,$package,2);
  if ($pack_type eq 'default') {   if ($pack_type eq 'default') {
     $do_default=1;      $do_default=1;
Line 11854  my %metaentry; Line 12521  my %metaentry;
 my %importedpartids;  my %importedpartids;
 my %importedrespids;  my %importedrespids;
 sub metadata {  sub metadata {
     my ($uri,$what,$liburi,$prefix,$depthcount)=@_;      my ($uri,$what,$toolsymb,$liburi,$prefix,$depthcount)=@_;
     $uri=&declutter($uri);      $uri=&declutter($uri);
     # if it is a non metadata possible uri return quickly      # if it is a non metadata possible uri return quickly
     if (($uri eq '') ||       if (($uri eq '') || 
Line 11878  sub metadata { Line 12545  sub metadata {
  my ($result,$cached)=&is_cached_new('meta',$uri);   my ($result,$cached)=&is_cached_new('meta',$uri);
  if (defined($cached)) { return $result->{':'.$what}; }   if (defined($cached)) { return $result->{':'.$what}; }
     }      }
   
   #
   # If the uri is for an external tool the file from
   # which metadata should be retrieved depends on whether
   # the tool had been configured to be gradable (set in the Course
   # Editor or Resource Editor).
   #
   # If a valid symb has been included as the third arg in the call
   # to &metadata() that can be used to retrieve the value of
   # parameter_0_gradable set for the resource, and included in the
   # uploaded map containing the tool. The value is retrieved via
   # &EXT(), if a valid symb is available.  Otherwise the value of
   # gradable in the exttool_$marker.db file for the tool instance
   # is retrieved via &get().
   #
   # When lonuserstate::traceroute() calls lonnet::EXT() for 
   # hiddenresource and encrypturl (during course initialization)
   # the map-level parameter for resource.0.gradable included in the 
   # uploaded map containing the tool will not yet have been stored
   # in the user_course_parms.db file for the user's session, so in 
   # this case fall back to retrieving gradable status from the
   # exttool_$marker.db file.
   #
   # In order to avoid an infinite loop, &metadata() will return
   # before a call to &EXT(), if the uri is for an external tool
   # and the $what for which metadata is being requested is
   # parameter_0_gradable or 0_gradable.
   #
   
       if ($uri =~ /ext\.tool$/) {
           if (($what eq 'parameter_0_gradable') || ($what eq '0_gradable')) {
               return;
           } else {
               my ($checked,$use_passback);
               if ($toolsymb ne '') {
                   (undef,undef,my $tooluri) = &decode_symb($toolsymb);
                   if (($tooluri eq $uri) && (&EXT('resource.0.gradable',$toolsymb))) {
                       $checked = 1;
                       if (&EXT('resource.0.gradable',$toolsymb) =~ /^yes$/i) {
                           $use_passback = 1;
                       }
                   }
               }
               unless ($checked) {
                   my ($ignore,$cdom,$cnum,$marker) = split(m{/},$uri);
                   $marker=~s/\D//g;
                   if ($marker) {
                       my %toolsettings=&get('exttool_'.$marker,['gradable'],$cdom,$cnum);
                       $use_passback = $toolsettings{'gradable'};
                   }
               }
               if ($use_passback) {
                   $filename = '/home/httpd/html/res/lib/templates/LTIpassback.tool';
               } else {
                   $filename = '/home/httpd/html/res/lib/templates/LTIstandard.tool';
               }
           }
       }
   
     {      {
 # Imported parts would go here  # Imported parts would go here
         my @origfiletagids=();          my @origfiletagids=();
Line 11986  sub metadata { Line 12712  sub metadata {
 # Check metadata for imported file to  # Check metadata for imported file to
 # see if it contained response items  # see if it contained response items
 #  #
                           my ($origfile,@libfilekeys);
                         my %currmetaentry = %metaentry;                          my %currmetaentry = %metaentry;
                         my $libresponseorder = &metadata($location,'responseorder');                          @libfilekeys = split(/,/,&metadata($location,'keys',undef,undef,undef,
                         my $origfile;                                                             $depthcount+1));
                         if ($libresponseorder ne '') {                          if (grep(/^responseorder$/,@libfilekeys)) {
                             if ($#origfiletagids<0) {                              my $libresponseorder = &metadata($location,'responseorder',undef,undef,
                                 undef(%importedrespids);                                                               undef,$depthcount+1);
                                 undef(%importedpartids);                              if ($libresponseorder ne '') {
                             }                                  if ($#origfiletagids<0) {
                             @{$importedrespids{$importid}} = split(/\s*,\s*/,$libresponseorder);                                      undef(%importedrespids);
                             if (@{$importedrespids{$importid}} > 0) {                                      undef(%importedpartids);
                                 $importedresponses = 1;                                  }
                                   my @respids = split(/\s*,\s*/,$libresponseorder);
                                   if (@respids) {
                                       $importedrespids{$importid} = join(',',map { $importid.'_'.$_ } @respids);
                                   }
                                   if ($importedrespids{$importid} ne '') {
                                       $importedresponses = 1;
 # We need to get the original file and the imported file to get the response order correct  # We need to get the original file and the imported file to get the response order correct
 # Load and inspect original file  # Load and inspect original file
                                 if ($#origfiletagids<0) {                                      if ($#origfiletagids<0) {
                                     my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri);                                          my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri);
                                     $origfile=&getfile($origfilelocation);                                          $origfile=&getfile($origfilelocation);
                                     @origfiletagids=($origfile=~/<((?:\w+)response|import|part)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);                                          @origfiletagids=($origfile=~/<((?:\w+)response|import|part)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
                                       }
                                 }                                  }
                             }                              }
                         }                          }
Line 12010  sub metadata { Line 12744  sub metadata {
 # hash populated for imported library file  # hash populated for imported library file
                         %metaentry = %currmetaentry;                          %metaentry = %currmetaentry;
                         undef(%currmetaentry);                          undef(%currmetaentry);
                         if ($importmode eq 'problem') {                          if ($importmode eq 'part') {
 # Import as problem/response  
                            $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});  
                         } elsif ($importmode eq 'part') {  
 # Import as part(s)  # Import as part(s)
                            $importedparts=1;                             $importedparts=1;
 # We need to get the original file and the imported file to get the part order correct  # We need to get the original file and the imported file to get the part order correct
Line 12028  sub metadata { Line 12759  sub metadata {
                                    @origfiletagids=($origfile=~/<(part|import)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);                                     @origfiletagids=($origfile=~/<(part|import)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
                                }                                 }
                            }                             }
                              my @impfilepartids;
 # Load and inspect imported file  # If <partorder> tag is included in metadata for the imported file
                            my $impfile=&getfile($location);  # get the parts in the imported file from that.
                            my @impfilepartids=($impfile=~/<part[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);                             if (grep(/^partorder$/,@libfilekeys)) {
                                  %currmetaentry = %metaentry;
                                  my $libpartorder = &metadata($location,'partorder',undef,undef,undef,
                                                               $depthcount+1);
                                  %metaentry = %currmetaentry;
                                  undef(%currmetaentry);
                                  if ($libpartorder ne '') {
                                      @impfilepartids=split(/\s*,\s*/,$libpartorder);
                                  }
                              } else {
   # If no <partorder> tag available, load and inspect imported file
                                  my $impfile=&getfile($location);
                                  @impfilepartids=($impfile=~/<part[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
                              }
                            if ($#impfilepartids>=0) {                             if ($#impfilepartids>=0) {
 # This problem had parts  # This problem had parts
                                $importedpartids{$token->[2]->{'id'}}=join(',',@impfilepartids);                                 $importedpartids{$token->[2]->{'id'}}=join(',',@impfilepartids);
Line 12042  sub metadata { Line 12786  sub metadata {
                                $importedpartids{$token->[2]->{'id'}}=$token->[2]->{'id'};                                 $importedpartids{$token->[2]->{'id'}}=$token->[2]->{'id'};
                            }                             }
                         } else {                          } else {
   # Import as problem or as normal import
                               $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
                               unless ($importmode eq 'problem') {
 # Normal import  # Normal import
                            $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});                                  if (defined($token->[2]->{'id'})) {
                            if (defined($token->[2]->{'id'})) {                                      $unikey.='_'.$token->[2]->{'id'};
                               $unikey.='_'.$token->[2]->{'id'};                                  }
                            }                              }
   # Check metadata for imported file to
   # see if it contained parts
                               if (grep(/^partorder$/,@libfilekeys)) {
                                   %currmetaentry = %metaentry;
                                   my $libpartorder = &metadata($location,'partorder',undef,undef,undef,
                                                                $depthcount+1);
                                   %metaentry = %currmetaentry;
                                   undef(%currmetaentry);
                                   if ($libpartorder ne '') {
                                       $importedparts = 1;
                                       $importedpartids{$token->[2]->{'id'}}=$libpartorder;
                                   }
                               }
                         }                          }
   
  if ($depthcount<20) {   if ($depthcount<20) {
     my $metadata =       my $metadata = 
  &metadata($uri,'keys', $location,$unikey,   &metadata($uri,'keys',$toolsymb,$location,$unikey,
   $depthcount+1);    $depthcount+1);
     foreach my $meta (split(',',$metadata)) {      foreach my $meta (split(',',$metadata)) {
  $metaentry{':'.$meta}=$metaentry{':'.$meta};   $metaentry{':'.$meta}=$metaentry{':'.$meta};
  $metathesekeys{$meta}=1;   $metathesekeys{$meta}=1;
     }      }
   
                         }                          }
     } else {      } else {
 #  #
Line 12126  sub metadata { Line 12884  sub metadata {
  $dir=~s|[^/]*$||;   $dir=~s|[^/]*$||;
  $location=&filelocation($dir,$location);   $location=&filelocation($dir,$location);
  my $rights_metadata =   my $rights_metadata =
     &metadata($uri,'keys',$location,'_rights',      &metadata($uri,'keys',$toolsymb,$location,'_rights',
       $depthcount+1);        $depthcount+1);
  foreach my $rights (split(',',$rights_metadata)) {   foreach my $rights (split(',',$rights_metadata)) {
     #$metaentry{':'.$rights}=$metacache{$uri}->{':'.$rights};      #$metaentry{':'.$rights}=$metacache{$uri}->{':'.$rights};
Line 12147  sub metadata { Line 12905  sub metadata {
                 $metathesekeys{'partorder'}=1;                  $metathesekeys{'partorder'}=1;
             }              }
             if ($importedresponses) {              if ($importedresponses) {
 # We had imported responses and need to rebuild responseorder  # We had imported responses and need to rebuil responseorder
                 $metaentry{':responseorder'}='';                  $metaentry{':responseorder'}='';
                 $metathesekeys{'responseorder'}=1;                  $metathesekeys{'responseorder'}=1;
             }              }
Line 12161  sub metadata { Line 12919  sub metadata {
                 } elsif ($origfiletagids[$index] eq 'import') {                  } elsif ($origfiletagids[$index] eq 'import') {
                     if ($importedparts) {                      if ($importedparts) {
 # We have imported parts at this position  # We have imported parts at this position
                         $metaentry{':partorder'}.=','.$importedpartids{$origid};                          if ($importedpartids{$origid} ne '') {
                               $metaentry{':partorder'}.=','.$importedpartids{$origid};
                           }
                     }                      }
                     if ($importedresponses) {                      if ($importedresponses) {
 # We have imported responses at this position  # We have imported responses at this position
                         if (ref($importedrespids{$origid}) eq 'ARRAY') {                          if ($importedrespids{$origid} ne '') {
                             $metaentry{':responseorder'}.=','.join(',',map { $origid.'_'.$_ } @{$importedrespids{$origid}});                              $metaentry{':responseorder'}.=','.$importedrespids{$origid};
                         }                          }
                     }                      }
                 } else {                  } else {
Line 12183  sub metadata { Line 12943  sub metadata {
                 $metaentry{':responseorder'}=~s/^\,//;                  $metaentry{':responseorder'}=~s/^\,//;
             }              }
         }          }
   
  $metaentry{':keys'} = join(',',keys(%metathesekeys));   $metaentry{':keys'} = join(',',keys(%metathesekeys));
  &metadata_generate_part0(\%metathesekeys,\%metaentry,$uri);   &metadata_generate_part0(\%metathesekeys,\%metaentry,$uri);
  $metaentry{':allpossiblekeys'}=join(',',keys(%metathesekeys));   $metaentry{':allpossiblekeys'}=join(',',keys(%metathesekeys));
  &do_cache_new('meta',$uri,\%metaentry,$cachetime);          unless ($liburi) {
       &do_cache_new('meta',$uri,\%metaentry,$cachetime);
           }
 # this is the end of "was not already recently cached  # this is the end of "was not already recently cached
     }      }
     return $metaentry{':'.$what};      return $metaentry{':'.$what};
Line 12460  sub symbverify { Line 13221  sub symbverify {
             $ids=$bighash{'ids_'.&clutter($thisurl)};              $ids=$bighash{'ids_'.&clutter($thisurl)};
         }          }
         unless ($ids) {          unless ($ids) {
             my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl;              my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl;  
             $ids=$bighash{$idkey};              $ids=$bighash{$idkey};
         }          }
         if ($ids) {          if ($ids) {
Line 12476  sub symbverify { Line 13237  sub symbverify {
                    if (ref($encstate)) {                     if (ref($encstate)) {
                        $$encstate = $bighash{'encrypted_'.$id};                         $$encstate = $bighash{'encrypted_'.$id};
                    }                     }
                    if (($env{'request.role.adv'}) ||     if (($env{'request.role.adv'}) ||
                        ($bighash{'encrypted_'.$id} eq $env{'request.enc'}) ||         ($bighash{'encrypted_'.$id} eq $env{'request.enc'}) ||
                        ($thisurl eq '/adm/navmaps')) {                         ($thisurl eq '/adm/navmaps')) {
                        $okay=1;         $okay=1;
                        last;                         last;
                    }     }
                }         }
            }     }
         }          }
  untie(%bighash);   untie(%bighash);
     }      }
Line 12571  sub symbread { Line 13332  sub symbread {
     unless ($thisfn) {      unless ($thisfn) {
         if ($env{'request.symb'}) {          if ($env{'request.symb'}) {
             return $env{$cache_str}=&symbclean($env{'request.symb'});              return $env{$cache_str}=&symbclean($env{'request.symb'});
         }   }
         $thisfn=$env{'request.filename'};   $thisfn=$env{'request.filename'};
     }      }
     if ($thisfn=~m|^/enc/|) { $thisfn=&Apache::lonenc::unencrypted($thisfn); }      if ($thisfn=~m|^/enc/|) { $thisfn=&Apache::lonenc::unencrypted($thisfn); }
 # is that filename actually a symb? Verify, clean, and return  # is that filename actually a symb? Verify, clean, and return
Line 12651  sub symbread { Line 13412  sub symbread {
                              }                               }
                          }                           }
                      }                       }
                  } elsif ((!$donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) {                   } elsif ((!$donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) { 
 # ------------------------------------------ There is more than one possibility  # ------------------------------------------ There is more than one possibility
                      my $realpossible=0;                       my $realpossible=0;
                      foreach my $id (@possibilities) {                       foreach my $id (@possibilities) {
Line 12659  sub symbread { Line 13420  sub symbread {
                          my $canaccess;                           my $canaccess;
                          if (($donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) {                           if (($donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) {
                              $canaccess = 1;                               $canaccess = 1;
                          } else {                           } else { 
                              $canaccess = &allowed('bre',$file);                               $canaccess = &allowed('bre',$file);
                          }                           }
                          if ($canaccess) {                           if ($canaccess) {
               my ($mapid,$resid)=split(/\./,$id);                my ($mapid,$resid)=split(/\./,$id);
                              if ($bighash{'map_type_'.$mapid} ne 'page') {                               if ($bighash{'map_type_'.$mapid} ne 'page') {
                                  my $poss_syval=&encode_symb($bighash{'map_id_'.$mapid},                                   my $poss_syval=&encode_symb($bighash{'map_id_'.$mapid},
                                                              $resid,$thisfn);               $resid,$thisfn);
                                  next if ($bighash{'randomout_'.$id} && !$env{'request.role.adv'});                                   next if ($bighash{'randomout_'.$id} && !$env{'request.role.adv'});
                                  next unless (($noenccheck) || ($bighash{'encrypted_'.$id} eq $env{'request.enc'}));                                   next unless (($noenccheck) || ($bighash{'encrypted_'.$id} eq $env{'request.enc'}));
                                  if ($checkforblock) {                                   if ($checkforblock) {
Line 12847  sub rndseed { Line 13608  sub rndseed {
  $which =&get_rand_alg($courseid);   $which =&get_rand_alg($courseid);
     }      }
     if (defined(&getCODE())) {      if (defined(&getCODE())) {
   
  if ($which eq '64bit5') {   if ($which eq '64bit5') {
     return &rndseed_CODE_64bit5($symb,$courseid,$domain,$username);      return &rndseed_CODE_64bit5($symb,$courseid,$domain,$username);
  } elsif ($which eq '64bit4') {   } elsif ($which eq '64bit4') {
Line 13032  sub rndseed_CODE_64bit5 { Line 13794  sub rndseed_CODE_64bit5 {
 sub setup_random_from_rndseed {  sub setup_random_from_rndseed {
     my ($rndseed)=@_;      my ($rndseed)=@_;
     if ($rndseed =~/([,:])/) {      if ($rndseed =~/([,:])/) {
  my ($num1,$num2) = map { abs($_); } (split(/[,:]/,$rndseed));          my ($num1,$num2) = map { abs($_); } (split(/[,:]/,$rndseed));
         if ((!$num1) || (!$num2) || ($num1 > 2147483562) || ($num2 > 2147483398)) {          if ((!$num1) || (!$num2) || ($num1 > 2147483562) || ($num2 > 2147483398)) {
             &Math::Random::random_set_seed_from_phrase($rndseed);              &Math::Random::random_set_seed_from_phrase($rndseed);
         } else {          } else {
Line 13216  sub repcopy_userfile { Line 13978  sub repcopy_userfile {
     }      }
 # now the path exists for sure  # now the path exists for sure
 # get a user agent  # get a user agent
     my $ua=new LWP::UserAgent;  
     my $transferfile=$file.'.in.transfer';      my $transferfile=$file.'.in.transfer';
 # FIXME: this should flock  # FIXME: this should flock
     if (-e $transferfile) { return 'ok'; }      if (-e $transferfile) { return 'ok'; }
Line 13227  sub repcopy_userfile { Line 13988  sub repcopy_userfile {
     my $protocol = $protocol{$homeserver};      my $protocol = $protocol{$homeserver};
     $protocol = 'http' if ($protocol ne 'https');      $protocol = 'http' if ($protocol ne 'https');
     $request=new HTTP::Request('GET',$protocol.'://'.$hostname.'/raw/'.$uri);      $request=new HTTP::Request('GET',$protocol.'://'.$hostname.'/raw/'.$uri);
     my $response=$ua->request($request,$transferfile);      my $response = &LONCAPA::LWPReq::makerequest($homeserver,$request,$transferfile,\%perlvar,'',0,1);
 # did it work?  # did it work?
     if ($response->is_error()) {      if ($response->is_error()) {
  unlink($transferfile);   unlink($transferfile);
Line 13273  sub getuploaded { Line 14034  sub getuploaded {
     my $protocol = $protocol{$homeserver};      my $protocol = $protocol{$homeserver};
     $protocol = 'http' if ($protocol ne 'https');      $protocol = 'http' if ($protocol ne 'https');
     $uri = $protocol.'://'.$hostname.'/raw/'.$uri;      $uri = $protocol.'://'.$hostname.'/raw/'.$uri;
     my $ua=new LWP::UserAgent;  
     my $request=new HTTP::Request($reqtype,$uri);      my $request=new HTTP::Request($reqtype,$uri);
     my $response=$ua->request($request);      my $response=&LONCAPA::LWPReq::makerequest($homeserver,$request,'',\%perlvar,'',0,1);
     $$rtncode = $response->code;      $$rtncode = $response->code;
     if (! $response->is_success()) {      if (! $response->is_success()) {
  return 'failed';   return 'failed';
Line 13460  sub uses_sts { Line 14220  sub uses_sts {
                 return $sts_on;                  return $sts_on;
             }              }
         }          }
         my $ua=new LWP::UserAgent;  
         my $url = $protocol{$lonhost}.'://'.$hostname.'/index.html';          my $url = $protocol{$lonhost}.'://'.$hostname.'/index.html';
         my $request=new HTTP::Request('HEAD',$url);          my $request=new HTTP::Request('HEAD',$url);
         my $response=$ua->request($request);          my $response=&LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,'','','',1);
         if ($response->is_success) {          if ($response->is_success) {
             my $has_sts = $response->header('Strict-Transport-Security');              my $has_sts = $response->header('Strict-Transport-Security');
             if ($has_sts eq '') {              if ($has_sts eq '') {
Line 13625  sub get_dns { Line 14384  sub get_dns {
     }      }
     while (%alldns) {      while (%alldns) {
  my ($dns) = sort { $b cmp $a } keys(%alldns);   my ($dns) = sort { $b cmp $a } keys(%alldns);
  my $ua=new LWP::UserAgent;  
         $ua->timeout(30);  
  my $request=new HTTP::Request('GET',"$alldns{$dns}://$dns$url");   my $request=new HTTP::Request('GET',"$alldns{$dns}://$dns$url");
  my $response=$ua->request($request);          my $response = &LONCAPA::LWPReq::makerequest('',$request,'',\%perlvar,30,0);
         delete($alldns{$dns});          delete($alldns{$dns});
  next if ($response->is_error());   next if ($response->is_error());
  my @content = split("\n",$response->content);          if ($url eq '/adm/dns/loncapaCRL') {
         unless ($nocache) {              return &$func($response);
     &do_cache_new('dns',$url,\@content,30*24*60*60);          } else {
       my @content = split("\n",$response->content);
       unless ($nocache) {
           &do_cache_new('dns',$url,\@content,30*24*60*60);
       }
       &$func(\@content,$hashref);
               return;
         }          }
  &$func(\@content,$hashref);  
  return;  
     }      }
     my $which = (split('/',$url))[3];      my $which = (split('/',$url,4))[3];
     &logthis("unable to contact DNS defaulting to on disk file dns_$which.tab\n");      if ($which eq 'loncapaCRL') {
     if (open(my $config,"<","$perlvar{'lonTabDir'}/dns_$which.tab")) {          my $diskfile = "$perlvar{'lonCertificateDirectory'}/$perlvar{'lonnetCertRevocationList'}";
         my @content = <$config>;          if (-e $diskfile) {
         &$func(\@content,$hashref);              &logthis("unable to contact DNS, on disk file $diskfile not updated");
           } else {
               &logthis("unable to contact DNS, no on disk file $diskfile available");
           }
       } else {
           &logthis("unable to contact DNS defaulting to on disk file dns_$which.tab\n");
           if (open(my $config,"<","$perlvar{'lonTabDir'}/dns_$which.tab")) {
               my @content = <$config>;
               close($config);
               &$func(\@content,$hashref);
           }
     }      }
     return;      return;
 }  }
Line 13671  sub parse_dns_checksums_tab { Line 14442  sub parse_dns_checksums_tab {
     if (ref($lines) eq 'ARRAY') {      if (ref($lines) eq 'ARRAY') {
         chomp(@{$lines});          chomp(@{$lines});
         my $version = shift(@{$lines});          my $version = shift(@{$lines});
         if ($version eq $release) {          if ($version eq $release) {  
             foreach my $line (@{$lines}) {              foreach my $line (@{$lines}) {
                 my ($file,$version,$shasum) = split(/,/,$line);                  my ($file,$version,$shasum) = split(/,/,$line);
                 if ($file =~ m{^/etc/httpd/conf}) {                  if ($file =~ m{^/etc/httpd/conf}) {
Line 13703  sub fetch_dns_checksums { Line 14474  sub fetch_dns_checksums {
     return \%checksums;      return \%checksums;
 }  }
   
   sub fetch_crl_pemfile {
       return &get_dns("/adm/dns/loncapaCRL",\&save_crl_pem,1,1);
   }
   
   sub save_crl_pem {
       my ($response) = @_;
       my ($msg,$hadchanges);
       if (ref($response)) {
           my $now = time;
           my $lonca = $perlvar{'lonCertificateDirectory'}.'/'.$perlvar{'lonnetCertificateAuthority'};
           my $tmpcrl = $tmpdir.'/'.$perlvar{'lonnetCertRevocationList'}.'_'.$now.'.'.$$.'.tmp';
           if (open(my $fh,'>',"$tmpcrl")) {
               print $fh $response->content;
               close($fh);
               if (-e $lonca) {
                   if (open(PIPE,"openssl crl -in $tmpcrl -inform pem -CAfile $lonca -noout 2>&1 |")) {
                       my $check = <PIPE>;
                       close(PIPE);
                       chomp($check);
                       if ($check eq 'verify OK') {
                           my $dest = "$perlvar{'lonCertificateDirectory'}/$perlvar{'lonnetCertRevocationList'}";
                           my $backup;
                           if (-e $dest) {
                               if (&File::Copy::move($dest,"$dest.bak")) {
                                   $backup = 'ok';
                               }
                           }
                           if (&File::Copy::move($tmpcrl,$dest)) {
                               $msg = 'ok';
                               if ($backup) {
                                   my (%oldnums,%newnums);
                                   if (open(PIPE, "openssl crl -inform PEM -text -noout -in $dest.bak |grep 'Serial Number' |")) {
                                       while (<PIPE>) {
                                           $oldnums{(split(/:/))[1]} = 1;
                                       }
                                       close(PIPE);
                                   }
                                   if (open(PIPE, "openssl crl -inform PEM -text -noout -in $dest |grep 'Serial Number' |")) {
                                       while(<PIPE>) {
                                           $newnums{(split(/:/))[1]} = 1;
                                       }
                                       close(PIPE);
                                   }
                                   foreach my $key (sort {$b <=> $a } (keys(%newnums))) {
                                       unless (exists($oldnums{$key})) {
                                           $hadchanges = 1;
                                           last;
                                       }
                                   }
                                   unless ($hadchanges) {
                                       foreach my $key (sort {$b <=> $a } (keys(%oldnums))) {
                                           unless (exists($newnums{$key})) {
                                               $hadchanges = 1;
                                               last;
                                           }
                                       }
                                   }
                               }
                           }
                       } else {
                           unlink($tmpcrl);
                       }
                   } else {
                       unlink($tmpcrl);
                   }
               } else {
                   unlink($tmpcrl);
               }
           }
       }
       return ($msg,$hadchanges);
   }
   
 # ------------------------------------------------------------ Read domain file  # ------------------------------------------------------------ Read domain file
 {  {
     my $loaded;      my $loaded;
Line 13825  sub fetch_dns_checksums { Line 14669  sub fetch_dns_checksums {
  &purge_remembered();   &purge_remembered();
  &reset_domain_info();   &reset_domain_info();
  &reset_hosts_ip_info();   &reset_hosts_ip_info();
           undef(%internetdom);
  undef(%name_to_host);   undef(%name_to_host);
  undef(%hostname);   undef(%hostname);
  undef(%hostdom);   undef(%hostdom);
Line 13867  sub fetch_dns_checksums { Line 14712  sub fetch_dns_checksums {
         return %hostdom;          return %hostdom;
     }      }
   
       sub all_host_intdom {
           &load_hosts_tab() if (!$loaded);
           return %internetdom;
       }
   
     sub is_library {      sub is_library {
  &load_hosts_tab() if (!$loaded);   &load_hosts_tab() if (!$loaded);
   
Line 14097  sub all_loncaparevs { Line 14947  sub all_loncaparevs {
     return qw(1.1 1.2 1.3 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11);      return qw(1.1 1.2 1.3 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11);
 }  }
   
 # ------------------------------------------------------- Read loncaparev table  # ---------------------------------------------------------- Read loncaparev table
 {  {
     sub load_loncaparevs {      sub load_loncaparevs { 
         if (-e "$perlvar{'lonTabDir'}/loncaparevs.tab") {          if (-e "$perlvar{'lonTabDir'}/loncaparevs.tab") {
             if (open(my $config,"<","$perlvar{'lonTabDir'}/loncaparevs.tab")) {              if (open(my $config,"<","$perlvar{'lonTabDir'}/loncaparevs.tab")) {
                 while (my $configline=<$config>) {                  while (my $configline=<$config>) {
Line 14113  sub all_loncaparevs { Line 14963  sub all_loncaparevs {
     }      }
 }  }
   
 # ----------------------------------------------------- Read serverhostID table  # ---------------------------------------------------------- Read serverhostID table
 {  {
     sub load_serverhomeIDs {      sub load_serverhomeIDs {
         if (-e "$perlvar{'lonTabDir'}/serverhomeIDs.tab") {          if (-e "$perlvar{'lonTabDir'}/serverhomeIDs.tab") {
Line 14205  BEGIN { Line 15055  BEGIN {
     close($config);      close($config);
 }  }
   
 # --------------------------------------------------------- Read loncaparev table  # ---------------------------------------------------------- Read loncaparev table
   
 &load_loncaparevs();  &load_loncaparevs();
   
 # ------------------------------------------------------- Read serverhostID table  # ---------------------------------------------------------- Read serverhostID table
   
 &load_serverhomeIDs();  &load_serverhomeIDs();
   
Line 14223  BEGIN { Line 15073  BEGIN {
                 my $item = $token->[1];                  my $item = $token->[1];
                 my $name = $token->[2]{'name'};                  my $name = $token->[2]{'name'};
                 my $value = $token->[2]{'value'};                  my $value = $token->[2]{'value'};
                 if ($item ne '' && $name ne '' && $value ne '') {                  my $valuematch = $token->[2]{'valuematch'};
                   my $namematch = $token->[2]{'namematch'};
                   if ($item eq 'parameter') {
                       if (($namematch ne '') || (($name ne '') && ($value ne '' || $valuematch ne ''))) {
                           my $release = $parser->get_text();
                           $release =~ s/(^\s*|\s*$ )//gx;
                           $needsrelease{$item.':'.$name.':'.$value.':'.$valuematch.':'.$namematch} = $release;
                       }
                   } elsif ($item ne '' && $name ne '') {
                     my $release = $parser->get_text();                      my $release = $parser->get_text();
                     $release =~ s/(^\s*|\s*$ )//gx;                      $release =~ s/(^\s*|\s*$ )//gx;
                     $needsrelease{$item.':'.$name.':'.$value} = $release;                      $needsrelease{$item.':'.$name.':'.$value} = $release;
Line 14506  the answer, and also caches if there is Line 15364  the answer, and also caches if there is
   
 =item *  =item *
 X<idget()>  X<idget()>
 B<idget($udom,@ids)>: find the usernames behind a list of IDs  B<idget($udom,$idsref,$namespace)>: find the usernames behind either 
 (IDs are a unique resource in a domain, there must be only 1 ID per  a list of student/employee IDs or clicker IDs
 username, and only 1 username per ID in a specific domain) (returns  (student/employee IDs are a unique resource in a domain, there must be 
 hash: id=>name,id=>name)  only 1 ID per username, and only 1 username per ID in a specific domain).
   clickerIDs are not necessarily unique, as students might share clickers.
   (returns hash: id=>name,id=>name)
   
 =item *  =item *
 X<idrget()>  X<idrget()>
Line 14518  usernames (returns hash: name=>id,name=> Line 15378  usernames (returns hash: name=>id,name=>
   
 =item *  =item *
 X<idput()>  X<idput()>
 B<idput($udom,%ids)>: store away a list of names and associated IDs  B<idput($udom,$idsref,$uhome,$namespace)>: store away a list of 
   names and associated student/employee IDs or clicker IDs.
   
   =item *
   X<iddel()>
   B<iddel($udom,$idshashref,$uhome,$namespace)>: delete unwanted 
   student/employee ID or clicker ID username look-ups from domain.
   The homeserver ($uhome) and namespace ($namespace) are optional.
   If no $uhome is provided, it will be determined usig &homeserver()
   for each user.  If no $namespace is provided, the default is ids.
   
   =item *
   X<updateclickers()>
   B<updateclickers($udom,$action,$idshashref,$uhome,$critical)>: update 
   clicker ID-to-username look-ups in clickers.db on library server.
   Permitted actions are add or del (i.e., add or delete). The 
   clickers.db contains clickerID as keys (escaped), and each corresponding
   value is an escaped comma-separated list of usernames (for whom the
   library server is the homeserver), who registered that particular ID.
   If $critical is true, the update will be sent via &critical, otherwise
   &reply() will be used.
   
 =item *  =item *
 X<rolesinit()>  X<rolesinit()>
Line 14566  The first argument is required, all othe Line 15446  The first argument is required, all othe
   
 $priv is the privilege being checked.  $priv is the privilege being checked.
 $uri contains additional information about what is being checked for access (e.g.,  $uri contains additional information about what is being checked for access (e.g.,
 URL, course ID etc.).  URL, course ID etc.). 
 $symb is the unique resource instance identifier in a course; if needed,  $symb is the unique resource instance identifier in a course; if needed,
 but not provided, it will be retrieved via a call to &symbread().  but not provided, it will be retrieved via a call to &symbread(). 
 $role is the role for which a priv is being checked (only used if priv is evb).  $role is the role for which a priv is being checked (only used if priv is evb). 
 $clientip is the user's IP address (only used when checking for access to portfolio  $clientip is the user's IP address (only used when checking for access to portfolio 
 files).  files).
 $noblockcheck, if true, skips calls to &has_comm_blocking() for the bre priv. This  $noblockcheck, if true, skips calls to &has_comm_blocking() for the bre priv. This 
 prevents recursive calls to &allowed.  prevents recursive calls to &allowed.
   
  F: full access   F: full access
Line 14582  prevents recursive calls to &allowed. Line 15462  prevents recursive calls to &allowed.
  2: browse allowed   2: browse allowed
  A: passphrase authentication needed   A: passphrase authentication needed
  B: access temporarily blocked because of a blocking event in a course.   B: access temporarily blocked because of a blocking event in a course.
    D: access blocked because access is required via session initiated via deep-link 
   
 =item *  =item *
   
Line 14634  provided for types, will default to retu Line 15515  provided for types, will default to retu
 =item *  =item *
   
 in_course($udom,$uname,$cdom,$cnum,$type,$hideprivileged) : determine if  in_course($udom,$uname,$cdom,$cnum,$type,$hideprivileged) : determine if
 user: $uname:$udom has a role in the course: $cdom_$cnum.  user: $uname:$udom has a role in the course: $cdom_$cnum. 
   
 Additional optional arguments are: $type (if role checking is to be restricted  Additional optional arguments are: $type (if role checking is to be restricted 
 to certain user status types -- previous (expired roles), active (currently  to certain user status types -- previous (expired roles), active (currently
 available roles) or future (roles available in the future), and  available roles) or future (roles available in the future), and
 $hideprivileged -- if true will not report course roles for users who  $hideprivileged -- if true will not report course roles for users who
Line 14875  values that are the resource value.  I b Line 15756  values that are the resource value.  I b
 versions are also returned.  versions are also returned.
   
 get_numsuppfiles($cnum,$cdom) : retrieve number of files in a course's  get_numsuppfiles($cnum,$cdom) : retrieve number of files in a course's
 supplemental content area. This routine caches the number of files for  supplemental content area. This routine caches the number of files for 
 10 minutes.  10 minutes.
   
 =back  =back
Line 15045  condval($condidx) : value of condition i Line 15926  condval($condidx) : value of condition i
   
 =item *  =item *
   
 metadata($uri,$what,$liburi,$prefix,$depthcount) : request a  metadata($uri,$what,$toolsymb,$liburi,$prefix,$depthcount) : request a
 resource's metadata, $what should be either a specific key, or either  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  '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.  packages that this resource currently uses, the last 3 arguments are 
   only used internally for recursive metadata.
   
 this function automatically caches all requests  the toolsymb is only used where the uri is for an external tool (for which
   the uri as well as the symb are guaranteed to be unique).
   
   this function automatically caches all requests except any made recursively
   to retrieve a list of metadata keys for an imported library file ($liburi is 
   defined).
   
 =item *  =item *
   
Line 15060  will be stored for query Line 15947  will be stored for query
   
 =item *  =item *
   
 symbread($filename,$donotrecurse,$ignorecachednull,$checkforblock,$possibles) :  symbread($filename,$donotrecurse,$ignorecachednull,$checkforblock,$possibles) : 
 return symbolic list entry (all arguments optional).  return symbolic list entry (all arguments optional). 
   
 Args: filename is the filename (including path) for the file for which a symb  Args: filename is the filename (including path) for the file for which a symb 
 is required; donotrecurse, if true will prevent calls to allowed() being made  is required; donotrecurse, if true will prevent calls to allowed() being made 
 to check access status if more than one resource was found in the bighash  to check access status if more than one resource was found in the bighash 
 (see rev. 1.249) to avoid an infinite loop if an ambiguous resource is part of  (see rev. 1.249) to avoid an infinite loop if an ambiguous resource is part of 
 a randompick); ignorecachednull, if true will prevent a symb of '' being  a randompick); ignorecachednull, if true will prevent a symb of '' being 
 returned if $env{$cache_str} is defined as ''; checkforblock if true will  returned if $env{$cache_str} is defined as ''; checkforblock if true will
 cause possible symbs to be checked to determine if they are subject to content  cause possible symbs to be checked to determine if they are subject to content
 blocking, if so they will not be included as possible symbs; possibles is a  blocking, if so they will not be included as possible symbs; possibles is a
 ref to a hash, which, as a side effect, will be populated with all possible  ref to a hash, which, as a side effect, will be populated with all possible 
 symbs (content blocking not tested).  symbs (content blocking not tested).
    
 returns the data handle  returns the data handle
   
 =item *  =item *
Line 15083  and is a possible symb for the URL in $t Line 15970  and is a possible symb for the URL in $t
 resource that the user accessed using /enc/ returns a 1 on success, 0  resource that the user accessed using /enc/ returns a 1 on success, 0
 on failure, user must be in a course, as it assumes the existence of  on failure, user must be in a course, as it assumes the existence of
 the course initial hash, and uses $env('request.course.id'}.  The third  the course initial hash, and uses $env('request.course.id'}.  The third
 arg is an optional reference to a scalar.  If this arg is passed in the  arg is an optional reference to a scalar.  If this arg is passed in the 
 call to symbverify, it will be set to 1 if the symb has been set to be   call to symbverify, it will be set to 1 if the symb has been set to be 
 encrypted; otherwise it will be null.  encrypted; otherwise it will be null.  
   
 =item *  =item *
   
Line 15138  expirespread($uname,$udom,$stype,$usymb) Line 16025  expirespread($uname,$udom,$stype,$usymb)
 devalidate($symb) : devalidate temporary spreadsheet calculations,  devalidate($symb) : devalidate temporary spreadsheet calculations,
 forcing spreadsheet to reevaluate the resource scores next time.  forcing spreadsheet to reevaluate the resource scores next time.
   
 =item *  =item * 
   
 can_edit_resource($file,$cnum,$cdom,$resurl,$symb,$group) : determine if current user can edit a particular resource,  can_edit_resource($file,$cnum,$cdom,$resurl,$symb,$group) : determine if current user can edit a particular resource,
 when viewing in course context.  when viewing in course context.
   
  input: six args -- filename (decluttered), course number, course domain,   input: six args -- filename (decluttered), course number, course domain,
                     url, symb (if registered) and group (if this is a                      url, symb (if registered) and group (if this is a 
                     group item -- e.g., bulletin board, group page etc.).                      group item -- e.g., bulletin board, group page etc.).
   
  output: array of five scalars --   output: array of five scalars --
Line 15152  when viewing in course context. Line 16039  when viewing in course context.
          $home -- homeserver of resource (i.e., for author if published,           $home -- homeserver of resource (i.e., for author if published,
                                           or course if uploaded.).                                            or course if uploaded.).
          $switchserver --  1 if server switch will be needed.           $switchserver --  1 if server switch will be needed.
          $forceedit -- 1 if icon/link should be to go to edit mode           $forceedit -- 1 if icon/link should be to go to edit mode 
          $forceview -- 1 if icon/link should be to go to view mode           $forceview -- 1 if icon/link should be to go to view mode
   
 =item *  =item *
   
 is_course_upload($file,$cnum,$cdom)  is_course_upload($file,$cnum,$cdom)
   
 Used in course context to determine if current file was uploaded to  Used in course context to determine if current file was uploaded to 
 the course (i.e., would be found in /userfiles/docs on the course's  the course (i.e., would be found in /userfiles/docs on the course's 
 homeserver.  homeserver.
   
   input: 3 args -- filename (decluttered), course number and course domain.    input: 3 args -- filename (decluttered), course number and course domain.
Line 15174  homeserver. Line 16061  homeserver.
   
 =item *  =item *
   
 store($storehash,$symb,$namespace,$udom,$uname,$laststore) : stores hash   store($storehash,$symb,$namespace,$udom,$uname,$laststore) : stores hash
 permanently for this url; hashref needs to be given and should be a \%hashname;  permanently for this url; hashref needs to be given and should be a \%hashname;
 the remaining args aren't required and if they aren't passed or are '' they will  the remaining args aren't required and if they aren't passed or are '' they will
 be derived from the env (with the exception of $laststore, which is an  be derived from the env (with the exception of $laststore, which is an 
 optional arg used when a user's submission is stored in grading).  optional arg used when a user's submission is stored in grading).
 $laststore is $version=$timestamp, where $version is the most recent version  $laststore is $version=$timestamp, where $version is the most recent version
 number retrieved for the corresponding $symb in the $namespace db file, and  number retrieved for the corresponding $symb in the $namespace db file, and
 $timestamp is the timestamp for that transaction (UNIX time).  $timestamp is the timestamp for that transaction (UNIX time).
 $laststore is currently only passed when cstore() is called by  $laststore is currently only passed when cstore() is called by 
 structuretags::finalize_storage().  structuretags::finalize_storage().
   
 =item *  =item *
   
 cstore($storehash,$symb,$namespace,$udom,$uname,$laststore) : same as store   cstore($storehash,$symb,$namespace,$udom,$uname,$laststore) : same as store
 but uses critical subroutine  but uses critical subroutine
   
 =item *  =item *
Line 15325  server ($udom and $uhome are optional) Line 16212  server ($udom and $uhome are optional)
   
 =item *   =item * 
   
 get_domain_defaults($target_domain,$ignore_cache) : returns hash with defaults  get_domain_defaults($target_domain,$ignore_cache) : returns hash with defaults 
 for: authentication, language, quotas, timezone, date locale, and portal URL in  for: authentication, language, quotas, timezone, date locale, and portal URL in
 the target domain.  the target domain.
   
Line 15359  requestcourses: ability to request cours Line 16246  requestcourses: ability to request cours
 =over  =over
   
 =item  =item
 official, unofficial, community, textbook  official, unofficial, community, textbook, placement
   
 =back  =back
   
Line 15380  for course's uploaded content. Line 16267  for course's uploaded content.
 =over  =over
   
 =item  =item
 canuse_pdfforms, officialcredits, unofficialcredits, textbookcredits, officialquota, unofficialquota,  canuse_pdfforms, officialcredits, unofficialcredits, textbookcredits, officialquota, unofficialquota, 
 communityquota, textbookquota  communityquota, textbookquota, placementquota
   
 =back  =back
   
Line 15391  on your servers. Line 16278  on your servers.
   
 =over  =over
   
 =item  =item 
 remotesessions, hostedsessions  remotesessions, hostedsessions
   
 =back  =back
Line 15399  remotesessions, hostedsessions Line 16286  remotesessions, hostedsessions
 =back  =back
   
 In cases where a domain coordinator has never used the "Set Domain Configuration"  In cases where a domain coordinator has never used the "Set Domain Configuration"
 utility to create a configuration.db file on a domain's primary library server  utility to create a configuration.db file on a domain's primary library server 
 only the following domain defaults: auth_def, auth_arg_def, lang_def  only the following domain defaults: auth_def, auth_arg_def, lang_def
 -- corresponding values are authentication type (internal, krb4, krb5,  -- corresponding values are authentication type (internal, krb4, krb5,
 or localauth), initial password or a kerberos realm, language (e.g., en-us) --  or localauth), initial password or a kerberos realm, language (e.g., en-us) -- 
 will be available. Values are retrieved from cache (if current), unless the  will be available. Values are retrieved from cache (if current), unless the
 optional $ignore_cache arg is true, or from domain's configuration.db (if available),  optional $ignore_cache arg is true, or from domain's configuration.db (if available),
 or lastly from values in lonTabs/dns_domain,tab, or lonTabs/domain.tab.  or lastly from values in lonTabs/dns_domain,tab, or lonTabs/domain.tab.
Line 15831  Returns: Line 16718  Returns:
   
 get_timebased_id():  get_timebased_id():
   
 Attempts to get a unique timestamp-based suffix for use with items added to a  Attempts to get a unique timestamp-based suffix for use with items added to a 
 course via the Course Editor (e.g., folders, composite pages,  course via the Course Editor (e.g., folders, composite pages, 
 group bulletin boards).  group bulletin boards).
   
 Args: (first three required; six others optional)  Args: (first three required; six others optional)
Line 15843  Args: (first three required; six others Line 16730  Args: (first three required; six others
 2. keyid (alphanumeric): name of temporary locking key in hash,  2. keyid (alphanumeric): name of temporary locking key in hash,
    e.g., num, boardids     e.g., num, boardids
   
 3. namespace: name of gdbm file used to store suffixes already assigned;  3. namespace: name of gdbm file used to store suffixes already assigned;  
    file will be named nohist_namespace.db     file will be named nohist_namespace.db
   
 4. cdom: domain of course; default is current course domain from %env  4. cdom: domain of course; default is current course domain from %env
   
 5. cnum: course number; default is current course number from %env  5. cnum: course number; default is current course number from %env
   
 6. idtype: set to concat if an additional digit is to be appended to the  6. idtype: set to concat if an additional digit is to be appended to the 
    unix timestamp to form the suffix, if the plain timestamp is already     unix timestamp to form the suffix, if the plain timestamp is already
    in use.  Default is to not do this, but simply increment the unix     in use.  Default is to not do this, but simply increment the unix 
    timestamp by 1 until a unique key is obtained.     timestamp by 1 until a unique key is obtained.
   
 7. who: holder of locking key; defaults to user:domain for user.  7. who: holder of locking key; defaults to user:domain for user.
   
 8. locktries: number of attempts to obtain a lock (sleep of 1s before  8. locktries: number of attempts to obtain a lock (sleep of 1s before 
    retrying); default is 3.     retrying); default is 3.
   
 9. maxtries: number of attempts to obtain a unique suffix; default is 20.  9. maxtries: number of attempts to obtain a unique suffix; default is 20.  
   
 Returns:  Returns:
   

Removed from v.1.1172.2.118.2.11  
changed lines
  Added in v.1.1429


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