--- loncom/lonnet/perl/lonnet.pm	2021/08/01 19:28:11	1.1462
+++ loncom/lonnet/perl/lonnet.pm	2022/10/22 21:13:47	1.1499
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1462 2021/08/01 19:28:11 raeburn Exp $
+# $Id: lonnet.pm,v 1.1499 2022/10/22 21:13:47 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -985,9 +985,9 @@ sub spareserver {
                                                      :  $userloadpercent;
     my ($uint_dom,$remotesessions);
     if (($udom ne '') && (&domain($udom) ne '')) {
-        my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
-        $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
-        my %udomdefaults = &Apache::lonnet::get_domain_defaults($udom);
+        my $uprimary_id = &domain($udom,'primary');
+        $uint_dom = &internet_dom($uprimary_id);
+        my %udomdefaults = &get_domain_defaults($udom);
         $remotesessions = $udomdefaults{'remotesessions'};
     }
     my $spareshash = &this_host_spares($udom);
@@ -1023,7 +1023,7 @@ sub spareserver {
                 if ($protocol{$spare_server} eq 'https') {
                     $protocol = $protocol{$spare_server};
                 }
-                my $alias = &Apache::lonnet::use_proxy_alias($r,$spare_server);
+                my $alias = &use_proxy_alias($r,$spare_server);
                 $hostname = $alias if ($alias ne '');
 	        $spare_server = $protocol.'://'.$hostname;
             }
@@ -1211,7 +1211,7 @@ sub choose_server {
         unless (defined($cached)) {
             my $cachetime = 60*60*24;
             my %domconfig =
-                &Apache::lonnet::get_dom('configuration',['loadbalancing'],$udom);
+                &get_dom('configuration',['loadbalancing'],$udom);
             if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
                 $balancers = &do_cache_new('loadbalancing',$udom,$domconfig{'loadbalancing'},
                                            $cachetime);
@@ -1323,7 +1323,7 @@ sub changepass {
 sub queryauthenticate {
     my ($uname,$udom)=@_;
     my $uhome=&homeserver($uname,$udom);
-    if (!$uhome) {
+    if ((!$uhome) || ($uhome eq 'no_host')) {
 	&logthis("User $uname at $udom is unknown when looking for authentication mechanism");
 	return 'no_host';
     }
@@ -1372,16 +1372,39 @@ 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;
-    my $host_idn = &Apache::lonnet::internet_dom($lonhost);
+    my $host_idn = &internet_dom($lonhost);
     if (ref($remotesessions) eq 'HASH') {
         if (ref($remotesessions->{'excludedomain'}) eq 'ARRAY') {
             if (grep(/^\Q$host_idn\E$/,@{$remotesessions->{'excludedomain'}})) {
@@ -1417,8 +1440,8 @@ sub can_host_session {
     }
     if ($canhost) {
         if (ref($hostedsessions) eq 'HASH') {
-            my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
-            my $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
+            my $uprimary_id = &domain($udom,'primary');
+            my $uint_dom = &internet_dom($uprimary_id);
             if (ref($hostedsessions->{'excludedomain'}) eq 'ARRAY') {
                 if (($uint_dom ne '') && 
                     (grep(/^\Q$uint_dom\E$/,@{$hostedsessions->{'excludedomain'}}))) {
@@ -1510,7 +1533,7 @@ sub spares_for_offload  {
     } else {
         my $cachetime = 60*60*24;
         my %domconfig =
-            &Apache::lonnet::get_dom('configuration',['usersessions'],$dom_in_use);
+            &get_dom('configuration',['usersessions'],$dom_in_use);
         if (ref($domconfig{'usersessions'}) eq 'HASH') {
             if (ref($domconfig{'usersessions'}{'spares'}) eq 'HASH') {
                 if (ref($domconfig{'usersessions'}{'spares'}{$lonhost_in_use}) eq 'HASH') {
@@ -1562,9 +1585,9 @@ sub check_loadbalancing {
         $rule_in_effect,$offloadto,$otherserver,$setcookie,$dom_balancers);
     my $lonhost = $perlvar{'lonHostID'};
     my @hosts = &current_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 $uprimary_id = &domain($udom,'primary');
+    my $uintdom = &internet_dom($uprimary_id);
+    my $intdom = &internet_dom($lonhost);
     my $serverhomedom = &host_domain($lonhost);
     my $domneedscache;
     my $cachetime = 60*60*24;
@@ -1578,7 +1601,7 @@ sub check_loadbalancing {
     my ($result,$cached)=&is_cached_new('loadbalancing',$dom_in_use);
     unless (defined($cached)) {
         my %domconfig =
-            &Apache::lonnet::get_dom('configuration',['loadbalancing'],$dom_in_use);
+            &get_dom('configuration',['loadbalancing'],$dom_in_use);
         if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
             $result = &do_cache_new('loadbalancing',$dom_in_use,$domconfig{'loadbalancing'},$cachetime);
         } else {
@@ -1639,7 +1662,7 @@ sub check_loadbalancing {
         ($result,$cached)=&is_cached_new('loadbalancing',$serverhomedom);
         unless (defined($cached)) {
             my %domconfig =
-                &Apache::lonnet::get_dom('configuration',['loadbalancing'],$serverhomedom);
+                &get_dom('configuration',['loadbalancing'],$serverhomedom);
             if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
                 $result = &do_cache_new('loadbalancing',$serverhomedom,$domconfig{'loadbalancing'},$cachetime);
             } else {
@@ -1772,7 +1795,7 @@ sub get_loadbalancer_targets {
             }
         } elsif ($rule_in_effect eq 'externalbalancer') {
             my %domconfig =
-                &Apache::lonnet::get_dom('configuration',['loadbalancing'],$udom);
+                &get_dom('configuration',['loadbalancing'],$udom);
             if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
                 if ($domconfig{'loadbalancing'}{'lonhost'} ne '') {
                     if (&hostname($domconfig{'loadbalancing'}{'lonhost'}) ne '') {
@@ -1836,15 +1859,15 @@ sub trusted_domains {
         return ($trusted,$untrusted);
     }
     my $callprimary = &domain($calldom,'primary');
-    my $intcalldom = &Apache::lonnet::internet_dom($callprimary);
+    my $intcalldom = &internet_dom($callprimary);
     if ($intcalldom eq '') {
         return ($trusted,$untrusted);
     }
 
-    my ($trustconfig,$cached)=&Apache::lonnet::is_cached_new('trust',$calldom);
+    my ($trustconfig,$cached)=&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);
+        my %domconfig = &get_dom('configuration',['trust'],$calldom);
+        &do_cache_new('trust',$calldom,$domconfig{'trust'},3600);
         $trustconfig = $domconfig{'trust'};
     }
     if (ref($trustconfig)) {
@@ -2262,6 +2285,57 @@ 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) = @_;
@@ -2271,7 +2345,7 @@ sub get_domainconfiguser {
 sub retrieve_inst_usertypes {
     my ($udom) = @_;
     my (%returnhash,@order);
-    my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
+    my %domdefs = &get_domain_defaults($udom);
     if ((ref($domdefs{'inststatustypes'}) eq 'HASH') && 
         (ref($domdefs{'inststatusorder'}) eq 'ARRAY')) {
         return ($domdefs{'inststatustypes'},$domdefs{'inststatusorder'});
@@ -2304,7 +2378,7 @@ sub retrieve_inst_usertypes {
 
 sub is_domainimage {
     my ($url) = @_;
-    if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo)/+[^/]-) {
+    if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo|login)/+[^/]-) {
         if (&domain($1) ne '') {
             return '1';
         }
@@ -2542,6 +2616,10 @@ sub inst_rulecheck {
                     $response=&unescape(&reply('instselfcreatecheck:'.
                                                &escape($udom).':'.&escape($uname).
                                               ':'.$rulestr,$homeserver));
+                } elsif ($item eq 'unamemap') {
+                    $response=&unescape(&reply('instunamemapcheck:'.
+                                               &escape($udom).':'.&escape($uname).
+                                              ':'.$rulestr,$homeserver));
                 }
                 if ($response ne 'refused') {
                     my @pairs=split(/\&/,$response);
@@ -2571,6 +2649,9 @@ 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);
@@ -2612,12 +2693,12 @@ sub get_domain_defaults {
     }
     my %domdefaults;
     my %domconfig =
-         &Apache::lonnet::get_dom('configuration',['defaults','quotas',
+         &get_dom('configuration',['defaults','quotas',
                                   'requestcourses','inststatus',
                                   'coursedefaults','usersessions',
                                   'requestauthor','selfenrollment',
                                   'coursecategories','ssl','autoenroll',
-                                  'trust','helpsettings','wafproxy'],$domain);
+                                  'trust','helpsettings','wafproxy','ltisec'],$domain);
     my @coursetypes = ('official','unofficial','community','textbook','placement');
     if (ref($domconfig{'defaults'}) eq 'HASH') {
         $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; 
@@ -2626,9 +2707,12 @@ 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');
@@ -2666,6 +2750,7 @@ 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'};
@@ -2698,7 +2783,10 @@ 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') {
@@ -2773,6 +2861,7 @@ sub get_domain_defaults {
     }
     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'};
@@ -2786,7 +2875,19 @@ sub get_domain_defaults {
                 $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;
 }
@@ -2806,7 +2907,7 @@ sub get_dom_cats {
         } else {
             $cats = {};
         }
-        &Apache::lonnet::do_cache_new('cats',$dom,$cats,3600);
+        &do_cache_new('cats',$dom,$cats,3600);
     }
     return $cats;
 }
@@ -2861,13 +2962,33 @@ sub course_portal_url {
     if ($domdefaults{'portal_def'}) {
         $firsturl = $domdefaults{'portal_def'};
     } else {
-        my $alias = &Apache::lonnet::use_proxy_alias($r,$chome);
+        my $alias = &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;
+}
+
 # --------------------------------------------- Get domain config for passwords
 
 sub get_passwdconf {
@@ -3039,7 +3160,7 @@ sub courseid_to_courseurl {
 	return "/$cdom/$cnum";
     }
 
-    my %courseinfo=&Apache::lonnet::coursedescription($courseid);
+    my %courseinfo=&coursedescription($courseid);
     if (exists($courseinfo{'num'})) {
 	return "/$courseinfo{'domain'}/$courseinfo{'num'}";
     }
@@ -3237,14 +3358,14 @@ sub userenvironment {
 # ---------------------------------------------------------- Get a studentphoto
 sub studentphoto {
     my ($udom,$unam,$ext) = @_;
-    my $home=&Apache::lonnet::homeserver($unam,$udom);
+    my $home=&homeserver($unam,$udom);
     if (defined($env{'request.course.id'})) {
         if ($env{'course.'.$env{'request.course.id'}.'.internal.showphoto'}) {
             if ($udom eq $env{'course.'.$env{'request.course.id'}.'.domain'}) {
                 return(&retrievestudentphoto($udom,$unam,$ext)); 
             } else {
                 my ($result,$perm_reqd)=
-		    &Apache::lonnet::auto_photo_permission($unam,$udom);
+		    &auto_photo_permission($unam,$udom);
                 if ($result eq 'ok') {
                     if (!($perm_reqd eq 'yes')) {
                         return(&retrievestudentphoto($udom,$unam,$ext));
@@ -3254,7 +3375,7 @@ sub studentphoto {
         }
     } else {
         my ($result,$perm_reqd) = 
-	    &Apache::lonnet::auto_photo_permission($unam,$udom);
+	    &auto_photo_permission($unam,$udom);
         if ($result eq 'ok') {
             if (!($perm_reqd eq 'yes')) {
                 return(&retrievestudentphoto($udom,$unam,$ext));
@@ -3266,14 +3387,14 @@ sub studentphoto {
 
 sub retrievestudentphoto {
     my ($udom,$unam,$ext,$type) = @_;
-    my $home=&Apache::lonnet::homeserver($unam,$udom);
-    my $ret=&Apache::lonnet::reply("studentphoto:$udom:$unam:$ext:$type",$home);
+    my $home=&homeserver($unam,$udom);
+    my $ret=&reply("studentphoto:$udom:$unam:$ext:$type",$home);
     if ($ret eq 'ok') {
         my $url="/uploaded/$udom/$unam/internal/studentphoto.$ext";
         if ($type eq 'thumbnail') {
             $url="/uploaded/$udom/$unam/internal/studentphoto_tn.$ext"; 
         }
-        my $tokenurl=&Apache::lonnet::tokenwrapper($url);
+        my $tokenurl=&tokenwrapper($url);
         return $tokenurl;
     } else {
         if ($type eq 'thumbnail') {
@@ -3550,8 +3671,8 @@ sub ssi {
         ($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'}:'')))) {
+        (&allowed('mgr',$env{'request.course.id'}.
+                        ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {
         $islocal = 1;
     }
     $response= &LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,
@@ -3702,7 +3823,7 @@ sub can_edit_resource {
     }
 
     if ($env{'request.course.id'}) {
-        my $crsedit = &Apache::lonnet::allowed('mdc',$env{'request.course.id'});
+        my $crsedit = &allowed('mdc',$env{'request.course.id'});
         if ($group ne '') {
 # if this is a group homepage or group bulletin board, check group privs
             my $allowed = 0;
@@ -3731,7 +3852,7 @@ sub can_edit_resource {
             }
         } else {
             if ($resurl =~ m{^/?adm/viewclasslist$}) {
-                unless (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) {
+                unless (&allowed('opa',$env{'request.course.id'})) {
                     return;
                 }
             } elsif (!$crsedit) {
@@ -4659,7 +4780,7 @@ sub bubblesheet_converter {
                     next if (($num == 1) && ($csvoptions{'hdr'} == 1));
                     $line =~ s{[\r\n]+$}{};
                     my %found;
-                    my @values = split(/,/,$line);
+                    my @values = split(/,/,$line,-1);
                     my ($qstart,$record);
                     for (my $i=0; $i<@values; $i++) {
                         if ((($qstart ne '') && ($i > $qstart)) ||
@@ -4842,6 +4963,7 @@ sub get_scantronformat_file {
                 close($fh);
             }
         }
+        chomp(@lines);
     }
     return @lines;
 }
@@ -4963,6 +5085,29 @@ 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'};
             }
@@ -4989,7 +5134,7 @@ sub flushcourselogs {
     foreach my $entry (keys(%userrolehash)) {
         my ($role,$uname,$udom,$runame,$rudom,$rsec)=
 	    split(/\:/,$entry);
-        if (&Apache::lonnet::put('nohist_userroles',
+        if (&put('nohist_userroles',
              { $role.':'.$uname.':'.$udom.':'.$rsec => $userrolehash{$entry} },
                 $rudom,$runame) eq 'ok') {
 	    delete $userrolehash{$entry};
@@ -5188,7 +5333,7 @@ sub domainrolelog {
     my ($trole,$username,$domain,$area,$tstart,$tend,$delflag,$context)=@_;
     if ($area =~ m{^/($match_domain)/$}) {
         my $cdom = $1;
-        my $domconfiguser = &Apache::lonnet::get_domainconfiguser($cdom);
+        my $domconfiguser = &get_domainconfiguser($cdom);
         my $namespace = 'rolelog';
         my %storehash = (
                            role    => $trole,
@@ -5419,8 +5564,8 @@ sub get_my_adhocroles {
     } elsif ($cid =~ /^($match_domain)_($match_courseid)$/) {
         $cdom = $1;
         $cnum = $2;
-        %info = &Apache::lonnet::get('environment',['internal.coursecode'],
-                                     $cdom,$cnum);
+        %info = &get('environment',['internal.coursecode'],
+                     $cdom,$cnum);
     }
     if (($info{'internal.coursecode'} ne '') && ($checkreg)) {
         my $user = $env{'user.name'}.':'.$env{'user.domain'};
@@ -5747,7 +5892,7 @@ sub extract_lastaccess {
 
 sub dcmailput {
     my ($domain,$msgid,$message,$server)=@_;
-    my $status = &Apache::lonnet::critical(
+    my $status = &critical(
        'dcmailput:'.$domain.':'.&escape($msgid).'='.
        &escape($message),$server);
     return $status;
@@ -6749,31 +6894,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 {
@@ -7133,7 +7278,7 @@ sub dump {
     my $rep;
     if ($encrypt) {
         $rep=&reply("encrypt:edump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
-    } else { 
+    } else {
         $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
     }
     my @pairs=split(/\&/,$rep);
@@ -7340,7 +7485,7 @@ sub putstore {
                      '&host='.&escape($perlvar{'lonHostID'}).
                      '&version='.$esc_v.
                      '&by='.&escape($env{'user.name'}.':'.$env{'user.domain'});
-       &Apache::lonnet::courselog($symb.':'.$uname.':'.$udomain.':PUTSTORE:'.$namevalue);
+       &courselog($symb.':'.$uname.':'.$udomain.':PUTSTORE:'.$namevalue);
    }
    if ($reply eq 'unknown_cmd') {
        # gfall back to way things use to be done
@@ -7490,16 +7635,16 @@ sub get_timebased_id {
     my $tries = 0;
 
 # attempt to get lock on nohist_$namespace file
-    my $gotlock = &Apache::lonnet::newput('nohist_'.$namespace,$lockhash,$cdom,$cnum);
+    my $gotlock = &newput('nohist_'.$namespace,$lockhash,$cdom,$cnum);
     while (($gotlock ne 'ok') && $tries <$locktries) {
         $tries ++;
         sleep 1;
-        $gotlock = &Apache::lonnet::newput('nohist_'.$namespace,$lockhash,$cdom,$cnum);
+        $gotlock = &newput('nohist_'.$namespace,$lockhash,$cdom,$cnum);
     }
 
 # attempt to get unique identifier, based on current timestamp
     if ($gotlock eq 'ok') {
-        my %inuse = &Apache::lonnet::dump('nohist_'.$namespace,$cdom,$cnum,$prefix);
+        my %inuse = &dump('nohist_'.$namespace,$cdom,$cnum,$prefix);
         my $id = time;
         $newid = $id;
         if ($idtype eq 'addcode') {
@@ -7520,7 +7665,7 @@ sub get_timebased_id {
             my %new_item =  (
                               $prefix."\0".$newid => $who,
                             );
-            my $putresult = &Apache::lonnet::put('nohist_'.$namespace,\%new_item,
+            my $putresult = &put('nohist_'.$namespace,\%new_item,
                                                  $cdom,$cnum);
             if ($putresult ne 'ok') {
                 undef($newid);
@@ -7560,15 +7705,15 @@ sub portfolio_access {
     if ($result) {
         my %setters;
         if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
-            my ($startblock,$endblock) =
-                &Apache::loncommon::blockcheck(\%setters,'port',$unum,$udom);
-            if ($startblock && $endblock) {
+            my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) =
+                &Apache::loncommon::blockcheck(\%setters,'port',$clientip,$unum,$udom);
+            if (($startblock && $endblock) || ($by_ip)) {
                 return 'B';
             }
         } else {
-            my ($startblock,$endblock) =
-                &Apache::loncommon::blockcheck(\%setters,'port');
-            if ($startblock && $endblock) {
+            my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) =
+                &Apache::loncommon::blockcheck(\%setters,'port',$clientip);
+            if (($startblock && $endblock) || ($by_ip)) {
                 return 'B';
             }
         }
@@ -7834,6 +7979,7 @@ sub usertools_access {
                       blog      => 1,
                       webdav    => 1,
                       portfolio => 1,
+                      timezone  => 1,
                  );
     }
     return if (!defined($tools{$tool}));
@@ -7958,7 +8104,7 @@ sub is_course_owner {
             if ($env{'course.'.$cdom.'_'.$cnum.'.internal.courseowner'} eq $uname.':'.$udom) {
                 return 1;
             } else {
-                my %courseinfo = &Apache::lonnet::coursedescription($cdom.'/'.$cnum);
+                my %courseinfo = &coursedescription($cdom.'/'.$cnum);
                 if ($courseinfo{'internal.courseowner'} eq $uname.':'.$udom) {
                     return 1;
                 }
@@ -8029,6 +8175,7 @@ sub check_can_request {
     my @options = ('approval','validate','autolimit');
     my $optregex = join('|',@options);
     if ((ref($can_request) eq 'HASH') && (ref($types) eq 'ARRAY')) {
+        my %willtrust;
         foreach my $type (@{$types}) {
             if (&usertools_access($uname,$udom,$type,undef,
                                   'requestcourses')) {
@@ -8048,12 +8195,17 @@ sub check_can_request {
                         if (ref($request_domains) eq 'HASH') {
                             my ($otherdom) = ($item =~ /^($match_domain):($optregex)(=?\d*)$/);
                             if ($otherdom ne '') {
-                                if (ref($request_domains->{$type}) eq 'ARRAY') {
-                                    unless (grep(/^\Q$otherdom\E$/,@{$request_domains->{$type}})) {
+                                unless (exists($willtrust{$otherdom})) {
+                                    $willtrust{$otherdom} = &will_trust('reqcrs',$env{'user.domain'},$otherdom);
+                                }
+                                if ($willtrust{$otherdom}) {
+                                    if (ref($request_domains->{$type}) eq 'ARRAY') {
+                                        unless (grep(/^\Q$otherdom\E$/,@{$request_domains->{$type}})) {
+                                            push(@{$request_domains->{$type}},$otherdom);
+                                        }
+                                    } else {
                                         push(@{$request_domains->{$type}},$otherdom);
                                     }
-                                } else {
-                                    push(@{$request_domains->{$type}},$otherdom);
                                 }
                             }
                         }
@@ -8130,7 +8282,7 @@ sub allowed {
     $uri=&declutter($uri);
 
     if ($priv eq 'evb') {
-# Evade communication block restrictions for specified role in a course
+# Evade communication block restrictions for specified role in a course or domain
         if ($env{'user.priv.'.$role} =~/evb\&([^\:]*)/) {
             return $1;
         } else {
@@ -8151,9 +8303,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) = 
-            &Apache::loncommon::blockcheck(\%setters,'port');
-        if ($startblock && $endblock) {
+        my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = 
+            &Apache::loncommon::blockcheck(\%setters,'port',$clientip);
+        if (($startblock && $endblock) || ($by_ip)) {
             return 'B';
         } else {
             return 'F';
@@ -8844,7 +8996,7 @@ sub get_comm_blocks {
     if ((defined($cached)) && (ref($blocksref) eq 'HASH')) {
         %commblocks = %{$blocksref};
     } else {
-        %commblocks = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
+        %commblocks = &dump('comm_block',$cdom,$cnum);
         my $cachetime = 600;
         &do_cache_new('comm_block',$hashid,\%commblocks,$cachetime);
     }
@@ -8855,7 +9007,11 @@ sub get_commblock_resources {
     my ($blocks) = @_;
     my %blockers = ();
     return %blockers unless ($env{'request.course.id'});
-    return %blockers if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/);
+    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\&([^\:]*)/);
     my %commblocks;
     if (ref($blocks) eq 'HASH') {
         %commblocks = %{$blocks};
@@ -8887,10 +9043,9 @@ 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;
+                    my (@interval,$mapname);
                     my $type = 'map';
                     if ($item eq 'course') {
                         $type = 'course';
@@ -8899,36 +9054,11 @@ 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 {
-                            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;
-                                                    }
-                                                }
-                                            }
-                                        }
-                                    }
-                                }
+                            $mapname = &deversion($item);
+                            if (ref($navmap)) {
+                                my $timelimit = $navmap->get_mapparam(undef,$mapname,'0.interval');
+                                @interval = ($timelimit,'map');
                             }
                         }
                     }
@@ -8946,10 +9076,37 @@ sub get_commblock_resources {
                             my $timesup = $first_access+$timelimit;
                             if ($timesup > $now) {
                                 my $activeblock;
-                                foreach my $res (@to_test) {
-                                    if ($res->answerable()) {
-                                        $activeblock = 1;
-                                        last;
+                                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;
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                        }
                                     }
                                 }
                                 if ($activeblock) {
@@ -8979,8 +9136,12 @@ 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);
@@ -9065,17 +9226,34 @@ sub deeplink_check {
         if ($deeplink eq '') {
             $allow = 1;
         } else {
-            my ($listed,$scope,$access) = split(/,/,$deeplink);
-            if ($access eq 'any') {
+            my ($state,$others,$listed,$scope,$protect) = split(/,/,$deeplink);
+            if ($state ne 'only') {
                 $allow = 1;
-            } elsif ($deeplink_symb) {
-                if ($access eq 'only') {
+            } 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)) {
                     if ($scope eq 'res') {
                         if ($symb eq $deeplink_symb) {
                             $allow = 1;
                         }
                     } elsif (($scope eq 'map') || ($scope eq 'rec')) {
-                        my ($map_from_symb,$map_from_login); 
+                        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]);
@@ -9093,23 +9271,6 @@ sub deeplink_check {
                             }
                         }
                     }
-                } else {
-                    my ($acctype,$item) = split(/:/,$access);
-                    if (($acctype eq 'lti') && ($env{'user.linkprotector'})) {
-                        if (grep(/^\Q$item\E$/,split(/,/,$env{'user.linkprotector'}))) {
-                            my %tinyurls = &get('tiny',[$symb],$cdom,$cnum);
-                            if (grep(/\Q$tinyurls{$symb}\E$/,split(/,/,$env{'user.linkproturis'}))) {
-                                $allow = 1;
-                            }
-                        }
-                    } elsif (($acctype eq 'key') && ($env{'user.deeplinkkey'})) {
-                        if (grep(/^\Q$item\E$/,split(/,/,$env{'user.deeplinkkey'}))) {
-                            my %tinyurls = &get('tiny',[$symb],$cdom,$cnum);
-                            if (grep(/\Q$tinyurls{$symb}\E$/,split(/,/,$env{'user.keyedlinkuri'}))) {
-                                $allow = 1;
-                            }
-                        }
-                    }
                 }
             }
         }
@@ -9379,7 +9540,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;
     }
@@ -10461,10 +10622,10 @@ sub store_coowners {
     }
     if (($putresult eq 'ok') || ($delresult eq 'ok')) {
         my %crsinfo =
-            &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,undef,undef,'.');
+            &courseiddump($cdom,'.',1,'.','.',$cnum,undef,undef,'.');
         if (ref($crsinfo{$cid}) eq 'HASH') {
             $crsinfo{$cid}{'co-owners'} = \@newcoowners;
-            my $cidput = &Apache::lonnet::courseidput($cdom,\%crsinfo,$chome,'notime');
+            my $cidput = &courseidput($cdom,\%crsinfo,$chome,'notime');
         }
     }
 }
@@ -10682,7 +10843,7 @@ sub modifyuser {
         return 'error: '.$reply;
     }
     if ($names{'permanentemail'} ne $oldnames{'permanentemail'}) {
-        &Apache::lonnet::devalidate_cache_new('emailscache',$uname.':'.$udom);
+        &devalidate_cache_new('emailscache',$uname.':'.$udom);
     }
     my $sqlresult = &update_allusers_table($uname,$udom,\%names);
     &devalidate_cache_new('namescache',$uname.':'.$udom);
@@ -10762,7 +10923,7 @@ sub modify_student_enrollment {
     }
     my $fullname = &format_name($first,$middle,$last,$gene,'lastname');
     my $user = "$uname:$udom";
-    my %old_entry = &Apache::lonnet::get('classlist',[$user],$cdom,$cnum);
+    my %old_entry = &get('classlist',[$user],$cdom,$cnum);
     my $reply=cput('classlist',
 		   {$user => 
 			join(':',$end,$start,$uid,$usec,$fullname,$type,$locktype,$credits,$instsec) },
@@ -10896,7 +11057,7 @@ sub createcourse {
         }
     }
     my %host_servers =
-        &Apache::lonnet::get_servers($udom,'library');
+        &get_servers($udom,'library');
     unless ($host_servers{$course_server}) {
         return 'error: invalid home server for course: '.$course_server;
     }
@@ -12181,15 +12342,24 @@ sub resdata {
 
 sub get_domain_lti {
     my ($cdom,$context) = @_;
-    my ($name,%lti);
+    my ($name,$cachename,%lti);
     if ($context eq 'consumer') {
         $name = 'ltitools';
     } elsif ($context eq 'provider') {
         $name = 'lti';
+    } elsif ($context eq 'linkprot') {
+        $name = 'ltisec';
     } else {
         return %lti;
     }
-    my ($result,$cached)=&is_cached_new($name,$cdom);
+
+    if ($context eq 'linkprot') {
+        $cachename = $context;
+    } else {
+        $cachename = $name;
+    }
+    
+    my ($result,$cached)=&is_cached_new($cachename,$cdom);
     if (defined($cached)) {
         if (ref($result) eq 'HASH') {
             %lti = %{$result};
@@ -12197,43 +12367,191 @@ sub get_domain_lti {
     } else {
         my %domconfig = &get_dom('configuration',[$name],$cdom);
         if (ref($domconfig{$name}) eq 'HASH') {
-            %lti = %{$domconfig{$name}};
-            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};
+            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};
+                            }
                         }
                     }
                 }
             }
         }
         my $cachetime = 24*60*60;
-        &do_cache_new($name,$cdom,\%lti,$cachetime);
+        &do_cache_new($cachename,$cdom,\%lti,$cachetime);
     }
     return %lti;
 }
 
-sub get_numsuppfiles {
-    my ($cnum,$cdom,$ignorecache)=@_;
+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)=@_;
+    my $hashid=$cnum.':'.$cdom;
+    my ($numexttools,$cached);
+    unless ($ignorecache) {
+        ($numexttools,$cached) = &is_cached_new('supptools',$hashid);
+    }
+    unless (defined($cached)) {
+        my $chome=&homeserver($cnum,$cdom);
+        $numexttools = 0;
+        unless ($chome eq 'no_host') {
+            my ($supplemental) = &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 ++;
+                        }
+                    }
+                }
+            }
+        }
+        &do_cache_new('supptools',$hashid,$numexttools,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) = &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;
+}
+
+sub get_supplemental {
+    my ($cnum,$cdom,$ignorecache,$possdel)=@_;
     my $hashid=$cnum.':'.$cdom;
-    my ($suppcount,$cached);
+    my ($supplemental,$cached,$set_httprefs);
     unless ($ignorecache) {
-        ($suppcount,$cached) = &is_cached_new('suppcount',$hashid);
+        ($supplemental,$cached) = &is_cached_new('supplemental',$hashid);
     }
     unless (defined($cached)) {
         my $chome=&homeserver($cnum,$cdom);
         unless ($chome eq 'no_host') {
-            ($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);
+            my @order = @LONCAPA::map::order;
+            my @resources = @LONCAPA::map::resources;
+            my @resparms = @LONCAPA::map::resparms;
+            my @zombies = @LONCAPA::map::zombies;
+            my ($errors,%ids,%hidden);
+            $errors =
+                &Apache::loncommon::recurse_supplemental($cnum,$cdom,
+                                                         'supplemental.sequence',
+                                                         $errors,$possdel,\%ids,\%hidden);
+            @LONCAPA::map::order = @order;
+            @LONCAPA::map::resources = @resources;
+            @LONCAPA::map::resparms = @resparms;
+            @LONCAPA::map::zombies = @zombies;
+            $set_httprefs = 1;
+            if ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
+                &Apache::lonnet::appenv({'request.course.suppupdated' => time});
+            }
+            $supplemental = {
+                               ids => \%ids,
+                               hidden => \%hidden,
+                            };
+            &do_cache_new('supplemental',$hashid,$supplemental,600);
         }
-        &do_cache_new('suppcount',$hashid,$suppcount,600);
     }
-    return $suppcount;
+    return ($supplemental,$set_httprefs);
 }
 
 #
@@ -12306,7 +12624,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})) {
@@ -13309,13 +13627,13 @@ sub get_reservable_slots {
 sub get_course_slots {
     my ($cnum,$cdom) = @_;
     my $hashid=$cnum.':'.$cdom;
-    my ($result,$cached) = &Apache::lonnet::is_cached_new('allslots',$hashid);
+    my ($result,$cached) = &is_cached_new('allslots',$hashid);
     if (defined($cached)) {
         if (ref($result) eq 'HASH') {
             return %{$result};
         }
     } else {
-        my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum);
+        my %slots=&dump('slots',$cdom,$cnum);
         my ($tmp) = keys(%slots);
         if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
             &do_cache_new('allslots',$hashid,\%slots,600);
@@ -13361,11 +13679,68 @@ sub get_coursechange {
 }
 
 sub devalidate_coursechange_cache {
-    my ($cnum,$cdom)=@_;
-    my $hashid=$cnum.':'.$cdom;
+    my ($cdom,$cnum)=@_;
+    my $hashid=$cdom.'_'.$cnum;
     &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=&current_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 {
@@ -13552,17 +13927,10 @@ 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{$targetfn};
+	        $syval=$hash{$thisfn};
                 untie(%hash);
             }
             if ($syval && $checkforblock) {
@@ -14370,10 +14738,15 @@ sub machine_ids {
 
 sub additional_machine_domains {
     my @domains;
-    open(my $fh,"<","$perlvar{'lonTabDir'}/expected_domains.tab");
-    while( my $line = <$fh>) {
-        $line =~ s/\s//g;
-        push(@domains,$line);
+    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);
+        }
     }
     return @domains;
 }
@@ -14531,7 +14904,7 @@ sub get_requestor_ip {
 
 sub get_proxy_settings {
     my ($dom_in_use) = @_;
-    my %domdefaults = &Apache::lonnet::get_domain_defaults($dom_in_use);
+    my %domdefaults = &get_domain_defaults($dom_in_use);
     my $proxyinfo = {
                        ipheader => $domdefaults{'waf_ipheader'},
                        trusted  => $domdefaults{'waf_trusted'},
@@ -14564,12 +14937,11 @@ sub get_proxy_alias {
         if ($cached) {
             return $alias;
         }
-        my $dom = &Apache::lonnet::host_domain($lonid);
+        my $dom = &host_domain($lonid);
         if ($dom ne '') {
             my $cachetime = 60*60*24;
             my %domconfig =
-                &Apache::lonnet::get_dom('configuration',['wafproxy'],$dom);
-            my $alias;
+                &get_dom('configuration',['wafproxy'],$dom);
             if (ref($domconfig{'wafproxy'}) eq 'HASH') {
                 if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') {
                     $alias = $domconfig{'wafproxy'}{'alias'}{$lonid};
@@ -14587,7 +14959,7 @@ sub use_proxy_alias {
     if ($alias) {
         my $dom = &host_domain($lonid);
         if ($dom ne '') {
-            my $proxyinfo = &get_proxy_settings($dom );
+            my $proxyinfo = &get_proxy_settings($dom);
             my ($vpnint,$remote_ip);
             if (ref($proxyinfo) eq 'HASH') {
                 $vpnint = $proxyinfo->{'vpnint'};
@@ -14603,6 +14975,81 @@ sub use_proxy_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 = &host_domain($lonid);
+        if ($dom ne '') {
+            my $cachetime = 60*60*24;
+            my %domconfig =
+                &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 = &current_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 (&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 = &host_domain($lonid);
+    if ($dom ne '') {
+        my $cachetime = 60*60*24;
+        my %domconfig =
+            &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 {
@@ -14720,7 +15167,7 @@ sub get_dns {
     my ($url,$func,$ignore_cache,$nocache,$hashref) = @_;
     if (!$ignore_cache) {
 	my ($content,$cached)=
-	    &Apache::lonnet::is_cached_new('dns',$url);
+	    &is_cached_new('dns',$url);
 	if ($cached) {
 	    &$func($content,$hashref);
 	    return;
@@ -14799,7 +15246,7 @@ sub get_dns {
 sub parse_dns_checksums_tab {
     my ($lines,$hashref) = @_;
     my $lonhost = $perlvar{'lonHostID'};
-    my $machine_dom = &Apache::lonnet::host_domain($lonhost);
+    my $machine_dom = &host_domain($lonhost);
     my $loncaparev = &get_server_loncaparev($machine_dom);
     my $distro = (split(/\:/,&get_server_distarch($lonhost)))[0];
     my $webconfdir = '/etc/httpd/conf';
@@ -14843,7 +15290,7 @@ sub parse_dns_checksums_tab {
 
 sub fetch_dns_checksums {
     my %checksums;
-    my $machine_dom = &Apache::lonnet::host_domain($perlvar{'lonHostID'});
+    my $machine_dom = &host_domain($perlvar{'lonHostID'});
     my $loncaparev = &get_server_loncaparev($machine_dom,$perlvar{'lonHostID'});
     my ($release,$timestamp) = split(/\-/,$loncaparev);
     &get_dns("/adm/dns/checksums/$release",\&parse_dns_checksums_tab,1,1,
@@ -15231,7 +15678,7 @@ sub parse_getdns_url {
 		return %iphost;
 	    }
 	    my ($ip_info,$cached)=
-		&Apache::lonnet::is_cached_new('iphost','iphost');
+		&is_cached_new('iphost','iphost');
 	    if ($cached) {
 		%iphost      = %{$ip_info->[0]};
 		%name_to_ip  = %{$ip_info->[1]};
@@ -15243,7 +15690,7 @@ sub parse_getdns_url {
 	# get yesterday's info for fallback
 	my %old_name_to_ip;
 	my ($ip_info,$cached)=
-	    &Apache::lonnet::is_cached_new('iphost','iphost');
+	    &is_cached_new('iphost','iphost');
 	if ($cached) {
 	    %old_name_to_ip = %{$ip_info->[1]};
 	}
@@ -15310,7 +15757,7 @@ sub parse_getdns_url {
         my ($lonid) = @_;
         return if ($lonid eq '');
         my ($idnref,$cached)=
-            &Apache::lonnet::is_cached_new('internetnames',$lonid);
+            &is_cached_new('internetnames',$lonid);
         if ($cached) {
             return $idnref;
         }
@@ -16150,10 +16597,6 @@ 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