--- loncom/lonnet/perl/lonnet.pm 2023/01/23 18:12:09 1.1172.2.146.2.12 +++ loncom/lonnet/perl/lonnet.pm 2021/01/05 04:23:46 1.1437 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1172.2.146.2.12 2023/01/23 18:12:09 raeburn Exp $ +# $Id: lonnet.pm,v 1.1437 2021/01/05 04:23:46 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -71,11 +71,12 @@ delayed. package Apache::lonnet; use strict; -use LWP::UserAgent(); use HTTP::Date; use Image::Magick; use CGI::Cookie; +use Encode; + use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir $deftex $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease %managerstab $passwdmin); @@ -96,11 +97,11 @@ use Digest::MD5; use Math::Random; use File::MMagic; use Net::CIDR; -use Sys::Hostname::FQDN(); use LONCAPA qw(:DEFAULT :match); use LONCAPA::Configuration; use LONCAPA::lonmetadata; use LONCAPA::Lond; +use LONCAPA::LWPReq; use LONCAPA::transliterate; use File::Copy; @@ -113,6 +114,7 @@ require Exporter; our @ISA = qw (Exporter); our @EXPORT = qw(%env); + # ------------------------------------ Logging (parameters, docs, slots, roles) { my $logid; @@ -128,19 +130,19 @@ our @EXPORT = qw(%env); my $now = time(); my $id=$now.'00000'.$$.'00000'.$logid; my $ip = &get_requestor_ip(); - my $logentry = { - $id => { - 'exe_uname' => $env{'user.name'}, - 'exe_udom' => $env{'user.domain'}, - 'exe_time' => $now, - 'exe_ip' => $ip, - 'delflag' => $delflag, - 'logentry' => $storehash, - 'uname' => $uname, - 'udom' => $udom, - } + my $logentry = { + $id => { + 'exe_uname' => $env{'user.name'}, + 'exe_udom' => $env{'user.domain'}, + 'exe_time' => $now, + 'exe_ip' => $ip, + 'delflag' => $delflag, + 'logentry' => $storehash, + 'uname' => $uname, + 'udom' => $udom, + } }; - return &put('nohist_'.$hash_name,$logentry,$cdom,$cnum); + return &put('nohist_'.$hash_name,$logentry,$cdom,$cnum); } } @@ -185,7 +187,7 @@ sub create_connection { Type => SOCK_STREAM, Timeout => 10); 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>; chomp($result); return 1 if ($result eq 'done'); @@ -230,6 +232,54 @@ sub get_server_distarch { 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 } ¤t_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 { my ($dom,$lonhost,$ignore_cache,$caller) = @_; if (defined($lonhost)) { @@ -264,14 +314,12 @@ sub get_server_loncaparev { $answer = &reply('serverloncaparev',$lonhost); if (($answer eq 'unknown_cmd') || ($answer eq 'con_lost')) { if ($caller eq 'loncron') { - my $ua=new LWP::UserAgent; - $ua->timeout(4); my $hostname = &hostname($lonhost); my $protocol = $protocol{$lonhost}; $protocol = 'http' if ($protocol ne 'https'); my $url = $protocol.'://'.$hostname.'/adm/about.html'; 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()) { my $content = $response->content; if ($content =~ /

VERSION\:\s*([\w.\-]+)<\/p>/) { @@ -362,7 +410,8 @@ sub remote_devalidate_cache { my $items; return unless (ref($cachekeys) eq 'ARRAY'); my $cachestr = join('&',@{$cachekeys}); - return &reply('devalidatecache:'.&escape($cachestr),$lonhost); + my $response = &reply('devalidatecache:'.&escape($cachestr),$lonhost); + return $response; } # -------------------------------------------------- Non-critical communication @@ -394,7 +443,7 @@ sub subreply { } else { &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; if ($client) { @@ -467,7 +516,7 @@ sub reconlonc { &logthis("lonc at pid $loncpid responding, sending USR1"); kill USR1 => $loncpid; sleep 1; - } else { + } else { &logthis( "WARNING:". " lonc at pid $loncpid not responding, giving up"); @@ -662,7 +711,7 @@ sub check_for_valid_session { } } if (!-e "$lonidsdir/$handle.id") { - if ((ref($domref)) && ($name eq 'lonID') && + if ((ref($domref)) && ($name eq 'lonID') && ($handle =~ /^($match_username)\_\d+\_($match_domain)\_(.+)$/)) { my ($possuname,$possudom,$possuhome) = ($1,$2,$3); if ((&domain($possudom) ne '') && (&homeserver($possuname,$possudom) eq $possuhome)) { @@ -691,8 +740,10 @@ sub check_for_valid_session { if (ref($userhashref) eq 'HASH') { $userhashref->{'name'} = $disk_env{'user.name'}; $userhashref->{'domain'} = $disk_env{'user.domain'}; - if ($disk_env{'request.role'}) { - $userhashref->{'role'} = $disk_env{'request.role'}; + $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); @@ -922,7 +973,7 @@ sub userload { # ------------------------------ Find server with least workload from spare.tab sub spareserver { - my ($r,$loadpercent,$userloadpercent,$want_server_name,$udom) = @_; + my ($loadpercent,$userloadpercent,$want_server_name,$udom) = @_; my $spare_server; if ($userloadpercent !~ /\d/) { $userloadpercent=0; } my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent @@ -967,8 +1018,6 @@ sub spareserver { if ($protocol{$spare_server} eq 'https') { $protocol = $protocol{$spare_server}; } - my $alias = &Apache::lonnet::use_proxy_alias($r,$spare_server); - $hostname = $alias if ($alias ne ''); $spare_server = $protocol.'://'.$hostname; } } @@ -1035,6 +1084,19 @@ sub find_existing_session { 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 # and is not overloaded. sub check_for_balancer_cookie { @@ -1150,12 +1212,12 @@ sub choose_server { } } foreach my $lonhost (keys(%servers)) { - my $loginvia; if ($skiploadbal) { if (ref($balancers) eq 'HASH') { next if (exists($balancers->{$lonhost})); } } + my $loginvia; if ($checkloginvia) { $loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost}; if ($loginvia) { @@ -1254,7 +1316,7 @@ sub changepass { sub queryauthenticate { my ($uname,$udom)=@_; my $uhome=&homeserver($uname,$udom); - if ((!$uhome) || ($uhome eq 'no_host')) { + if (!$uhome) { &logthis("User $uname at $udom is unknown when looking for authentication mechanism"); return 'no_host'; } @@ -1303,35 +1365,12 @@ sub authenticate { } if ($answer eq 'non_authorized') { &logthis("User $uname at $udom rejected by $uhome"); - return 'no_host'; + return 'no_host'; } &logthis("User $uname at $udom threw error $answer when checking authentication mechanism"); return 'no_host'; } -sub can_switchserver { - my ($udom,$home) = @_; - my ($canswitch,@intdoms); - my $internet_names = &get_internet_names($home); - if (ref($internet_names) eq 'ARRAY') { - @intdoms = @{$internet_names}; - } - my $uint_dom = &internet_dom(&domain($udom,'primary')); - if ($uint_dom ne '' && grep(/^\Q$uint_dom\E$/,@intdoms)) { - $canswitch = 1; - } else { - my $serverhomeID = &get_server_homeID(&hostname($home)); - my $serverhomedom = &host_domain($serverhomeID); - my %defdomdefaults = &get_domain_defaults($serverhomedom); - my %udomdefaults = &get_domain_defaults($udom); - my $remoterev = &get_server_loncaparev('',$home); - $canswitch = &can_host_session($udom,$home,$remoterev, - $udomdefaults{'remotesessions'}, - $defdomdefaults{'hostedsessions'}); - } - return $canswitch; -} - sub can_host_session { my ($udom,$lonhost,$remoterev,$remotesessions,$hostedsessions) = @_; my $canhost = 1; @@ -1406,15 +1445,6 @@ sub spare_can_host { $canhost = 0; } } - if ($canhost) { - if (ref($defdomdefaults{'offloadoth'}) eq 'HASH') { - if ($defdomdefaults{'offloadoth'}{$try_server}) { - unless (&shared_institution($udom,$try_server)) { - $canhost = 0; - } - } - } - } if (($canhost) && ($uint_dom)) { my @intdoms; my $internet_names = &get_internet_names($try_server); @@ -1513,14 +1543,14 @@ sub get_lonbalancer_config { sub check_loadbalancing { my ($uname,$udom,$caller) = @_; 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 @hosts = ¤t_machine_ids(); my $uprimary_id = &Apache::lonnet::domain($udom,'primary'); my $uintdom = &Apache::lonnet::internet_dom($uprimary_id); my $intdom = &Apache::lonnet::internet_dom($lonhost); my $serverhomedom = &host_domain($lonhost); - my $domneedscache; + my $domneedscache; my $cachetime = 60*60*24; if (($uintdom ne '') && ($uintdom eq $intdom)) { @@ -1540,7 +1570,7 @@ sub check_loadbalancing { } } if (ref($result) eq 'HASH') { - ($is_balancer,$currtargets,$currrules,$setcookie) = + ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers) = &check_balancer_result($result,@hosts); if ($is_balancer) { if (ref($currrules) eq 'HASH') { @@ -1601,7 +1631,7 @@ sub check_loadbalancing { } } if (ref($result) eq 'HASH') { - ($is_balancer,$currtargets,$currrules,$setcookie) = + ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers) = &check_balancer_result($result,@hosts); if ($is_balancer) { if (ref($currrules) eq 'HASH') { @@ -1677,12 +1707,12 @@ sub check_loadbalancing { if (($is_balancer) && (!$homeintdom)) { undef($setcookie); } - return ($is_balancer,$otherserver,$setcookie); + return ($is_balancer,$otherserver,$setcookie,$offloadto,$dom_balancers); } sub check_balancer_result { my ($result,@hosts) = @_; - my ($is_balancer,$currtargets,$currrules,$setcookie); + my ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers); if (ref($result) eq 'HASH') { if ($result->{'lonhost'} ne '') { my $currbalancer = $result->{'lonhost'}; @@ -1691,20 +1721,24 @@ sub check_balancer_result { $currtargets = $result->{'targets'}; $currrules = $result->{'rules'}; } + $dom_balancers = $currbalancer; } else { - foreach my $key (keys(%{$result})) { - if (($key ne '') && (grep(/^\Q$key\E$/,@hosts)) && - (ref($result->{$key}) eq 'HASH')) { - $is_balancer = 1; - $currrules = $result->{$key}{'rules'}; - $currtargets = $result->{$key}{'targets'}; - $setcookie = $result->{$key}{'cookie'}; - last; + if (keys(%{$result})) { + foreach my $key (keys(%{$result})) { + if (($key ne '') && (grep(/^\Q$key\E$/,@hosts)) && + (ref($result->{$key}) eq 'HASH')) { + $is_balancer = 1; + $currrules = $result->{$key}{'rules'}; + $currtargets = $result->{$key}{'targets'}; + $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 { @@ -1776,6 +1810,101 @@ sub internet_dom_servers { 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 my %homecache; @@ -1801,17 +1930,33 @@ sub homeserver { 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 { - my ($udom,@ids)=@_; + my ($udom,$idsref,$namespace)=@_; 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'); foreach my $tryserver (keys(%servers)) { my $idlist=join('&', map { &escape($_); } @ids); - $idlist=~tr/A-Z/a-z/; - my $reply=&reply("idget:$udom:".$idlist,$tryserver); + if ($namespace eq 'ids') { + $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=(); if (($reply ne 'con_lost') && ($reply!~/^error\:/)) { @answer=split(/\&/,$reply); @@ -1820,9 +1965,9 @@ sub idget { for ($i=0;$i<=$#ids;$i++) { if ($answer[$i]) { $returnhash{$ids[$i]}=&unescape($answer[$i]); - } + } } - } + } return %returnhash; } @@ -1837,60 +1982,141 @@ sub idrget { 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 { - my ($udom,%ids)=@_; + my ($udom,$idsref,$uhom,$namespace)=@_; my %servers=(); + my %ids=(); + my %byid = (); + if (ref($idsref) eq 'HASH') { + %ids=%{$idsref}; + } + if ($namespace eq '') { + $namespace = 'ids'; + } foreach my $uname (keys(%ids)) { &cput('environment',{'id'=>$ids{$uname}},$udom,$uname); - my $uhom=&homeserver($uname,$udom); + if ($uhom eq '') { + $uhom=&homeserver($uname,$udom); + } if ($uhom ne 'no_host') { - my $id=&escape($ids{$uname}); - $id=~tr/A-Z/a-z/; my $esc_unam=&escape($uname); - if ($servers{$uhom}) { - $servers{$uhom}.='&'.$id.'='.$esc_unam; + if ($namespace eq 'ids') { + my $id=&escape($ids{$uname}); + $id=~tr/A-Z/a-z/; + my $esc_unam=&escape($uname); + $servers{$uhom}.=$id.'='.$esc_unam.'&'; } 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)) { - &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 { - my ($udom,$idshashref,$uhome)=@_; + my ($udom,$idshashref,$uhome,$namespace)=@_; my %result=(); - unless (ref($idshashref) eq 'HASH') { + my %ids=(); + my %byid = (); + if (ref($idshashref) eq 'HASH') { + %ids=%{$idshashref}; + } else { return %result; } + if ($namespace eq '') { + $namespace = 'ids'; + } my %servers=(); - while (my ($id,$uname) = each(%{$idshashref})) { - my $uhom; - if ($uhome) { - $uhom = $uhome; - } else { - $uhom=&homeserver($uname,$udom); - } - if ($uhom ne 'no_host') { - if ($servers{$uhom}) { + while (my ($id,$unamestr) = each(%ids)) { + if ($namespace eq 'ids') { + my $uhom = $uhome; + if ($uhom eq '') { + $uhom=&homeserver($unamestr,$udom); + } + if ($uhom ne 'no_host') { $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)) { - $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; } +# ----- 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 sub dump_dom { my ($namespace, $udom, $regexp) = @_; @@ -1905,7 +2131,7 @@ sub dump_dom { # ------------------------------------------ get items from domain db files sub get_dom { - my ($namespace,$storearr,$udom,$uhome,$encrypt)=@_; + my ($namespace,$storearr,$udom,$uhome)=@_; return if ($udom eq 'public'); my $items=''; foreach my $item (@$storearr) { @@ -1929,15 +2155,10 @@ sub get_dom { } if ($udom && $uhome && ($uhome ne 'no_host')) { my $rep; - if (grep { $_ eq $uhome } ¤t_machine_ids()) { - # domain information is hosted on this machine - $rep = &LONCAPA::Lond::get_dom("getdom:$udom:$namespace:$items"); + if ($namespace =~ /^enc/) { + $rep=&reply("encrypt:egetdom:$udom:$namespace:$items",$uhome); } else { - if ($encrypt) { - $rep=&reply("encrypt:egetdom:$udom:$namespace:$items",$uhome); - } else { - $rep=&reply("getdom:$udom:$namespace:$items",$uhome); - } + $rep=&reply("getdom:$udom:$namespace:$items",$uhome); } my %returnhash; if ($rep eq '' || $rep =~ /^error: 2 /) { @@ -1961,7 +2182,7 @@ sub get_dom { # -------------------------------------------- put items in domain db files sub put_dom { - my ($namespace,$storehash,$udom,$uhome,$encrypt)=@_; + my ($namespace,$storehash,$udom,$uhome)=@_; if (!$udom) { $udom=$env{'user.domain'}; if (defined(&domain($udom,'primary'))) { @@ -1982,7 +2203,7 @@ sub put_dom { $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&'; } $items=~s/\&$//; - if ($encrypt) { + if ($namespace =~ /^enc/) { return &reply("encrypt:putdom:$udom:$namespace:$items",$uhome); } else { return &reply("putdom:$udom:$namespace:$items",$uhome); @@ -2020,57 +2241,6 @@ sub del_dom { } } -sub store_dom { - my ($storehash,$id,$namespace,$dom,$home,$encrypt) = @_; - $$storehash{'ip'}=&get_requestor_ip(); - $$storehash{'host'}=$perlvar{'lonHostID'}; - my $namevalue=''; - foreach my $key (keys(%{$storehash})) { - $namevalue.=&escape($key).'='.&freeze_escape($$storehash{$key}).'&'; - } - $namevalue=~s/\&$//; - if (grep { $_ eq $home } current_machine_ids()) { - return LONCAPA::Lond::store_dom("storedom:$dom:$namespace:$id:$namevalue"); - } else { - if ($namespace eq 'private') { - return 'refused'; - } elsif ($encrypt) { - return reply("encrypt:storedom:$dom:$namespace:$id:$namevalue",$home); - } else { - return reply("storedom:$dom:$namespace:$id:$namevalue",$home); - } - } -} - -sub restore_dom { - my ($id,$namespace,$dom,$home,$encrypt) = @_; - my $answer; - if (grep { $_ eq $home } current_machine_ids()) { - $answer = LONCAPA::Lond::restore_dom("restoredom:$dom:$namespace:$id"); - } elsif ($namespace ne 'private') { - if ($encrypt) { - $answer=&reply("encrypt:restoredom:$dom:$namespace:$id",$home); - } else { - $answer=&reply("restoredom:$dom:$namespace:$id",$home); - } - } - my %returnhash=(); - unless (($answer eq '') || ($answer eq 'con_lost') || ($answer eq 'refused') || - ($answer eq 'unknown_cmd') || ($answer eq 'rejected')) { - foreach my $line (split(/\&/,$answer)) { - my ($name,$value)=split(/\=/,$line); - $returnhash{&unescape($name)}=&thaw_unescape($value); - } - my $version; - for ($version=1;$version<=$returnhash{'version'};$version++) { - foreach my $item (split(/\:/,$returnhash{$version.':keys'})) { - $returnhash{$item}=$returnhash{$version.':'.$item}; - } - } - } - return %returnhash; -} - # ----------------------------------construct domainconfig user for a domain sub get_domainconfiguser { my ($udom) = @_; @@ -2113,7 +2283,7 @@ sub retrieve_inst_usertypes { sub is_domainimage { my ($url) = @_; - if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo|login)/+[^/]-) { + if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo)/+[^/]-) { if (&domain($1) ne '') { return '1'; } @@ -2131,10 +2301,9 @@ sub inst_directory_query { unless ($homeserver eq $perlvar{'lonHostID'}) { if ($srch->{'srchby'} eq 'email') { 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) || - (($major == 2) && ($minor < 11)) || - (($major == 2) && ($minor == 11) && ($subver < 3))) { + (($major == 2) && ($minor < 12))) { return; } } @@ -2183,10 +2352,9 @@ sub usersearch { unless ($tryserver eq $perlvar{'lonHostID'}) { if ($srch->{'srchby'} eq 'email') { 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) || - (($major == 2) && ($minor < 11)) || - (($major == 2) && ($minor == 11) && ($subver < 3))); + (($major == 2) && ($minor < 12))); } } my $host=&hostname($tryserver); @@ -2278,7 +2446,7 @@ sub get_multiple_instusers { my ($udom,$users,$caller) = @_; my ($outcome,$results); if (ref($users) eq 'HASH') { - my $count = keys(%{$users}); + my $count = keys(%{$users}); my $requested = &freeze_escape($users); my $homeserver = &domain($udom,'primary'); if ($homeserver ne '') { @@ -2322,7 +2490,7 @@ sub get_multiple_instusers { } else { ($outcome,my $userdata) = split(/=/,$response,2); if ($outcome eq 'ok') { - $results = &thaw_unescape($userdata); + $results = &thaw_unescape($userdata); } } } @@ -2349,10 +2517,6 @@ sub inst_rulecheck { $response=&unescape(&reply('instidrulecheck:'.&escape($udom). ':'.&escape($id).':'.$rulestr, $homeserver)); - } elsif ($item eq 'unamemap') { - $response=&unescape(&reply('instunamemapcheck:'. - &escape($udom).':'.&escape($uname). - ':'.$rulestr,$homeserver)); } elsif ($item eq 'selfcreate') { $response=&unescape(&reply('instselfcreatecheck:'. &escape($udom).':'.&escape($uname). @@ -2386,9 +2550,6 @@ sub inst_userrules { } elsif ($check eq 'email') { $response=&reply('instemailrules:'.&escape($udom), $homeserver); - } elsif ($check eq 'unamemap') { - $response=&reply('unamemaprules:'.&escape($udom), - $homeserver); } else { $response=&reply('instuserrules:'.&escape($udom), $homeserver); @@ -2434,9 +2595,9 @@ sub get_domain_defaults { 'requestcourses','inststatus', 'coursedefaults','usersessions', 'requestauthor','selfenrollment', - 'coursecategories','autoenroll', - 'helpsettings','wafproxy','ltisec'],$domain); - my @coursetypes = ('official','unofficial','community','textbook'); + 'coursecategories','ssl','autoenroll', + 'trust','helpsettings','wafproxy'],$domain); + my @coursetypes = ('official','unofficial','community','textbook','placement'); if (ref($domconfig{'defaults'}) eq 'HASH') { $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'}; @@ -2444,12 +2605,9 @@ sub get_domain_defaults { $domdefaults{'timezone_def'} = $domconfig{'defaults'}{'timezone_def'}; $domdefaults{'datelocale_def'} = $domconfig{'defaults'}{'datelocale_def'}; $domdefaults{'portal_def'} = $domconfig{'defaults'}{'portal_def'}; - $domdefaults{'portal_def_email'} = $domconfig{'defaults'}{'portal_def_email'}; - $domdefaults{'portal_def_web'} = $domconfig{'defaults'}{'portal_def_web'}; $domdefaults{'intauth_cost'} = $domconfig{'defaults'}{'intauth_cost'}; $domdefaults{'intauth_switch'} = $domconfig{'defaults'}{'intauth_switch'}; $domdefaults{'intauth_check'} = $domconfig{'defaults'}{'intauth_check'}; - $domdefaults{'unamemap_rule'} = $domconfig{'defaults'}{'unamemap_rule'}; } else { $domdefaults{'lang_def'} = &domain($domain,'lang_def'); $domdefaults{'auth_def'} = &domain($domain,'auth_def'); @@ -2472,7 +2630,7 @@ sub get_domain_defaults { } } 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}; } } @@ -2485,8 +2643,8 @@ sub get_domain_defaults { } } if (ref($domconfig{'coursedefaults'}) eq 'HASH') { + $domdefaults{'canuse_pdfforms'} = $domconfig{'coursedefaults'}{'canuse_pdfforms'}; $domdefaults{'usejsme'} = $domconfig{'coursedefaults'}{'usejsme'}; - $domdefaults{'inline_chem'} = $domconfig{'coursedefaults'}{'inline_chem'}; $domdefaults{'uselcmath'} = $domconfig{'coursedefaults'}{'uselcmath'}; if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') { $domdefaults{'postsubmit'} = $domconfig{'coursedefaults'}{'postsubmit'}{'client'}; @@ -2502,8 +2660,8 @@ sub get_domain_defaults { } if ($domdefaults{'postsubmit'} eq 'on') { if (ref($domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') { - $domdefaults{$type.'postsubtimeout'} = - $domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}{$type}; + $domdefaults{$type.'postsubtimeout'} = + $domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}{$type}; } } } @@ -2519,10 +2677,7 @@ sub get_domain_defaults { } if ($domconfig{'coursedefaults'}{'texengine'}) { $domdefaults{'texengine'} = $domconfig{'coursedefaults'}{'texengine'}; - } - if (exists($domconfig{'coursedefaults'}{'ltiauth'})) { - $domdefaults{'crsltiauth'} = $domconfig{'coursedefaults'}{'ltiauth'}; - } + } } if (ref($domconfig{'usersessions'}) eq 'HASH') { if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') { @@ -2534,9 +2689,6 @@ sub get_domain_defaults { if (ref($domconfig{'usersessions'}{'offloadnow'}) eq 'HASH') { $domdefaults{'offloadnow'} = $domconfig{'usersessions'}{'offloadnow'}; } - if (ref($domconfig{'usersessions'}{'offloadoth'}) eq 'HASH') { - $domdefaults{'offloadoth'} = $domconfig{'usersessions'}{'offloadoth'}; - } } if (ref($domconfig{'selfenrollment'}) eq 'HASH') { if (ref($domconfig{'selfenrollment'}{'admin'}) eq 'HASH') { @@ -2576,9 +2728,27 @@ sub get_domain_defaults { $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') { $domdefaults{'autofailsafe'} = $domconfig{'autoenroll'}{'autofailsafe'}; - $domdefaults{'failsafe'} = $domconfig{'autoenroll'}{'failsafe'}; } if (ref($domconfig{'helpsettings'}) eq 'HASH') { $domdefaults{'submitbugs'} = $domconfig{'helpsettings'}{'submitbugs'}; @@ -2587,24 +2757,12 @@ sub get_domain_defaults { } } if (ref($domconfig{'wafproxy'}) eq 'HASH') { - foreach my $item ('ipheader','trusted','vpnint','vpnext','sslopt') { + foreach my $item ('ipheader','trusted','vpnint','vpnext') { if ($domconfig{'wafproxy'}{$item}) { $domdefaults{'waf_'.$item} = $domconfig{'wafproxy'}{$item}; } } - } - if (ref($domconfig{'ltisec'}) eq 'HASH') { - if (ref($domconfig{'ltisec'}{'encrypt'}) eq 'HASH') { - $domdefaults{'linkprotenc_crs'} = $domconfig{'ltisec'}{'encrypt'}{'crs'}; - $domdefaults{'linkprotenc_dom'} = $domconfig{'ltisec'}{'encrypt'}{'dom'}; - $domdefaults{'ltienc_consumers'} = $domconfig{'ltisec'}{'encrypt'}{'consumers'}; - } - if (ref($domconfig{'ltisec'}{'private'}) eq 'HASH') { - if (ref($domconfig{'ltisec'}{'private'}{'keys'}) eq 'ARRAY') { - $domdefaults{'privhosts'} = $domconfig{'ltisec'}{'private'}{'keys'}; - } - } - } + } &do_cache_new('domdefaults',$domain,\%domdefaults,$cachetime); return %domdefaults; } @@ -2641,7 +2799,6 @@ sub get_dom_instcats { if (&auto_instcode_format($caller,$dom,\%coursecodes,\%codes, \@codetitles,\%cat_titles,\%cat_order) eq 'ok') { $instcats = { - totcodes => $totcodes, codes => \%codes, codetitles => \@codetitles, cat_titles => \%cat_titles, @@ -2669,6 +2826,22 @@ sub retrieve_instcodes { 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 sub get_passwdconf { @@ -2692,44 +2865,6 @@ sub get_passwdconf { return %passwdconf; } -sub course_portal_url { - my ($cnum,$cdom,$r) = @_; - 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 { - my $alias = &Apache::lonnet::use_proxy_alias($r,$chome); - $hostname = $alias if ($alias ne ''); - $firsturl = $protocol.'://'.$hostname; - } - return $firsturl; -} - -sub url_prefix { - my ($r,$dom,$home,$context) = @_; - my $prefix; - my %domdefs = &get_domain_defaults($dom); - if ($domdefs{'portal_def'} && $domdefs{'portal_def_'.$context}) { - if ($domdefs{'portal_def'} =~ m{^(https?://[^/]+)}) { - $prefix = $1; - } - } - if ($prefix eq '') { - my $hostname = &hostname($home); - my $protocol = $protocol{$home}; - $protocol = 'http' if ($protocol{$home} ne 'https'); - my $alias = &use_proxy_alias($r,$home); - $hostname = $alias if ($alias ne ''); - $prefix = $protocol.'://'.$hostname; - } - return $prefix; -} - # --------------------------------------------------- Assign a key to a student sub assign_access_key { @@ -2975,9 +3110,7 @@ sub devalidate_cache_new { sub is_cached_new { my ($name,$id,$debug) = @_; - my $remembered_id=$name.':'.$id; # this is to avoid make_key (which is slow) for - # keys in %remembered hash, which persists for - # duration of request (no restriction on key length). + my $remembered_id=$name.':'.$id; # this is to avoid make_key (which is slow) whenever possible if (exists($remembered{$remembered_id})) { if ($debug) { &Apache::lonnet::logthis("Early return $remembered_id of $remembered{$remembered_id} "); } $accessed{$remembered_id}=[&gettimeofday()]; @@ -3239,9 +3372,13 @@ sub repcopy { mkdir($path,0777); } } - my $ua=new LWP::UserAgent; 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()) { unlink($transname); my $message=$response->status_line; @@ -3251,7 +3388,12 @@ sub repcopy { } else { if ($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()) { unlink($filename.'.meta'); &logthis( @@ -3281,7 +3423,12 @@ sub unsubscribe { } elsif (grep { $_ eq $home } ¤t_machine_ids()) { $answer = 'home'; } 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; } @@ -3314,29 +3461,11 @@ sub ssi_body { # --------------------------------------------------------- Server Side Include sub absolute_url { - my ($host_name,$unalias,$keep_proto) = @_; + my ($host_name) = @_; my $protocol = ($ENV{'SERVER_PORT'} == 443?'https://':'http://'); if ($host_name eq '') { $host_name = $ENV{'SERVER_NAME'}; } - if ($unalias) { - my $alias = &get_proxy_alias(); - if ($alias eq $host_name) { - my $lonhost = $perlvar{'lonHostID'}; - my $hostname = &hostname($lonhost); - my $lcproto; - if (($keep_proto) || ($hostname eq '')) { - $lcproto = $protocol; - } else { - $lcproto = $protocol{$lonhost}; - $lcproto = 'http' if ($lcproto ne 'https'); - $lcproto .= '://'; - } - unless ($hostname eq '') { - return $lcproto.$hostname; - } - } - } return $protocol.$host_name; } @@ -3353,48 +3482,36 @@ sub absolute_url { sub ssi { my ($fn,%form)=@_; - my ($host,$request,$response); - $host = &absolute_url('',1); + my $request; $form{'no_update_last_known'}=1; &Apache::lonenc::check_encrypt(\$fn); if (%form) { - $request=new HTTP::Request('POST',$host.$fn); - $request->content(join('&',map { + $request=new HTTP::Request('POST',&absolute_url().$fn); + $request->content(join('&',map { my $name = escape($_); - "$name=" . ( ref($form{$_}) eq 'ARRAY' - ? join("&$name=", map {escape($_) } @{$form{$_}}) - : &escape($form{$_}) ); + "$name=" . ( ref($form{$_}) eq 'ARRAY' + ? join("&$name=", map {escape($_) } @{$form{$_}}) + : &escape($form{$_}) ); } keys(%form))); } else { - $request=new HTTP::Request('GET',$host.$fn); + $request=new HTTP::Request('GET',&absolute_url().$fn); } $request->header(Cookie => $ENV{'HTTP_COOKIE'}); - + my $lonhost = $perlvar{'lonHostID'}; + my $islocal; if (($env{'request.course.id'}) && ($form{'grade_courseid'} eq $env{'request.course.id'}) && ($form{'grade_username'} ne '') && ($form{'grade_domain'} ne '') && ($form{'grade_symb'} ne '') && (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}. ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) { - if (LWP::UserAgent->VERSION >= 5.834) { - 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); + $islocal = 1; } + my $response= &LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar, + '','','',$islocal); + if (wantarray) { return ($response->content, $response); } else { @@ -3404,9 +3521,8 @@ sub ssi { sub externalssi { my ($url)=@_; - my $ua=new LWP::UserAgent; my $request=new HTTP::Request('GET',$url); - my $response=$ua->request($request); + my $response = &LONCAPA::LWPReq::makerequest('',$request,'',\%perlvar); if (wantarray) { return ($response->content, $response); } else { @@ -3414,8 +3530,9 @@ 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 # metadata file). @@ -3435,11 +3552,9 @@ sub remove_stale_resfile { if ($hostname) { my $protocol = $protocol{$homeserver}; $protocol = 'http' if ($protocol ne 'https'); - my $uri = $protocol.'://'.$hostname.'/raw/'.&declutter($url); - my $ua=new LWP::UserAgent; - $ua->timeout(5); - my $request=new HTTP::Request('HEAD',$uri); - my $response=$ua->request($request); + my $uri = &declutter($url); + my $request=new HTTP::Request('HEAD',$protocol.'://'.$hostname.'/raw/'.$uri); + my $response = &LONCAPA::LWPReq::makerequest($homeserver,$request,'',\%perlvar,5,0,1); if ($response->is_success()) { my $remmodtime = &HTTP::Date::str2time( $response->header('Last-modified') ); my $locmodtime = (stat($fname))[9]; @@ -3502,7 +3617,7 @@ sub allowuploaded { # # Determine if the current user should be able to edit a particular resource, # 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. # (b) When displaying folder contents in course editor, used to determine if # "Edit" link will be displayed alongside resource. @@ -3510,12 +3625,12 @@ sub allowuploaded { # input: six args -- filename (decluttered), course number, course domain, # url, symb (if registered) and group (if this is a group # 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 # $home -- homeserver of resource (i.e., for author if published, # or course if uploaded.). # $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 # @@ -3604,7 +3719,7 @@ sub can_edit_resource { $forceedit = 1; } $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$}) { $incourse = 1; if ($env{'form.forceedit'}) { @@ -3655,7 +3770,7 @@ sub can_edit_resource { } } elsif ($resurl eq '/res/lib/templates/simpleproblem.problem/smpedit') { my $template = '/res/lib/templates/simpleproblem.problem'; - if (&is_on_map($template)) { + if (&is_on_map($template)) { $incourse = 1; $forceview = 1; $cfile = $template; @@ -3715,7 +3830,7 @@ sub can_edit_resource { $cfile=$file; } } - if (($cfile ne '') && (!$incourse || $uploaded) && + if (($cfile ne '') && (!$incourse || $uploaded) && (($home ne '') && ($home ne 'no_host'))) { my @ids=¤t_machine_ids(); unless (grep(/^\Q$home\E$/,@ids)) { @@ -3742,9 +3857,9 @@ sub in_course { if ($hideprivileged) { my $skipuser; my %coursehash = &coursedescription($cdom.'_'.$cnum); - my @possdoms = ($cdom); - if ($coursehash{'checkforpriv'}) { - push(@possdoms,split(/,/,$coursehash{'checkforpriv'})); + my @possdoms = ($cdom); + if ($coursehash{'checkforpriv'}) { + push(@possdoms,split(/,/,$coursehash{'checkforpriv'})); } if (&privileged($uname,$udom,\@possdoms)) { $skipuser = 1; @@ -3938,10 +4053,6 @@ sub clean_filename { # Replace all .\d. sequences with _\d. so they no longer look like version # numbers $fname=~s/\.(\d+)(?=\.)/_$1/g; -# Replace three or more adjacent underscores with one for consistency -# with loncfile::filename_check() so complete url can be extracted by -# lonnet::decode_symb() - $fname=~s/_{3,}/_/g; return $fname; } @@ -3987,7 +4098,7 @@ sub resizeImage { # input: $formname - the contents of the file are in $env{"form.$formname"} # the desired filename is in $env{"form.$formname.filename"} # $context - possible values: coursedoc, existingfile, overwrite, -# canceloverwrite, scantron or ''. +# canceloverwrite, scantron or ''. # if 'coursedoc': upload to the current course # if 'existingfile': write file to tmp/overwrites directory # if 'canceloverwrite': delete file written to tmp/overwrites directory @@ -3995,7 +4106,7 @@ sub resizeImage { # $subdir - directory in userfile to store the file into # $parser - instruction to parse file for objects ($parser = parse) or # 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 }). # $allfiles - reference to hash for embedded objects # $codebase - reference to hash for codebase of java objects @@ -4045,7 +4156,7 @@ sub userfileupload { } else { $docudom = $env{'user.domain'}; } - if ($destuname =~ /^$match_username$/) { + if ($destuname =~ /^$match_username$/) { $docuname = $destuname; } else { $docuname = $env{'user.name'}; @@ -4271,7 +4382,7 @@ sub extract_embedded_items { } if (lc($tagname) eq 'a') { unless (($attr->{'href'} =~ /^#/) || ($attr->{'href'} eq '')) { - &add_filetype($allfiles,$attr->{'href'},'href'); + &add_filetype($allfiles,$attr->{'href'},'href'); } } if (lc($tagname) eq 'script') { @@ -4452,7 +4563,7 @@ sub bubblesheet_converter { ($fullpath =~ m{^\Q$perlvar{'lonDocRoot'}/userfiles/$cdom/\E$match_courseid/scantron_orig}) && (-e $fullpath) && (ref($config) eq 'HASH') && ($format ne '')) { my (%csvcols,%csvoptions); - if (ref($config->{'fields'}) eq 'HASH') { + if (ref($config->{'fields'}) eq 'HASH') { %csvcols = %{$config->{'fields'}}; } if (ref($config->{'options'}) eq 'HASH') { @@ -4499,7 +4610,7 @@ sub bubblesheet_converter { next if (($num == 1) && ($csvoptions{'hdr'} == 1)); $line =~ s{[\r\n]+$}{}; my %found; - my @values = split(/,/,$line,-1); + my @values = split(/,/,$line); my ($qstart,$record); for (my $i=0; $i<@values; $i++) { if ((($qstart ne '') && ($i > $qstart)) || @@ -4682,7 +4793,6 @@ sub get_scantronformat_file { close($fh); } } - chomp(@lines); } return @lines; } @@ -4804,29 +4914,6 @@ sub flushcourselogs { if (! defined($dom) || $dom eq '' || ! defined($name) || $name eq '') { my $cid = $env{'request.course.id'}; -# -# FIXME 11/29/2021 -# Typo in rev. 1.458 (2003/12/09)?? -# These should likely by $env{'course.'.$cid.'.domain'} and $env{'course.'.$cid.'.num'} -# -# While these ramain as $env{'request.'.$cid.'.domain'} and $env{'request.'.$cid.'.num'} -# $dom and $name will always be null, so the &inc() call will default to storing this data -# in a nohist_accesscount.db file for the user rather than the course. -# -# That said there is a lot of noise in the data being stored. -# So counts for prtspool/ and adm/ etc. are recorded. -# -# A review of which items ending '___count' are written to %accesshash should likely be -# made before deciding whether to set these to 'course.' instead of 'request.' -# -# Under the current scheme each user receives a nohist_accesscount.db file listing -# accesses for things which are not published resources, regardless of course, and -# there is not a nohist_accesscount.db file in a course, which might log accesses from -# anyone in the course for things which are not published resources. -# -# For an author, nohist_accesscount.db ends up having records for other items -# mixed up with the legitimate access counts for the author's published resources. -# $dom = $env{'request.'.$cid.'.domain'}; $name = $env{'request.'.$cid.'.num'}; } @@ -4875,19 +4962,19 @@ sub flushcourselogs { delete $domainrolehash{$entry}; } foreach my $dom (keys(%domrolebuffer)) { - my %servers; - if (defined(&domain($dom,'primary'))) { - my $primary=&domain($dom,'primary'); - my $hostname=&hostname($primary); - $servers{$primary} = $hostname; - } else { - %servers = &get_servers($dom,'library'); - } + my %servers; + if (defined(&domain($dom,'primary'))) { + my $primary=&domain($dom,'primary'); + my $hostname=&hostname($primary); + $servers{$primary} = $hostname; + } else { + %servers = &get_servers($dom,'library'); + } foreach my $tryserver (keys(%servers)) { if (&reply('domroleput:'.$dom.':'. - $domrolebuffer{$dom},$tryserver) eq 'ok') { - last; - } else { + $domrolebuffer{$dom},$tryserver) eq 'ok') { + last; + } else { &logthis('Put of domain roles failed for '.$dom.' and '.$tryserver); } } @@ -5219,7 +5306,7 @@ sub get_my_roles { } else { my $possdoms = [$domain]; if (ref($roledoms) eq 'ARRAY') { - push(@{$possdoms},@{$roledoms}); + push(@{$possdoms},@{$roledoms}); } if (&privileged($username,$domain,$possdoms,\@privroles)) { if (!$nothide{$username.':'.$domain}) { @@ -5516,16 +5603,16 @@ sub courseiddump { if (($domfilter eq '') || (&host_domain($tryserver) eq $domfilter)) { my $rep; - if (grep { $_ eq $tryserver } ¤t_machine_ids()) { - $rep = &LONCAPA::Lond::dump_course_id_handler( - join(":", (&host_domain($tryserver), $sincefilter, - &escape($descfilter), &escape($instcodefilter), + if (grep { $_ eq $tryserver } current_machine_ids()) { + $rep = LONCAPA::Lond::dump_course_id_handler( + join(":", (&host_domain($tryserver), $sincefilter, + &escape($descfilter), &escape($instcodefilter), &escape($ownerfilter), &escape($coursefilter), - &escape($typefilter), &escape($regexp_ok), - $as_hash, &escape($selfenrollonly), - &escape($catfilter), $showhidden, $caller, - &escape($cloner), &escape($cc_clone), $cloneonly, - &escape($createdbefore), &escape($createdafter), + &escape($typefilter), &escape($regexp_ok), + $as_hash, &escape($selfenrollonly), + &escape($catfilter), $showhidden, $caller, + &escape($cloner), &escape($cc_clone), $cloneonly, + &escape($createdbefore), &escape($createdafter), &escape($creationcontext),$domcloner,$hasuniquecode, $reqcrsdom,&escape($reqinstcode)))); } else { @@ -5541,7 +5628,7 @@ sub courseiddump { &escape($creationcontext).':'.$domcloner.':'.$hasuniquecode. ':'.$reqcrsdom.':'.&escape($reqinstcode),$tryserver); } - + my @pairs=split(/\&/,$rep); foreach my $item (@pairs) { my ($key,$value)=split(/\=/,$item,2); @@ -5768,93 +5855,6 @@ sub set_first_access { } } -sub checkout { - my ($symb,$tuname,$tudom,$tcrsid)=@_; - my $now=time; - my $lonhost=$perlvar{'lonHostID'}; - my $ip = &get_requestor_ip(); - my $infostr=&escape( - 'CHECKOUTTOKEN&'. - $tuname.'&'. - $tudom.'&'. - $tcrsid.'&'. - $symb.'&'. - $now.'&'.$ip); - my $token=&reply('tmpput:'.$infostr,$lonhost); - if ($token=~/^error\:/) { - &logthis("WARNING: ". - "Checkout tmpput failed ".$tudom.' - '.$tuname.' - '.$symb. - ""); - 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' => $ip); - - unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') { - return ''; - } else { - &logthis("WARNING: ". - "Checkout cstore failed ".$tudom.' - '.$tuname.' - '.$symb. - ""); - } - - if (&log($tudom,$tuname,&homeserver($tuname,$tudom), - &escape('Checkout '.$infostr.' - '. - $token)) ne 'ok') { - return ''; - } else { - &logthis("WARNING: ". - "Checkout log failed ".$tudom.' - '.$tuname.' - '.$symb. - ""); - } - 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 $ip = &get_requestor_ip(); - 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' => $ip); - - 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 sub expirespread { @@ -6448,7 +6448,7 @@ sub privileged { my $now = time; my $roles; if (ref($possroles) eq 'ARRAY') { - $roles = $possroles; + $roles = $possroles; } else { $roles = ['dc','su']; } @@ -6475,7 +6475,7 @@ sub privileged { for my $role (@rolesdump{grep { ! /^rolesdef_/ } keys(%rolesdump)}) { my ($trole, $tend, $tstart) = split(/_/, $role); if (grep(/^\Q$trole\E$/,@{$roles})) { - return 1 unless ($tend && $tend < $now) + return 1 unless ($tend && $tend < $now) or ($tstart && $tstart > $now); } } @@ -6513,7 +6513,7 @@ sub privileged_by_domain { my ($trole,$uname,$udom,$rest) = split(/:/,$item,4); my ($end,$start) = split(/:/,$dompersonnel{$server}{$item}); next if ($end && $end < $now); - $privileged{$dom}{$trole}{$uname.':'.$udom} = + $privileged{$dom}{$trole}{$uname.':'.$udom} = $dompersonnel{$server}{$item}; } } @@ -6700,31 +6700,31 @@ sub course_adhocrole_privs { $full{$priv} = $restrict; } foreach my $item (split(/,/,$overrides{"internal.adhocpriv.$rolename"})) { - next if ($item eq ''); - my ($rule,$rest) = split(/=/,$item); - next unless (($rule eq 'off') || ($rule eq 'on')); - foreach my $priv (split(/:/,$rest)) { - if ($priv ne '') { - if ($rule eq 'off') { - $possremove{$priv} = 1; - } else { - $possadd{$priv} = 1; - } - } - } - } - foreach my $priv (sort(keys(%full))) { - if (exists($currprivs{$priv})) { - unless (exists($possremove{$priv})) { - $storeprivs{$priv} = $currprivs{$priv}; - } - } elsif (exists($possadd{$priv})) { - $storeprivs{$priv} = $full{$priv}; - } - } - $coursepriv = ':'.join(':',map { $_.'&'.$storeprivs{$_}; } sort(keys(%storeprivs))); - } - return $coursepriv; + next if ($item eq ''); + my ($rule,$rest) = split(/=/,$item); + next unless (($rule eq 'off') || ($rule eq 'on')); + foreach my $priv (split(/:/,$rest)) { + if ($priv ne '') { + if ($rule eq 'off') { + $possremove{$priv} = 1; + } else { + $possadd{$priv} = 1; + } + } + } + } + foreach my $priv (sort(keys(%full))) { + if (exists($currprivs{$priv})) { + unless (exists($possremove{$priv})) { + $storeprivs{$priv} = $currprivs{$priv}; + } + } elsif (exists($possadd{$priv})) { + $storeprivs{$priv} = $full{$priv}; + } + } + $coursepriv = ':'.join(':',map { $_.'&'.$storeprivs{$_}; } sort(keys(%storeprivs))); + } + return $coursepriv; } sub group_roleprivs { @@ -6946,7 +6946,7 @@ sub check_adhoc_privs { my $cckey = 'user.role.'.$checkrole.'./'.$cdom.'/'.$cnum; if ($sec) { $cckey .= '/'.$sec; - } + } my $setprivs; if ($env{$cckey}) { my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend); @@ -6992,7 +6992,7 @@ sub set_adhoc_privileges { ($caller eq 'tiny')) { &appenv( {'request.role' => $spec, 'request.role.domain' => $dcdom, - 'request.course.sec' => $sec, + 'request.course.sec' => $sec, } ); my $tadv=0; @@ -7052,19 +7052,20 @@ sub unserialize { return {} if $rep =~ /^error/; my %returnhash=(); - foreach my $item (split(/\&/,$rep)) { - my ($key, $value) = split(/=/, $item, 2); - $key = unescape($key) unless $escapedkeys; - next if $key =~ /^error: 2 /; - $returnhash{$key} = &thaw_unescape($value); - } + foreach my $item (split(/\&/,$rep)) { + my ($key, $value) = split(/=/, $item, 2); + $key = unescape($key) unless $escapedkeys; + next if $key =~ /^error: 2 /; + $returnhash{$key} = &thaw_unescape($value); + } + #return %returnhash; return \%returnhash; -} +} # see Lond::dump_with_regexp # if $escapedkeys hash keys won't get unescaped. sub dump { - my ($namespace,$udomain,$uname,$regexp,$range,$escapedkeys,$encrypt)=@_; + my ($namespace,$udomain,$uname,$regexp,$range,$escapedkeys)=@_; if (!$udomain) { $udomain=$env{'user.domain'}; } if (!$uname) { $uname=$env{'user.name'}; } my $uhome=&homeserver($uname,$udomain); @@ -7074,24 +7075,20 @@ sub dump { } else { $regexp='.'; } - if (grep { $_ eq $uhome } ¤t_machine_ids()) { + if (grep { $_ eq $uhome } current_machine_ids()) { # 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'}); - return %{&unserialize($reply, $escapedkeys)}; - } - my $rep; - if ($encrypt) { - $rep=&reply("encrypt:edump:$udomain:$uname:$namespace:$regexp:$range",$uhome); - } else { - $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome); + return %{unserialize($reply, $escapedkeys)}; } + my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome); my @pairs=split(/\&/,$rep); my %returnhash=(); if (!($rep =~ /^error/ )) { foreach my $item (@pairs) { my ($key,$value)=split(/=/,$item,2); - $key = &unescape($key) unless ($escapedkeys); + $key = unescape($key) unless $escapedkeys; + #$key = &unescape($key); next if ($key =~ /^error: 2 /); $returnhash{$key}=&thaw_unescape($value); } @@ -7135,7 +7132,7 @@ sub currentdump { my $rep; 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))); } else { $rep = reply('currentdump:'.$sdom.':'.$sname.':'.$courseid,$uhome); @@ -7145,7 +7142,7 @@ sub currentdump { # my %returnhash=(); # - if ($rep eq "unknown_cmd") { + if ($rep eq 'unknown_cmd') { # an old lond will not know currentdump # Do a dump and make it look like a currentdump my @tmp = &dumpstore($courseid,$sdom,$sname,'.'); @@ -7231,7 +7228,7 @@ sub inc { # --------------------------------------------------------------- put interface sub put { - my ($namespace,$storehash,$udomain,$uname,$encrypt)=@_; + my ($namespace,$storehash,$udomain,$uname)=@_; if (!$udomain) { $udomain=$env{'user.domain'}; } if (!$uname) { $uname=$env{'user.name'}; } my $uhome=&homeserver($uname,$udomain); @@ -7240,11 +7237,7 @@ sub put { $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&'; } $items=~s/\&$//; - if ($encrypt) { - return &reply("encrypt:put:$udomain:$uname:$namespace:$items",$uhome); - } else { - return &reply("put:$udomain:$uname:$namespace:$items",$uhome); - } + return &reply("put:$udomain:$uname:$namespace:$items",$uhome); } # ------------------------------------------------------------ newput interface @@ -7401,13 +7394,13 @@ sub tmpdel { return &reply("tmpdel:$token",$server); } -# ------------------------------------------------------------ get_timebased_id +# ------------------------------------------------------------ get_timebased_id sub get_timebased_id { my ($prefix,$keyid,$namespace,$cdom,$cnum,$idtype,$who,$locktries, $maxtries) = @_; 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'); } @@ -7421,7 +7414,7 @@ sub get_timebased_id { if (!$maxtries) { $maxtries = 10; } - + if (($cdom eq '') || ($cnum eq '')) { if ($env{'request.course.id'}) { $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; @@ -7509,15 +7502,15 @@ sub portfolio_access { if ($result) { my %setters; if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') { - my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = - &Apache::loncommon::blockcheck(\%setters,'port',$clientip,$unum,$udom); - if (($startblock && $endblock) || ($by_ip)) { + my ($startblock,$endblock) = + &Apache::loncommon::blockcheck(\%setters,'port',$unum,$udom); + if ($startblock && $endblock) { return 'B'; } } else { - my ($startblock,$endblock,$triggerblock,$by_ip,$blockdo) = - &Apache::loncommon::blockcheck(\%setters,'port',$clientip); - if (($startblock && $endblock) || ($by_ip)) { + my ($startblock,$endblock) = + &Apache::loncommon::blockcheck(\%setters,'port'); + if ($startblock && $endblock) { return 'B'; } } @@ -7576,7 +7569,7 @@ sub get_portfolio_access { if (ref($access_hash->{$ipkey}{'ip'}) eq 'ARRAY') { if (&Apache::loncommon::check_ip_acc(join(',',@{$access_hash->{$ipkey}{'ip'}}),$clientip)) { $allowed = 1; - last; + last; } } } @@ -7770,6 +7763,8 @@ sub usertools_access { unofficial => 1, community => 1, textbook => 1, + placement => 1, + lti => 1, ); } elsif ($context eq 'requestauthor') { %tools = ( @@ -7781,7 +7776,6 @@ sub usertools_access { blog => 1, webdav => 1, portfolio => 1, - timezone => 1, ); } return if (!defined($tools{$tool})); @@ -7805,7 +7799,7 @@ sub usertools_access { my ($toolstatus,$inststatus,$envkey); if ($context eq 'requestauthor') { - $envkey = $context; + $envkey = $context; } else { $envkey = $context.'.'.$tool; } @@ -7967,25 +7961,29 @@ sub is_advanced_user { } sub check_can_request { - my ($dom,$can_request,$request_domains) = @_; + my ($dom,$can_request,$request_domains,$uname,$udom) = @_; 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 @options = ('approval','validate','autolimit'); my $optregex = join('|',@options); if ((ref($can_request) eq 'HASH') && (ref($types) eq 'ARRAY')) { foreach my $type (@{$types}) { - if (&usertools_access($env{'user.name'}, - $env{'user.domain'}, - $type,undef,'requestcourses')) { + if (&usertools_access($uname,$udom,$type,undef, + 'requestcourses')) { $canreq ++; 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; } } - 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}); if (@curr > 0) { foreach my $item (@curr) { @@ -8002,7 +8000,7 @@ sub check_can_request { } } } - unless($dom eq $env{'user.domain'}) { + unless ($dom eq $env{'user.domain'}) { $canreq ++; if (grep(/^\Q$dom\E:($optregex)(=?\d*)$/,@curr)) { $can_request->{$type} = 1; @@ -8067,14 +8065,14 @@ sub customaccess { # ------------------------------------------------- Check for a user privilege sub allowed { - my ($priv,$uri,$symb,$role,$clientip,$noblockcheck,$ignorecache,$nodeeplinkcheck,$nodeeplinkout)=@_; + my ($priv,$uri,$symb,$role,$clientip,$noblockcheck,$ignorecache)=@_; my $ver_orguri=$uri; $uri=&deversion($uri); my $orguri=$uri; $uri=&declutter($uri); if ($priv eq 'evb') { -# Evade communication block restrictions for specified role in a course or domain +# Evade communication block restrictions for specified role in a course if ($env{'user.priv.'.$role} =~/evb\&([^\:]*)/) { return $1; } else { @@ -8095,9 +8093,9 @@ sub allowed { if (($space=~/^(uploaded|editupload)$/) && ($env{'user.name'} eq $name) && ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir[0])) { my %setters; - my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = - &Apache::loncommon::blockcheck(\%setters,'port',$clientip); - if (($startblock && $endblock) || ($by_ip)) { + my ($startblock,$endblock) = + &Apache::loncommon::blockcheck(\%setters,'port'); + if ($startblock && $endblock) { return 'B'; } else { return 'F'; @@ -8193,8 +8191,8 @@ sub allowed { my $adom = $1; foreach my $key (keys(%env)) { if ($key =~ m{^user\.role\.(ca|aa)/\Q$adom\E}) { - my ($start,$end) = split(/\./,$env{$key}); - if (($now >= $start) && (!$end || $end > $now)) { + my ($start,$end) = split('.',$env{$key}); + if (($now >= $start) && (!$end || $end < $now)) { $ownaccess = 1; last; } @@ -8206,8 +8204,8 @@ sub allowed { foreach my $role ('ca','aa') { if ($env{"user.role.$role./$adom/$aname"}) { my ($start,$end) = - split(/\./,$env{"user.role.$role./$adom/$aname"}); - if (($now >= $start) && (!$end || $end > $now)) { + split('.',$env{"user.role.$role./$adom/$aname"}); + if (($now >= $start) && (!$end || $end < $now)) { $ownaccess = 1; last; } @@ -8292,10 +8290,7 @@ sub allowed { if ($env{'user.priv.'.$env{'request.role'}.'./'} =~/\Q$priv\E\&([^\:]*)/) { my $value = $1; - my $deeplinkblock; - unless ($nodeeplinkcheck) { - $deeplinkblock = &deeplink_check($priv,$symb,$uri); - } + my $deeplinkblock = &deeplink_check($priv,$symb,$uri); if ($deeplinkblock) { $thisallowed='D'; } elsif ($noblockcheck) { @@ -8318,10 +8313,7 @@ sub allowed { $refuri=&declutter($refuri); my ($match) = &is_on_map($refuri); if ($match) { - my $deeplinkblock; - unless ($nodeeplinkcheck) { - $deeplinkblock = &deeplink_check($priv,$symb,$refuri); - } + my $deeplinkblock = &deeplink_check($priv,$symb,$refuri); if ($deeplinkblock) { $thisallowed='D'; } elsif ($noblockcheck) { @@ -8346,7 +8338,7 @@ sub allowed { && &is_portfolio_url($uri)) { $thisallowed = &portfolio_access($uri,$clientip); } - + # Full access at system, domain or course-wide level? Exit. if ($thisallowed=~/F/) { return 'F'; @@ -8397,13 +8389,7 @@ sub allowed { =~/\Q$priv\E\&([^\:]*)/) { my $value = $1; if ($priv eq 'bre') { - my $deeplinkblock; - unless ($nodeeplinkcheck) { - $deeplinkblock = &deeplink_check($priv,$symb,$uri); - } - if ($deeplinkblock) { - $thisallowed = 'D'; - } elsif ($noblockcheck) { + if ($noblockcheck) { $thisallowed.=$value; } else { my @blockers = &has_comm_blocking($priv,$symb,$uri,$ignorecache); @@ -8445,10 +8431,7 @@ sub allowed { =~/\Q$priv\E\&([^\:]*)/) { my $value = $1; if ($priv eq 'bre') { - my $deeplinkblock; - unless ($nodeeplinkcheck) { - $deeplinkblock = &deeplink_check($priv,$symb,$refuri); - } + my $deeplinkblock = &deeplink_check($priv,$symb,$refuri); if ($deeplinkblock) { $thisallowed = 'D'; } elsif ($noblockcheck) { @@ -8496,48 +8479,16 @@ sub allowed { # # Possibly locked functionality, check all courses -# In roles.tab, L (unless locked) available for bre, pch, plc, pac and sma. # Locks might take effect only after 10 minutes cache expiration for other -# courses, and 2 minutes for current course, in which user has st or ta role -# which is neither expired nor a future role (unless current course). +# courses, and 2 minutes for current course - my ($needlockcheck,$now,$crsonly); + my $envkey; if ($thisallowed=~/L/) { - $now = time; - if ($priv eq 'bre') { - if ($uri ne '') { - if ($orguri =~ m{^/+res/}) { - if ($uri =~ m{^lib/templates/}) { - if ($env{'request.course.id'}) { - $crsonly = 1; - $needlockcheck = 1; - } - } else { - $needlockcheck = 1; - } - } elsif ($env{'request.course.id'}) { - my ($crsdom,$crsnum) = split('_',$env{'request.course.id'}); - if (($uri =~ m{^(adm|uploaded|public)/$crsdom/$crsnum/}) || - ($uri =~ m{^adm/$match_domain/$match_username/\d+/(smppg|bulletinboard)$})) { - $crsonly = 1; - } - $needlockcheck = 1; - } - } - } elsif (($priv eq 'pch') || ($priv eq 'plc') || ($priv eq 'pac') || ($priv eq 'sma')) { - $needlockcheck = 1; - } - } - if ($needlockcheck) { - foreach my $envkey (keys(%env)) { + foreach $envkey (keys(%env)) { if ($envkey=~/^user\.role\.(st|ta)\.([^\.]*)/) { my $courseid=$2; my $roleid=$1.'.'.$2; $courseid=~s/^\///; - unless ($env{'request.role'} eq $roleid) { - my ($start,$end) = split(/\./,$env{$envkey}); - next unless (($now >= $start) && (!$end || $end > $now)); - } my $expiretime=600; if ($env{'request.role'} eq $roleid) { $expiretime=120; @@ -8560,7 +8511,7 @@ sub allowed { } if (($env{$prefix.'priv.'.$priv.'.lock.sections'}=~/\,\Q$csec\E\,/) || ($env{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) { - if ($env{$prefix.'priv.'.$priv.'.lock.expire'}>time) { + if ($env{'priv.'.$priv.'.lock.expire'}>time) { &log($env{'user.domain'},$env{'user.name'}, $env{'user.home'}, 'Locked by priv: '.$priv.' for '.$uri.' due to '. @@ -8599,7 +8550,7 @@ sub allowed { my $unamedom=$env{'user.name'}.':'.$env{'user.domain'}; if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.roles.denied'} =~/\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'}.':'. 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '. $env{'request.course.id'}); @@ -8609,7 +8560,7 @@ sub allowed { if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'} =~/\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'}. 'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '. $env{'request.course.id'}); @@ -8631,17 +8582,6 @@ sub allowed { } } -# Restricted for deeplinked session? - - if ($env{'request.deeplink.login'}) { - if ($env{'acc.deeplinkout'} && !$nodeeplinkout) { - if (!$symb) { $symb=&symbread($uri,1); } - if (($symb) && ($env{'acc.deeplinkout'}=~/\&\Q$symb\E\&/)) { - return ''; - } - } - } - # Restricted by state or randomout? if ($thisallowed=~/X/) { @@ -8680,7 +8620,7 @@ sub constructaccess { my ($ownername,$ownerdomain,$ownerhome); ($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 unless (($ownername) && ($ownerdomain)) { return ''; } @@ -8701,6 +8641,15 @@ sub constructaccess { $ownerhome = &homeserver($ownername,$ownerdomain); 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, @@ -8745,14 +8694,14 @@ sub constructaccess { my $cacheduser=''; # Course for which data are being temporarily cached. my $cachedcid=''; -# Cached blockers for this user (a hash of blocking items). +# Cached blockers for this user (a hash of blocking items). my %cachedblockers=(); # When the data were last cached. my $cachedlast=''; sub load_all_blockers { my ($uname,$udom)=@_; - if (($uname ne '') && ($udom ne '')) { + if (($uname ne '') && ($udom ne '')) { if (($cacheduser eq $uname.':'.$udom) && ($cachedcid eq $env{'request.course.id'}) && (abs($cachedlast-time)<5)) { @@ -8790,18 +8739,14 @@ sub get_commblock_resources { my ($blocks) = @_; my %blockers = (); return %blockers unless ($env{'request.course.id'}); - my $courseurl = &courseid_to_courseurl($env{'request.course.id'}); - if ($env{'request.course.sec'}) { - $courseurl .= '/'.$env{'request.course.sec'}; - } - return %blockers if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseurl} =~/evb\&([^\:]*)/); + return %blockers if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/); my %commblocks; if (ref($blocks) eq 'HASH') { %commblocks = %{$blocks}; } else { %commblocks = &get_comm_blocks(); } - return %blockers unless (keys(%commblocks) > 0); + return %blockers unless (keys(%commblocks) > 0); my $navmap = Apache::lonnavmaps::navmap->new(); return %blockers unless (ref($navmap)); my $now = time; @@ -8813,7 +8758,7 @@ sub get_commblock_resources { if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') { if (ref($commblocks{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') { 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') { @@ -8826,9 +8771,10 @@ sub get_commblock_resources { } } elsif ($block =~ /^firstaccess____(.+)$/) { my $item = $1; + my @to_test; if (ref($commblocks{$block}{'blocks'}) eq 'HASH') { if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') { - my (@interval,$mapname); + my @interval; my $type = 'map'; if ($item eq 'course') { $type = 'course'; @@ -8837,16 +8783,41 @@ sub get_commblock_resources { if ($item =~ /___\d+___/) { $type = 'resource'; @interval=&EXT("resource.0.interval",$item); + if (ref($navmap)) { + my $res = $navmap->getBySymb($item); + push(@to_test,$res); + } } else { - $mapname = &deversion($item); - if (ref($navmap)) { - my $timelimit = $navmap->get_mapparam(undef,$mapname,'0.interval'); - @interval = ($timelimit,'map'); + my $mapsymb = &symbread($item,1); + if ($mapsymb) { + if (ref($navmap)) { + my $mapres = $navmap->getBySymb($mapsymb); + if (ref($mapres)) { + my $first = $mapres->map_start(); + my $finish = $mapres->map_finish(); + my $it = $navmap->getIterator($first,$finish,undef,0,0); + if (ref($it)) { + my $res; + while ($res = $it->next(undef,1)) { + next unless (ref($res)); + my $symb = $res->symb(); + next if (($symb eq $mapsymb) || ($symb eq '')); + @interval=&EXT("resource.0.interval",$symb); + if ($interval[1] eq 'map') { + if ($res->answerable()) { + push(@to_test,$res); + last; + } + } + } + } + } + } } } } if ($interval[0] =~ /^(\d+)/) { - my $timelimit = $1; + my $timelimit = $1; my $first_access; if ($type eq 'resource') { $first_access=&get_first_access($interval[1],$item); @@ -8859,37 +8830,10 @@ sub get_commblock_resources { my $timesup = $first_access+$timelimit; if ($timesup > $now) { my $activeblock; - if ($type eq 'resource') { - if (ref($navmap)) { - my $res = $navmap->getBySymb($item); - if ($res->answerable()) { - $activeblock = 1; - } - } - } elsif ($type eq 'map') { - my $mapsymb = &symbread($mapname,1); - if (($mapsymb) && (ref($navmap))) { - my $mapres = $navmap->getBySymb($mapsymb); - if (ref($mapres)) { - my $first = $mapres->map_start(); - my $finish = $mapres->map_finish(); - my $it = $navmap->getIterator($first,$finish,undef,0,0); - if (ref($it)) { - my $res; - while ($res = $it->next(undef,1)) { - next unless (ref($res)); - my $symb = $res->symb(); - next if (($symb eq $mapsymb) || ($symb eq '')); - @interval=&EXT("resource.0.interval",$symb); - if ($interval[1] eq 'map') { - if ($res->answerable()) { - $activeblock = 1; - last; - } - } - } - } - } + foreach my $res (@to_test) { + if ($res->answerable()) { + $activeblock = 1; + last; } } if ($activeblock) { @@ -8919,12 +8863,8 @@ sub has_comm_blocking { my @blockers; return unless ($env{'request.course.id'}); return unless ($priv eq 'bre'); + return if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/); return if ($env{'request.state'} eq 'construct'); - my $courseurl = &courseid_to_courseurl($env{'request.course.id'}); - if ($env{'request.course.sec'}) { - $courseurl .= '/'.$env{'request.course.sec'}; - } - return if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseurl} =~/evb\&([^\:]*)/); my %blockinfo; if (ref($blocks) eq 'HASH') { %blockinfo = &get_commblock_resources($blocks); @@ -8939,7 +8879,7 @@ sub has_comm_blocking { } if ($symb) { @symbs = ($symb); - } elsif (keys(%possibles)) { + } elsif (keys(%possibles)) { @symbs = keys(%possibles); } my $noblock; @@ -8974,7 +8914,7 @@ sub has_comm_blocking { } } } - unless ($noblock) { + unless ($noblock) { return @blockers; } return; @@ -8999,9 +8939,29 @@ sub deeplink_check { @symbs = keys(%possibles); } - my ($deeplink_symb,$allow); - if ($env{'request.deeplink.login'}) { - $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom); + 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); @@ -9009,48 +8969,34 @@ sub deeplink_check { if ($deeplink eq '') { $allow = 1; } else { - my ($state,$others,$listed,$scope,$protect) = split(/,/,$deeplink); - if ($state ne 'only') { + my ($listed,$scope,$access) = split(/,/,$deeplink); + if ($access eq 'any') { $allow = 1; - } else { - my $check_deeplink_entry; - if ($protect ne 'none') { - my ($acctype,$item) = split(/:/,$protect); - if (($acctype eq 'ltic') && ($env{'user.linkprotector'})) { - if (grep(/^\Q$item\Ec$/,split(/,/,$env{'user.linkprotector'}))) { - $check_deeplink_entry = 1 - } - } elsif (($acctype eq 'ltid') && ($env{'user.linkprotector'})) { - if (grep(/^\Q$item\Ed$/,split(/,/,$env{'user.linkprotector'}))) { - $check_deeplink_entry = 1; - } - } elsif (($acctype eq 'key') && ($env{'user.deeplinkkey'})) { - if (grep(/^\Q$item\E$/,split(/,/,$env{'user.deeplinkkey'}))) { - $check_deeplink_entry = 1; - } - } - } - if (($protect eq 'none') || ($check_deeplink_entry)) { + } elsif ($login) { + if ($access eq 'only') { if ($scope eq 'res') { - if ($symb eq $deeplink_symb) { + if ($symb eq $login) { $allow = 1; } - } elsif (($scope eq 'map') || ($scope eq 'rec')) { - my ($map_from_symb,$map_from_login); - $map_from_symb = &deversion((&decode_symb($symb))[0]); - if ($deeplink_symb =~ /\.(page|sequence)$/) { - $map_from_login = &deversion((&decode_symb($deeplink_symb))[2]); - } else { - $map_from_login = &deversion((&decode_symb($deeplink_symb))[0]); + } 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; + } } - if (($map_from_symb) && ($map_from_login)) { - if ($map_from_symb eq $map_from_login) { + } 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; - } elsif ($scope eq 'rec') { - my @recurseup = &get_map_hierarchy($map_from_symb,$env{'request.course.id'}); - if (grep(/^\Q$map_from_login\E$/,@recurseup)) { - $allow = 1; - } } } } @@ -9062,7 +9008,7 @@ sub deeplink_check { return 1; } -# -------------------------------- Deversion and split uri into path an filename +# -------------------------------- Deversion and split uri into path an filename # # Removes the version from a URI and @@ -9188,9 +9134,9 @@ sub metadata_query { my @server_list = (defined($server_array) ? @$server_array : keys(%libserv) ); for my $server (@server_list) { - my $domains = ''; + my $domains = ''; if (ref($domains_hash) eq 'HASH') { - $domains = $domains_hash->{$server}; + $domains = $domains_hash->{$server}; } unless ($custom or $customshow) { my $reply=&reply("querysend:".&escape($query).':::'.&escape($domains),$server); @@ -9323,7 +9269,7 @@ sub fetch_enrollment_query { } sub get_query_reply { - my ($queryid,$sleep,$loopmax) = @_; + my ($queryid,$sleep,$loopmax) = @_;; if (($sleep eq '') || ($sleep !~ /^\d+\.?\d*$/)) { $sleep = 0.2; } @@ -9456,25 +9402,6 @@ sub auto_validate_instcode { return ($outcome,$description,$defaultcredits); } -sub auto_validate_inst_crosslist { - my ($cnum,$cdom,$instcode,$inst_xlist,$coowner) = @_; - my ($homeserver,$response); - if (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/)) { - $homeserver = &homeserver($cnum,$cdom); - } - if (!defined($homeserver)) { - if ($cdom =~ /^$match_domain$/) { - $homeserver = &domain($cdom,'primary'); - } - } - unless (($homeserver eq '') || ($homeserver eq 'no_host')) { - $response=&reply('autovalidateinstcrosslist:'.$cdom.':'. - &escape($instcode).':'.&escape($inst_xlist).':'. - &escape($coowner),$homeserver); - } - return $response; -} - sub auto_create_password { my ($cnum,$cdom,$authparam,$udom) = @_; my ($homeserver,$response); @@ -9746,38 +9673,6 @@ sub auto_validate_class_sec { return $response; } -sub auto_instsec_reformat { - my ($cdom,$action,$instsecref) = @_; - return unless(($action eq 'clutter') || ($action eq 'declutter')); - my @homeservers; - if (defined(&domain($cdom,'primary'))) { - push(@homeservers,&domain($cdom,'primary')); - } else { - my %servers = &get_servers($cdom,'library'); - foreach my $tryserver (keys(%servers)) { - if (!grep(/^\Q$tryserver\E$/,@homeservers)) { - push(@homeservers,$tryserver); - } - } - } - my $response; - my %reformatted = %{$instsecref}; - foreach my $server (@homeservers) { - if (ref($instsecref) eq 'HASH') { - my $info = &freeze_escape($instsecref); - my $response=&reply('autoinstsecreformat:'.$cdom.':'. - $action.':'.$info,$server); - next if ($response =~ /(con_lost|error|no_such_host|refused|unknown_command)/); - my @items = split(/&/,$response); - foreach my $item (@items) { - my ($key,$value) = split(/=/,$item); - $reformatted{&unescape($key)} = &thaw_unescape($value); - } - } - } - return %reformatted; -} - sub auto_validate_instclasses { my ($cdom,$cnum,$owners,$classesref) = @_; my ($homeserver,%validations); @@ -9821,7 +9716,8 @@ sub auto_crsreq_update { ':'.&escape($action).':'.&escape($ownername).':'. &escape($ownerdomain).':'.&escape($fullname).':'. &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)/) { my @items = split(/&/,$response); foreach my $item (@items) { @@ -10081,6 +9977,7 @@ sub plaintext { my %rolenames = ( Course => 'std', Community => 'alt1', + Placement => 'std', ); if ($cid ne '') { if ($env{'course.'.$cid.'.'.$short.'.plaintext'} ne '') { @@ -10162,7 +10059,7 @@ sub assignrole { } if ($refused) { 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; if ($role eq 'cc' || $role eq 'co') { %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner')); @@ -10182,8 +10079,12 @@ sub assignrole { } } } - } elsif (($selfenroll == 1) && ($role eq 'st') && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) { - $refused = ''; + } elsif (($selfenroll == 1) && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) { + if ($role eq 'st') { + $refused = ''; + } elsif (($context eq 'ltienroll') && ($env{'request.lti.login'})) { + $refused = ''; + } } elsif ($context eq 'requestcourses') { my @possroles = ('st','ta','ep','in','cc','co'); if ((grep(/^\Q$role\E$/,@possroles)) && ($env{'user.name'} ne '' && $env{'user.domain'} ne '')) { @@ -10202,7 +10103,7 @@ sub assignrole { } } } 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')) { if ($env{'environment.requestauthor'} eq 'automatic') { $refused = ''; @@ -10210,13 +10111,13 @@ sub assignrole { my %domdefaults = &get_domain_defaults($udom); if (ref($domdefaults{'requestauthor'}) eq 'HASH') { my $checkbystatus; - if ($env{'user.adv'}) { + if ($env{'user.adv'}) { my $disposition = $domdefaults{'requestauthor'}{'_LC_adv'}; if ($disposition eq 'automatic') { $refused = ''; } elsif ($disposition eq '') { $checkbystatus = 1; - } + } } else { $checkbystatus = 1; } @@ -10304,7 +10205,7 @@ sub assignrole { $context); } elsif (($role eq 'ca') || ($role eq 'aa')) { &coauthorrolelog($role,$uname,$udom,$url,$origstart,$origend,$delflag, - $context); + $context); } if ($role eq 'cc') { &autoupdate_coowners($url,$end,$start,$uname,$udom); @@ -10322,23 +10223,11 @@ sub autoupdate_coowners { if ($domdesign{$cdom.'.autoassign.co-owners'}) { my %coursehash = &coursedescription($cdom.'_'.$cnum); my $instcode = $coursehash{'internal.coursecode'}; - my $xlists = $coursehash{'internal.crosslistings'}; if ($instcode ne '') { if (($start && $start <= $now) && ($end == 0) || ($end > $now)) { unless ($coursehash{'internal.courseowner'} eq $uname.':'.$udom) { my ($delcoowners,@newcoowners,$putresult,$delresult,$coowners); my ($result,$desc) = &auto_validate_instcode($cnum,$cdom,$instcode,$uname.':'.$udom); - unless ($result eq 'valid') { - if ($xlists ne '') { - foreach my $xlist (split(',',$xlists)) { - my ($inst_crosslist,$lcsec) = split(':',$xlist); - $result = - &auto_validate_inst_crosslist($cnum,$cdom,$instcode, - $inst_crosslist,$uname.':'.$udom); - last if ($result eq 'valid'); - } - } - } if ($result eq 'valid') { if ($coursehash{'internal.co-owners'}) { foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) { @@ -10351,15 +10240,17 @@ sub autoupdate_coowners { } else { push(@newcoowners,$uname.':'.$udom); } - } elsif ($coursehash{'internal.co-owners'}) { - foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) { - unless ($coowner eq $uname.':'.$udom) { - push(@newcoowners,$coowner); + } else { + if ($coursehash{'internal.co-owners'}) { + foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) { + unless ($coowner eq $uname.':'.$udom) { + push(@newcoowners,$coowner); + } + } + unless (@newcoowners > 0) { + $delcoowners = 1; + $coowners = ''; } - } - unless (@newcoowners > 0) { - $delcoowners = 1; - $coowners = ''; } } if (@newcoowners || $delcoowners) { @@ -10475,10 +10366,14 @@ sub modifyuser { my $newuser; if ($uhome eq 'no_host') { $newuser = 1; + unless (($umode && ($upass ne '')) || ($umode eq 'localauth') || + ($umode eq 'lti')) { + return 'error: more information needed to create new user'; + } } # ----------------------------------------------------------------- Create User if (($uhome eq 'no_host') && - (($umode && $upass) || ($umode eq 'localauth'))) { + (($umode && $upass) || ($umode eq 'localauth') || ($umode eq 'lti'))) { my $unhome=''; if (defined($desiredhome) && &host_domain($desiredhome) eq $udom) { $unhome = $desiredhome; @@ -10520,7 +10415,7 @@ sub modifyuser { 'current user id "'.$uidhash{$uname}.'".'; } } else { - &idput($udom,($uname => $uid)); + &idput($udom,{$uname => $uid},$uhome,'ids'); } } # -------------------------------------------------------------- Add names, etc @@ -10645,7 +10540,7 @@ sub modifystudent { # student's environment $uid = undef if (!$forceid); $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); return $reply; } @@ -10927,6 +10822,7 @@ sub generate_coursenum { sub is_course { my ($cdom, $cnum) = scalar(@_) == 1 ? ($_[0] =~ /^($match_domain)_($match_courseid)$/) : @_; + return unless (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/)); my $uhome=&homeserver($cnum,$cdom); my $iscourse; @@ -10945,7 +10841,7 @@ sub is_course { &do_cache_new('iscourse',$hashid,$iscourse,3600); } } - return unless($iscourse); + return unless ($iscourse); return wantarray ? ($cdom, $cnum) : $cdom.'_'.$cnum; } @@ -11144,6 +11040,90 @@ sub files_not_in_path { 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 sub get_portfile_permissions { @@ -11288,49 +11268,132 @@ sub modify_access_controls { } 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 $action = 'activate'; - my $aclnum = 0; - if (&is_portfolio_url($requrl)) { + my %current_perms; + foreach my $requrl (sort(keys(%urls))) { + my $action; + if ($urls{$requrl} eq 'add') { + $action = 'activate'; + } else { + $action = 'none'; + } + my $aclnum = 0; my (undef,$udom,$unum,$file_name,$group) = &parse_portfolio_url($requrl); - my $current_perms = &get_portfile_permissions($udom,$unum); - my %access_controls = &get_access_controls($current_perms, + unless (exists($current_perms{$unum.':'.$udom})) { + $current_perms{$unum.':'.$udom} = &get_portfile_permissions($udom,$unum); + } + my %access_controls = &get_access_controls($current_perms{$unum.':'.$udom}, $group,$file_name); foreach my $key (keys(%{$access_controls{$file_name}})) { my ($num,$scope,$end,$start) = ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/); - if ($scope eq 'public') { - if ($start <= $now && $end == 0) { - $action = 'none'; - } else { + if ($scope eq $accesstype) { + if (($start <= $now) && ($end == 0)) { + if ($accesstype eq 'ip') { + 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'; $aclnum = $num; + last; } - last; } } if ($action eq 'none') { - return 'ok'; + next; } else { my %changes; my $newend = 0; my $newstart = $now; - my $newkey = $aclnum.':public_'.$newend.'_'.$newstart; + my $newkey = $aclnum.':'.$accesstype.'_'.$newend.'_'.$newstart; $changes{$action}{$newkey} = { - type => 'public', + type => $accesstype, time => { start => $newstart, end => $newend, }, }; + if ($accesstype eq 'ip') { + $changes{$action}{$newkey}{'ip'} = [$ip]; + } my ($outcome,$deloutcome,$new_values,$translation) = &modify_access_controls($file_name,\%changes,$udom,$unum); - return $outcome; + unless ($outcome eq 'ok') { + $errors .= $outcome.' '; + } } + } + if ($errors) { + $errors =~ s/\s$//; + return $errors; } else { - return 'invalid'; + return 'ok'; } } @@ -11535,11 +11598,12 @@ sub dirlist { foreach my $user (sort(keys(%allusers))) { push(@alluserslist,$user.'&user'); } + if (!%listerror) { # no errors return (\@alluserslist); } elsif (scalar(keys(%servers)) == 1) { - # one library server, one error + # one library server, one error my ($key) = keys(%listerror); return (\@alluserslist, $listerror{$key}); } elsif ( grep { $_ eq 'con_lost' } values(%listerror) ) { @@ -11548,7 +11612,7 @@ sub dirlist { return (\@alluserslist, 'con_lost'); } else { # multiple library servers and no con_lost -> data should be - # complete. + # complete. return (\@alluserslist); } @@ -11624,6 +11688,115 @@ sub stat_file { 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 # gets the value of a specific preevaluated condition @@ -11787,7 +11960,14 @@ sub get_userresdata { # Parameters: # $name - Course/user name. # $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. # Returns: # The value of the first reasource in @which that is found in the @@ -11797,7 +11977,8 @@ sub get_userresdata { # 'user', an undefined reference is returned. # If none of the resources are found, an undef is returned sub resdata { - my ($name,$domain,$type,@which)=@_; + my ($name,$domain,$type,$mapp,$recursed,$recurseup,$courseid, + $modifier,$what,@which)=@_; my $result; if ($type eq 'course') { $result=&get_courseresdata($name,$domain); @@ -11806,7 +11987,21 @@ sub resdata { } if (!ref($result)) { return $result; } foreach my $item (@which) { - if (defined($result->{$item->[0]})) { + if ($item->[1] eq 'course') { + 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]]; } } @@ -11815,24 +12010,15 @@ sub resdata { sub get_domain_lti { my ($cdom,$context) = @_; - my ($name,$cachename,%lti); + my ($name,%lti); if ($context eq 'consumer') { $name = 'ltitools'; } elsif ($context eq 'provider') { $name = 'lti'; - } elsif ($context eq 'linkprot') { - $name = 'ltisec'; } else { return %lti; } - - if ($context eq 'linkprot') { - $cachename = $context; - } else { - $cachename = $name; - } - - my ($result,$cached)=&is_cached_new($cachename,$cdom); + my ($result,$cached)=&is_cached_new($name,$cdom); if (defined($cached)) { if (ref($result) eq 'HASH') { %lti = %{$result}; @@ -11840,154 +12026,43 @@ sub get_domain_lti { } else { my %domconfig = &get_dom('configuration',[$name],$cdom); if (ref($domconfig{$name}) eq 'HASH') { - if ($context eq 'linkprot') { - if (ref($domconfig{$name}{'linkprot'}) eq 'HASH') { - %lti = %{$domconfig{$name}{'linkprot'}}; - } - } else { - %lti = %{$domconfig{$name}}; - } - if (($context eq 'consumer') && (keys(%lti))) { - my %encdomconfig = &get_dom('encconfig',[$name],$cdom,undef,1); - if (ref($encdomconfig{$name}) eq 'HASH') { - foreach my $id (keys(%lti)) { - if (ref($encdomconfig{$name}{$id}) eq 'HASH') { - foreach my $item ('key','secret') { - $lti{$id}{$item} = $encdomconfig{$name}{$id}{$item}; - } + %lti = %{$domconfig{$name}}; + my %encdomconfig = &get_dom('encconfig',[$name],$cdom); + if (ref($encdomconfig{$name}) eq 'HASH') { + foreach my $id (keys(%lti)) { + if (ref($encdomconfig{$name}{$id}) eq 'HASH') { + foreach my $item ('key','secret') { + $lti{$id}{$item} = $encdomconfig{$name}{$id}{$item}; } } } } } my $cachetime = 24*60*60; - &do_cache_new($cachename,$cdom,\%lti,$cachetime); + &do_cache_new($name,$cdom,\%lti,$cachetime); } return %lti; } -sub get_course_lti { - my ($cnum,$cdom) = @_; - my $hashid=$cdom.'_'.$cnum; - my %courselti; - my ($result,$cached)=&is_cached_new('courselti',$hashid); - if (defined($cached)) { - if (ref($result) eq 'HASH') { - %courselti = %{$result}; - } - } else { - %courselti = &dump('lti',$cdom,$cnum,undef,undef,undef,1); - my $cachetime = 24*60*60; - &do_cache_new('courselti',$hashid,\%courselti,$cachetime); - } - return %courselti; -} - -sub courselti_itemid { - my ($cnum,$cdom,$url,$method,$params,$context) = @_; - my ($chome,$itemid); - $chome = &homeserver($cnum,$cdom); - return if ($chome eq 'no_host'); - if (ref($params) eq 'HASH') { - my $rep; - if (grep { $_ eq $chome } current_machine_ids()) { - $rep = LONCAPA::Lond::crslti_itemid($cdom,$cnum,$url,$method,$params,$perlvar{'lonVersion'}); - } else { - my $escurl = &escape($url); - my $escmethod = &escape($method); - my $items = &freeze_escape($params); - $rep = &reply("encrypt:lti:$cdom:$cnum:$context:$escurl:$escmethod:$items",$chome); - } - unless (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') || - ($rep eq 'unknown_cmd')) { - $itemid = $rep; - } - } - return $itemid; -} - -sub domainlti_itemid { - my ($cdom,$url,$method,$params,$context) = @_; - my ($primary_id,$itemid); - $primary_id = &domain($cdom,'primary'); - return if ($primary_id eq ''); - if (ref($params) eq 'HASH') { - my $rep; - if (grep { $_ eq $primary_id } current_machine_ids()) { - $rep = LONCAPA::Lond::domlti_itemid($cdom,$context,$url,$method,$params,$perlvar{'lonVersion'}); - } else { - my $cnum = ''; - my $escurl = &escape($url); - my $escmethod = &escape($method); - my $items = &freeze_escape($params); - $rep = &reply("encrypt:lti:$cdom:$cnum:$context:$escurl:$escmethod:$items",$primary_id); - } - unless (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') || - ($rep eq 'unknown_cmd')) { - $itemid = $rep; - } - } - return $itemid; -} - -sub count_supptools { - my ($cnum,$cdom,$ignorecache,$reload)=@_; +sub get_numsuppfiles { + my ($cnum,$cdom,$ignorecache)=@_; my $hashid=$cnum.':'.$cdom; - my ($numexttools,$cached); + my ($suppcount,$cached); unless ($ignorecache) { - ($numexttools,$cached) = &is_cached_new('supptools',$hashid); + ($suppcount,$cached) = &is_cached_new('suppcount',$hashid); } unless (defined($cached)) { my $chome=&homeserver($cnum,$cdom); - $numexttools = 0; unless ($chome eq 'no_host') { - my ($supplemental) = &Apache::loncommon::get_supplemental($cnum,$cdom,$reload); - if (ref($supplemental) eq 'HASH') { - if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) { - foreach my $key (keys(%{$supplemental->{'ids'}})) { - if ($key =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) { - $numexttools ++; - } - } - } - } + ($suppcount,my $supptools,my $errors) = (0,0,0); + my $suppmap = 'supplemental.sequence'; + ($suppcount,$supptools,$errors) = + &Apache::loncommon::recurse_supplemental($cnum,$cdom,$suppmap,$suppcount, + $supptools,$errors); } - &do_cache_new('supptools',$hashid,$numexttools,600); + &do_cache_new('suppcount',$hashid,$suppcount,600); } - return $numexttools; -} - -sub has_unhidden_suppfiles { - my ($cnum,$cdom,$ignorecache,$possdel)=@_; - my $hashid=$cnum.':'.$cdom; - my ($showsupp,$cached); - unless ($ignorecache) { - ($showsupp,$cached) = &is_cached_new('showsupp',$hashid); - } - unless (defined($cached)) { - my $chome=&homeserver($cnum,$cdom); - unless ($chome eq 'no_host') { - my ($supplemental) = &Apache::loncommon::get_supplemental($cnum,$cdom,$ignorecache,$possdel); - if (ref($supplemental) eq 'HASH') { - if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) { - foreach my $key (keys(%{$supplemental->{'ids'}})) { - next if ($key =~ /\.sequence$/); - if (ref($supplemental->{'ids'}->{$key}) eq 'ARRAY') { - foreach my $id (@{$supplemental->{'ids'}->{$key}}) { - unless ($supplemental->{'hidden'}->{$id}) { - $showsupp = 1; - last; - } - } - } - last if ($showsupp); - } - } - } - } - &do_cache_new('showsupp',$hashid,$showsupp,600); - } - return $showsupp; + return $suppcount; } # @@ -12060,7 +12135,7 @@ sub EXT { if ( (defined($Apache::lonhomework::parsing_a_problem) || defined($Apache::lonhomework::parsing_a_task)) && - ($symbparm eq &symbread()) ) { + ($symbparm eq &symbread()) ) { # if we are in the middle of processing the resource the # get the value we are planning on committing if (defined($Apache::lonhomework::results{$qualifierrest})) { @@ -12181,23 +12256,22 @@ sub EXT { } } - my ($section, $group, @groups); - my ($courselevelm,$courselevel); + my ($section, $group, @groups, @recurseup, $recursed); + my ($courselevelm,$courseleveli,$courselevel,$mapp); if (($courseid eq '') && ($cid)) { $courseid = $cid; } if (($symbparm && $courseid) && - (($courseid eq $env{'request.course.id'}) || ($courseid eq $cid))) { + (($courseid eq $env{'request.course.id'}) || ($courseid eq $cid))) { #print '
'.$space.' - '.$qualifier.' - '.$spacequalifierrest; # ----------------------------------------------------- Cascading lookup scheme my $symbp=$symbparm; - my $mapp=&deversion((&decode_symb($symbp))[0]); - + $mapp=&deversion((&decode_symb($symbp))[0]); my $symbparm=$symbp.'.'.$spacequalifierrest; + my $recurseparm=$mapp.'___(rec).'.$spacequalifierrest; my $mapparm=$mapp.'___(all).'.$spacequalifierrest; - if (($env{'user.name'} eq $uname) && ($env{'user.domain'} eq $udom)) { $section=$env{'request.course.sec'}; @@ -12214,17 +12288,21 @@ sub EXT { my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest; my $seclevelr=$courseid.'.['.$section.'].'.$symbparm; + my $secleveli=$courseid.'.['.$section.'].'.$recurseparm; my $seclevelm=$courseid.'.['.$section.'].'.$mapparm; $courselevel=$courseid.'.'.$spacequalifierrest; my $courselevelr=$courseid.'.'.$symbparm; + $courseleveli=$courseid.'.'.$recurseparm; $courselevelm=$courseid.'.'.$mapparm; # ----------------------------------------------------------- first, check user - my $userreply=&resdata($uname,$udom,'user', + my $userreply=&resdata($uname,$udom,'user',$mapp,\$recursed, + \@recurseup,$courseid,'.',$spacequalifierrest, ([$courselevelr,'resource'], [$courselevelm,'map' ], + [$courseleveli,'map' ], [$courselevel, 'course' ])); if (defined($userreply)) { return &get_reply($userreply); } @@ -12232,15 +12310,18 @@ sub EXT { my $coursereply; if (@groups > 0) { $coursereply = &check_group_parms($courseid,\@groups,$symbparm, - $mapparm,$spacequalifierrest); - if (defined($coursereply)) { return &get_reply($coursereply); } + $recurseparm,$mapparm,$spacequalifierrest, + $mapp,\$recursed,\@recurseup); + if (defined($coursereply)) { return &get_reply($coursereply); } } $coursereply=&resdata($env{'course.'.$courseid.'.num'}, $env{'course.'.$courseid.'.domain'}, - 'course', + 'course',$mapp,\$recursed,\@recurseup, + $courseid,'.['.$section.'].',$spacequalifierrest, ([$seclevelr, 'resource'], [$seclevelm, 'map' ], + [$secleveli, 'map' ], [$seclevel, 'course' ], [$courselevelr,'resource'])); if (defined($coursereply)) { return &get_reply($coursereply); } @@ -12257,8 +12338,9 @@ sub EXT { if ($thisparm) { return &get_reply([$thisparm,'resource']); } } # ------------------------------------------ fourth, look in resource metadata - - $spacequalifierrest=~s/\./\_/; + + my $what = $spacequalifierrest; + $what=~s/\./\_/; my $filename; if (!$symbparm) { $symbparm=&symbread(); } if ($symbparm) { @@ -12266,18 +12348,24 @@ sub EXT { } else { $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']); } - $metadata=&metadata($filename,'parameter_'.$spacequalifierrest); + $metadata=&metadata($filename,'parameter_'.$what,$toolsymb); if (defined($metadata)) { return &get_reply([$metadata,'resource']); } -# ---------------------------------------------- fourth, look in rest of course +# ----------------------------------------------- fifth, look in rest of course if ($symbparm && defined($courseid) && $courseid eq $env{'request.course.id'}) { my $coursereply=&resdata($env{'course.'.$courseid.'.num'}, $env{'course.'.$courseid.'.domain'}, - 'course', + 'course',$mapp,\$recursed,\@recurseup, + $courseid,'.',$spacequalifierrest, ([$courselevelm,'map' ], + [$courseleveli,'map' ], [$courselevel, 'course'])); if (defined($coursereply)) { return &get_reply($coursereply); } } @@ -12292,7 +12380,7 @@ sub EXT { if (defined($partgeneral[0])) { return &get_reply(\@partgeneral); } } 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']); } # ---------------------------------------------------- Any other user namespace } elsif ($realm eq 'environment') { @@ -12319,7 +12407,7 @@ sub EXT { } } elsif ($realm eq 'client') { if ($space eq 'remote_addr') { - return &get_requestor_ip(); + return $ENV{'REMOTE_ADDR'}; } } return ''; @@ -12338,19 +12426,24 @@ sub get_reply { } sub check_group_parms { - my ($courseid,$groups,$symbparm,$mapparm,$what) = @_; - my @groupitems = (); - my $resultitem; - my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$what,'course']); + my ($courseid,$groups,$symbparm,$recurseparm,$mapparm,$what,$mapp, + $recursed,$recurseupref) = @_; + my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$recurseparm,'map'], + [$what,'course']); + my $coursereply; foreach my $group (@{$groups}) { + my @groupitems = (); foreach my $level (@levels) { my $item = $courseid.'.['.$group.'].'.$level->[0]; 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; } @@ -12385,11 +12478,11 @@ sub sort_course_groups { # Sort groups b } sub packages_tab_default { - my ($uri,$varname)=@_; + my ($uri,$varname,$toolsymb)=@_; my (undef,$part,$name)=split(/\./,$varname); 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); if ($pack_type eq 'default') { $do_default=1; @@ -12458,7 +12551,7 @@ my %metaentry; my %importedpartids; my %importedrespids; sub metadata { - my ($uri,$what,$liburi,$prefix,$depthcount)=@_; + my ($uri,$what,$toolsymb,$liburi,$prefix,$depthcount)=@_; $uri=&declutter($uri); # if it is a non metadata possible uri return quickly if (($uri eq '') || @@ -12482,6 +12575,65 @@ sub metadata { my ($result,$cached)=&is_cached_new('meta',$uri); 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 my @origfiletagids=(); @@ -12590,23 +12742,31 @@ sub metadata { # Check metadata for imported file to # see if it contained response items # + my ($origfile,@libfilekeys); my %currmetaentry = %metaentry; - my $libresponseorder = &metadata($location,'responseorder'); - my $origfile; - if ($libresponseorder ne '') { - if ($#origfiletagids<0) { - undef(%importedrespids); - undef(%importedpartids); - } - @{$importedrespids{$importid}} = split(/\s*,\s*/,$libresponseorder); - if (@{$importedrespids{$importid}} > 0) { - $importedresponses = 1; + @libfilekeys = split(/,/,&metadata($location,'keys',undef,undef,undef, + $depthcount+1)); + if (grep(/^responseorder$/,@libfilekeys)) { + my $libresponseorder = &metadata($location,'responseorder',undef,undef, + undef,$depthcount+1); + if ($libresponseorder ne '') { + if ($#origfiletagids<0) { + undef(%importedrespids); + undef(%importedpartids); + } + 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 # Load and inspect original file - if ($#origfiletagids<0) { - my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri); - $origfile=&getfile($origfilelocation); - @origfiletagids=($origfile=~/<((?:\w+)response|import|part)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs); + if ($#origfiletagids<0) { + my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri); + $origfile=&getfile($origfilelocation); + @origfiletagids=($origfile=~/<((?:\w+)response|import|part)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs); + } } } } @@ -12614,10 +12774,7 @@ sub metadata { # hash populated for imported library file %metaentry = %currmetaentry; undef(%currmetaentry); - if ($importmode eq 'problem') { -# Import as problem/response - $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'}); - } elsif ($importmode eq 'part') { + if ($importmode eq 'part') { # Import as part(s) $importedparts=1; # We need to get the original file and the imported file to get the part order correct @@ -12632,10 +12789,23 @@ sub metadata { @origfiletagids=($origfile=~/<(part|import)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs); } } - -# Load and inspect imported file - my $impfile=&getfile($location); - my @impfilepartids=($impfile=~/]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs); + my @impfilepartids; +# If tag is included in metadata for the imported file +# get the parts in the imported file from that. + 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 tag available, load and inspect imported file + my $impfile=&getfile($location); + @impfilepartids=($impfile=~/]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs); + } if ($#impfilepartids>=0) { # This problem had parts $importedpartids{$token->[2]->{'id'}}=join(',',@impfilepartids); @@ -12646,22 +12816,36 @@ sub metadata { $importedpartids{$token->[2]->{'id'}}=$token->[2]->{'id'}; } } else { +# Import as problem or as normal import + $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'}); + unless ($importmode eq 'problem') { # Normal import - $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'}); - if (defined($token->[2]->{'id'})) { - $unikey.='_'.$token->[2]->{'id'}; - } + if (defined($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) { my $metadata = - &metadata($uri,'keys', $location,$unikey, + &metadata($uri,'keys',$toolsymb,$location,$unikey, $depthcount+1); foreach my $meta (split(',',$metadata)) { $metaentry{':'.$meta}=$metaentry{':'.$meta}; $metathesekeys{$meta}=1; } - } } else { # @@ -12730,7 +12914,7 @@ sub metadata { $dir=~s|[^/]*$||; $location=&filelocation($dir,$location); my $rights_metadata = - &metadata($uri,'keys',$location,'_rights', + &metadata($uri,'keys',$toolsymb,$location,'_rights', $depthcount+1); foreach my $rights (split(',',$rights_metadata)) { #$metaentry{':'.$rights}=$metacache{$uri}->{':'.$rights}; @@ -12751,7 +12935,7 @@ sub metadata { $metathesekeys{'partorder'}=1; } if ($importedresponses) { -# We had imported responses and need to rebuild responseorder +# We had imported responses and need to rebuil responseorder $metaentry{':responseorder'}=''; $metathesekeys{'responseorder'}=1; } @@ -12765,12 +12949,14 @@ sub metadata { } elsif ($origfiletagids[$index] eq 'import') { if ($importedparts) { # We have imported parts at this position - $metaentry{':partorder'}.=','.$importedpartids{$origid}; + if ($importedpartids{$origid} ne '') { + $metaentry{':partorder'}.=','.$importedpartids{$origid}; + } } if ($importedresponses) { # We have imported responses at this position - if (ref($importedrespids{$origid}) eq 'ARRAY') { - $metaentry{':responseorder'}.=','.join(',',map { $origid.'_'.$_ } @{$importedrespids{$origid}}); + if ($importedrespids{$origid} ne '') { + $metaentry{':responseorder'}.=','.$importedrespids{$origid}; } } } else { @@ -12787,11 +12973,12 @@ sub metadata { $metaentry{':responseorder'}=~s/^\,//; } } - $metaentry{':keys'} = join(',',keys(%metathesekeys)); &metadata_generate_part0(\%metathesekeys,\%metaentry,$uri); $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 } return $metaentry{':'.$what}; @@ -12999,68 +13186,11 @@ sub get_coursechange { } sub devalidate_coursechange_cache { - my ($cdom,$cnum)=@_; - my $hashid=$cdom.'_'.$cnum; + my ($cnum,$cdom)=@_; + my $hashid=$cnum.':'.$cdom; &devalidate_cache_new('crschange',$hashid); } -sub get_suppchange { - my ($cdom,$cnum) = @_; - if ($cdom eq '' || $cnum eq '') { - return unless ($env{'request.course.id'}); - $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - } - my $hashid=$cdom.'_'.$cnum; - my ($change,$cached)=&is_cached_new('suppchange',$hashid); - if ((defined($cached)) && ($change ne '')) { - return $change; - } else { - my %crshash = &get('environment',['internal.supplementalchange'],$cdom,$cnum); - if ($crshash{'internal.supplementalchange'} eq '') { - $change = $env{'course.'.$cdom.'_'.$cnum.'.internal.created'}; - if ($change eq '') { - %crshash = &get('environment',['internal.created'],$cdom,$cnum); - $change = $crshash{'internal.created'}; - } - } else { - $change = $crshash{'internal.supplementalchange'}; - } - my $cachetime = 600; - &do_cache_new('suppchange',$hashid,$change,$cachetime); - } - return $change; -} - -sub devalidate_suppchange_cache { - my ($cdom,$cnum)=@_; - my $hashid=$cdom.'_'.$cnum; - &devalidate_cache_new('suppchange',$hashid); -} - -sub update_supp_caches { - my ($cdom,$cnum) = @_; - my %servers = &internet_dom_servers($cdom); - my @ids=¤t_machine_ids(); - foreach my $server (keys(%servers)) { - next if (grep(/^\Q$server\E$/,@ids)); - my $hashid=$cnum.':'.$cdom; - my $cachekey = &escape('showsupp').':'.&escape($hashid); - &remote_devalidate_cache($server,[$cachekey]); - } - &has_unhidden_suppfiles($cnum,$cdom,1,1); - &count_supptools($cnum,$cdom,1); - my $now = time; - if ($env{'request.course.id'} eq $cdom.'_'.$cnum) { - &Apache::lonnet::appenv({'request.course.suppupdated' => $now}); - } - &put('environment',{'internal.supplementalchange' => $now}, - $cdom,$cnum); - &Apache::lonnet::appenv( - {'course.'.$cdom.'_'.$cnum.'.internal.supplementalchange' => $now}); - &do_cache_new('suppchange',$cdom.'_'.$cnum,$now,600); -} - # ------------------------------------------------- Update symbolic store links sub symblist { @@ -13121,7 +13251,7 @@ sub symbverify { $ids=$bighash{'ids_'.&clutter($thisurl)}; } unless ($ids) { - my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl; + my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl; $ids=$bighash{$idkey}; } if ($ids) { @@ -13137,14 +13267,14 @@ sub symbverify { if (ref($encstate)) { $$encstate = $bighash{'encrypted_'.$id}; } - if (($env{'request.role.adv'}) || - ($bighash{'encrypted_'.$id} eq $env{'request.enc'}) || + if (($env{'request.role.adv'}) || + ($bighash{'encrypted_'.$id} eq $env{'request.enc'}) || ($thisurl eq '/adm/navmaps')) { - $okay=1; + $okay=1; last; - } - } - } + } + } + } } untie(%bighash); } @@ -13232,8 +13362,8 @@ sub symbread { unless ($thisfn) { if ($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); } # is that filename actually a symb? Verify, clean, and return @@ -13247,10 +13377,17 @@ sub symbread { my %bighash; my $syval=''; if (($env{'request.course.fn'}) && ($thisfn)) { + my $targetfn = $thisfn; + if ( ($thisfn =~ m/^(uploaded|editupload)\//) && ($thisfn !~ m/\.(page|sequence)$/) ) { + $targetfn = 'adm/wrapper/'.$thisfn; + } + if ($targetfn =~ m|^adm/wrapper/(ext/.*)|) { + $targetfn=$1; + } unless ($ignoresymbdb) { if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { - $syval=$hash{$thisfn}; + $syval=$hash{$targetfn}; untie(%hash); } if ($syval && $checkforblock) { @@ -13305,7 +13442,7 @@ sub symbread { } } } - } elsif ((!$donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) { + } elsif ((!$donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) { # ------------------------------------------ There is more than one possibility my $realpossible=0; foreach my $id (@possibilities) { @@ -13313,14 +13450,14 @@ sub symbread { my $canaccess; if (($donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) { $canaccess = 1; - } else { + } else { $canaccess = &allowed('bre',$file); } if ($canaccess) { my ($mapid,$resid)=split(/\./,$id); if ($bighash{'map_type_'.$mapid} ne 'page') { my $poss_syval=&encode_symb($bighash{'map_id_'.$mapid}, - $resid,$thisfn); + $resid,$thisfn); next if ($bighash{'randomout_'.$id} && !$env{'request.role.adv'}); next unless (($noenccheck) || ($bighash{'encrypted_'.$id} eq $env{'request.enc'})); if ($checkforblock) { @@ -13501,6 +13638,7 @@ sub rndseed { $which =&get_rand_alg($courseid); } if (defined(&getCODE())) { + if ($which eq '64bit5') { return &rndseed_CODE_64bit5($symb,$courseid,$domain,$username); } elsif ($which eq '64bit4') { @@ -13686,7 +13824,7 @@ sub rndseed_CODE_64bit5 { sub setup_random_from_rndseed { my ($rndseed)=@_; if ($rndseed =~/([,:])/) { - my ($num1,$num2) = map { abs($_); } (split(/[,:]/,$rndseed)); + my ($num1,$num2) = map { abs($_); } (split(/[,:]/,$rndseed)); if ((!$num1) || (!$num2) || ($num1 > 2147483562) || ($num2 > 2147483398)) { &Math::Random::random_set_seed_from_phrase($rndseed); } else { @@ -13870,7 +14008,6 @@ sub repcopy_userfile { } # now the path exists for sure # get a user agent - my $ua=new LWP::UserAgent; my $transferfile=$file.'.in.transfer'; # FIXME: this should flock if (-e $transferfile) { return 'ok'; } @@ -13881,7 +14018,7 @@ sub repcopy_userfile { my $protocol = $protocol{$homeserver}; $protocol = 'http' if ($protocol ne 'https'); $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? if ($response->is_error()) { unlink($transferfile); @@ -13927,9 +14064,8 @@ sub getuploaded { my $protocol = $protocol{$homeserver}; $protocol = 'http' if ($protocol ne 'https'); $uri = $protocol.'://'.$hostname.'/raw/'.$uri; - my $ua=new LWP::UserAgent; 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; if (! $response->is_success()) { return 'failed'; @@ -14059,15 +14195,10 @@ sub machine_ids { sub additional_machine_domains { my @domains; - if (-e "$perlvar{'lonTabDir'}/expected_domains.tab") { - if (open(my $fh,"<","$perlvar{'lonTabDir'}/expected_domains.tab")) { - while( my $line = <$fh>) { - chomp($line); - $line =~ s/\s//g; - push(@domains,$line); - } - close($fh); - } + open(my $fh,"<","$perlvar{'lonTabDir'}/expected_domains.tab"); + while( my $line = <$fh>) { + $line =~ s/\s//g; + push(@domains,$line); } return @domains; } @@ -14086,12 +14217,9 @@ sub default_login_domain { } sub shared_institution { - my ($dom,$lonhost) = @_; - if ($lonhost eq '') { - $lonhost = $perlvar{'lonHostID'}; - } + my ($dom) = @_; my $same_intdom; - my $hostintdom = &internet_dom($lonhost); + my $hostintdom = &internet_dom($perlvar{'lonHostID'}); if ($hostintdom ne '') { my %iphost = &get_iphost(); my $primary_id = &domain($dom,'primary'); @@ -14122,10 +14250,9 @@ sub uses_sts { return $sts_on; } } - my $ua=new LWP::UserAgent; my $url = $protocol{$lonhost}.'://'.$hostname.'/index.html'; 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) { my $has_sts = $response->header('Strict-Transport-Security'); if ($has_sts eq '') { @@ -14148,42 +14275,16 @@ sub uses_sts { return; } -sub waf_allssl { - my ($host_name) = @_; - my $alias = &get_proxy_alias(); - if ($host_name eq '') { - $host_name = $ENV{'SERVER_NAME'}; - } - if (($host_name ne '') && ($alias eq $host_name)) { - my $serverhomedom = &host_domain($perlvar{'lonHostID'}); - my %defdomdefaults = &get_domain_defaults($serverhomedom); - if ($defdomdefaults{'waf_sslopt'}) { - return $defdomdefaults{'waf_sslopt'}; - } - } - return; -} - sub get_requestor_ip { my ($r,$nolookup,$noproxy) = @_; my $from_ip; if (ref($r)) { - if ($r->can('useragent_ip')) { - if ($noproxy && $r->can('client_ip')) { - $from_ip = $r->client_ip(); - } else { - $from_ip = $r->useragent_ip(); - } - } elsif ($r->connection->can('remote_ip')) { - $from_ip = $r->connection->remote_ip(); - } else { - $from_ip = $r->get_remote_host($nolookup); - } + $from_ip = $r->get_remote_host($nolookup); } else { $from_ip = $ENV{'REMOTE_ADDR'}; } - return $from_ip if ($noproxy); - # Who controls proxy settings for server + return $from_ip if ($noproxy); + # Who controls proxy settings for server my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'}; my $proxyinfo = &get_proxy_settings($dom_in_use); if ((ref($proxyinfo) eq 'HASH') && ($from_ip)) { @@ -14231,8 +14332,7 @@ sub get_proxy_settings { ipheader => $domdefaults{'waf_ipheader'}, trusted => $domdefaults{'waf_trusted'}, vpnint => $domdefaults{'waf_vpnint'}, - vpnext => $domdefaults{'waf_vpnext'}, - sslopt => $domdefaults{'waf_sslopt'}, + vpnext => $domdefaults{'waf_vpnext'}; }; return $proxyinfo; } @@ -14247,131 +14347,29 @@ sub ip_match { } sub get_proxy_alias { - my ($lonid) = @_; - if ($lonid eq '') { - $lonid = $perlvar{'lonHostID'}; - } - if (!defined(&hostname($lonid))) { - return; - } - if ($lonid ne '') { - my ($alias,$cached) = &is_cached_new('proxyalias',$lonid); + my $lonhost = $perlvar{'lonHostID'}; + if ($lonhost ne '') { + my ($alias,$cached) = &is_cached_new('proxyalias',$lonhost); if ($cached) { return $alias; } - my $dom = &Apache::lonnet::host_domain($lonid); + my $dom = &Apache::lonnet::host_domain($lonhost); if ($dom ne '') { my $cachetime = 60*60*24; my %domconfig = &Apache::lonnet::get_dom('configuration',['wafproxy'],$dom); + my $alias; if (ref($domconfig{'wafproxy'}) eq 'HASH') { if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') { - $alias = $domconfig{'wafproxy'}{'alias'}{$lonid}; + $alias = $domconfig{'wafproxy'}{'alias'}{$lonhost}; } } - return &do_cache_new('proxyalias',$lonid,$alias,$cachetime); + return &do_cache_new('proxyalias',$lonhost,$alias,$cachetime); } } return; } -sub use_proxy_alias { - my ($r,$lonid) = @_; - my $alias = &get_proxy_alias($lonid); - if ($alias) { - my $dom = &host_domain($lonid); - if ($dom ne '') { - my $proxyinfo = &get_proxy_settings($dom); - my ($vpnint,$remote_ip); - if (ref($proxyinfo) eq 'HASH') { - $vpnint = $proxyinfo->{'vpnint'}; - if ($vpnint) { - $remote_ip = &get_requestor_ip($r,1,1); - } - } - unless ($vpnint && &ip_match($remote_ip,$vpnint)) { - return $alias; - } - } - } - return; -} - -sub alias_sso { - my ($lonid) = @_; - if ($lonid eq '') { - $lonid = $perlvar{'lonHostID'}; - } - if (!defined(&hostname($lonid))) { - return; - } - if ($lonid ne '') { - my ($use_alias,$cached) = &is_cached_new('proxysaml',$lonid); - if ($cached) { - return $use_alias; - } - my $dom = &Apache::lonnet::host_domain($lonid); - if ($dom ne '') { - my $cachetime = 60*60*24; - my %domconfig = - &Apache::lonnet::get_dom('configuration',['wafproxy'],$dom); - if (ref($domconfig{'wafproxy'}) eq 'HASH') { - if (ref($domconfig{'wafproxy'}{'saml'}) eq 'HASH') { - $use_alias = $domconfig{'wafproxy'}{'saml'}{$lonid}; - } - } - return &do_cache_new('proxysaml',$lonid,$use_alias,$cachetime); - } - } - return; -} - -sub get_saml_landing { - my ($lonid) = @_; - if ($lonid eq '') { - my $defdom = &default_login_domain(); - my @hosts = ¤t_machine_ids(); - if (@hosts > 1) { - foreach my $hostid (@hosts) { - if (&host_domain($hostid) eq $defdom) { - $lonid = $hostid; - last; - } - } - } else { - $lonid = $perlvar{'lonHostID'}; - } - if ($lonid) { - unless (&Apache::lonnet::host_domain($lonid) eq $defdom) { - return; - } - } else { - return; - } - } elsif (!defined(&hostname($lonid))) { - return; - } - my ($landing,$cached) = &is_cached_new('samllanding',$lonid); - if ($cached) { - return $landing; - } - my $dom = &Apache::lonnet::host_domain($lonid); - if ($dom ne '') { - my $cachetime = 60*60*24; - my %domconfig = - &Apache::lonnet::get_dom('configuration',['login'],$dom); - if (ref($domconfig{'login'}) eq 'HASH') { - if (ref($domconfig{'login'}{'saml'}) eq 'HASH') { - if (ref($domconfig{'login'}{'saml'}{$lonid}) eq 'HASH') { - $landing = 1; - } - } - } - return &do_cache_new('samllanding',$lonid,$landing,$cachetime); - } - return; -} - # ------------------------------------------------------------- Declutters URLs sub declutter { @@ -14511,36 +14509,36 @@ sub get_dns { } while (%alldns) { my ($dns) = sort { $b cmp $a } keys(%alldns); - my @content; - if ($dns eq Sys::Hostname::FQDN::fqdn()) { - my $command = (split('/',$url))[3]; - my ($dir,$file) = &parse_getdns_url($command,$url); - delete($alldns{$dns}); - next if (($dir eq '') || ($file eq '')); - if (open(my $config,'<',"$dir/$file")) { - @content = <$config>; - close($config); - } - } else { - my $ua=new LWP::UserAgent; - $ua->timeout(30); - my $request=new HTTP::Request('GET',"$alldns{$dns}://$dns$url"); - my $response=$ua->request($request); - delete($alldns{$dns}); - next if ($response->is_error()); - @content = split("\n",$response->content); - } - unless ($nocache) { - &do_cache_new('dns',$url,\@content,30*24*60*60); + my $request=new HTTP::Request('GET',"$alldns{$dns}://$dns$url"); + my $response = &LONCAPA::LWPReq::makerequest('',$request,'',\%perlvar,30,0); + delete($alldns{$dns}); + next if ($response->is_error()); + if ($url eq '/adm/dns/loncapaCRL') { + return &$func($response); + } 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]; - &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>; - &$func(\@content,$hashref); + my $which = (split('/',$url,4))[3]; + if ($which eq 'loncapaCRL') { + my $diskfile = "$perlvar{'lonCertificateDirectory'}/$perlvar{'lonnetCertRevocationList'}"; + if (-e $diskfile) { + &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; } @@ -14569,7 +14567,7 @@ sub parse_dns_checksums_tab { if (ref($lines) eq 'ARRAY') { chomp(@{$lines}); my $version = shift(@{$lines}); - if ($version eq $release) { + if ($version eq $release) { foreach my $line (@{$lines}) { my ($file,$version,$shasum) = split(/,/,$line); if ($file =~ m{^/etc/httpd/conf}) { @@ -14601,19 +14599,77 @@ sub fetch_dns_checksums { return \%checksums; } -sub parse_getdns_url { - my ($command,$url) = @_; - my $dir = $perlvar{'lonTabDir'}; - my $file; - if ($command eq 'hosts') { - $file = 'dns_hosts.tab'; - } elsif ($command eq 'domain') { - $file = 'dns_domain.tab'; - } elsif ($command eq 'checksums') { - my $version = (split('/',$url))[4]; - $file = "dns_checksums/$version.tab", +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 = ; + 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 () { + $oldnums{(split(/:/))[1]} = 1; + } + close(PIPE); + } + if (open(PIPE, "openssl crl -inform PEM -text -noout -in $dest |grep 'Serial Number' |")) { + while() { + $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 ($dir,$file); + return ($msg,$hadchanges); } # ------------------------------------------------------------ Read domain file @@ -14738,6 +14794,7 @@ sub parse_getdns_url { &purge_remembered(); &reset_domain_info(); &reset_hosts_ip_info(); + undef(%internetdom); undef(%name_to_host); undef(%hostname); undef(%hostdom); @@ -14780,6 +14837,11 @@ sub parse_getdns_url { return %hostdom; } + sub all_host_intdom { + &load_hosts_tab() if (!$loaded); + return %internetdom; + } + sub is_library { &load_hosts_tab() if (!$loaded); @@ -15010,9 +15072,9 @@ 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); } -# ------------------------------------------------------- Read loncaparev table +# ---------------------------------------------------------- Read loncaparev table { - sub load_loncaparevs { + sub load_loncaparevs { if (-e "$perlvar{'lonTabDir'}/loncaparevs.tab") { if (open(my $config,"<","$perlvar{'lonTabDir'}/loncaparevs.tab")) { while (my $configline=<$config>) { @@ -15026,7 +15088,7 @@ sub all_loncaparevs { } } -# ----------------------------------------------------- Read serverhostID table +# ---------------------------------------------------------- Read serverhostID table { sub load_serverhomeIDs { if (-e "$perlvar{'lonTabDir'}/serverhomeIDs.tab") { @@ -15118,11 +15180,11 @@ BEGIN { close($config); } -# --------------------------------------------------------- Read loncaparev table +# ---------------------------------------------------------- Read loncaparev table &load_loncaparevs(); -# ------------------------------------------------------- Read serverhostID table +# ---------------------------------------------------------- Read serverhostID table &load_serverhomeIDs(); @@ -15136,7 +15198,15 @@ BEGIN { my $item = $token->[1]; my $name = $token->[2]{'name'}; 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(); $release =~ s/(^\s*|\s*$ )//gx; $needsrelease{$item.':'.$name.':'.$value} = $release; @@ -15419,10 +15489,12 @@ the answer, and also caches if there is =item * X -B: find the usernames behind a list of IDs -(IDs are a unique resource in a domain, there must be only 1 ID per -username, and only 1 username per ID in a specific domain) (returns -hash: id=>name,id=>name) +B: find the usernames behind either +a list of student/employee IDs or clicker IDs +(student/employee IDs are a unique resource in a domain, there must be +only 1 ID per username, and only 1 username per ID in a specific domain). +clickerIDs are not necessarily unique, as students might share clickers. +(returns hash: id=>name,id=>name) =item * X @@ -15431,7 +15503,27 @@ usernames (returns hash: name=>id,name=> =item * X -B: store away a list of names and associated IDs +B: store away a list of +names and associated student/employee IDs or clicker IDs. + +=item * +X +B: 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 +B: 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 * X @@ -15479,13 +15571,13 @@ The first argument is required, all othe $priv is the privilege being checked. $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, -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). -$clientip is the user's IP address (only used when checking for access to portfolio +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). +$clientip is the user's IP address (only used when checking for access to portfolio 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. F: full access @@ -15495,7 +15587,7 @@ prevents recursive calls to &allowed. 2: browse allowed A: passphrase authentication needed 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 + D: access blocked because access is required via session initiated via deep-link =item * @@ -15548,9 +15640,9 @@ provided for types, will default to retu =item * 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 available roles) or future (roles available in the future), and $hideprivileged -- if true will not report course roles for users who @@ -15788,6 +15880,10 @@ data base, returning a hash that is keye values that are the resource value. I believe that the timestamps and versions are also returned. +get_numsuppfiles($cnum,$cdom) : retrieve number of files in a course's +supplemental content area. This routine caches the number of files for +10 minutes. + =back =head2 Course Modification @@ -15955,12 +16051,18 @@ condval($condidx) : value of condition i =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 '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. + +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 +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 * @@ -15970,20 +16072,20 @@ will be stored for query =item * -symbread($filename,$donotrecurse,$ignorecachednull,$checkforblock,$possibles) : -return symbolic list entry (all arguments optional). +symbread($filename,$donotrecurse,$ignorecachednull,$checkforblock,$possibles) : +return symbolic list entry (all arguments optional). -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 -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 -a randompick); ignorecachednull, if true will prevent a symb of '' being +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 +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 +a randompick); ignorecachednull, if true will prevent a symb of '' being 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 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). - + returns the data handle =item * @@ -15993,9 +16095,9 @@ and is a possible symb for the URL in $t 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 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 -encrypted; otherwise it will be null. +encrypted; otherwise it will be null. =item * @@ -16048,13 +16150,13 @@ expirespread($uname,$udom,$stype,$usymb) devalidate($symb) : devalidate temporary spreadsheet calculations, 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, when viewing in course context. 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.). output: array of five scalars -- @@ -16062,15 +16164,15 @@ when viewing in course context. $home -- homeserver of resource (i.e., for author if published, or course if uploaded.). $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 =item * is_course_upload($file,$cnum,$cdom) -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 +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 homeserver. input: 3 args -- filename (decluttered), course number and course domain. @@ -16084,20 +16186,20 @@ homeserver. =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; 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). $laststore is $version=$timestamp, where $version is the most recent version number retrieved for the corresponding $symb in the $namespace db file, and $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(). =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 =item * @@ -16235,7 +16337,7 @@ server ($udom and $uhome are optional) =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 the target domain. @@ -16269,7 +16371,7 @@ requestcourses: ability to request cours =over =item -official, unofficial, community, textbook +official, unofficial, community, textbook, placement =back @@ -16290,8 +16392,8 @@ for course's uploaded content. =over =item -canuse_pdfforms, officialcredits, unofficialcredits, textbookcredits, officialquota, unofficialquota, -communityquota, textbookquota +canuse_pdfforms, officialcredits, unofficialcredits, textbookcredits, officialquota, unofficialquota, +communityquota, textbookquota, placementquota =back @@ -16301,7 +16403,7 @@ on your servers. =over -=item +=item remotesessions, hostedsessions =back @@ -16309,10 +16411,10 @@ remotesessions, hostedsessions =back 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 -- 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 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. @@ -16741,8 +16843,8 @@ Returns: get_timebased_id(): -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, +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, group bulletin boards). Args: (first three required; six others optional) @@ -16753,24 +16855,24 @@ Args: (first three required; six others 2. keyid (alphanumeric): name of temporary locking key in hash, 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 4. cdom: domain of course; default is current course domain 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 - 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. 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. -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: