--- loncom/lonnet/perl/lonnet.pm	2019/07/18 18:28:58	1.1411
+++ loncom/lonnet/perl/lonnet.pm	2021/10/11 00:27:15	1.1468
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1411 2019/07/18 18:28:58 raeburn Exp $
+# $Id: lonnet.pm,v 1.1468 2021/10/11 00:27:15 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -79,7 +79,7 @@ use Encode;
 
 use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir $deftex
             $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease
-            %managerstab);
+            %managerstab $passwdmin);
 
 my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash,
     %userrolehash, $processmarker, $dumpcount, %coursedombuf,
@@ -96,6 +96,8 @@ use Cache::Memcached;
 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;
@@ -128,12 +130,13 @@ our @EXPORT = qw(%env);
 	$logid ++;
         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'    => $ENV{'REMOTE_ADDR'},
+                                   'exe_ip'    => $ip,
                                    'delflag'   => $delflag,
                                    'logentry'  => $storehash,
                                    'uname'     => $uname,
@@ -465,14 +468,15 @@ sub reply {
             my $subcmd = $1;
             if (($subcmd eq 'auth') || ($subcmd eq 'passwd') ||
                 ($subcmd eq 'changeuserauth') || ($subcmd eq 'makeuser') ||
-                ($subcmd eq 'putdom') || ($subcmd eq 'autoexportgrades')) {
+                ($subcmd eq 'putdom') || ($subcmd eq 'autoexportgrades') ||
+                ($subcmd eq 'put')) {
                 (undef,undef,my @rest) = split(/:/,$cmd);
                 if (($subcmd eq 'auth') || ($subcmd eq 'putdom')) {
                     splice(@rest,2,1,'Hidden');
                 } elsif ($subcmd eq 'passwd') {
                     splice(@rest,2,2,('Hidden','Hidden'));
                 } elsif (($subcmd eq 'changeuserauth') || ($subcmd eq 'makeuser') ||
-                         ($subcmd eq 'autoexportgrades')) {
+                         ($subcmd eq 'autoexportgrades') || ($subcmd eq 'put')) {
                     splice(@rest,3,1,'Hidden');
                 }
                 $logged = join(':',('encrypt:'.$subcmd,@rest));
@@ -738,6 +742,9 @@ 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'};
@@ -971,7 +978,7 @@ sub userload {
 # ------------------------------ Find server with least workload from spare.tab
 
 sub spareserver {
-    my ($loadpercent,$userloadpercent,$want_server_name,$udom) = @_;
+    my ($r,$loadpercent,$userloadpercent,$want_server_name,$udom) = @_;
     my $spare_server;
     if ($userloadpercent !~ /\d/) { $userloadpercent=0; }
     my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent 
@@ -1016,6 +1023,8 @@ 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;
             }
         }
@@ -1150,6 +1159,21 @@ sub check_for_balancer_cookie {
     return ($otherserver,$cookie);
 }
 
+sub updatebalcookie {
+    my ($cookie,$balancer,$lastentry)=@_;
+    if ($cookie =~ /^($match_domain)\_($match_username)\_[a-f0-9]{32}$/) {
+        my ($udom,$uname) = ($1,$2);
+        my $uprimary_id = &domain($udom,'primary');
+        my $uintdom = &internet_dom($uprimary_id);
+        my $intdom = &internet_dom($balancer);
+        my $serverhomedom = &host_domain($balancer);
+        if (($uintdom ne '') && ($uintdom eq $intdom)) {
+            return &reply('updatebalcookie:'.&escape($cookie).':'.&escape($lastentry),$balancer);
+        }
+    }
+    return;
+}
+
 sub delbalcookie {
     my ($cookie,$balancer) =@_;
     if ($cookie =~ /^($match_domain)\_($match_username)\_[a-f0-9]{32}$/) {
@@ -1159,7 +1183,7 @@ sub delbalcookie {
         my $intdom = &internet_dom($balancer);
         my $serverhomedom = &host_domain($balancer);
         if (($uintdom ne '') && ($uintdom eq $intdom)) {
-            return &reply("delbalcookie:$cookie",$balancer);
+            return &reply('delbalcookie:'.&escape($cookie),$balancer);
         }
     }
 }
@@ -1230,6 +1254,28 @@ sub choose_server {
     return ($login_host,$hostname,$portal_path,$isredirect,$lowest_load);
 }
 
+sub get_course_sessions {
+    my ($cnum,$cdom,$lastactivity) = @_;
+    my %servers = &internet_dom_servers($cdom);
+    my %returnhash;
+    foreach my $server (sort(keys(%servers))) {
+        my $rep = &reply("coursesessions:$cdom:$cnum:$lastactivity",$server);
+        my @pairs=split(/\&/,$rep);
+        unless (($rep eq 'unknown_cmd') || ($rep =~ /^error/)) {
+            foreach my $item (@pairs) {
+                my ($key,$value)=split(/=/,$item,2);
+                $key = &unescape($key);
+                next if ($key =~ /^error: 2 /);
+                if (exists($returnhash{$key})) {
+                    next if ($value < $returnhash{$key});
+                }
+                $returnhash{$key}=$value;
+            }
+        }
+    }
+    return %returnhash;
+}
+
 # --------------------------------------------- Try to change a user's password
 
 sub changepass {
@@ -1406,6 +1452,15 @@ 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);
@@ -1624,7 +1679,7 @@ sub check_loadbalancing {
     if ($domneedscache) {
         &do_cache_new('loadbalancing',$domneedscache,$is_balancer,$cachetime);
     }
-    if ($is_balancer) {
+    if (($is_balancer) && ($caller ne 'switchserver')) {
         my $lowest_load = 30000;
         if (ref($offloadto) eq 'HASH') {
             if (ref($offloadto->{'primary'}) eq 'ARRAY') {
@@ -1664,9 +1719,9 @@ sub check_loadbalancing {
                 }
             }
         }
-        unless ($homeintdom) {
-            undef($setcookie);
-        }
+    }
+    if (($is_balancer) && (!$homeintdom)) {
+        undef($setcookie);
     }
     return ($is_balancer,$otherserver,$setcookie,$offloadto,$dom_balancers);
 }
@@ -2092,7 +2147,7 @@ sub dump_dom {
 # ------------------------------------------ get items from domain db files   
 
 sub get_dom {
-    my ($namespace,$storearr,$udom,$uhome)=@_;
+    my ($namespace,$storearr,$udom,$uhome,$encrypt)=@_;
     return if ($udom eq 'public');
     my $items='';
     foreach my $item (@$storearr) {
@@ -2116,10 +2171,15 @@ sub get_dom {
     }
     if ($udom && $uhome && ($uhome ne 'no_host')) {
         my $rep;
-        if ($namespace =~ /^enc/) {
-            $rep=&reply("encrypt:egetdom:$udom:$namespace:$items",$uhome);
+        if (grep { $_ eq $uhome } &current_machine_ids()) {
+            # domain information is hosted on this machine
+            $rep = &LONCAPA::Lond::get_dom("getdom:$udom:$namespace:$items");
         } else {
-            $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
+            if ($encrypt) {
+                $rep=&reply("encrypt:egetdom:$udom:$namespace:$items",$uhome);
+            } else {
+                $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
+            }
         }
         my %returnhash;
         if ($rep eq '' || $rep =~ /^error: 2 /) {
@@ -2143,7 +2203,7 @@ sub get_dom {
 # -------------------------------------------- put items in domain db files 
 
 sub put_dom {
-    my ($namespace,$storehash,$udom,$uhome)=@_;
+    my ($namespace,$storehash,$udom,$uhome,$encrypt)=@_;
     if (!$udom) {
         $udom=$env{'user.domain'};
         if (defined(&domain($udom,'primary'))) {
@@ -2164,7 +2224,7 @@ sub put_dom {
             $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&';
         }
         $items=~s/\&$//;
-        if ($namespace =~ /^enc/) {
+        if ($encrypt) {
             return &reply("encrypt:putdom:$udom:$namespace:$items",$uhome);
         } else {
             return &reply("putdom:$udom:$namespace:$items",$uhome);
@@ -2244,7 +2304,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';
         }
@@ -2261,7 +2321,7 @@ sub inst_directory_query {
     if ($homeserver ne '') {
         unless ($homeserver eq $perlvar{'lonHostID'}) {
             if ($srch->{'srchby'} eq 'email') {
-                my $lcrev = &get_server_loncaparev(undef,$homeserver);
+                my $lcrev = &get_server_loncaparev($udom,$homeserver);
                 my ($major,$minor) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                 if (($major eq '' && $minor eq '') || ($major < 2) ||
                     (($major == 2) && ($minor < 12))) {
@@ -2312,7 +2372,7 @@ sub usersearch {
         if (&host_domain($tryserver) eq $dom) {
             unless ($tryserver eq $perlvar{'lonHostID'}) {
                 if ($srch->{'srchby'} eq 'email') {
-                    my $lcrev = &get_server_loncaparev(undef,$tryserver);
+                    my $lcrev = &get_server_loncaparev($dom,$tryserver);
                     my ($major,$minor) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                     next if (($major eq '' && $minor eq '') || ($major < 2) ||
                              (($major == 2) && ($minor < 12)));
@@ -2557,7 +2617,7 @@ sub get_domain_defaults {
                                   'coursedefaults','usersessions',
                                   'requestauthor','selfenrollment',
                                   'coursecategories','ssl','autoenroll',
-                                  'trust','helpsettings'],$domain);
+                                  'trust','helpsettings','wafproxy'],$domain);
     my @coursetypes = ('official','unofficial','community','textbook','placement');
     if (ref($domconfig{'defaults'}) eq 'HASH') {
         $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; 
@@ -2650,6 +2710,9 @@ 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') {
@@ -2682,7 +2745,7 @@ sub get_domain_defaults {
     if (ref($domconfig{'coursecategories'}) eq 'HASH') {
         $domdefaults{'catauth'} = 'std';
         $domdefaults{'catunauth'} = 'std';
-        if ($domconfig{'coursecategories'}{'auth'}) { 
+        if ($domconfig{'coursecategories'}{'auth'}) {
             $domdefaults{'catauth'} = $domconfig{'coursecategories'}{'auth'};
         }
         if ($domconfig{'coursecategories'}{'unauth'}) {
@@ -2717,12 +2780,78 @@ sub get_domain_defaults {
             $domdefaults{'adhocroles'} = $domconfig{'helpsettings'}{'adhoc'};
         }
     }
+    if (ref($domconfig{'wafproxy'}) eq 'HASH') {
+        foreach my $item ('ipheader','trusted','vpnint','vpnext','sslopt') {
+            if ($domconfig{'wafproxy'}{$item}) {
+                $domdefaults{'waf_'.$item} = $domconfig{'wafproxy'}{$item};
+            }
+        }
+    } 
     &do_cache_new('domdefaults',$domain,\%domdefaults,$cachetime);
     return %domdefaults;
 }
 
+sub get_dom_cats {
+    my ($dom) = @_;
+    return unless (&domain($dom));
+    my ($cats,$cached)=&is_cached_new('cats',$dom);
+    unless (defined($cached)) {
+        my %domconfig = &get_dom('configuration',['coursecategories'],$dom);
+        if (ref($domconfig{'coursecategories'}) eq 'HASH') {
+            if (ref($domconfig{'coursecategories'}{'cats'}) eq 'HASH') {
+                %{$cats} = %{$domconfig{'coursecategories'}{'cats'}};
+            } else {
+                $cats = {};
+            }
+        } else {
+            $cats = {};
+        }
+        &Apache::lonnet::do_cache_new('cats',$dom,$cats,3600);
+    }
+    return $cats;
+}
+
+sub get_dom_instcats {
+    my ($dom) = @_;
+    return unless (&domain($dom));
+    my ($instcats,$cached)=&is_cached_new('instcats',$dom);
+    unless (defined($cached)) {
+        my (%coursecodes,%codes,@codetitles,%cat_titles,%cat_order);
+        my $totcodes = &retrieve_instcodes(\%coursecodes,$dom);
+        if ($totcodes > 0) {
+            my $caller = 'global';
+            if (&auto_instcode_format($caller,$dom,\%coursecodes,\%codes,
+                                      \@codetitles,\%cat_titles,\%cat_order) eq 'ok') {
+                $instcats = {
+                                codes => \%codes,
+                                codetitles => \@codetitles,
+                                cat_titles => \%cat_titles,
+                                cat_order => \%cat_order,
+                            };
+                &do_cache_new('instcats',$dom,$instcats,3600);
+            }
+        }
+    }
+    return $instcats;
+}
+
+sub retrieve_instcodes {
+    my ($coursecodes,$dom) = @_;
+    my $totcodes;
+    my %courses = &courseiddump($dom,'.',1,'.','.','.',undef,undef,'Course');
+    foreach my $course (keys(%courses)) {
+        if (ref($courses{$course}) eq 'HASH') {
+            if ($courses{$course}{'inst_code'} ne '') {
+                $$coursecodes{$course} = $courses{$course}{'inst_code'};
+                $totcodes ++;
+            }
+        }
+    }
+    return $totcodes;
+}
+
 sub course_portal_url {
-    my ($cnum,$cdom) = @_;
+    my ($cnum,$cdom,$r) = @_;
     my $chome = &homeserver($cnum,$cdom);
     my $hostname = &hostname($chome);
     my $protocol = $protocol{$chome};
@@ -2732,6 +2861,8 @@ sub course_portal_url {
     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;
@@ -3302,6 +3433,32 @@ sub repcopy {
     }
 }
 
+# ------------------------------------------------- Unsubscribe from a resource
+
+sub unsubscribe {
+    my ($fname) = @_;
+    my $answer;
+    if ($fname=~/\/(aboutme|syllabus|bulletinboard|smppg)$/) { return $answer; }
+    $fname=~s/[\n\r]//g;
+    my $author=$fname;
+    $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
+    my ($udom,$uname)=split(/\//,$author);
+    my $home=homeserver($uname,$udom);
+    if ($home eq 'no_host') {
+        $answer = 'no_host';
+    } elsif (grep { $_ eq $home } &current_machine_ids()) {
+        $answer = 'home';
+    } else {
+        my $defdom = $perlvar{'lonDefDomain'};
+        if (&will_trust('content',$defdom,$udom)) {
+            $answer = reply("unsub:$fname",$home);
+        } else {
+            $answer = 'untrusted';
+        }
+    }
+    return $answer;
+}
+
 # ------------------------------------------------ Get server side include body
 sub ssi_body {
     my ($filelink,%form)=@_;
@@ -3330,11 +3487,29 @@ sub ssi_body {
 # --------------------------------------------------------- Server Side Include
 
 sub absolute_url {
-    my ($host_name) = @_;
+    my ($host_name,$unalias,$keep_proto) = @_;
     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;
 }
 
@@ -3351,12 +3526,13 @@ sub absolute_url {
 sub ssi {
 
     my ($fn,%form)=@_;
-    my $request;
+    my ($host,$request,$response);
+    $host = &absolute_url('',1);
 
     $form{'no_update_last_known'}=1;
     &Apache::lonenc::check_encrypt(\$fn);
     if (%form) {
-      $request=new HTTP::Request('POST',&absolute_url().$fn);
+      $request=new HTTP::Request('POST',$host.$fn);
       $request->content(join('&',map { 
             my $name = escape($_);
             "$name=" . ( ref($form{$_}) eq 'ARRAY' 
@@ -3364,7 +3540,7 @@ sub ssi {
             : &escape($form{$_}) );    
         } keys(%form)));
     } else {
-      $request=new HTTP::Request('GET',&absolute_url().$fn);
+      $request=new HTTP::Request('GET',$host.$fn);
     }
 
     $request->header(Cookie => $ENV{'HTTP_COOKIE'});
@@ -3378,8 +3554,8 @@ sub ssi {
                                  ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {
         $islocal = 1;
     }
-    my $response= &LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,
-                                                '','','',$islocal);
+    $response= &LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,
+                                             '','','',$islocal);
 
     if (wantarray) {
 	return ($response->content, $response);
@@ -3447,12 +3623,18 @@ sub remove_stale_resfile {
                                     $stale = 1;
                                 }
                                 if ($stale) {
-                                    unlink($fname);
-                                    if ($uri!~/\.meta$/) {
-                                        unlink($fname.'.meta');
+                                    if (unlink($fname)) {
+                                        if ($uri!~/\.meta$/) {
+                                            if (-e $fname.'.meta') {
+                                                unlink($fname.'.meta');
+                                            }
+                                        }
+                                        my $unsubresult = &unsubscribe($fname);
+                                        unless ($unsubresult eq 'ok') {
+                                            &logthis("no unsub of $fname from $homeserver, reason: $unsubresult");
+                                        }
+                                        $removed = 1;
                                     }
-                                    &reply("unsub:$fname",$homeserver);
-                                    $removed = 1;
                                 }
                             }
                         }
@@ -3602,6 +3784,18 @@ sub can_edit_resource {
                         $forceedit = 1;
                     }
                     $cfile = $resurl;
+                } elsif (($resurl =~ m{^/ext/}) && ($symb ne '')) {
+                    my ($map,$id,$res) = &decode_symb($symb);
+                    if ($map =~ /\.page$/) {
+                        $incourse = 1;
+                        if ($env{'form.forceedit'}) {
+                            $forceview = 1;
+                            $cfile = $map;
+                        } else {
+                            $forceedit = 1;
+                            $cfile =  '/adm/wrapper'.$resurl;
+                        }
+                    }
                 } elsif ($resurl =~ m{^/adm/wrapper/adm/$cdom/$cnum/\d+/ext\.tool$}) {
                     $incourse = 1;
                     if ($env{'form.forceedit'}) {
@@ -3627,13 +3821,13 @@ sub can_edit_resource {
                     $cfile = $template;
                 }
             } elsif (($resurl =~ m{^/adm/wrapper/ext/}) && ($env{'form.folderpath'} =~ /^supplemental/)) {
-                    $incourse = 1;
-                    if ($env{'form.forceedit'}) {
-                        $forceview = 1;
-                    } else {
-                        $forceedit = 1;
-                    }
-                    $cfile = $resurl;
+                $incourse = 1;
+                if ($env{'form.forceedit'}) {
+                    $forceview = 1;
+                } else {
+                    $forceedit = 1;
+                }
+                $cfile = $resurl;
             } elsif (($resurl =~ m{^/adm/wrapper/adm/$cdom/$cnum/\d+/ext\.tool$}) && ($env{'form.folderpath'} =~ /^supplemental/)) {
                 $incourse = 1;
                 if ($env{'form.forceedit'}) {
@@ -3904,6 +4098,10 @@ 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;
 }
 
@@ -4874,7 +5072,11 @@ sub courseacclog {
                 if ($formitem =~ /^HWFILE(?:SIZE|TOOBIG)/) {
                     $what.=':'.$formitem.'='.$env{$key};
                 } elsif ($formitem !~ /^HWFILE(?:[^.]+)$/) {
-                    $what.=':'.$formitem.'='.$env{$key};
+                    if ($formitem eq 'proctorpassword') {
+                        $what.=':'.$formitem.'=' . '*' x length($env{$key});
+                    } else {
+                        $what.=':'.$formitem.'='.$env{$key};
+                    }
                 }
             }
         }
@@ -5956,7 +6158,7 @@ sub tmpreset {
   if (!$domain) { $domain=$env{'user.domain'}; }
   if (!$stuname) { $stuname=$env{'user.name'}; }
   if ($domain eq 'public' && $stuname eq 'public') {
-      $stuname=$ENV{'REMOTE_ADDR'};
+      $stuname=&get_requestor_ip();
   }
   my $path=LONCAPA::tempdir();
   my %hash;
@@ -5993,7 +6195,7 @@ sub tmpstore {
   if (!$domain) { $domain=$env{'user.domain'}; }
   if (!$stuname) { $stuname=$env{'user.name'}; }
   if ($domain eq 'public' && $stuname eq 'public') {
-      $stuname=$ENV{'REMOTE_ADDR'};
+      $stuname=&get_requestor_ip();
   }
   my $now=time;
   my %hash;
@@ -6037,7 +6239,7 @@ sub tmprestore {
   if (!$domain) { $domain=$env{'user.domain'}; }
   if (!$stuname) { $stuname=$env{'user.name'}; }
   if ($domain eq 'public' && $stuname eq 'public') {
-      $stuname=$ENV{'REMOTE_ADDR'};
+      $stuname=&get_requestor_ip();
   }
   my %returnhash;
   $namespace=~s/\//\_/g;
@@ -6093,7 +6295,7 @@ sub store {
     }
     if (!$home) { $home=$env{'user.home'}; }
 
-    $$storehash{'ip'}=$ENV{'REMOTE_ADDR'};
+    $$storehash{'ip'}=&get_requestor_ip();
     $$storehash{'host'}=$perlvar{'lonHostID'};
 
     my $namevalue='';
@@ -6129,7 +6331,7 @@ sub cstore {
     }
     if (!$home) { $home=$env{'user.home'}; }
 
-    $$storehash{'ip'}=$ENV{'REMOTE_ADDR'};
+    $$storehash{'ip'}=&get_requestor_ip();
     $$storehash{'host'}=$perlvar{'lonHostID'};
 
     my $namevalue='';
@@ -6912,7 +7114,7 @@ sub unserialize {
 # see Lond::dump_with_regexp
 # if $escapedkeys hash keys won't get unescaped.
 sub dump {
-    my ($namespace,$udomain,$uname,$regexp,$range,$escapedkeys)=@_;
+    my ($namespace,$udomain,$uname,$regexp,$range,$escapedkeys,$encrypt)=@_;
     if (!$udomain) { $udomain=$env{'user.domain'}; }
     if (!$uname) { $uname=$env{'user.name'}; }
     my $uhome=&homeserver($uname,$udomain);
@@ -6928,7 +7130,12 @@ sub dump {
                     $uname, $namespace, $regexp, $range)), $perlvar{'lonVersion'});
         return %{unserialize($reply, $escapedkeys)};
     }
-    my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
+    my $rep;
+    if ($encrypt) {
+        $rep=&reply("encrypt:edump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
+    } else {
+        $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
+    }
     my @pairs=split(/\&/,$rep);
     my %returnhash=();
     if (!($rep =~ /^error/ )) {
@@ -7075,7 +7282,7 @@ sub inc {
 # --------------------------------------------------------------- put interface
 
 sub put {
-   my ($namespace,$storehash,$udomain,$uname)=@_;
+   my ($namespace,$storehash,$udomain,$uname,$encrypt)=@_;
    if (!$udomain) { $udomain=$env{'user.domain'}; }
    if (!$uname) { $uname=$env{'user.name'}; }
    my $uhome=&homeserver($uname,$udomain);
@@ -7084,7 +7291,11 @@ sub put {
        $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&';
    }
    $items=~s/\&$//;
-   return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
+   if ($encrypt) {
+       return &reply("encrypt:put:$udomain:$uname:$namespace:$items",$uhome);
+   } else {
+       return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
+   }
 }
 
 # ------------------------------------------------------------ newput interface
@@ -7124,7 +7335,8 @@ sub putstore {
        foreach my $key (keys(%{$storehash})) {
            $namevalue.=&escape($key).'='.&freeze_escape($storehash->{$key}).'&';
        }
-       $namevalue .= 'ip='.&escape($ENV{'REMOTE_ADDR'}).
+       my $ip = &get_requestor_ip();
+       $namevalue .= 'ip='.&escape($ip).
                      '&host='.&escape($perlvar{'lonHostID'}).
                      '&version='.$esc_v.
                      '&by='.&escape($env{'user.name'}.':'.$env{'user.domain'});
@@ -7911,7 +8123,7 @@ sub customaccess {
 # ------------------------------------------------- Check for a user privilege
 
 sub allowed {
-    my ($priv,$uri,$symb,$role,$clientip,$noblockcheck)=@_;
+    my ($priv,$uri,$symb,$role,$clientip,$noblockcheck,$ignorecache,$nodeeplinkcheck,$nodeeplinkout)=@_;
     my $ver_orguri=$uri;
     $uri=&deversion($uri);
     my $orguri=$uri;
@@ -7928,7 +8140,7 @@ sub allowed {
 
     if (defined($env{'allowed.'.$priv})) { return $env{'allowed.'.$priv}; }
 # Free bre access to adm and meta resources
-    if (((($uri=~/^adm\//) && ($uri !~ m{/(?:smppg|bulletinboard|ext\.tool)$})) 
+    if (((($uri=~/^adm\//) && ($uri !~ m{/(?:smppg|bulletinboard|viewclasslist|aboutme|ext\.tool)$})) 
 	 || (($uri=~/\.meta$/) && ($uri!~m|^uploaded/|) )) 
 	&& ($priv eq 'bre')) {
 	return 'F';
@@ -8037,8 +8249,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;
                                 }
@@ -8050,8 +8262,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;
                                 }
@@ -8136,13 +8348,16 @@ sub allowed {
             if ($env{'user.priv.'.$env{'request.role'}.'./'}
                   =~/\Q$priv\E\&([^\:]*)/) {
                 my $value = $1;
-                my $deeplinkblock = &deeplink_check($priv,$symb,$uri);
+                my $deeplinkblock;
+                unless ($nodeeplinkcheck) {
+                    $deeplinkblock = &deeplink_check($priv,$symb,$uri);
+                }
                 if ($deeplinkblock) {
                     $thisallowed='D';
                 } elsif ($noblockcheck) {
                     $thisallowed.=$value;
                 } else {
-                    my @blockers = &has_comm_blocking($priv,$symb,$uri);
+                    my @blockers = &has_comm_blocking($priv,$symb,$uri,$ignorecache);
                     if (@blockers > 0) {
                         $thisallowed = 'B';
                     } else {
@@ -8159,13 +8374,16 @@ sub allowed {
                     $refuri=&declutter($refuri);
                     my ($match) = &is_on_map($refuri);
                     if ($match) {
-                        my $deeplinkblock = &deeplink_check($priv,$symb,$refuri);
+                        my $deeplinkblock;
+                        unless ($nodeeplinkcheck) {
+                            $deeplinkblock = &deeplink_check($priv,$symb,$refuri);
+                        }
                         if ($deeplinkblock) {
                             $thisallowed='D';
                         } elsif ($noblockcheck) {
                             $thisallowed='F';
                         } else {
-                            my @blockers = &has_comm_blocking($priv,$symb,$refuri);
+                            my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);
                             if (@blockers > 0) {
                                 $thisallowed = 'B';
                             } else {
@@ -8235,10 +8453,16 @@ sub allowed {
                =~/\Q$priv\E\&([^\:]*)/) {
                my $value = $1;
                if ($priv eq 'bre') {
-                   if ($noblockcheck) {
+                   my $deeplinkblock;
+                   unless ($nodeeplinkcheck) {
+                       $deeplinkblock = &deeplink_check($priv,$symb,$uri);
+                   }
+                   if ($deeplinkblock) {
+                       $thisallowed = 'D';
+                   } elsif ($noblockcheck) {
                        $thisallowed.=$value;
                    } else {
-                       my @blockers = &has_comm_blocking($priv,$symb,$uri);
+                       my @blockers = &has_comm_blocking($priv,$symb,$uri,$ignorecache);
                        if (@blockers > 0) {
                            $thisallowed = 'B';
                        } else {
@@ -8251,7 +8475,7 @@ sub allowed {
                $checkreferer=0;
            }
        }
-       
+
        if ($checkreferer) {
 	  my $refuri=$env{'httpref.'.$orguri};
             unless ($refuri) {
@@ -8277,13 +8501,16 @@ sub allowed {
                   =~/\Q$priv\E\&([^\:]*)/) {
                   my $value = $1;
                   if ($priv eq 'bre') {
-                      my $deeplinkblock = &deeplink_check($priv,$symb,$refuri);
+                      my $deeplinkblock;
+                      unless ($nodeeplinkcheck) {
+                          $deeplinkblock = &deeplink_check($priv,$symb,$refuri);
+                      }
                       if ($deeplinkblock) {
                           $thisallowed = 'D';
                       } elsif ($noblockcheck) {
                           $thisallowed.=$value;
                       } else {
-                          my @blockers = &has_comm_blocking($priv,$symb,$refuri);
+                          my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);
                           if (@blockers > 0) {
                               $thisallowed = 'B';
                           } else {
@@ -8325,16 +8552,48 @@ 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
+# 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).
 
-    my $envkey;
+    my ($needlockcheck,$now,$crsonly);
     if ($thisallowed=~/L/) {
-        foreach $envkey (keys(%env)) {
+        $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)) {
            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;
@@ -8357,7 +8616,7 @@ sub allowed {
                }
                if (($env{$prefix.'priv.'.$priv.'.lock.sections'}=~/\,\Q$csec\E\,/)
                 || ($env{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {
-		   if ($env{'priv.'.$priv.'.lock.expire'}>time) {
+		   if ($env{$prefix.'priv.'.$priv.'.lock.expire'}>time) {
                        &log($env{'user.domain'},$env{'user.name'},
                             $env{'user.home'},
                             'Locked by priv: '.$priv.' for '.$uri.' due to '.
@@ -8369,7 +8628,7 @@ sub allowed {
 	   }
        }
     }
-   
+
 #
 # Rest of the restrictions depend on selected course
 #
@@ -8428,6 +8687,17 @@ 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/) {
@@ -8538,22 +8808,27 @@ sub constructaccess {
 #
 # User for whom data are being temporarily cached.
 my $cacheduser='';
+# Course for which data are being temporarily cached.
+my $cachedcid='';
 # 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,$blocks)=@_;
+    my ($uname,$udom)=@_;
     if (($uname ne '') && ($udom ne '')) { 
         if (($cacheduser eq $uname.':'.$udom) &&
+            ($cachedcid eq $env{'request.course.id'}) &&
             (abs($cachedlast-time)<5)) {
             return;
         }
     }
     $cachedlast=time;
     $cacheduser=$uname.':'.$udom;
-    %cachedblockers = &get_commblock_resources($blocks);
+    $cachedcid=$env{'request.course.id'};
+    %cachedblockers = &get_commblock_resources();
+    return;
 }
 
 sub get_comm_blocks {
@@ -8633,14 +8908,23 @@ sub get_commblock_resources {
                             if ($mapsymb) {
                                 if (ref($navmap)) {
                                     my $mapres = $navmap->getBySymb($mapsymb);
-                                    @to_test = $mapres->retrieveResources($mapres,undef,0,0,0,1);
-                                    foreach my $res (@to_test) {
-                                        my $symb = $res->symb();
-                                        next if ($symb eq $mapsymb);
-                                        if ($symb ne '') {
-                                            @interval=&EXT("resource.0.interval",$symb);
-                                            if ($interval[1] eq 'map') {
-                                                last;
+                                    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;
+                                                    }
+                                                }
                                             }
                                         }
                                     }
@@ -8691,17 +8975,23 @@ sub get_commblock_resources {
 }
 
 sub has_comm_blocking {
-    my ($priv,$symb,$uri,$blocks) = @_;
+    my ($priv,$symb,$uri,$ignoresymbdb,$noenccheck,$blocked,$blocks) = @_;
     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');
-    &load_all_blockers($env{'user.name'},$env{'user.domain'},$blocks);
-    return unless (keys(%cachedblockers) > 0);
+    my %blockinfo;
+    if (ref($blocks) eq 'HASH') {
+        %blockinfo = &get_commblock_resources($blocks);
+    } else {
+        &load_all_blockers($env{'user.name'},$env{'user.domain'});
+        %blockinfo = %cachedblockers;
+    }
+    return unless (keys(%blockinfo) > 0);
     my (%possibles,@symbs);
     if (!$symb) {
-        $symb = &symbread($uri,1,1,1,\%possibles);
+        $symb = &symbread($uri,1,1,1,\%possibles,$ignoresymbdb,$noenccheck);
     }
     if ($symb) {
         @symbs = ($symb);
@@ -8712,34 +9002,38 @@ sub has_comm_blocking {
     foreach my $symb (@symbs) {
         last if ($noblock);
         my ($map,$resid,$resurl)=&decode_symb($symb);
-        foreach my $block (keys(%cachedblockers)) {
+        foreach my $block (keys(%blockinfo)) {
             if ($block =~ /^firstaccess____(.+)$/) {
                 my $item = $1;
-                if (($item eq $map) || ($item eq $symb)) {
-                    $noblock = 1;
-                    last;
+                unless ($blocked) {
+                    if (($item eq $map) || ($item eq $symb)) {
+                        $noblock = 1;
+                        last;
+                    }
                 }
             }
-            if (ref($cachedblockers{$block}) eq 'HASH') {
-                if (ref($cachedblockers{$block}{'resources'}) eq 'HASH') {
-                    if ($cachedblockers{$block}{'resources'}{$symb}) {
+            if (ref($blockinfo{$block}) eq 'HASH') {
+                if (ref($blockinfo{$block}{'resources'}) eq 'HASH') {
+                    if ($blockinfo{$block}{'resources'}{$symb}) {
                         unless (grep(/^\Q$block\E$/,@blockers)) {
                             push(@blockers,$block);
                         }
                     }
                 }
-            }
-            if (ref($cachedblockers{$block}{'maps'}) eq 'HASH') {
-                if ($cachedblockers{$block}{'maps'}{$map}) {
-                    unless (grep(/^\Q$block\E$/,@blockers)) {
-                        push(@blockers,$block);
+                if (ref($blockinfo{$block}{'maps'}) eq 'HASH') {
+                    if ($blockinfo{$block}{'maps'}{$map}) {
+                        unless (grep(/^\Q$block\E$/,@blockers)) {
+                            push(@blockers,$block);
+                        }
                     }
                 }
             }
         }
     }
-    return if ($noblock);
-    return @blockers;
+    unless ($noblock) { 
+        return @blockers;
+    }
+    return;
 }
 }
 
@@ -8761,29 +9055,9 @@ sub deeplink_check {
         @symbs = keys(%possibles);
     }
 
-    my ($login,$switchrole,$allow);
-    if ($env{'request.deeplink.login'} =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
-        my $key = $1;
-        my $tinyurl;
-        my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
-        if (defined($cached)) {
-             $tinyurl = $result;
-        } else {
-             my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
-             my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
-             if ($currtiny{$key} ne '') {
-                 $tinyurl = $currtiny{$key};
-                 &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
-             }
-        }
-        if ($tinyurl ne '') {
-            my ($cnumreq,$posslogin) = split(/\&/,$tinyurl);
-            if ($cnumreq eq $cnum) {
-                $login = $posslogin;
-            } else {
-                $switchrole = 1;
-            }
-        }
+    my ($deeplink_symb,$allow);
+    if ($env{'request.deeplink.login'}) {
+        $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom);
     }
     foreach my $symb (@symbs) {
         last if ($allow);
@@ -8791,34 +9065,48 @@ 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 ($login) {
-                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 $login) {
+                        if ($symb eq $deeplink_symb) {
                             $allow = 1;
                         }
-                    } elsif ($scope eq 'map') {
-#FIXME Compare map for $env{'request.deeplink.login'} with map for $symb
-                    } elsif ($scope eq 'rec') {
-#FIXME Recurse up for $env{'request.deeplink.login'} with map for $symb
-                    }
-                } else {
-                    my ($acctype,$item) = split(/:/,$access);
-                    if (($acctype eq 'lti') && ($env{'user.linkprotector'})) {
-                        if (grep(/^\Q$item\E$/,split(/,/,$env{'user.linkprotector'}))) {
-                            my %tinyurls = &get('tiny',[$symb],$cdom,$cnum);
-                            if (grep(/\Q$tinyurls{$symb}\E$/,split(/,/,$env{'user.linkproturis'}))) {
-                                $allow = 1;
-                            }
+                    } elsif (($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 (($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'}))) {
+                        if (($map_from_symb) && ($map_from_login)) {
+                            if ($map_from_symb eq $map_from_login) {
                                 $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;
+                                }
                             }
                         }
                     }
@@ -9224,6 +9512,25 @@ 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);
@@ -9495,6 +9802,38 @@ 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);
@@ -10045,11 +10384,23 @@ 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'})) {
@@ -10062,18 +10413,16 @@ sub autoupdate_coowners {
                             } else {
                                 push(@newcoowners,$uname.':'.$udom);
                             }
-                        } 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 = '';
+                        } elsif ($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 = '';
+                            }
                         }
                         if (@newcoowners || $delcoowners) {
                             &store_coowners($cdom,$cnum,$coursehash{'home'},
@@ -10147,13 +10496,14 @@ sub modifyuserauth {
              ' in domain '.$env{'request.role.domain'});  
     my $reply=&reply('encrypt:changeuserauth:'.$udom.':'.$uname.':'.$umode.':'.
 		     &escape($upass),$uhome);
+    my $ip = &get_requestor_ip();
     &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},
         'Authentication changed for '.$udom.', '.$uname.', '.$umode.
-         '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply);
+         '(Remote '.$ip.'): '.$reply);
     &log($udom,,$uname,$uhome,
         'Authentication changed by '.$env{'user.domain'}.', '.
                                      $env{'user.name'}.', '.$umode.
-         '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply);
+         '(Remote '.$ip.'): '.$reply);
     unless ($reply eq 'ok') {
         &logthis('Authentication mode error: '.$reply);
 	return 'error: '.$reply;
@@ -10482,14 +10832,19 @@ sub writecoursepref {
 
 sub createcourse {
     my ($udom,$description,$url,$course_server,$nonstandard,$inst_code,
-        $course_owner,$crstype,$cnum,$context,$category)=@_;
+        $course_owner,$crstype,$cnum,$context,$category,$callercontext)=@_;
     $url=&declutter($url);
     my $cid='';
     if ($context eq 'requestcourses') {
         my $can_create = 0;
         my ($ownername,$ownerdom) = split(':',$course_owner);
         if ($udom eq $ownerdom) {
-            if (&usertools_access($ownername,$ownerdom,$category,undef,
+            my $reload;
+            if (($callercontext eq 'auto') &&
+               ($ownerdom eq $env{'user.domain'}) && ($ownername eq $env{'user.name'})) {
+                $reload = 'reload';
+            }
+            if (&usertools_access($ownername,$ownerdom,$category,$reload,
                                   $context)) {
                 $can_create = 1;
             }
@@ -10674,7 +11029,7 @@ sub store_userdata {
             if (($uhome eq '') || ($uhome eq 'no_host')) {
                 $result = 'error: no_host';
             } else {
-                $storehash->{'ip'} = $ENV{'REMOTE_ADDR'};
+                $storehash->{'ip'} = &get_requestor_ip();
                 $storehash->{'host'} = $perlvar{'lonHostID'};
 
                 my $namevalue='';
@@ -11843,7 +12198,7 @@ sub get_domain_lti {
         my %domconfig = &get_dom('configuration',[$name],$cdom);
         if (ref($domconfig{$name}) eq 'HASH') {
             %lti = %{$domconfig{$name}};
-            my %encdomconfig = &get_dom('encconfig',[$name],$cdom);
+            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') {
@@ -11860,6 +12215,23 @@ sub get_domain_lti {
     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 get_numsuppfiles {
     my ($cnum,$cdom,$ignorecache)=@_;
     my $hashid=$cnum.':'.$cdom;
@@ -11919,7 +12291,7 @@ sub EXT_cache_set {
 # --------------------------------------------------------- Value of a Variable
 sub EXT {
 
-    my ($varname,$symbparm,$udom,$uname,$usection,$recurse,$cid)=@_;
+    my ($varname,$symbparm,$udom,$uname,$usection,$recurse,$cid,$recurseupref)=@_;
     unless ($varname) { return ''; }
     #get real user name/domain, courseid and symb
     my $courseid;
@@ -12073,6 +12445,10 @@ sub EXT {
         }
 
 	my ($section, $group, @groups, @recurseup, $recursed);
+        if (ref($recurseupref) eq 'ARRAY') {
+            @recurseup = @{$recurseupref};
+            $recursed = 1;
+        }
 	my ($courselevelm,$courseleveli,$courselevel,$mapp);
         if (($courseid eq '') && ($cid)) {
             $courseid = $cid;
@@ -12221,6 +12597,10 @@ sub EXT {
 	if ($space eq 'name') {
 	    return $ENV{'SERVER_NAME'};
         }
+    } elsif ($realm eq 'client') {
+        if ($space eq 'remote_addr') {
+            return &get_requestor_ip();
+        }
     }
     return '';
 }
@@ -13049,18 +13429,16 @@ sub symbverify {
 
     if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db',
                             &GDBM_READER(),0640)) {
-        my $noclutter;
         if (($thisurl =~ m{^/adm/wrapper/ext/}) || ($thisurl =~ m{^ext/})) {
             $thisurl =~ s/\?.+$//;
             if ($map =~ m{^uploaded/.+\.page$}) {
                 $thisurl =~ s{^(/adm/wrapper|)/ext/}{http://};
                 $thisurl =~ s{^\Qhttp://https://\E}{https://};
-                $noclutter = 1;
             }
         }
         my $ids;
-        if ($noclutter) {
-            $ids=$bighash{'ids_'.$thisurl};
+        if ($map =~ m{^uploaded/.+\.page$}) {
+            $ids=$bighash{'ids_'.&clutter_with_no_wrapper($thisurl)};
         } else {
             $ids=$bighash{'ids_'.&clutter($thisurl)};
         }
@@ -13160,19 +13538,22 @@ sub deversion {
 # ------------------------------------------------------ Return symb list entry
 
 sub symbread {
-    my ($thisfn,$donotrecurse,$ignorecachednull,$checkforblock,$possibles)=@_;
+    my ($thisfn,$donotrecurse,$ignorecachednull,$checkforblock,$possibles,
+        $ignoresymbdb,$noenccheck)=@_;
     my $cache_str='request.symbread.cached.'.$thisfn;
     if (defined($env{$cache_str})) {
-        if ($ignorecachednull) {
-            return $env{$cache_str} unless ($env{$cache_str} eq '');
-        } else {
-            return $env{$cache_str};
+        unless (ref($possibles) eq 'HASH') {
+            if ($ignorecachednull) {
+                return $env{$cache_str} unless ($env{$cache_str} eq '');
+            } else {
+                return $env{$cache_str};
+            }
         }
     }
 # no filename provided? try from environment
     unless ($thisfn) {
         if ($env{'request.symb'}) {
-	    return $env{$cache_str}=&symbclean($env{'request.symb'});
+            return $env{$cache_str}=&symbclean($env{'request.symb'});
 	}
 	$thisfn=$env{'request.filename'};
     }
@@ -13195,10 +13576,18 @@ sub symbread {
 	if ($targetfn =~ m|^adm/wrapper/(ext/.*)|) {
 	    $targetfn=$1;
 	}
-        if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
-                      &GDBM_READER(),0640)) {
-	    $syval=$hash{$targetfn};
-            untie(%hash);
+        unless ($ignoresymbdb) {
+            if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
+                          &GDBM_READER(),0640)) {
+	        $syval=$hash{$targetfn};
+                untie(%hash);
+            }
+            if ($syval && $checkforblock) {
+                my @blockers = &has_comm_blocking('bre',$syval,$thisfn,$ignoresymbdb,$noenccheck);
+                if (@blockers) {
+                    $syval='';
+                }
+            }
         }
 # ---------------------------------------------------------- There was an entry
         if ($syval) {
@@ -13231,13 +13620,18 @@ sub symbread {
 		     $syval=&encode_symb($bighash{'map_id_'.$mapid},
 						    $resid,$thisfn);
                      if (ref($possibles) eq 'HASH') {
-                         $possibles->{$syval} = 1;    
+                         unless ($bighash{'randomout_'.$ids} || $env{'request.role.adv'}) {
+                             $possibles->{$syval} = 1;
+                         }
                      }
                      if ($checkforblock) {
-                         my @blockers = &has_comm_blocking('bre',$syval,$bighash{'src_'.$ids});
-                         if (@blockers) {
-                             $syval = '';
-                             return;
+                         unless ($bighash{'randomout_'.$ids} || $env{'request.role.adv'}) {
+                             my @blockers = &has_comm_blocking('bre',$syval,$bighash{'src_'.$ids},'',$noenccheck);
+                             if (@blockers) {
+                                 $syval = '';
+                                 untie(%bighash);
+                                 return $env{$cache_str}='';
+                             }
                          }
                      }
                  } elsif ((!$donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) { 
@@ -13256,12 +13650,13 @@ sub symbread {
                              if ($bighash{'map_type_'.$mapid} ne 'page') {
                                  my $poss_syval=&encode_symb($bighash{'map_id_'.$mapid},
 						             $resid,$thisfn);
-                                 if (ref($possibles) eq 'HASH') {
-                                     $possibles->{$syval} = 1;
-                                 }
+                                 next if ($bighash{'randomout_'.$id} && !$env{'request.role.adv'});
+                                 next unless (($noenccheck) || ($bighash{'encrypted_'.$id} eq $env{'request.enc'}));
                                  if ($checkforblock) {
-                                     my @blockers = &has_comm_blocking('bre',$poss_syval,$file);
-                                     unless (@blockers > 0) {
+                                     my @blockers = &has_comm_blocking('bre',$poss_syval,$file,'',$noenccheck);
+                                     if (@blockers > 0) {
+                                         $syval = '';
+                                     } else {
                                          $syval = $poss_syval;
                                          $realpossible++;
                                      }
@@ -13269,6 +13664,11 @@ sub symbread {
                                      $syval = $poss_syval;
                                      $realpossible++;
                                  }
+                                 if ($syval) {
+                                     if (ref($possibles) eq 'HASH') {
+                                         $possibles->{$syval} = 1;
+                                     }
+                                 }
                              }
 			 }
                      }
@@ -13987,10 +14387,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;
 }
@@ -14008,6 +14413,30 @@ sub default_login_domain {
     return $domain;
 }
 
+sub shared_institution {
+    my ($dom,$lonhost) = @_;
+    if ($lonhost eq '') {
+        $lonhost = $perlvar{'lonHostID'};
+    }
+    my $same_intdom;
+    my $hostintdom = &internet_dom($lonhost);
+    if ($hostintdom ne '') {
+        my %iphost = &get_iphost();
+        my $primary_id = &domain($dom,'primary');
+        my $primary_ip = &get_host_ip($primary_id);
+        if (ref($iphost{$primary_ip}) eq 'ARRAY') {
+            foreach my $id (@{$iphost{$primary_ip}}) {
+                my $intdom = &internet_dom($id);
+                if ($intdom eq $hostintdom) {
+                    $same_intdom = 1;
+                    last;
+                }
+            }
+        }
+    }
+    return $same_intdom;
+}
+
 sub uses_sts {
     my ($ignore_cache) = @_;
     my $lonhost = $perlvar{'lonHostID'};
@@ -14046,6 +14475,230 @@ 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);
+        }
+    } else {
+        $from_ip = $ENV{'REMOTE_ADDR'};
+    }
+    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)) {
+        if ($proxyinfo->{'vpnint'}) {
+            if (&ip_match($from_ip,$proxyinfo->{'vpnint'})) {
+                return $from_ip;
+            }
+        }
+        if ($proxyinfo->{'trusted'}) {
+            if (&ip_match($from_ip,$proxyinfo->{'trusted'})) {
+                my $ipheader = $proxyinfo->{'ipheader'};
+                my ($ip,$xfor);
+                if (ref($r)) {
+                    if ($ipheader) {
+                        $ip = $r->headers_in->{$ipheader};
+                    }
+                    $xfor = $r->headers_in->{'X-Forwarded-For'};
+                } else {
+                    if ($ipheader) {
+                        $ip = $ENV{'HTTP_'.uc($ipheader)};
+                    }
+                    $xfor = $ENV{'HTTP_X_FORWARDED_FOR'};
+                }
+                if (($ip eq '') && ($xfor ne '')) {
+                    foreach my $poss_ip (reverse(split(/\s*,\s*/,$xfor))) {
+                        unless (&ip_match($poss_ip,$proxyinfo->{'trusted'})) {
+                            $ip = $poss_ip;
+                            last;
+                        }
+                    }
+                }
+                if ($ip ne '') {
+                    return $ip;
+                }
+            }
+        }
+    }
+    return $from_ip;
+}
+
+sub get_proxy_settings {
+    my ($dom_in_use) = @_;
+    my %domdefaults = &Apache::lonnet::get_domain_defaults($dom_in_use);
+    my $proxyinfo = {
+                       ipheader => $domdefaults{'waf_ipheader'},
+                       trusted  => $domdefaults{'waf_trusted'},
+                       vpnint   => $domdefaults{'waf_vpnint'},
+                       vpnext   => $domdefaults{'waf_vpnext'},
+                       sslopt   => $domdefaults{'waf_sslopt'},
+                    };
+    return $proxyinfo;
+}
+
+sub ip_match {
+    my ($ip,$pattern_str) = @_;
+    $ip=Net::CIDR::cidrvalidate($ip);
+    if ($ip) {
+        return Net::CIDR::cidrlookup($ip,split(/\s*,\s*/,$pattern_str));
+    }
+    return;
+}
+
+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);
+        if ($cached) {
+            return $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'}{'alias'}) eq 'HASH') {
+                    $alias = $domconfig{'wafproxy'}{'alias'}{$lonid};
+                }
+            }
+            return &do_cache_new('proxyalias',$lonid,$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_shibboleth {
+    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 = &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 (&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 {
@@ -14185,14 +14838,33 @@ sub get_dns {
     }
     while (%alldns) {
 	my ($dns) = sort { $b cmp $a } keys(%alldns);
-	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());
+        my ($contents,@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);
+            }
+            if ($url eq '/adm/dns/loncapaCRL') {
+                $contents = join('',@content);
+            }
+        } else {
+	    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') {
+                $contents = $response->content;
+            } else {
+                @content = split("\n",$response->content);
+            }
+        }
         if ($url eq '/adm/dns/loncapaCRL') {
-            return &$func($response);
+            return &$func($contents);
         } else {
-	    my @content = split("\n",$response->content);
 	    unless ($nocache) {
 	        &do_cache_new('dns',$url,\@content,30*24*60*60);
 	    }
@@ -14280,14 +14952,14 @@ sub fetch_crl_pemfile {
 }
 
 sub save_crl_pem {
-    my ($response) = @_;
+    my ($content) = @_;
     my ($msg,$hadchanges);
-    if (ref($response)) {
+    if ($content ne '') {
         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;
+            print $fh $content;
             close($fh);
             if (-e $lonca) {
                 if (open(PIPE,"openssl crl -in $tmpcrl -inform pem -CAfile $lonca -noout 2>&1 |")) {
@@ -14348,6 +15020,24 @@ sub save_crl_pem {
     return ($msg,$hadchanges);
 }
 
+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",
+    } elsif ($command eq 'loncapaCRL') {
+        $dir = $perlvar{'lonCertificateDirectory'};
+        $file = $perlvar{'lonnetCertRevocationList'};
+    }
+    return ($dir,$file);
+}
+
 # ------------------------------------------------------------ Read domain file
 {
     my $loaded;
@@ -14919,6 +15609,11 @@ BEGIN {
     $deftex = LONCAPA::texengine();
 }
 
+# ------------- set default minimum length for passwords for internal auth users
+{
+    $passwdmin = LONCAPA::passwd_min();
+}
+
 $memcache=new Cache::Memcached({'servers'           => ['127.0.0.1:11211'],
 				'compress_threshold'=> 20_000,
  			        });