--- loncom/auth/switchserver.pm	2018/12/08 15:16:03	1.46
+++ loncom/auth/switchserver.pm	2023/06/02 01:20:26	1.65
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Switch Servers Handler
 #
-# $Id: switchserver.pm,v 1.46 2018/12/08 15:16:03 raeburn Exp $
+# $Id: switchserver.pm,v 1.65 2023/06/02 01:20:26 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -29,7 +29,7 @@
 package Apache::switchserver;
 
 use strict;
-use Apache::Constants qw(:common);
+use Apache::Constants qw(:common :remotehost);
 use Apache::lonnet;
 use Digest::MD5 qw(md5_hex);
 use CGI::Cookie();
@@ -54,11 +54,15 @@ sub init_env {
 }
 
 sub do_redirect {
-    my ($r,$url,$only_body,$extra_text) = @_;
+    my ($r,$url,$only_body,$extra_text,$write_to_opener) = @_;
     $r->send_http_header;
+    my $delay = 0.5;
+    if ($only_body && !$extra_text) {
+        $delay = 0;
+    }
     my $start_page = 
 	&Apache::loncommon::start_page('Switching Server ...',undef,
-				       {'redirect'       => [0.5,$url],
+				       {'redirect'       => [$delay,$url,'',$write_to_opener,1],
 					'only_body'      => $only_body,});
     my $end_page   = &Apache::loncommon::end_page();
     $r->print($start_page.$extra_text.$end_page);
@@ -93,12 +97,12 @@ sub flush_course_logs {
 
 sub handler {
     my ($r) = @_;
-    
+
     my $handle=&init_env($r);
     if (!defined($handle)) { return FORBIDDEN; }
 
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
-				['otherserver','role','origurl','symb']);
+				['otherserver','role','origurl','symb','lcssowin','edit']);
 
     my $switch_to=&Apache::lonnet::hostname($env{'form.otherserver'});
     if (! $env{'form.otherserver'}) {
@@ -107,7 +111,7 @@ sub handler {
 						   $env{'user.name'});
 	if (! $env{'form.otherserver'}) {
 	    $env{'form.otherserver'} = 
-		&Apache::lonnet::spareserver(30000,undef,1);
+		&Apache::lonnet::spareserver($r,30000,undef,1);
 	}
 	$switch_to=&Apache::lonnet::hostname($env{'form.otherserver'});
     }
@@ -119,6 +123,10 @@ sub handler {
         if ($Apache::lonnet::protocol{$env{'form.otherserver'}} eq 'https') {
             $protocol = $Apache::lonnet::protocol{$env{'form.otherserver'}};
         }
+        unless ($env{'form.otherserver'} eq $r->dir_config('lonHostID')) {
+            my $alias = &Apache::lonnet::use_proxy_alias($r,$env{'form.otherserver'});
+            $switch_to = $alias if ($alias ne '');
+        }
     }
 
     if ($env{'user.name'} eq 'public'
@@ -159,26 +167,69 @@ sub handler {
     }
 
     unless ($skip_canhost_check) {
-        my $canhost = 1;
-        my $uprimary_id = &Apache::lonnet::domain($env{'user.domain'},'primary');
-        my $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
-        my @intdoms;
-        my $internet_names = &Apache::lonnet::get_internet_names($env{'form.otherserver'});
-        if (ref($internet_names) eq 'ARRAY') {
-            @intdoms = @{$internet_names};
-        }
-        unless ($uint_dom ne '' && grep(/^\Q$uint_dom\E$/,@intdoms)) {
-            my $serverhomeID = &Apache::lonnet::get_server_homeID($switch_to);
-            my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
-            my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
-            my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
-            my $remoterev = &Apache::lonnet::get_server_loncaparev($env{'user.domain'},$env{'form.otherserver'});
-            $canhost = 
-                &Apache::lonnet::can_host_session($env{'user.domain'},
-                                                  $env{'form.otherserver'},
-                                                  $remoterev,
-                                                  $udomdefaults{'remotesessions'},
-                                                  $defdomdefaults{'hostedsessions'});
+        my $canhost = &Apache::lonnet::can_switchserver($env{'user.domain'},$env{'form.otherserver'});
+        unless ($canhost) {
+            if (($env{'request.course.id'}) && ($env{'form.symb'} ne '') &&
+                (&Apache::lonnet::allowed('mdc',$env{'request.course.id'}))) {
+                my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                if (($cdom ne '') && ($cnum ne '') && ($env{'form.role'} =~ m{^([^.]+)\Q./$cdom/$cnum\E$})) {
+                    my $symb = &Apache::lonnet::symbclean($env{'form.symb'});
+                    my ($map,$idx,$url) = &Apache::lonnet::decode_symb($symb);
+                    if (&Apache::lonnet::symbverify($symb,$url)) {
+                        my $fileloc = &Apache::lonnet::declutter(&Apache::lonnet::filelocation("",$url));
+                        my $resurl = &Apache::lonnet::clutter($url);
+                        if ($resurl =~ m{^/res/($match_domain)/($match_username)/}) {
+                            my ($audom,$auname) = ($1,$2);
+                            if (&Apache::lonnet::homeserver($auname,$audom) eq $env{'form.otherserver'}) {
+                                my @possroles = ("user.role.au./$audom/","user.role.ca./$audom/$auname","user.role.aa./$audom/$auname");
+                                my $hasrole;
+                                foreach my $rolekey (@possroles) {
+                                    if (exists($env{$rolekey})) {
+                                        my ($start,$end) = split(/\./,$env{$rolekey});
+                                        unless (($start && $start > $now) || ($end && $end < $now)) {
+                                            if ($rolekey eq "user.role.au./$audom/") {
+                                                $hasrole = $rolekey;
+                                            } elsif ((&Apache::lonnet::will_trust('othcoau',$env{'user.domain'},$audom)) &&
+                                                     (&Apache::lonnet::will_trust('coaurem',$audom,$env{'user.domain'}))) {
+                                                $hasrole = $rolekey;
+                                            }
+                                        }
+                                        if ($hasrole) {
+                                            $hasrole =~ s/^\Quser.role.\E//;
+                                            last;
+                                        }
+                                    }
+                                }
+                                if ($hasrole) {
+                                    $env{'form.role'} = $hasrole;
+                                    $env{'form.origurl'} = &Apache::lonnet::deversion($resurl);
+                                    $env{'form.origurl'} =~ s{^/res/}{/priv/};
+                                    delete($env{'form.symb'});
+                                    $canhost = 1;
+                                    if ($env{'form.edit'}) {
+                                        my $ip = &Apache::lonnet::get_requestor_ip($r,REMOTE_NOLOOKUP);
+                                        my %info=('ip'            => $ip,
+                                                  'domain'        => $env{'user.domain'},
+                                                  'username'      => $env{'user.name'},
+                                                  'home'          => $env{'user.home'},
+                                                  'role'          => $env{'form.role'},
+                                                  'server'        => $r->dir_config('lonHostID'),
+                                                  'origurl'       => $env{'form.origurl'});
+                                        &Apache::loncommon::content_type($r,'text/html');
+                                        my $token = &Apache::lonnet::tmpput(\%info,$env{'form.otherserver'});
+                                        my $url = $protocol.'://'.$switch_to.'/adm/login?'.
+                                                                 'domain='.$env{'user.domain'}.
+                                                                 '&amp;username='.$env{'user.name'}.
+                                                                 '&amp;token='.$token;
+                                        return &do_redirect($r,$url,0);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
         }
         unless ($canhost) { return FORBIDDEN; }
     }
@@ -207,7 +258,8 @@ sub handler {
     } else {
         $logmsg .= " (no role)";
     }
-    $logmsg .= ' '.$ENV{'REMOTE_ADDR'};
+    my $ip = &Apache::lonnet::get_requestor_ip($r,REMOTE_NOLOOKUP);
+    $logmsg .= ' '.$ip;
     &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
 			 $env{'user.home'},$logmsg);
 
@@ -227,26 +279,32 @@ sub handler {
 	return OK;
     }
 # -------------------------------------------------------- Menu script and info
-	   
+
 # ---------------------------------------------------------------- Get handover
 
-    my $newcookieid;
-    my ($is_balancer,$posshost,$setcookie,$offloadto,$dom_balancers) =
-        &Apache::lonnet::check_loadbalancing($env{'user.name'},$env{'user.domain'});
+    my ($is_balancer,$setcookie,$newcookieid,$otherbalcookie,$offloadto,$dom_balancers);
+    my $only_body = 0;
+    ($is_balancer,undef,$setcookie,$offloadto,$dom_balancers) =
+        &Apache::lonnet::check_loadbalancing($env{'user.name'},$env{'user.domain'},'switchserver');
     if ($is_balancer && $setcookie && $env{'form.otherserver'}) {
 
         # Set a balancer cookie unless browser already sent LON-CAPA load balancer
         # cookie which points at the target server
         my ($found_server,$balancer_cookie) = &Apache::lonnet::check_for_balancer_cookie($r,1);
 
-        unless (($found_server eq $env{'form.otherserver'}) &&
-                ($balancer_cookie =~ /^\Q$env{'user.domain'}\E_\Q$env{'user.name'}\E_/)) {
+        if (($found_server eq $env{'form.otherserver'}) &&
+            ($balancer_cookie =~ /^\Q$env{'user.domain'}\E_\Q$env{'user.name'}\E_/)) {
+            $only_body = 1;
+        } else {
             $newcookieid = &balancer_cookieid($r,$env{'form.otherserver'},
                                               $env{'user.name'},$env{'user.domain'});
         }
     }
+    if ((!$is_balancer) && ($env{'request.balancercookie'})) {
+        $otherbalcookie = $env{'request.balancercookie'};
+    }
 
-    my %info=('ip'            => $ENV{'REMOTE_ADDR'},
+    my %info=('ip'            => $ip,
               'domain'        => $env{'user.domain'},
               'username'      => $env{'user.name'},
               'home'          => $env{'user.home'},
@@ -267,19 +325,54 @@ sub handler {
     }
     if ($newcookieid) {
         $info{'balcookie'} = $newcookieid;
+    } elsif ($otherbalcookie) {
+        $info{'otherbalcookie'} = $otherbalcookie;
     }
     if ($env{'form.origurl'}) {
         $info{'origurl'} = $env{'form.origurl'};
+        if ($env{'form.origurl'} eq '/adm/email') {
+            if ($env{'request.display'} && ($env{'request.mailrecip'} eq "$env{'user.name'}:$env{'user.domain'}")) {
+                $info{'display'} = &escape($env{'request.display'});
+                $info{'mailrecip'} = &escape($env{'request.mailrecip'});
+            }
+        }
     }
     if ($env{'form.symb'}) {
         $info{'symb'} = $env{'form.symb'};
     }
+    my $ssologoutscript = '';
+    my $write_to_opener;
     if ($env{'request.sso.login'}) {
 	$info{'sso.login'} = $env{'request.sso.login'};
+        if (defined($r->dir_config("lonSSOUserLogoutScriptFile_$info{domain}"))) {
+            if (open(my $fh,'<',$r->dir_config("lonSSOUserLogoutScriptFile_$info{domain}"))) {
+                $ssologoutscript .= join('',<$fh>);
+                close($fh); 
+            }
+        }
+        if (defined($r->dir_config('lonSSOUserLogoutScriptFile'))) {
+            if (open(my $fh,'<',$r->dir_config('lonSSOUserLogoutScriptFile'))) {
+                $ssologoutscript .= join('',<$fh>);
+                close($fh);
+            }
+        }
     }
     if ($env{'request.sso.reloginserver'}) {
         $info{'sso.reloginserver'} = $env{'request.sso.reloginserver'};
     }
+    if ($env{'request.linkprot'}) {
+        $info{'linkprot'} = $env{'request.linkprot'};
+        foreach my $item ('linkprotuser','linkprotexit','linkprotpbid','linkprotpburl') {
+            if ($env{'request.'.$item}) {
+                $info{$item} = $env{'request.'.$item};
+            }
+        }
+    } elsif ($env{'request.linkkey'} ne '') {
+        $info{'linkkey'} = $env{'request.linkkey'};
+    }
+    if ($env{'request.deeplink.login'}) {
+        $info{'deeplink.login'} = $env{'request.deeplink.login'};
+    }
     if ($env{'request.lti.login'}) {
         $info{'lti.login'} = $env{'request.lti.login'};
     }
@@ -314,12 +407,18 @@ sub handler {
         $info{'lti.target'} = $env{'request.lti.target'};
     }
     my $token = &Apache::lonnet::tmpput(\%info,$env{'form.otherserver'});
-    my $url =$protocol.'://'.$switch_to.'/adm/login?'.
-	'domain='.$env{'user.domain'}.
-	'&amp;username='.$env{'user.name'}.
-	'&amp;token='.$token;
+    my @args = ("domain=$env{'user.domain'}",
+                "username=$env{'user.name'}",
+                "token=$token");
+    my $url = $protocol.'://'.$switch_to.'/adm/login?';
+    if ($env{'form.lcssowin'}) {
+        $url .= join('&',@args);
+        $only_body = 1;
+    } else {
+        $url .= join('&amp;',@args);
+    }
 # --------------------------------------------------------------- Screen Output
-    return &do_redirect($r, $url, 0);
+    return &do_redirect($r, $url, $only_body, $ssologoutscript, $env{'form.lcssowin'});
 }
 
 1;