--- loncom/interface/loncommon.pm	2018/04/15 00:28:07	1.1313
+++ loncom/interface/loncommon.pm	2020/06/09 21:32:32	1.1343
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1313 2018/04/15 00:28:07 raeburn Exp $
+# $Id: loncommon.pm,v 1.1343 2020/06/09 21:32:32 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -72,6 +72,7 @@ use Apache::lonuserstate();
 use Apache::courseclassifier();
 use LONCAPA qw(:DEFAULT :match);
 use LONCAPA::LWPReq;
+use HTTP::Request;
 use DateTime::TimeZone;
 use DateTime::Locale;
 use Encode();
@@ -79,7 +80,6 @@ use Text::Aspell;
 use Authen::Captcha;
 use Captcha::reCAPTCHA;
 use JSON::DWIW;
-use LWP::UserAgent;
 use Crypt::DES;
 use DynaLoader; # for Crypt::DES version
 use MIME::Lite;
@@ -203,7 +203,7 @@ BEGIN {
     {
         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                                    '/language.tab';
-        if ( open(my $fh,"<$langtabfile") ) {
+        if ( open(my $fh,'<',$langtabfile) ) {
             while (my $line = <$fh>) {
                 next if ($line=~/^\#/);
                 chomp($line);
@@ -225,7 +225,7 @@ BEGIN {
     {
         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                                   '/copyright.tab';
-        if ( open (my $fh,"<$copyrightfile") ) {
+        if ( open (my $fh,'<',$copyrightfile) ) {
             while (my $line = <$fh>) {
                 next if ($line=~/^\#/);
                 chomp($line);
@@ -239,7 +239,7 @@ BEGIN {
     {
         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                                   '/source_copyright.tab';
-        if ( open (my $fh,"<$sourcecopyrightfile") ) {
+        if ( open (my $fh,'<',$sourcecopyrightfile) ) {
             while (my $line = <$fh>) {
                 next if ($line =~ /^\#/);
                 chomp($line);
@@ -253,7 +253,7 @@ BEGIN {
 # -------------------------------------------------------------- default domain designs
     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
     my $designfile = $designdir.'/default.tab';
-    if ( open (my $fh,"<$designfile") ) {
+    if ( open (my $fh,'<',$designfile) ) {
         while (my $line = <$fh>) {
             next if ($line =~ /^\#/);
             chomp($line);
@@ -267,7 +267,7 @@ BEGIN {
     {
         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                                   '/filecategories.tab';
-        if ( open (my $fh,"<$categoryfile") ) {
+        if ( open (my $fh,'<',$categoryfile) ) {
 	    while (my $line = <$fh>) {
 		next if ($line =~ /^\#/);
 		chomp($line);
@@ -282,7 +282,7 @@ BEGIN {
     {
         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                '/filetypes.tab';
-        if ( open (my $fh,"<$typesfile") ) {
+        if ( open (my $fh,'<',$typesfile) ) {
             while (my $line = <$fh>) {
 		next if ($line =~ /^\#/);
 		chomp($line);
@@ -435,7 +435,7 @@ sub studentbrowser_javascript {
 <script type="text/javascript" language="Javascript">
 // <![CDATA[
     var stdeditbrowser;
-    function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadvonly) {
+    function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadv) {
         var url = '/adm/pickstudent?';
         var filter;
 	if (!ignorefilter) {
@@ -450,7 +450,12 @@ sub studentbrowser_javascript {
                                     '&udomelement='+udom+
                                     '&clicker='+clicker;
 	if (roleflag) { url+="&roles=1"; }
-        if (courseadvonly) { url+="&courseadvonly=1"; }
+        if (courseadv == 'condition') {
+            if (document.getElementById('courseadv')) {
+                courseadv = document.getElementById('courseadv').value;
+            }
+        }
+        if ((courseadv == 'only') || (courseadv == 'none')) { url+="&courseadv="+courseadv; }
         var title = 'Student_Browser';
         var options = 'scrollbars=1,resizable=1,menubar=0';
         options += ',width=700,height=600';
@@ -482,7 +487,7 @@ ENDRESBRW
 }
 
 sub selectstudent_link {
-   my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
+   my ($form,$unameele,$udomele,$courseadv,$clickerid)=@_;
    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                       &Apache::lonhtmlcommon::entity_encode($unameele)."','".
                       &Apache::lonhtmlcommon::entity_encode($udomele)."'";
@@ -493,8 +498,12 @@ sub selectstudent_link {
 	   return '';
        }
        $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
-       if ($courseadvonly)  {
-           $callargs .= ",'',1,1";
+       if ($courseadv eq 'only') {
+           $callargs .= ",'',1,'$courseadv'";
+       } elsif ($courseadv eq 'none') {
+           $callargs .= ",'','','$courseadv'";
+       } elsif ($courseadv eq 'condition') {
+           $callargs .= ",'','','$courseadv'";
        }
        return '<span class="LC_nobreak">'.
               '<a href="javascript:openstdbrowser('.$callargs.');">'.
@@ -1297,9 +1306,13 @@ sub help_open_topic {
     }
 
     # Add the text
+    my $target = ' target="_top"';
+    if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
+        $target = '';
+    }
     if ($text ne "") {	
 	$template.='<span class="LC_help_open_topic">'
-                  .'<a target="_top" href="'.$link.'">'
+                  .'<a'.$target.' href="'.$link.'">'
                   .$text.'</a>';
     }
 
@@ -1309,7 +1322,7 @@ sub help_open_topic {
     if ($imgid ne '') {
         $imgid = ' id="'.$imgid.'"';
     }
-    $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'
+    $template.=' <a'.$target.' href="'.$link.'" title="'.$title.'">'
               .'<img src="'.$helpicon.'" border="0"'
               .' alt="'.&mt('Help: [_1]',$topic).'"'
               .' title="'.$title.'" style="vertical-align:middle;"'.$imgid 
@@ -1502,19 +1515,24 @@ sub help_open_bug {
     {
 	$link = $url;
     }
+
+    my $target = ' target="_top"';
+    if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
+        $target = '';
+    }
     # Add the text
     if ($text ne "")
     {
 	$template .= 
   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
-  "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
+  "<td bgcolor='#FF5555'><a".$target." href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
     }
 
     # Add the graphic
     my $title = &mt('Report a Bug');
     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
     $template .= <<"ENDTEMPLATE";
- <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
+ <a$target href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
 ENDTEMPLATE
     if ($text ne '') { $template.='</td></tr></table>' };
     return $template;
@@ -2225,6 +2243,7 @@ sub import_crsauthor_form {
                 }
                 my @ordered = ();
                 foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$key}}))) {
+                    next if ($file =~ /\.rights$/);
                     if ($only) {
                         my ($ext) = ($file =~ /\.([^.]+)$/);
                         unless ($possexts{lc($ext)}) {
@@ -2268,11 +2287,13 @@ sub import_crsauthor_form {
                 unless ($possexts{lc($ext)}) {
                     next;
                 }
+            } else {
+                next if ($file =~ /\.rights$/);
             }
             push(@singledirfiles,$file);
         }
         if (@singledirfiles) {
-            $possdirs == 1;
+            $possdirs = 1;
         }
     }
     if (($possdirs == 1) && (@singledirfiles)) {
@@ -3554,6 +3575,79 @@ sub get_assignable_auth {
     return ($authnum,%can_assign);
 }
 
+sub check_passwd_rules {
+    my ($domain,$plainpass) = @_;
+    my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
+    my ($min,$max,@chars,@brokerule,$warning);
+    $min = $Apache::lonnet::passwdmin;
+    if (ref($passwdconf{'chars'}) eq 'ARRAY') {
+        if ($passwdconf{'min'} =~ /^\d+$/) {
+            if ($passwdconf{'min'} > $min) {
+                $min = $passwdconf{'min'};
+            }
+        }
+        if ($passwdconf{'max'} =~ /^\d+$/) {
+            $max = $passwdconf{'max'};
+        }
+        @chars = @{$passwdconf{'chars'}};
+    }
+    if (($min) && (length($plainpass) < $min)) {
+        push(@brokerule,'min');
+    }
+    if (($max) && (length($plainpass) > $max)) {
+        push(@brokerule,'max');
+    }
+    if (@chars) {
+        my %rules;
+        map { $rules{$_} = 1; } @chars;
+        if ($rules{'uc'}) {
+            unless ($plainpass =~ /[A-Z]/) {
+                push(@brokerule,'uc');
+            }
+        }
+        if ($rules{'lc'}) {
+            unless ($plainpass =~ /[a-z]/) {
+                push(@brokerule,'lc');
+            }
+        }
+        if ($rules{'num'}) {
+            unless ($plainpass =~ /\d/) {
+                push(@brokerule,'num');
+            }
+        }
+        if ($rules{'spec'}) {
+            unless ($plainpass =~ /[!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/) {
+                push(@brokerule,'spec');
+            }
+        }
+    }
+    if (@brokerule) {
+        my %rulenames = &Apache::lonlocal::texthash(
+            uc   => 'At least one upper case letter',
+            lc   => 'At least one lower case letter',
+            num  => 'At least one number',
+            spec => 'At least one non-alphanumeric',
+        );
+        $rulenames{'uc'} .= ': ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+        $rulenames{'lc'} .= ': abcdefghijklmnopqrstuvwxyz';
+        $rulenames{'num'} .= ': 0123456789';
+        $rulenames{'spec'} .= ': !&quot;\#$%&amp;\'()*+,-./:;&lt;=&gt;?@[\]^_\`{|}~';
+        $rulenames{'min'} = &mt('Minimum password length: [_1]',$min);
+        $rulenames{'max'} = &mt('Maximum password length: [_1]',$max);
+        $warning = &mt('Password did not satisfy the following:').'<ul>';
+        foreach my $rule ('min','max','uc','lc','num','spec') {
+            if (grep(/^$rule$/,@brokerule)) {
+                $warning .= '<li>'.$rulenames{$rule}.'</li>';
+            }
+        }
+        $warning .= '</ul>';
+    }
+    if (wantarray) {
+        return @brokerule;
+    }
+    return $warning;
+}
+
 ###############################################################
 ##    Get Kerberos Defaults for Domain                 ##
 ###############################################################
@@ -5422,8 +5516,13 @@ sub check_ip_acc {
     if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
         return 1;
     }
-    my $allowed;
-    my $ip=$ENV{'REMOTE_ADDR'} || $clientip || $env{'request.host'};
+    my ($ip,$allowed);
+    if (($ENV{'REMOTE_ADDR'} eq '127.0.0.1') ||
+        ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) {
+        $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
+    } else {
+        $ip = $ENV{'REMOTE_ADDR'} || $env{'request.host'} || $clientip;
+    }
 
     my $name;
     my %access = (
@@ -5638,7 +5737,7 @@ sub get_legacy_domconf {
     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
     my $designfile =  $designdir.'/'.$udom.'.tab';
     if (-e $designfile) {
-        if ( open (my $fh,"<$designfile") ) {
+        if ( open (my $fh,'<',$designfile) ) {
             while (my $line = <$fh>) {
                 next if ($line =~ /^\#/);
                 chomp($line);
@@ -5837,17 +5936,18 @@ sub CSTR_pageheader {
         $title = &mt('Authoring Space');
     }
 
-    my $target;
+    my ($target,$crumbtarget) = (' target="_top"','_top'); #FIXME lonpubdir: target="_parent"
     if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
-        $target = ' target="_top"'; #FIXME lonpubdir: target="_parent"
+        $target = '';
+        $crumbtarget = '';
     }
 
     my $output =
          '<div>'
         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
         .'<b>'.$title.'</b> '
-        .'<form name="dirs" method="post" action="'.$formaction.$target.'>'
-        .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef);
+        .'<form name="dirs" method="post" action="'.$formaction.'"'.$target.'>'
+        .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef);
 
     if ($lastitem) {
         $output .=
@@ -5861,7 +5961,7 @@ sub CSTR_pageheader {
     } else {
         $output .=
              '<br />'
-            #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"
+            #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/',$crumbtarget,'/priv','','+1',1)."</b></tt><br />"
             .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
             .'</form>'
             .&Apache::lonmenu::constspaceform();
@@ -5919,6 +6019,19 @@ Inputs:
             inlineremote items to be added in "Functions" menu below
             breadcrumbs.
 
+=item * $ltiscope, optional argument, will be one of: resource, map or
+            course, if LON-CAPA is in LTI Provider context. Value is
+            the scope of use, i.e., launch was for access to a single, a map
+            or the entire course.
+
+=item * $ltiuri, optional argument, if LON-CAPA is in LTI Provider
+            context, this will contain the URL for the landing item in
+            the course, after launch from an LTI Consumer
+
+=item * $ltimenu, optional argument, if LON-CAPA is in LTI Provider
+            context, this will contain a reference to hash of items
+            to be included in the page header and/or inline menu.
+
 =back
 
 Returns: A uniform header for LON-CAPA web pages.  
@@ -5930,7 +6043,7 @@ other decorations will be returned.
 
 sub bodytag {
     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
-        $no_nav_bar,$bgcolor,$args,$advtoolsref)=@_;
+        $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri,$ltimenu)=@_;
 
     my $public;
     if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
@@ -6001,7 +6114,18 @@ sub bodytag {
     if ($public) {
 	undef($role);
     }
-    
+
+    if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
+        if (ref($ltimenu) eq 'HASH') {
+            unless ($ltimenu->{'role'}) {
+                undef($role);
+            }
+            unless ($ltimenu->{'coursetitle'}) {
+                $realm='&nbsp;';
+            }
+        }
+    }
+
     my $titleinfo = '<h1>'.$title.'</h1>';
     #
     # Extra info if you are the DC
@@ -6035,27 +6159,29 @@ sub bodytag {
         $bodytag .= Apache::lonhtmlcommon::scripttag(
             Apache::lonmenu::utilityfunctions($httphost), 'start');
 
-        my ($left,$right) = Apache::lonmenu::primary_menu($crstype);
+        unless ($args->{'no_primary_menu'}) {
+            my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu);
 
-        if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
-             if ($dc_info) {
-                 $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
-             }
-             $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
-                <em>$realm</em> $dc_info</div>|;
-            return $bodytag;
-        }
+            if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
+                if ($dc_info) {
+                    $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
+                }
+                $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
+                               <em>$realm</em> $dc_info</div>|;
+                return $bodytag;
+            }
 
-        unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
-            $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
-        }
+            unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
+                $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
+            }
 
-        $bodytag .= $right;
+            $bodytag .= $right;
 
-        if ($dc_info) {
-            $dc_info = &dc_courseid_toggle($dc_info);
+            if ($dc_info) {
+                $dc_info = &dc_courseid_toggle($dc_info);
+            }
+            $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
         }
-        $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
 
         #if directed to not display the secondary menu, don't.  
         if ($args->{'no_secondary_menu'}) {
@@ -6063,17 +6189,20 @@ sub bodytag {
         }
         #don't show menus for public users
         if (!$public){
-            $bodytag .= Apache::lonmenu::secondary_menu($httphost);
+            unless ($args->{'no_inline_menu'}) {
+                $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
+                                                            $args->{'no_primary_menu'});
+            }
             $bodytag .= Apache::lonmenu::serverform();
             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
             if ($env{'request.state'} eq 'construct') {
                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,
-                                $args->{'bread_crumbs'},'','',$hostname);
+                                $args->{'bread_crumbs'},'','',$hostname,$ltiscope,$ltiuri);
             } elsif ($forcereg) {
                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
                                                             $args->{'group'},
                                                             $args->{'hide_buttons'},
-                                                            $hostname);
+                                                            $hostname,$ltiscope,$ltiuri);
             } else {
                 $bodytag .= 
                     &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
@@ -7142,7 +7271,8 @@ table.LC_prior_tries td {
   padding: 6px;
 }
 
-.LC_answer_unknown {
+.LC_answer_unknown,
+.LC_answer_warning {
   background: orange;
   color: black;
   padding: 6px;
@@ -8138,6 +8268,14 @@ ul.LC_funclist li {
 		cursor:pointer;
 }
 
+pre.LC_wordwrap {
+  white-space: pre-wrap;
+  white-space: -moz-pre-wrap;
+  white-space: -pre-wrap;
+  white-space: -o-pre-wrap;
+  word-wrap: break-word;
+}
+
 /*
   styles used for response display
 */
@@ -8388,43 +8526,85 @@ ADDMETA
                 my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'};
                 unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
                     my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
+                    my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
+                    my $offload;
                     if (ref($domdefs{'offloadnow'}) eq 'HASH') {
-                        my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                         if ($domdefs{'offloadnow'}{$lonhost}) {
-                            my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use);
-                            if (($newserver) && ($newserver ne $lonhost)) {
-                                my $numsec = 5;
-                                my $timeout = $numsec * 1000;
-                                my ($newurl,$locknum,%locks,$msg);
-                                if ($env{'request.role.adv'}) {
-                                    ($locknum,%locks) = &Apache::lonnet::get_locks();
-                                }
-                                my $disable_submit = 0;
-                                if ($requrl =~ /$LONCAPA::assess_re/) {
-                                    $disable_submit = 1;
+                            $offload = 1;
+                        }
+                    }
+                    unless ($offload) {
+                        if (ref($domdefs{'offloadoth'}) eq 'HASH') {
+                            if ($domdefs{'offloadoth'}{$lonhost}) {
+                                if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
+                                    (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
+                                    unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
+                                        $offload = 1;
+                                        $dom_in_use = $env{'user.domain'};
+                                    }
                                 }
-                                if ($locknum) {
-                                    my @lockinfo = sort(values(%locks));
-                                    $msg = &mt('Once the following tasks are complete: ')."\\n".
-                                           join(", ",sort(values(%locks)))."\\n".
-                                           &mt('your session will be transferred to a different server, after you click "Roles".');
+                            }
+                        }
+                    }
+                    if ($offload) {
+                        my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use);
+                        if (($newserver) && ($newserver ne $lonhost)) {
+                            my $numsec = 5;
+                            my $timeout = $numsec * 1000;
+                            my ($newurl,$locknum,%locks,$msg);
+                            if ($env{'request.role.adv'}) {
+                                ($locknum,%locks) = &Apache::lonnet::get_locks();
+                            }
+                            my $disable_submit = 0;
+                            if ($requrl =~ /$LONCAPA::assess_re/) {
+                                $disable_submit = 1;
+                            }
+                            if ($locknum) {
+                                my @lockinfo = sort(values(%locks));
+                                $msg = &mt('Once the following tasks are complete: ')."\n".
+                                       join(", ",sort(values(%locks)))."\n";
+                                if (&show_course()) {
+                                    $msg .= &mt('your session will be transferred to a different server, after you click "Courses".');
                                 } else {
-                                    if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
-                                        $msg = &mt('Your LON-CAPA submission has been recorded')."\\n";
-                                    }
-                                    $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
-                                    $newurl = '/adm/switchserver?otherserver='.$newserver;
-                                    if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
-                                        $newurl .= '&role='.$env{'request.role'};
+                                    $msg .= &mt('your session will be transferred to a different server, after you click "Roles".');
+                                }
+                            } else {
+                                if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
+                                    $msg = &mt('Your LON-CAPA submission has been recorded')."\n";
+                                }
+                                $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
+                                $newurl = '/adm/switchserver?otherserver='.$newserver;
+                                if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
+                                    $newurl .= '&role='.$env{'request.role'};
+                                }
+                                if ($env{'request.symb'}) {
+                                    my $shownsymb = &Apache::lonenc::check_encrypt($env{'request.symb'});
+                                    if ($shownsymb =~ m{^/enc/}) {
+                                        my $reqdmajor = 2;
+                                        my $reqdminor = 11;
+                                        my $reqdsubminor = 3;
+                                        my $newserverrev = &Apache::lonnet::get_server_loncaparev('',$newserver);
+                                        my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$newserver);
+                                        my ($major,$minor,$subminor) = ($remoterev =~ /^\'?(\d+)\.(\d+)\.(\d+|)[\w.\-]+\'?$/);
+                                        if (($major eq '' && $minor eq '') ||
+                                            (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)) ||
+                                            (($reqdmajor == $major) && ($reqdminor == $minor) && (($subminor eq '') ||
+                                             ($reqdsubminor > $subminor))))) {
+                                            undef($shownsymb);
+                                        }
                                     }
-                                    if ($env{'request.symb'}) {
-                                        $newurl .= '&symb='.$env{'request.symb'};
-                                    } else {
-                                        $newurl .= '&origurl='.$requrl;
+                                    if ($shownsymb) {
+                                        &js_escape(\$shownsymb);
+                                        $newurl .= '&symb='.$shownsymb;
                                     }
+                                } else {
+                                    my $shownurl = &Apache::lonenc::check_encrypt($requrl);
+                                    &js_escape(\$shownurl);
+                                    $newurl .= '&origurl='.$shownurl;
                                 }
-                                &js_escape(\$msg);
-                                $result.=<<OFFLOAD
+                            }
+                            &js_escape(\$msg);
+                            $result.=<<OFFLOAD
 <meta http-equiv="pragma" content="no-cache" />
 <script type="text/javascript">
 // <![CDATA[
@@ -8445,7 +8625,6 @@ function LC_Offload_Now() {
 // ]]>
 </script>
 OFFLOAD
-                            }
                         }
                     }
                 }
@@ -8675,11 +8854,42 @@ sub start_page {
     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
 
     $env{'internal.start_page'}++;
-    my ($result,@advtools);
+    my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu);
 
     if (! exists($args->{'skip_phases'}{'head'}) ) {
         $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
     }
+
+    if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
+        if ($env{'course.'.$env{'request.course.id'}.'.lti.override'}) {
+            unless ($env{'course.'.$env{'request.course.id'}.'.lti.topmenu'}) {
+                $args->{'no_primary_menu'} = 1;
+            }
+            unless ($env{'course.'.$env{'request.course.id'}.'.lti.inlinemenu'}) {
+                $args->{'no_inline_menu'} = 1;
+            }
+            if ($env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}) {
+                map { $ltimenu{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'});
+            }
+        } else {
+            my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
+            if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') {
+                unless ($lti{$env{'request.lti.login'}}{'topmenu'}) {
+                    $args->{'no_primary_menu'} = 1;
+                }
+                unless ($lti{$env{'request.lti.login'}}{'inlinemenu'}) {
+                    $args->{'no_inline_menu'} = 1;
+                }
+                if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') {
+                    map { $ltimenu{$_} = 1; } @{$lti{$env{'request.lti.login'}}{'lcmenu'}};
+                }
+            }
+        }
+        ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},
+                                  $env{'course.'.$env{'request.course.id'}.'.domain'},
+                                  $env{'course.'.$env{'request.course.id'}.'.num'});
+    }
     
     if (! exists($args->{'skip_phases'}{'body'}) ) {
 	if ($args->{'frameset'}) {
@@ -8693,7 +8903,7 @@ sub start_page {
                          $args->{'only_body'},      $args->{'domain'},
                          $args->{'force_register'}, $args->{'no_nav_bar'},
                          $args->{'bgcolor'},        $args,
-                         \@advtools);
+                         \@advtools,$ltiscope,$ltiuri,\%ltimenu);
         }
     }
 
@@ -8726,12 +8936,6 @@ sub start_page {
                 if (@advtools > 0) {
                     &Apache::lonmenu::advtools_crumbs(@advtools);
                 }
-                my $ltiscope;
-                if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
-                    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
-                    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
-                    ($ltiscope) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},$cdom,$cnum);
-                }
                 my $menulink;
                 # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item.
                 if ((exists($args->{'bread_crumbs_nomenu'})) ||
@@ -8869,8 +9073,7 @@ sub modal_link {
         $target_attr = 'target="'.$target.'"';
     }
     return <<"ENDLINK";
-<a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">
-           $linktext</a>
+<a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">$linktext</a>
 ENDLINK
 }
 
@@ -12141,7 +12344,7 @@ sub modify_html_refs {
                 return;
             }
         } 
-        if (open(my $fh,"<$container")) {
+        if (open(my $fh,'<',$container)) {
             $content = join('', <$fh>);
             close($fh);
         } else {
@@ -12206,7 +12409,7 @@ sub modify_html_refs {
                         }
                     }
                 } else {
-                    if (open(my $fh,">$container")) {
+                    if (open(my $fh,'>',$container)) {
                         print $fh $content;
                         close($fh);
                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
@@ -13835,7 +14038,7 @@ sub upfile_store {
     {
         my $datafile = $r->dir_config('lonDaemons').
                            '/tmp/'.$datatoken.'.tmp';
-        if ( open(my $fh,">$datafile") ) {
+        if ( open(my $fh,'>',$datafile) ) {
             print $fh $env{'form.upfile'};
             close($fh);
         }
@@ -13860,7 +14063,7 @@ sub load_tmp_file {
     {
         my $studentfile = $r->dir_config('lonDaemons').
                               '/tmp/'.$datatoken.'.tmp';
-        if ( open(my $fh,"<$studentfile") ) {
+        if ( open(my $fh,'<',$studentfile) ) {
             @studentdata=<$fh>;
             close($fh);
         }
@@ -13870,7 +14073,7 @@ sub load_tmp_file {
 
 sub valid_datatoken {
     my ($datatoken) = @_;
-    if ($datatoken =~ /^$match_username\_$match_domain\_enroll_$match_domain\_$match_courseid\_\d+_\d+$/) {
+    if ($datatoken =~ /^$match_username\_$match_domain\_enroll_(|$match_domain\_$match_courseid)\_\d+_\d+$/) {
         return $datatoken;
     }
     return;
@@ -15026,6 +15229,8 @@ Inputs:
 
 from -              Sender's email address
 
+replyto -           Reply-To email address
+
 to -                Email address of recipient
 
 subject -           Subject of email
@@ -15036,8 +15241,6 @@ cc_string -         Carbon copy email ad
 
 bcc -               Blind carbon copy email address
 
-type -              File type of attachment
-
 attachment_path -   Path of file to be attached
 
 file_name -         Name of file to be attached
@@ -15054,8 +15257,9 @@ attachment_text -   The body of an attac
 ############################################################
 
 sub mime_email {
-    my ($from, $to, $subject, $body, $cc_string, $bcc, $attachment_path, 
-        $file_name, $attachment_text) = @_;
+    my ($from,$replyto,$to,$subject,$body,$cc_string,$bcc,$attachment_path, 
+        $file_name,$attachment_text) = @_;
+ 
     my $msg = MIME::Lite->new(
              From    => $from,
              To      => $to,
@@ -15063,6 +15267,9 @@ sub mime_email {
              Type    =>'TEXT',
              Data    => $body,
              );
+    if ($replyto ne '') {
+        $msg->add("Reply-To" => $replyto);
+    }
     if ($cc_string ne '') {
         $msg->add("Cc" => $cc_string);
     }
@@ -15178,6 +15385,8 @@ jsarray (reference to array of categorie
 subcats (reference to hash of arrays containing all subcategories within each 
          category, -recursive)
 
+maxd (reference to hash used to hold max depth for all top-level categories).
+
 Returns: nothing
 
 Side effects: populates trails and allitems hash references.
@@ -15185,7 +15394,7 @@ Side effects: populates trails and allit
 =cut
 
 sub extract_categories {
-    my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
+    my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats,$maxd) = @_;
     if (ref($categories) eq 'HASH') {
         &gather_categories($categories,$cats,$idx,$jsarray);
         if (ref($cats->[0]) eq 'ARRAY') {
@@ -15213,12 +15422,15 @@ sub extract_categories {
                         if (ref($subcats) eq 'HASH') {
                             push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
                         }
-                        &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
+                        &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats,$maxd);
                     }
                 } else {
                     if (ref($subcats) eq 'HASH') {
                         $subcats->{$item} = [];
                     }
+                    if (ref($maxd) eq 'HASH') {
+                        $maxd->{$name} = 1;
+                    }
                 }
             }
         }
@@ -15256,13 +15468,13 @@ Side effects: populates trails and allit
 =cut
 
 sub recurse_categories {
-    my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
+    my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats,$maxd) = @_;
     my $shallower = $depth - 1;
     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
             my $name = $cats->[$depth]{$category}[$k];
             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
-            my $trailstr = join(' -&gt; ',(@{$parents},$category));
+            my $trailstr = join(' &raquo; ',(@{$parents},$category));
             if ($allitems->{$item} eq '') {
                 push(@{$trails},$trailstr);
                 $allitems->{$item} = scalar(@{$trails})-1;
@@ -15283,16 +15495,21 @@ sub recurse_categories {
                 }
             }
             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
-                                $subcats);
+                                $subcats,$maxd);
             pop(@{$parents});
         }
     } else {
         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
-        my $trailstr = join(' -&gt; ',(@{$parents},$category));
+        my $trailstr = join(' &raquo; ',(@{$parents},$category));
         if ($allitems->{$item} eq '') {
             push(@{$trails},$trailstr);
             $allitems->{$item} = scalar(@{$trails})-1;
         }
+        if (ref($maxd) eq 'HASH') {
+            if ($depth > $maxd->{$parents->[0]}) {
+                $maxd->{$parents->[0]} = $depth;
+            }
+        }
     }
     return;
 }
@@ -15324,8 +15541,8 @@ sub assign_categories_table {
     my ($cathash,$currcat,$type,$disabled) = @_;
     my $output;
     if (ref($cathash) eq 'HASH') {
-        my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
-        &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
+        my (@cats,@trails,%allitems,%idx,@jsarray,%maxd,@path,$maxdepth);
+        &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray,\%maxd);
         $maxdepth = scalar(@cats);
         if (@cats > 0) {
             my $itemcount = 0;
@@ -15846,7 +16063,8 @@ sub construct_course {
 	$outcome .= $clonemsg.$linefeed;
 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
 # Copy all files
-	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
+	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},
+	                                         $args->{'dateshift'},$args->{'crscode'});
 # Restore URL
 	$cenv{'url'}=$oldcenv{'url'};
 # Restore title
@@ -16113,12 +16331,17 @@ sub construct_course {
 # Open all assignments
 #
     if ($args->{'openall'}) {
+       my $opendate = time;
+       if ($args->{'openallfrom'} =~ /^\d+$/) {
+           $opendate = $args->{'openallfrom'};
+       }
        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
-       my %storecontent = ($storeunder         => time,
+       my %storecontent = ($storeunder         => $opendate,
                            $storeunder.'.type' => 'date_start');
-       
-       $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
-                 ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
+       $outcome .= &mt('All assignments open starting [_1]',
+                       &Apache::lonlocal::locallocaltime($opendate)).': '.
+                   &Apache::lonnet::cput
+                       ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
    }
 #
 # Set first page
@@ -16343,6 +16566,24 @@ sub compare_arrays {
     return @difference;
 }
 
+sub lon_status_items {
+    my %defaults = (
+                     E         => 100,
+                     W         => 4,
+                     N         => 1,
+                     U         => 5,
+                     threshold => 200,
+                     sysmail   => 2500,
+                   );
+    my %names = (
+                   E => 'Errors',
+                   W => 'Warnings',
+                   N => 'Notices',
+                   U => 'Unsent',
+                );
+    return (\%defaults,\%names);
+}
+
 # -------------------------------------------------------- Initialize user login
 sub init_user_environment {
     my ($r, $username, $domain, $authhost, $form, $args) = @_;
@@ -16377,18 +16618,18 @@ sub init_user_environment {
 	    opendir(DIR,$lonids);
 	    while ($filename=readdir(DIR)) {
 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
-                    if ($ENV{'SERVER_PORT'} == 443) {
+                    if (tie(my %oldenv,'GDBM_File',"$lonids/$filename",
+                            &GDBM_READER(),0640)) {
                         my $linkedfile;
-                        if (tie(my %oldenv,'GDBM_File',"$lonids/$cookie.id",
-                                &GDBM_READER(),0640)) {
-                            if (exists($oldenv{'user.linkedenv'})) {
-                                $linkedfile = $oldenv{'user.linkedenv'};
-                            }
-                            untie(%oldenv);
+                        if (exists($oldenv{'user.linkedenv'})) {
+                            $linkedfile = $oldenv{'user.linkedenv'};
                         }
-                        if (unlink($lonids.'/'.$filename)) {
-                            if ($linkedfile =~ /^[a-f0-9]+_linked\.id$/) {
-                                unlink($lonids.'/'.$linkedfile);
+                        untie(%oldenv);
+                        if (unlink("$lonids/$filename")) {
+                            if ($linkedfile =~ /^[a-f0-9]+_linked$/) {
+                                if (-l "$lonids/$linkedfile.id") {
+                                    unlink("$lonids/$linkedfile.id");
+                                }
                             }
                         }
                     } else {
@@ -17389,7 +17630,7 @@ sub needs_coursereinit {
 }
 
 sub update_content_constraints {
-    my ($cdom,$cnum,$chome,$cid) = @_;
+    my ($cdom,$cnum,$chome,$cid,$keeporder) = @_;
     my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
     my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
     my (%checkresponsetypes,%checkcrsrestypes);
@@ -17437,10 +17678,24 @@ sub update_content_constraints {
         }
         undef($navmap);
     }
+    my (@resources,@order,@resparms,@zombies);
+    if ($keeporder) {
+        use LONCAPA::map;
+        @resources = @LONCAPA::map::resources;
+        @order = @LONCAPA::map::order;
+        @resparms = @LONCAPA::map::resparms;
+        @zombies = @LONCAPA::map::zombies;
+    }
     my $suppmap = 'supplemental.sequence';
     my ($suppcount,$supptools,$errors) = (0,0,0);
     ($suppcount,$supptools,$errors) = &recurse_supplemental($cnum,$cdom,$suppmap,
                                                             $suppcount,$supptools,$errors);
+    if ($keeporder) {
+        @LONCAPA::map::resources = @resources;
+        @LONCAPA::map::order = @order;
+        @LONCAPA::map::resparms = @resparms;
+        @LONCAPA::map::zombies = @zombies;
+    }
     if ($supptools) {
         my ($major,$minor) = split(/\./,$checkcrsrestypes{'exttool'});
         if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
@@ -17467,7 +17722,7 @@ sub allmaps_incourse {
     if ($lastchange > $env{'request.course.tied'}) {
         my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
         unless ($ferr) {
-            &update_content_constraints($cdom,$cnum,$chome,$cid);
+            &update_content_constraints($cdom,$cnum,$chome,$cid,1);
         }
     }
     my $navmap = Apache::lonnavmaps::navmap->new();
@@ -17601,10 +17856,10 @@ sub symb_to_docspath {
 }
 
 sub captcha_display {
-    my ($context,$lonhost) = @_;
+    my ($context,$lonhost,$defdom) = @_;
     my ($output,$error);
     my ($captcha,$pubkey,$privkey,$version) = 
-        &get_captcha_config($context,$lonhost);
+        &get_captcha_config($context,$lonhost,$defdom);
     if ($captcha eq 'original') {
         $output = &create_captcha();
         unless ($output) {
@@ -17620,9 +17875,9 @@ sub captcha_display {
 }
 
 sub captcha_response {
-    my ($context,$lonhost) = @_;
+    my ($context,$lonhost,$defdom) = @_;
     my ($captcha_chk,$captcha_error);
-    my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost);
+    my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost,$defdom);
     if ($captcha eq 'original') {
         ($captcha_chk,$captcha_error) = &check_captcha();
     } elsif ($captcha eq 'recaptcha') {
@@ -17634,7 +17889,7 @@ sub captcha_response {
 }
 
 sub get_captcha_config {
-    my ($context,$lonhost) = @_;
+    my ($context,$lonhost,$dom_in_effect) = @_;
     my ($captcha,$pubkey,$privkey,$version,$hashtocheck);
     my $hostname = &Apache::lonnet::hostname($lonhost);
     my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
@@ -17682,7 +17937,28 @@ sub get_captcha_config {
         } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
             $captcha = 'original';
         }
-    }
+    } elsif ($context eq 'passwords') {
+        if ($dom_in_effect) {
+            my %passwdconf = &Apache::lonnet::get_passwdconf($dom_in_effect);
+            if ($passwdconf{'captcha'} eq 'recaptcha') {
+                if (ref($passwdconf{'recaptchakeys'}) eq 'HASH') {
+                    $pubkey = $passwdconf{'recaptchakeys'}{'public'};
+                    $privkey = $passwdconf{'recaptchakeys'}{'private'};
+                }
+                if ($privkey && $pubkey) {
+                    $captcha = 'recaptcha';
+                    $version = $passwdconf{'recaptchaversion'};
+                    if ($version ne '2') {
+                        $version = 1;
+                    }
+                } else {
+                    $captcha = 'original';
+                }
+            } elsif ($passwdconf{'captcha'} ne 'notused') {
+                $captcha = 'original';
+            }
+        }
+    } 
     return ($captcha,$pubkey,$privkey,$version);
 }
 
@@ -17706,6 +17982,9 @@ sub create_captcha {
             last;
         }
     }
+    if ($output eq '') {
+        &Apache::lonnet::logthis("Failed to create Captcha code after $tries attempts.");
+    }
     return $output;
 }
 
@@ -17957,7 +18236,7 @@ sub make_short_symbs {
             while (($gotlock ne 'ok') && ($tries<3)) {
                 $tries ++;
                 sleep 1;
-                $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
+                $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
             }
             if ($gotlock eq 'ok') {
                 $init = &shorten_symbs($cdom,$init,$su,\%coursetiny,\%tocreate,\%newunique,
@@ -18058,6 +18337,157 @@ sub shorten_symbs {
     return $init;
 }
 
+sub is_nonframeable {
+    my ($url,$absolute,$hostname,$ip,$nocache) = @_;
+    my ($remprotocol,$remhost) = ($url =~ m{^(https?)\://(([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,})}i);
+    return if (($remprotocol eq '') || ($remhost eq ''));
+
+    $remprotocol = lc($remprotocol);
+    $remhost = lc($remhost);
+    my $remport = 80;
+    if ($remprotocol eq 'https') {
+        $remport = 443;
+    }
+    my ($result,$cached) = &Apache::lonnet::is_cached_new('noiframe',$remhost.':'.$remport);
+    if ($cached) {
+        unless ($nocache) {
+            if ($result) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+    my $uselink;
+    my $request = new HTTP::Request('HEAD',$url);
+    my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',5);
+    if ($response->is_success()) {
+        my $secpolicy = lc($response->header('content-security-policy'));
+        my $xframeop = lc($response->header('x-frame-options'));
+        $secpolicy =~ s/^\s+|\s+$//g;
+        $xframeop =~ s/^\s+|\s+$//g;
+        if (($secpolicy ne '') || ($xframeop ne '')) {
+            my $remotehost = $remprotocol.'://'.$remhost;
+            my ($origin,$protocol,$port);
+            if ($ENV{'SERVER_PORT'} =~/^\d+$/) {
+                $port = $ENV{'SERVER_PORT'};
+            } else {
+                $port = 80;
+            }
+            if ($absolute eq '') {
+                $protocol = 'http:';
+                if ($port == 443) {
+                    $protocol = 'https:';
+                }
+                $origin = $protocol.'//'.lc($hostname);
+            } else {
+                $origin = lc($absolute);
+                ($protocol,$hostname) = ($absolute =~ m{^(https?:)//([^/]+)$});
+            }
+            if (($secpolicy) && ($secpolicy =~ /\Qframe-ancestors\E([^;]*)(;|$)/)) {
+                my $framepolicy = $1;
+                $framepolicy =~ s/^\s+|\s+$//g;
+                my @policies = split(/\s+/,$framepolicy);
+                if (@policies) {
+                    if (grep(/^\Q'none'\E$/,@policies)) {
+                        $uselink = 1;
+                    } else {
+                        $uselink = 1;
+                        if ((grep(/^\Q*\E$/,@policies)) || (grep(/^\Q$protocol\E$/,@policies)) ||
+                                (($origin ne '') && (grep(/^\Q$origin\E$/,@policies))) ||
+                                (($ip ne '') && (grep(/^\Q$ip\E$/,@policies)))) {
+                            undef($uselink);
+                        }
+                        if ($uselink) {
+                            if (grep(/^\Q'self'\E$/,@policies)) {
+                                if (($origin ne '') && ($remotehost eq $origin)) {
+                                    undef($uselink);
+                                }
+                            }
+                        }
+                        if ($uselink) {
+                            my @possok;
+                            if ($ip ne '') {
+                                push(@possok,$ip);
+                            }
+                            my $hoststr = '';
+                            foreach my $part (reverse(split(/\./,$hostname))) {
+                                if ($hoststr eq '') {
+                                    $hoststr = $part;
+                                } else {
+                                    $hoststr = "$part.$hoststr";
+                                }
+                                if ($hoststr eq $hostname) {
+                                    push(@possok,$hostname);
+                                } else {
+                                    push(@possok,"*.$hoststr");
+                                }
+                            }
+                            if (@possok) {
+                                foreach my $poss (@possok) {
+                                    last if (!$uselink);
+                                    foreach my $policy (@policies) {
+                                        if ($policy =~ m{^(\Q$protocol\E//|)\Q$poss\E(\Q:$port\E|)$}) {
+                                            undef($uselink);
+                                            last;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            } elsif ($xframeop ne '') {
+                $uselink = 1;
+                my @policies = split(/\s*,\s*/,$xframeop);
+                if (@policies) {
+                    unless (grep(/^deny$/,@policies)) {
+                        if ($origin ne '') {
+                            if (grep(/^sameorigin$/,@policies)) {
+                                if ($remotehost eq $origin) {
+                                    undef($uselink);
+                                }
+                            }
+                            if ($uselink) {
+                                foreach my $policy (@policies) {
+                                    if ($policy =~ /^allow-from\s*(.+)$/) {
+                                        my $allowfrom = $1;
+                                        if (($allowfrom ne '') && ($allowfrom eq $origin)) {
+                                            undef($uselink);
+                                            last;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if ($nocache) {
+        if ($cached) {
+            my $devalidate;
+            if ($uselink && !$result) {
+                $devalidate = 1;
+            } elsif (!$uselink && $result) {
+                $devalidate = 1;
+            }
+            if ($devalidate) {
+                &Apache::lonnet::devalidate_cache_new('noiframe',$remhost.':'.$remport);
+            }
+        }
+    } else {
+        if ($uselink) {
+            $result = 1;
+        } else {
+            $result = 0;
+        }
+        &Apache::lonnet::do_cache_new('noiframe',$remhost.':'.$remport,$result,3600);
+    }
+    return $uselink;
+}
+
 1;
 __END__;