--- loncom/lonnet/perl/lonnet.pm 2019/02/03 20:41:27 1.1172.2.93.4.12
+++ loncom/lonnet/perl/lonnet.pm 2021/02/01 00:22:04 1.1172.2.137
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.1172.2.93.4.12 2019/02/03 20:41:27 raeburn Exp $
+# $Id: lonnet.pm,v 1.1172.2.137 2021/02/01 00:22:04 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -74,10 +74,11 @@ use strict;
use LWP::UserAgent();
use HTTP::Date;
use Image::Magick;
+use CGI::Cookie;
-use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir
+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,
@@ -98,6 +99,7 @@ use LONCAPA qw(:DEFAULT :match);
use LONCAPA::Configuration;
use LONCAPA::lonmetadata;
use LONCAPA::Lond;
+use LONCAPA::transliterate;
use File::Copy;
@@ -123,12 +125,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,
@@ -261,9 +264,10 @@ sub get_server_loncaparev {
if ($caller eq 'loncron') {
my $ua=new LWP::UserAgent;
$ua->timeout(4);
+ my $hostname = &hostname($lonhost);
my $protocol = $protocol{$lonhost};
$protocol = 'http' if ($protocol ne 'https');
- my $url = $protocol.'://'.&hostname($lonhost).'/adm/about.html';
+ my $url = $protocol.'://'.$hostname.'/adm/about.html';
my $request=new HTTP::Request('GET',$url);
my $response=$ua->request($request);
unless ($response->is_error()) {
@@ -407,8 +411,26 @@ sub reply {
unless (defined(&hostname($server))) { return 'no_such_host'; }
my $answer=subreply($cmd,$server);
if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
- &logthis("WARNING:".
- " $cmd to $server returned $answer");
+ my $logged = $cmd;
+ if ($cmd =~ /^encrypt:([^:]+):/) {
+ my $subcmd = $1;
+ if (($subcmd eq 'auth') || ($subcmd eq 'passwd') ||
+ ($subcmd eq 'changeuserauth') || ($subcmd eq 'makeuser') ||
+ ($subcmd eq 'putdom') || ($subcmd eq 'autoexportgrades')) {
+ (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')) {
+ splice(@rest,3,1,'Hidden');
+ }
+ $logged = join(':',('encrypt:'.$subcmd,@rest));
+ }
+ }
+ &logthis("WARNING:".
+ " $logged to $server returned $answer");
}
return $answer;
}
@@ -603,18 +625,39 @@ sub transfer_profile_to_env {
sub check_for_valid_session {
my ($r,$name,$userhashref,$domref) = @_;
my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
- if ($name eq '') {
- $name = 'lonID';
- }
- my $lonid=$cookies{$name};
- return undef if (!$lonid);
-
- my $handle=&LONCAPA::clean_handle($lonid->value);
- my $lonidsdir;
+ my ($lonidsdir,$linkname,$pubname,$secure,$lonid);
if ($name eq 'lonDAV') {
$lonidsdir=$r->dir_config('lonDAVsessDir');
} else {
$lonidsdir=$r->dir_config('lonIDsDir');
+ if ($name eq '') {
+ $name = 'lonID';
+ }
+ }
+ if ($name eq 'lonID') {
+ $secure = 'lonSID';
+ $linkname = 'lonLinkID';
+ $pubname = 'lonPubID';
+ if (exists($cookies{$secure})) {
+ $lonid=$cookies{$secure};
+ } elsif (exists($cookies{$name})) {
+ $lonid=$cookies{$name};
+ } elsif ((exists($cookies{$linkname})) && ($ENV{'SERVER_PORT'} != 443)) {
+ $lonid=$cookies{$linkname};
+ } elsif (exists($cookies{$pubname})) {
+ $lonid=$cookies{$pubname};
+ }
+ } else {
+ $lonid=$cookies{$name};
+ }
+ return undef if (!$lonid);
+
+ my $handle=&LONCAPA::clean_handle($lonid->value);
+ if (-l "$lonidsdir/$handle.id") {
+ my $link = readlink("$lonidsdir/$handle.id");
+ if ((-e $link) && ($link =~ m{^\Q$lonidsdir\E/(.+)\.id$})) {
+ $handle = $1;
+ }
}
if (!-e "$lonidsdir/$handle.id") {
if ((ref($domref)) && ($name eq 'lonID') &&
@@ -639,6 +682,7 @@ sub check_for_valid_session {
if (!defined($disk_env{'user.name'})
|| !defined($disk_env{'user.domain'})) {
+ untie(%disk_env);
return undef;
}
@@ -646,6 +690,7 @@ sub check_for_valid_session {
$userhashref->{'name'} = $disk_env{'user.name'};
$userhashref->{'domain'} = $disk_env{'user.domain'};
}
+ untie(%disk_env);
return $handle;
}
@@ -670,6 +715,37 @@ sub timed_flock {
}
}
+sub get_sessionfile_vars {
+ my ($handle,$lonidsdir,$storearr) = @_;
+ my %returnhash;
+ unless (ref($storearr) eq 'ARRAY') {
+ return %returnhash;
+ }
+ if (-l "$lonidsdir/$handle.id") {
+ my $link = readlink("$lonidsdir/$handle.id");
+ if ((-e $link) && ($link =~ m{^\Q$lonidsdir\E/(.+)\.id$})) {
+ $handle = $1;
+ }
+ }
+ if ((-e "$lonidsdir/$handle.id") &&
+ ($handle =~ /^($match_username)\_\d+\_($match_domain)\_(.+)$/)) {
+ my ($possuname,$possudom,$possuhome) = ($1,$2,$3);
+ if ((&domain($possudom) ne '') && (&homeserver($possuname,$possudom) eq $possuhome)) {
+ if (open(my $idf,'+<',"$lonidsdir/$handle.id")) {
+ flock($idf,LOCK_SH);
+ if (tie(my %disk_env,'GDBM_File',"$lonidsdir/$handle.id",
+ &GDBM_READER(),0640)) {
+ foreach my $item (@{$storearr}) {
+ $returnhash{$item} = $disk_env{$item};
+ }
+ untie(%disk_env);
+ }
+ }
+ }
+ }
+ return %returnhash;
+}
+
# ---------------------------------------------------------- Append Environment
sub appenv {
@@ -823,6 +899,7 @@ sub userload {
while ($filename=readdir(LONIDS)) {
next if ($filename eq '.' || $filename eq '..');
next if ($filename =~ /publicuser_\d+\.id/);
+ next if ($filename =~ /^[a-f0-9]+_linked\.id$/);
my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9];
if ($curtime-$mtime < 1800) { $numusers++; }
}
@@ -878,13 +955,13 @@ sub spareserver {
}
if (!$want_server_name) {
- my $protocol = 'http';
- if ($protocol{$spare_server} eq 'https') {
- $protocol = $protocol{$spare_server};
- }
if (defined($spare_server)) {
my $hostname = &hostname($spare_server);
if (defined($hostname)) {
+ my $protocol = 'http';
+ if ($protocol{$spare_server} eq 'https') {
+ $protocol = $protocol{$spare_server};
+ }
$spare_server = $protocol.'://'.$hostname;
}
}
@@ -951,6 +1028,90 @@ sub find_existing_session {
return;
}
+# check if user's browser sent load balancer cookie and server still has session
+# and is not overloaded.
+sub check_for_balancer_cookie {
+ my ($r,$update_mtime) = @_;
+ my ($otherserver,$cookie);
+ my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
+ if (exists($cookies{'balanceID'})) {
+ my $balid = $cookies{'balanceID'};
+ $cookie=&LONCAPA::clean_handle($balid->value);
+ my $balancedir=$r->dir_config('lonBalanceDir');
+ if ((-d $balancedir) && (-e "$balancedir/$cookie.id")) {
+ if ($cookie =~ /^($match_domain)_($match_username)_[a-f0-9]+$/) {
+ my ($possudom,$possuname) = ($1,$2);
+ my $has_session = 0;
+ if ((&domain($possudom) ne '') &&
+ (&homeserver($possuname,$possudom) ne 'no_host')) {
+ my $try_server;
+ my $opened = open(my $idf,'+<',"$balancedir/$cookie.id");
+ if ($opened) {
+ flock($idf,LOCK_SH);
+ while (my $line = <$idf>) {
+ chomp($line);
+ if (&hostname($line) ne '') {
+ $try_server = $line;
+ last;
+ }
+ }
+ close($idf);
+ if (($try_server) &&
+ (&has_user_session($try_server,$possudom,$possuname))) {
+ my $lowest_load = 30000;
+ ($otherserver,$lowest_load) =
+ &compare_server_load($try_server,undef,$lowest_load);
+ if ($otherserver ne '' && $lowest_load < 100) {
+ $has_session = 1;
+ } else {
+ undef($otherserver);
+ }
+ }
+ }
+ }
+ if ($has_session) {
+ if ($update_mtime) {
+ my $atime = my $mtime = time;
+ utime($atime,$mtime,"$balancedir/$cookie.id");
+ }
+ } else {
+ unlink("$balancedir/$cookie.id");
+ }
+ }
+ }
+ }
+ 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}$/) {
+ 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('delbalcookie:'.&escape($cookie),$balancer);
+ }
+ }
+}
+
# -------------------------------- ask if server already has a session for user
sub has_user_session {
my ($lonid,$udom,$uname) = @_;
@@ -1017,6 +1178,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 {
@@ -1052,6 +1235,9 @@ sub changepass {
} elsif ($answer =~ "invalid_client") {
&logthis("$server refused to change $uname in $udom password because ".
"it was a reset by e-mail originating from an invalid server.");
+ } elsif ($answer =~ "^prioruse") {
+ &logthis("$server refused to change $uname in $udom password because ".
+ "the password had been used before");
}
return $answer;
}
@@ -1190,6 +1376,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);
@@ -1288,7 +1483,7 @@ sub get_lonbalancer_config {
sub check_loadbalancing {
my ($uname,$udom,$caller) = @_;
my ($is_balancer,$currtargets,$currrules,$dom_in_use,$homeintdom,
- $rule_in_effect,$offloadto,$otherserver);
+ $rule_in_effect,$offloadto,$otherserver,$setcookie);
my $lonhost = $perlvar{'lonHostID'};
my @hosts = ¤t_machine_ids();
my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
@@ -1315,7 +1510,7 @@ sub check_loadbalancing {
}
}
if (ref($result) eq 'HASH') {
- ($is_balancer,$currtargets,$currrules) =
+ ($is_balancer,$currtargets,$currrules,$setcookie) =
&check_balancer_result($result,@hosts);
if ($is_balancer) {
if (ref($currrules) eq 'HASH') {
@@ -1376,7 +1571,7 @@ sub check_loadbalancing {
}
}
if (ref($result) eq 'HASH') {
- ($is_balancer,$currtargets,$currrules) =
+ ($is_balancer,$currtargets,$currrules,$setcookie) =
&check_balancer_result($result,@hosts);
if ($is_balancer) {
if (ref($currrules) eq 'HASH') {
@@ -1408,7 +1603,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') {
@@ -1442,7 +1637,6 @@ sub check_loadbalancing {
$is_balancer = 0;
if ($uname ne '' && $udom ne '') {
if (($env{'user.name'} eq $uname) && ($env{'user.domain'} eq $udom)) {
-
&appenv({'user.loadbalexempt' => $lonhost,
'user.loadbalcheck.time' => time});
}
@@ -1450,12 +1644,15 @@ sub check_loadbalancing {
}
}
}
- return ($is_balancer,$otherserver);
+ if (($is_balancer) && (!$homeintdom)) {
+ undef($setcookie);
+ }
+ return ($is_balancer,$otherserver,$setcookie);
}
sub check_balancer_result {
my ($result,@hosts) = @_;
- my ($is_balancer,$currtargets,$currrules);
+ my ($is_balancer,$currtargets,$currrules,$setcookie);
if (ref($result) eq 'HASH') {
if ($result->{'lonhost'} ne '') {
my $currbalancer = $result->{'lonhost'};
@@ -1471,12 +1668,13 @@ sub check_balancer_result {
$is_balancer = 1;
$currrules = $result->{$key}{'rules'};
$currtargets = $result->{$key}{'targets'};
+ $setcookie = $result->{$key}{'cookie'};
last;
}
}
}
}
- return ($is_balancer,$currtargets,$currrules);
+ return ($is_balancer,$currtargets,$currrules,$setcookie);
}
sub get_loadbalancer_targets {
@@ -1700,12 +1898,7 @@ sub get_dom {
}
}
if ($udom && $uhome && ($uhome ne 'no_host')) {
- my $rep;
- if ($namespace =~ /^enc/) {
- $rep=&reply("encrypt:egetdom:$udom:$namespace:$items",$uhome);
- } else {
- $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
- }
+ my $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
my %returnhash;
if ($rep eq '' || $rep =~ /^error: 2 /) {
return %returnhash;
@@ -1749,11 +1942,7 @@ sub put_dom {
$items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&';
}
$items=~s/\&$//;
- if ($namespace =~ /^enc/) {
- return &reply("encrypt:putdom:$udom:$namespace:$items",$uhome);
- } else {
- return &reply("putdom:$udom:$namespace:$items",$uhome);
- }
+ return &reply("putdom:$udom:$namespace:$items",$uhome);
} else {
&logthis("put_dom failed - no homeserver and/or domain");
}
@@ -1846,11 +2035,11 @@ 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 ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.([\w.\-]+)\'?$/);
+ my $lcrev = &get_server_loncaparev($udom,$homeserver);
+ my ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.(\d+)[\w.\-]+\'?$/);
if (($major eq '' && $minor eq '') || ($major < 2) ||
- (($major == 2) && ($minor < 11)) ||
- (($major == 2) && ($minor == 11) && ($subver !~ /^2\.B/))) {
+ (($major == 2) && ($minor < 11)) ||
+ (($major == 2) && ($minor == 11) && ($subver < 3))) {
return;
}
}
@@ -1898,11 +2087,11 @@ 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 ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.([\w.\-]+)\'?$/);
+ my $lcrev = &get_server_loncaparev($dom,$tryserver);
+ my ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.(\d+)[\w.\-]+\'?$/);
next if (($major eq '' && $minor eq '') || ($major < 2) ||
- (($major == 2) && ($minor < 11)) ||
- (($major == 2) && ($minor == 11) && ($subver !~ /^2\.B/)));
+ (($major == 2) && ($minor < 11)) ||
+ (($major == 2) && ($minor == 11) && ($subver < 3)));
}
}
my $host=&hostname($tryserver);
@@ -2222,6 +2411,9 @@ sub get_domain_defaults {
} elsif ($domconfig{'coursedefaults'}{'canclone'}) {
$domdefaults{'canclone'}=$domconfig{'coursedefaults'}{'canclone'};
}
+ if ($domconfig{'coursedefaults'}{'texengine'}) {
+ $domdefaults{'texengine'} = $domconfig{'coursedefaults'}{'texengine'};
+ }
}
if (ref($domconfig{'usersessions'}) eq 'HASH') {
if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {
@@ -2233,6 +2425,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') {
@@ -2285,20 +2480,86 @@ sub get_domain_defaults {
return %domdefaults;
}
-sub course_portal_url {
- my ($cnum,$cdom) = @_;
- my $chome = &homeserver($cnum,$cdom);
- my $hostname = &hostname($chome);
- my $protocol = $protocol{$chome};
- $protocol = 'http' if ($protocol ne 'https');
- my %domdefaults = &get_domain_defaults($cdom);
- my $firsturl;
- if ($domdefaults{'portal_def'}) {
- $firsturl = $domdefaults{'portal_def'};
- } else {
- $firsturl = $protocol.'://'.$hostname;
+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;
+}
+
+# --------------------------------------------- Get domain config for passwords
+
+sub get_passwdconf {
+ my ($dom) = @_;
+ my (%passwdconf,$gotconf,$lookup);
+ my ($result,$cached)=&is_cached_new('passwdconf',$dom);
+ if (defined($cached)) {
+ if (ref($result) eq 'HASH') {
+ %passwdconf = %{$result};
+ $gotconf = 1;
+ }
+ }
+ unless ($gotconf) {
+ my %domconfig = &get_dom('configuration',['passwords'],$dom);
+ if (ref($domconfig{'passwords'}) eq 'HASH') {
+ %passwdconf = %{$domconfig{'passwords'}};
+ }
+ my $cachetime = 24*60*60;
+ &do_cache_new('passwdconf',$dom,\%passwdconf,$cachetime);
}
- return $firsturl;
+ return %passwdconf;
}
# --------------------------------------------------- Assign a key to a student
@@ -2836,6 +3097,27 @@ 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 } ¤t_machine_ids()) {
+ $answer = 'home';
+ } else {
+ $answer = reply("unsub:$fname",$home);
+ }
+ return $answer;
+}
+
# ------------------------------------------------ Get server side include body
sub ssi_body {
my ($filelink,%form)=@_;
@@ -2885,8 +3167,7 @@ sub absolute_url {
sub ssi {
my ($fn,%form)=@_;
- my $ua=new LWP::UserAgent;
- my $request;
+ my ($request,$response);
$form{'no_update_last_known'}=1;
&Apache::lonenc::check_encrypt(\$fn);
@@ -2903,7 +3184,30 @@ sub ssi {
}
$request->header(Cookie => $ENV{'HTTP_COOKIE'});
- my $response= $ua->request($request);
+
+ if (($env{'request.course.id'}) &&
+ ($form{'grade_courseid'} eq $env{'request.course.id'}) &&
+ ($form{'grade_username'} ne '') && ($form{'grade_domain'} ne '') &&
+ ($form{'grade_symb'} ne '') &&
+ (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}.
+ ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {
+ if (LWP::UserAgent->VERSION >= 5.834) {
+ my $ua=new LWP::UserAgent;
+ $ua->local_address('127.0.0.1');
+ $response = $ua->request($request);
+ } else {
+ {
+ require LWP::Protocol::http;
+ local @LWP::Protocol::http::EXTRA_SOCK_OPTS = (LocalAddr => '127.0.0.1');
+ my $ua=new LWP::UserAgent;
+ $response = $ua->request($request);
+ @LWP::Protocol::http::EXTRA_SOCK_OPTS = ();
+ }
+ }
+ } else {
+ my $ua=new LWP::UserAgent;
+ $response = $ua->request($request);
+ }
if (wantarray) {
return ($response->content, $response);
} else {
@@ -2923,6 +3227,78 @@ sub externalssi {
}
}
+# If the local copy of a replicated resource is outdated, trigger a
+# connection from the homeserver to flush the delayed queue. If no update
+# happens, remove local copies of outdated resource (and corresponding
+# metadata file).
+
+sub remove_stale_resfile {
+ my ($url) = @_;
+ my $removed;
+ if ($url=~m{^/res/($match_domain)/($match_username)/}) {
+ my $audom = $1;
+ my $auname = $2;
+ unless (($url =~ /\.\d+\.\w+$/) || ($url =~ m{^/res/lib/templates/})) {
+ my $homeserver = &homeserver($auname,$audom);
+ unless (($homeserver eq 'no_host') ||
+ (grep { $_ eq $homeserver } ¤t_machine_ids())) {
+ my $fname = &filelocation('',$url);
+ if (-e $fname) {
+ my $hostname = &hostname($homeserver);
+ if ($hostname) {
+ my $protocol = $protocol{$homeserver};
+ $protocol = 'http' if ($protocol ne 'https');
+ my $uri = $protocol.'://'.$hostname.'/raw/'.&declutter($url);
+ my $ua=new LWP::UserAgent;
+ $ua->timeout(5);
+ my $request=new HTTP::Request('HEAD',$uri);
+ my $response=$ua->request($request);
+ if ($response->is_success()) {
+ my $remmodtime = &HTTP::Date::str2time( $response->header('Last-modified') );
+ my $locmodtime = (stat($fname))[9];
+ if ($locmodtime < $remmodtime) {
+ my $stale;
+ my $answer = &reply('pong',$homeserver);
+ if ($answer eq $homeserver.':'.$perlvar{'lonHostID'}) {
+ sleep(0.2);
+ $locmodtime = (stat($fname))[9];
+ if ($locmodtime < $remmodtime) {
+ my $posstransfer = $fname.'.in.transfer';
+ if ((-e $posstransfer) && ($remmodtime < (stat($posstransfer))[9])) {
+ $removed = 1;
+ } else {
+ $stale = 1;
+ }
+ } else {
+ $removed = 1;
+ }
+ } else {
+ $stale = 1;
+ }
+ if ($stale) {
+ 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;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return $removed;
+}
+
# -------------------------------- Allow a /uploaded/ URI to be vouched for
sub allowuploaded {
@@ -3061,14 +3437,18 @@ sub can_edit_resource {
$forceedit = 1;
}
$cfile = $resurl;
- } elsif ($resurl =~ m{^/adm/wrapper/adm/$cdom/$cnum/\d+/ext\.tool$}) {
- $incourse = 1;
- if ($env{'form.forceedit'}) {
- $forceview = 1;
- } else {
- $forceedit = 1;
+ } 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;
+ }
}
- $cfile = $resurl;
} elsif ($resurl =~ m{^/?adm/viewclasslist$}) {
$incourse = 1;
if ($env{'form.forceedit'}) {
@@ -3086,14 +3466,6 @@ 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;
- } elsif (($resurl =~ m{^/adm/wrapper/adm/$cdom/$cnum/\d+/ext\.tool$}) && ($env{'form.folderpath'} =~ /^supplemental/)) {
$incourse = 1;
if ($env{'form.forceedit'}) {
$forceview = 1;
@@ -3110,13 +3482,8 @@ sub can_edit_resource {
$cfile = &clutter($res);
} else {
$cfile = $env{'form.suppurl'};
- my $escfile = &unescape($cfile);
- if ($escfile =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) {
- $cfile = '/adm/wrapper'.$escfile;
- } else {
- $escfile =~ s{^http://}{};
- $cfile = &escape("/adm/wrapper/ext/$escfile");
- }
+ $cfile =~ s{^http://}{};
+ $cfile = '/adm/wrapper/ext/'.$cfile;
}
} elsif ($resurl =~ m{^/?adm/viewclasslist$}) {
if ($env{'form.forceedit'}) {
@@ -3355,6 +3722,9 @@ sub clean_filename {
}
# Replace spaces by underscores
$fname=~s/\s+/\_/g;
+# Transliterate non-ascii text to ascii
+ my $lang = &Apache::lonlocal::current_language();
+ $fname = &LONCAPA::transliterate::fname_to_ascii($fname,$lang);
# Replace all other weird characters by nothing
$fname=~s{[^/\w\.\-]}{}g;
# Replace all .\d. sequences with _\d. so they no longer look like version
@@ -3362,6 +3732,7 @@ sub clean_filename {
$fname=~s/\.(\d+)(?=\.)/_$1/g;
return $fname;
}
+
# This Function checks if an Image's dimensions exceed either $resizewidth (width)
# or $resizeheight (height) - both pixels. If so, the image is scaled to produce an
# image with the same aspect ratio as the original, but with dimensions which do
@@ -3404,7 +3775,7 @@ sub resizeImage {
# input: $formname - the contents of the file are in $env{"form.$formname"}
# the desired filename is in $env{"form.$formname.filename"}
# $context - possible values: coursedoc, existingfile, overwrite,
-# canceloverwrite, scantron or ''.
+# canceloverwrite, scantron or ''.
# if 'coursedoc': upload to the current course
# if 'existingfile': write file to tmp/overwrites directory
# if 'canceloverwrite': delete file written to tmp/overwrites directory
@@ -3412,8 +3783,8 @@ sub resizeImage {
# $subdir - directory in userfile to store the file into
# $parser - instruction to parse file for objects ($parser = parse) or
# if context is 'scantron', $parser is hashref of csv column mapping
-# (e.g.,{ PaperID => 0, LastName => 1, FirstName => 2, ID => 3,
-# Section => 4, CODE => 5, FirstQuestion => 9 }).
+# (e.g.,{ PaperID => 0, LastName => 1, FirstName => 2, ID => 3,
+# Section => 4, CODE => 5, FirstQuestion => 9 }).
# $allfiles - reference to hash for embedded objects
# $codebase - reference to hash for codebase of java objects
# $desuname - username for permanent storage of uploaded file
@@ -3436,6 +3807,14 @@ sub userfileupload {
$fname=&clean_filename($fname);
# See if there is anything left
unless ($fname) { return 'error: no uploaded file'; }
+ # If filename now begins with a . prepend unix timestamp _ milliseconds
+ if ($fname =~ /^\./) {
+ my ($s,$usec) = &gettimeofday();
+ while (length($usec) < 6) {
+ $usec = '0'.$usec;
+ }
+ $fname = $s.'_'.substr($usec,0,3).$fname;
+ }
# Files uploaded to help request form, or uploaded to "create course" page are handled differently
if ((($formname eq 'screenshot') && ($subdir eq 'helprequests')) ||
(($formname eq 'coursecreatorxml') && ($subdir eq 'batchupload')) ||
@@ -3454,7 +3833,7 @@ sub userfileupload {
} else {
$docudom = $env{'user.domain'};
}
- if ($destuname =~ /^$match_username$/) {
+ if ($destuname =~ /^$match_username$/) {
$docuname = $destuname;
} else {
$docuname = $env{'user.name'};
@@ -4321,7 +4700,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};
+ }
}
}
}
@@ -5077,10 +5460,9 @@ my %cachedtimes=();
my $cachedtime='';
sub load_all_first_access {
- my ($uname,$udom,$ignorecache)=@_;
+ my ($uname,$udom)=@_;
if (($cachedkey eq $uname.':'.$udom) &&
- (abs($cachedtime-time)<5) && (!$env{'form.markaccess'}) &&
- (!$ignorecache)) {
+ (abs($cachedtime-time)<5) && (!$env{'form.markaccess'})) {
return;
}
$cachedtime=time;
@@ -5089,7 +5471,7 @@ sub load_all_first_access {
}
sub get_first_access {
- my ($type,$argsymb,$argmap,$ignorecache)=@_;
+ my ($type,$argsymb,$argmap)=@_;
my ($symb,$courseid,$udom,$uname)=&whichuser();
if ($argsymb) { $symb=$argsymb; }
my ($map,$id,$res)=&decode_symb($symb);
@@ -5101,7 +5483,7 @@ sub get_first_access {
} else {
$res=$symb;
}
- &load_all_first_access($uname,$udom,$ignorecache);
+ &load_all_first_access($uname,$udom);
return $cachedtimes{"$courseid\0$res"};
}
@@ -5118,7 +5500,12 @@ sub set_first_access {
}
$cachedkey='';
my $firstaccess=&get_first_access($type,$symb,$map);
- if (!$firstaccess) {
+ if ($firstaccess) {
+ &logthis("First access time already set ($firstaccess) when attempting ".
+ "to set new value (type: $type, extent: $res) for $uname:$udom ".
+ "in $courseid");
+ return 'already_set';
+ } else {
my $start = time;
my $putres = &put('firstaccesstimes',{"$courseid\0$res"=>$start},
$udom,$uname);
@@ -5131,6 +5518,12 @@ sub set_first_access {
'course.'.$courseid.'.timerinterval.'.$res => $interval,
}
);
+ if (($cachedtime) && (abs($start-$cachedtime) < 5)) {
+ $cachedtimes{"$courseid\0$res"} = $start;
+ }
+ } elsif ($putres ne 'refused') {
+ &logthis("Result: $putres when attempting to set first access time ".
+ "(type: $type, extent: $res) for $uname:$udom in $courseid");
}
return $putres;
}
@@ -5142,13 +5535,14 @@ sub checkout {
my ($symb,$tuname,$tudom,$tcrsid)=@_;
my $now=time;
my $lonhost=$perlvar{'lonHostID'};
+ my $ip = &get_requestor_ip();
my $infostr=&escape(
'CHECKOUTTOKEN&'.
$tuname.'&'.
$tudom.'&'.
$tcrsid.'&'.
$symb.'&'.
- $now.'&'.$ENV{'REMOTE_ADDR'});
+ $now.'&'.$ip);
my $token=&reply('tmpput:'.$infostr,$lonhost);
if ($token=~/^error\:/) {
&logthis("WARNING: ".
@@ -5162,7 +5556,7 @@ sub checkout {
my %infohash=('resource.0.outtoken' => $token,
'resource.0.checkouttime' => $now,
- 'resource.0.outremote' => $ENV{'REMOTE_ADDR'});
+ 'resource.0.outremote' => $ip);
unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
return '';
@@ -5193,6 +5587,7 @@ sub checkin {
$lonhost=~tr/A-Z/a-z/;
my $dtoken=$ta.'_'.&hostname($lonhost).'_'.$tb;
$dtoken=~s/\W/\_/g;
+ my $ip = &get_requestor_ip();
my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=
split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));
@@ -5209,7 +5604,7 @@ sub checkin {
my %infohash=('resource.0.intoken' => $token,
'resource.0.checkintime' => $now,
- 'resource.0.inremote' => $ENV{'REMOTE_ADDR'});
+ 'resource.0.inremote' => $ip);
unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
return '';
@@ -6507,7 +6902,7 @@ sub currentdump {
#
my %returnhash=();
#
- if ($rep eq 'unknown_cmd') {
+ if ($rep eq "unknown_cmd") {
# an old lond will not know currentdump
# Do a dump and make it look like a currentdump
my @tmp = &dumpstore($courseid,$sdom,$sname,'.');
@@ -6642,7 +7037,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'});
@@ -7423,7 +7819,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)=@_;
my $ver_orguri=$uri;
$uri=&deversion($uri);
my $orguri=$uri;
@@ -7440,7 +7836,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)$}))
|| (($uri=~/\.meta$/) && ($uri!~m|^uploaded/|) ))
&& ($priv eq 'bre')) {
return 'F';
@@ -7605,8 +8001,34 @@ sub allowed {
if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseuri}
=~/\Q$priv\E\&([^\:]*)/) {
- unless (($priv eq 'bro') && (!$ownaccess)) {
- $thisallowed.=$1;
+ if ($priv eq 'mip') {
+ my $rem = $1;
+ if (($uri ne '') && ($env{'request.course.id'} eq $uri) &&
+ ($env{'course.'.$env{'request.course.id'}.'.internal.courseowner'} eq $env{'user.name'}.':'.$env{'user.domain'})) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ if ($cdom ne '') {
+ my %passwdconf = &get_passwdconf($cdom);
+ if (ref($passwdconf{'crsownerchg'}) eq 'HASH') {
+ if (ref($passwdconf{'crsownerchg'}{'by'}) eq 'ARRAY') {
+ if (@{$passwdconf{'crsownerchg'}{'by'}}) {
+ my @inststatuses = split(':',$env{'environment.inststatus'});
+ unless (@inststatuses) {
+ @inststatuses = ('default');
+ }
+ foreach my $status (@inststatuses) {
+ if (grep(/^\Q$status\E$/,@{$passwdconf{'crsownerchg'}{'by'}})) {
+ $thisallowed.=$rem;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ unless (($priv eq 'bro') && (!$ownaccess)) {
+ $thisallowed.=$1;
+ }
}
}
@@ -7622,7 +8044,7 @@ sub allowed {
if ($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 {
@@ -7642,7 +8064,7 @@ sub allowed {
if ($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 {
@@ -7689,6 +8111,16 @@ sub allowed {
if ($env{'request.course.id'}) {
+# If this is modifying password (internal auth) domains must match for user and user's role.
+
+ if ($priv eq 'mip') {
+ if ($env{'user.domain'} eq $env{'request.role.domain'}) {
+ return $thisallowed;
+ } else {
+ return '';
+ }
+ }
+
$courseprivid=$env{'request.course.id'};
if ($env{'request.course.sec'}) {
$courseprivid.='/'.$env{'request.course.sec'};
@@ -7705,7 +8137,7 @@ sub allowed {
if ($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 {
@@ -7718,7 +8150,7 @@ sub allowed {
$checkreferer=0;
}
}
-
+
if ($checkreferer) {
my $refuri=$env{'httpref.'.$orguri};
unless ($refuri) {
@@ -7747,7 +8179,7 @@ sub allowed {
if ($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 {
@@ -7833,7 +8265,7 @@ sub allowed {
}
}
}
-
+
#
# Rest of the restrictions depend on selected course
#
@@ -7991,22 +8423,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 {
@@ -8086,14 +8523,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;
+ }
+ }
}
}
}
@@ -8101,8 +8547,7 @@ sub get_commblock_resources {
}
}
}
- if ($interval[0] =~ /^(\d+)/) {
- my $timelimit = $1;
+ if ($interval[0] =~ /^\d+$/) {
my $first_access;
if ($type eq 'resource') {
$first_access=&get_first_access($interval[1],$item);
@@ -8112,7 +8557,7 @@ sub get_commblock_resources {
$first_access=&get_first_access($interval[1]);
}
if ($first_access) {
- my $timesup = $first_access+$timelimit;
+ my $timesup = $first_access+$interval[0];
if ($timesup > $now) {
my $activeblock;
foreach my $res (@to_test) {
@@ -8144,17 +8589,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);
@@ -8165,34 +8616,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;
}
}
@@ -9486,19 +9941,35 @@ sub store_coowners {
sub modifyuserauth {
my ($udom,$uname,$umode,$upass)=@_;
my $uhome=&homeserver($uname,$udom);
- unless (&allowed('mau',$udom)) { return 'refused'; }
+ my $allowed;
+ if (&allowed('mau',$udom)) {
+ $allowed = 1;
+ } elsif (($umode eq 'internal') && ($udom eq $env{'user.domain'}) &&
+ ($env{'request.course.id'}) && (&allowed('mip',$env{'request.course.id'})) &&
+ (!$env{'course.'.$env{'request.course.id'}.'.internal.nopasswdchg'})) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ if (($cdom ne '') && ($cnum ne '')) {
+ my $is_owner = &is_course_owner($cdom,$cnum);
+ if ($is_owner) {
+ $allowed = 1;
+ }
+ }
+ }
+ unless ($allowed) { return 'refused'; }
&logthis('Call to modify user authentication '.$udom.', '.$uname.', '.
$umode.' by '.$env{'user.name'}.' at '.$env{'user.domain'}.
' 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;
@@ -9979,13 +10450,25 @@ sub generate_coursenum {
sub is_course {
my ($cdom, $cnum) = scalar(@_) == 1 ?
($_[0] =~ /^($match_domain)_($match_courseid)$/) : @_;
-
- return unless $cdom and $cnum;
-
- my %courses = &courseiddump($cdom, '.', 1, '.', '.', $cnum, undef, undef,
- '.');
-
- return unless(exists($courses{$cdom.'_'.$cnum}));
+ return unless (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/));
+ my $uhome=&homeserver($cnum,$cdom);
+ my $iscourse;
+ if (grep { $_ eq $uhome } current_machine_ids()) {
+ $iscourse = &LONCAPA::Lond::is_course($cdom,$cnum);
+ } else {
+ my $hashid = $cdom.':'.$cnum;
+ ($iscourse,my $cached) = &is_cached_new('iscourse',$hashid);
+ unless (defined($cached)) {
+ my %courses = &courseiddump($cdom, '.', 1, '.', '.',
+ $cnum,undef,undef,'.');
+ $iscourse = 0;
+ if (exists($courses{$cdom.'_'.$cnum})) {
+ $iscourse = 1;
+ }
+ &do_cache_new('iscourse',$hashid,$iscourse,3600);
+ }
+ }
+ return unless($iscourse);
return wantarray ? ($cdom, $cnum) : $cdom.'_'.$cnum;
}
@@ -10167,7 +10650,7 @@ sub files_not_in_path {
my $filename = $user."savedfiles";
my @return_files;
my $path_part;
- open(IN,'<',LONCAPA::tempdir().$filename);
+ open(IN, '<',LONCAPA::tempdir().$filename);
while (my $line = ) {
#ok, I know it's clunky, but I want it to work
my @paths_and_file = split(m|/|, $line);
@@ -10827,7 +11310,7 @@ sub get_userresdata {
# Parameters:
# $name - Course/user name.
# $domain - Name of the domain the user/course is registered on.
-# $type - Type of thing $name is (must be 'course' or 'user')
+# $type - Type of thing $name is (must be 'course' or 'user'
# @which - Array of names of resources desired.
# Returns:
# The value of the first reasource in @which that is found in the
@@ -10846,44 +11329,13 @@ sub resdata {
}
if (!ref($result)) { return $result; }
foreach my $item (@which) {
- if (ref($item) eq 'ARRAY') {
- if (defined($result->{$item->[0]})) {
- return [$result->{$item->[0]},$item->[1]];
- }
- }
+ if (defined($result->{$item->[0]})) {
+ return [$result->{$item->[0]},$item->[1]];
+ }
}
return undef;
}
-sub get_domain_ltitools {
- my ($cdom) = @_;
- my %ltitools;
- my ($result,$cached)=&is_cached_new('ltitools',$cdom);
- if (defined($cached)) {
- if (ref($result) eq 'HASH') {
- %ltitools = %{$result};
- }
- } else {
- my %domconfig = &get_dom('configuration',['ltitools'],$cdom);
- if (ref($domconfig{'ltitools'}) eq 'HASH') {
- %ltitools = %{$domconfig{'ltitools'}};
- my %encdomconfig = &get_dom('encconfig',['ltitools'],$cdom);
- if (ref($encdomconfig{'ltitools'}) eq 'HASH') {
- foreach my $id (keys(%ltitools)) {
- if (ref($encdomconfig{'ltitools'}{$id}) eq 'HASH') {
- foreach my $item ('key','secret') {
- $ltitools{$id}{$item} = $encdomconfig{'ltitools'}{$id}{$item};
- }
- }
- }
- }
- }
- my $cachetime = 24*60*60;
- &do_cache_new('ltitools',$cdom,\%ltitools,$cachetime);
- }
- return %ltitools;
-}
-
sub get_numsuppfiles {
my ($cnum,$cdom,$ignorecache)=@_;
my $hashid=$cnum.':'.$cdom;
@@ -11339,7 +11791,7 @@ sub metadata {
# if it is a non metadata possible uri return quickly
if (($uri eq '') ||
(($uri =~ m|^/*adm/|) &&
- ($uri !~ m|^adm/includes|) && ($uri !~ m{/(smppg|bulletinboard|ext\.tool)$})) ||
+ ($uri !~ m|^adm/includes|) && ($uri !~ m{/(smppg|bulletinboard)$})) ||
($uri =~ m|/$|) || ($uri =~ m|/.meta$|) || ($uri =~ m{^/*uploaded/.+\.sequence$})) {
return undef;
}
@@ -11926,18 +12378,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)};
}
@@ -12037,13 +12487,16 @@ 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
@@ -12072,10 +12525,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) {
@@ -12108,13 +12569,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')) {
@@ -12133,12 +12599,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++;
}
@@ -12146,6 +12613,11 @@ sub symbread {
$syval = $poss_syval;
$realpossible++;
}
+ if ($syval) {
+ if (ref($possibles) eq 'HASH') {
+ $possibles->{$syval} = 1;
+ }
+ }
}
}
}
@@ -12683,9 +13155,10 @@ sub repcopy_userfile {
my $request;
$uri=~s/^\///;
my $homeserver = &homeserver($cnum,$cdom);
+ my $hostname = &hostname($homeserver);
my $protocol = $protocol{$homeserver};
$protocol = 'http' if ($protocol ne 'https');
- $request=new HTTP::Request('GET',$protocol.'://'.&hostname($homeserver).'/raw/'.$uri);
+ $request=new HTTP::Request('GET',$protocol.'://'.$hostname.'/raw/'.$uri);
my $response=$ua->request($request,$transferfile);
# did it work?
if ($response->is_error()) {
@@ -12709,9 +13182,10 @@ sub tokenwrapper {
$file=~s|(\?\.*)*$||;
&appenv({"userfile.$udom/$uname/$file" => $env{'request.course.id'}});
my $homeserver = &homeserver($uname,$udom);
+ my $hostname = &hostname($homeserver);
my $protocol = $protocol{$homeserver};
$protocol = 'http' if ($protocol ne 'https');
- return $protocol.'://'.&hostname($homeserver).'/'.$uri.
+ return $protocol.'://'.$hostname.'/'.$uri.
(($uri=~/\?/)?'&':'?').'token='.$token.
'&tokenissued='.$perlvar{'lonHostID'};
} else {
@@ -12727,9 +13201,10 @@ sub getuploaded {
my ($reqtype,$uri,$cdom,$cnum,$info,$rtncode) = @_;
$uri=~s/^\///;
my $homeserver = &homeserver($cnum,$cdom);
+ my $hostname = &hostname($homeserver);
my $protocol = $protocol{$homeserver};
$protocol = 'http' if ($protocol ne 'https');
- $uri = $protocol.'://'.&hostname($homeserver).'/raw/'.$uri;
+ $uri = $protocol.'://'.$hostname.'/raw/'.$uri;
my $ua=new LWP::UserAgent;
my $request=new HTTP::Request($reqtype,$uri);
my $response=$ua->request($request);
@@ -12883,6 +13358,80 @@ 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'};
+ my $hostname = &hostname($lonhost);
+ my $sts_on;
+ if ($protocol{$lonhost} eq 'https') {
+ my $cachetime = 12*3600;
+ if (!$ignore_cache) {
+ ($sts_on,my $cached)=&is_cached_new('stspolicy',$lonhost);
+ if (defined($cached)) {
+ return $sts_on;
+ }
+ }
+ my $ua=new LWP::UserAgent;
+ my $url = $protocol{$lonhost}.'://'.$hostname.'/index.html';
+ my $request=new HTTP::Request('HEAD',$url);
+ my $response=$ua->request($request);
+ if ($response->is_success) {
+ my $has_sts = $response->header('Strict-Transport-Security');
+ if ($has_sts eq '') {
+ $sts_on = 0;
+ } else {
+ if ($has_sts =~ /\Qmax-age=\E(\d+)/) {
+ my $maxage = $1;
+ if ($maxage) {
+ $sts_on = 1;
+ } else {
+ $sts_on = 0;
+ }
+ } else {
+ $sts_on = 0;
+ }
+ }
+ return &do_cache_new('stspolicy',$lonhost,$sts_on,$cachetime);
+ }
+ }
+ return;
+}
+
+sub get_requestor_ip {
+ my ($r,$nolookup,$noproxy) = @_;
+ my $from_ip;
+ if (ref($r)) {
+ $from_ip = $r->get_remote_host($nolookup);
+ } else {
+ $from_ip = $ENV{'REMOTE_ADDR'};
+ }
+ return $from_ip;
+}
+
# ------------------------------------------------------------- Declutters URLs
sub declutter {
@@ -12933,8 +13482,6 @@ sub clutter {
# &logthis("Got a blank emb style");
}
}
- } elsif ($thisfn =~ m{^/adm/$match_domain/$match_courseid/\d+/ext\.tool$}) {
- $thisfn='/adm/wrapper'.$thisfn;
}
return $thisfn;
}
@@ -13008,15 +13555,17 @@ sub get_dns {
}
my %alldns;
- open(my $config,"<","$perlvar{'lonTabDir'}/hosts.tab");
- foreach my $dns (<$config>) {
- next if ($dns !~ /^\^(\S*)/x);
- my $line = $1;
- my ($host,$protocol) = split(/:/,$line);
- if ($protocol ne 'https') {
- $protocol = 'http';
+ if (open(my $config,"<","$perlvar{'lonTabDir'}/hosts.tab")) {
+ foreach my $dns (<$config>) {
+ next if ($dns !~ /^\^(\S*)/x);
+ my $line = $1;
+ my ($host,$protocol) = split(/:/,$line);
+ if ($protocol ne 'https') {
+ $protocol = 'http';
+ }
+ $alldns{$host} = $protocol;
}
- $alldns{$host} = $protocol;
+ close($config);
}
while (%alldns) {
my ($dns) = sort { $b cmp $a } keys(%alldns);
@@ -13033,12 +13582,12 @@ sub get_dns {
&$func(\@content,$hashref);
return;
}
- close($config);
my $which = (split('/',$url))[3];
&logthis("unable to contact DNS defaulting to on disk file dns_$which.tab\n");
- open($config,"<","$perlvar{'lonTabDir'}/dns_$which.tab");
- my @content = <$config>;
- &$func(\@content,$hashref);
+ if (open(my $config,"<","$perlvar{'lonTabDir'}/dns_$which.tab")) {
+ my @content = <$config>;
+ &$func(\@content,$hashref);
+ }
return;
}
@@ -13650,6 +14199,16 @@ BEGIN {
}
+# ------------- set default texengine (domain default overrides this)
+{
+ $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,
});