--- loncom/lonnet/perl/lonnet.pm 2012/02/08 01:05:20 1.1056.4.33.2.2
+++ loncom/lonnet/perl/lonnet.pm 2011/11/29 11:54:33 1.1146
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.1056.4.33.2.2 2012/02/08 01:05:20 raeburn Exp $
+# $Id: lonnet.pm,v 1.1146 2011/11/29 11:54:33 foxr Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -96,6 +96,7 @@ use Math::Random;
use File::MMagic;
use LONCAPA qw(:DEFAULT :match);
use LONCAPA::Configuration;
+
use File::Copy;
my $readit;
@@ -288,7 +289,7 @@ sub get_server_homeID {
}
my $cachetime = 12*3600;
my $serverhomeID;
- if ($caller eq 'loncron') {
+ if ($caller eq 'loncron') {
my @machine_ids = &machine_ids($hostname);
foreach my $id (@machine_ids) {
my $response = &reply('serverhomeID',$id);
@@ -306,6 +307,52 @@ sub get_server_homeID {
return &do_cache_new('serverhomeID',$hostname,$serverhomeID,$cachetime);
}
+sub get_remote_globals {
+ my ($lonhost,$whathash,$ignore_cache) = @_;
+ my ($result,%returnhash,%whatneeded);
+ if (ref($whathash) eq 'HASH') {
+ foreach my $what (sort(keys(%{$whathash}))) {
+ my $hashid = $lonhost.'-'.$what;
+ my ($response,$cached);
+ unless ($ignore_cache) {
+ ($response,$cached)=&is_cached_new('lonnetglobal',$hashid);
+ }
+ if (defined($cached)) {
+ $returnhash{$what} = $response;
+ } else {
+ $whatneeded{$what} = 1;
+ }
+ }
+ if (keys(%whatneeded) == 0) {
+ $result = 'ok';
+ } else {
+ my $requested = &freeze_escape(\%whatneeded);
+ my $rep=&reply('readlonnetglobal:'.$requested,$lonhost);
+ if (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') ||
+ ($rep eq 'unknown_cmd')) {
+ $result = $rep;
+ } else {
+ $result = 'ok';
+ my @pairs=split(/\&/,$rep);
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/=/,$item,2);
+ my $what = &unescape($key);
+ my $hashid = $lonhost.'-'.$what;
+ $returnhash{$what}=&thaw_unescape($value);
+ &do_cache_new('lonnetglobal',$hashid,$returnhash{$what},600);
+ }
+ }
+ }
+ }
+ return ($result,\%returnhash);
+}
+
+sub remote_devalidate_cache {
+ my ($lonhost,$name,$id) = @_;
+ my $response = &reply('devalidatecache:'.&escape($name).':'.&escape($id),$lonhost);
+ return $response;
+}
+
# -------------------------------------------------- Non-critical communication
sub subreply {
my ($cmd,$server)=@_;
@@ -758,30 +805,6 @@ sub userload {
return $userloadpercent;
}
-# ------------------------------------------ Fight off request when overloaded
-
-sub overloaderror {
- my ($r,$checkserver)=@_;
- unless ($checkserver) { $checkserver=$perlvar{'lonHostID'}; }
- my $loadavg;
- if ($checkserver eq $perlvar{'lonHostID'}) {
- open(my $loadfile,'/proc/loadavg');
- $loadavg=<$loadfile>;
- $loadavg =~ s/\s.*//g;
- $loadavg = 100*$loadavg/$perlvar{'lonLoadLim'};
- close($loadfile);
- } else {
- $loadavg=&reply('load',$checkserver);
- }
- my $overload=$loadavg-100;
- if ($overload>0) {
- $r->err_headers_out->{'Retry-After'}=$overload;
- $r->log_error('Overload of '.$overload.' on '.$checkserver);
- return 413;
- }
- return '';
-}
-
# ------------------------------ Find server with least workload from spare.tab
sub spareserver {
@@ -797,26 +820,33 @@ sub spareserver {
my %udomdefaults = &Apache::lonnet::get_domain_defaults($udom);
$remotesessions = $udomdefaults{'remotesessions'};
}
- foreach my $try_server (@{ $spareid{'primary'} }) {
- if ($uint_dom) {
- next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
- $try_server));
+ my $spareshash = &this_host_spares($udom);
+ if (ref($spareshash) eq 'HASH') {
+ if (ref($spareshash->{'primary'}) eq 'ARRAY') {
+ foreach my $try_server (@{ $spareshash->{'primary'} }) {
+ if ($uint_dom) {
+ next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
+ $try_server));
+ }
+ ($spare_server, $lowest_load) =
+ &compare_server_load($try_server, $spare_server, $lowest_load);
+ }
}
- ($spare_server, $lowest_load) =
- &compare_server_load($try_server, $spare_server, $lowest_load);
- }
- my $found_server = ($spare_server ne '' && $lowest_load < 100);
-
- if (!$found_server) {
- foreach my $try_server (@{ $spareid{'default'} }) {
- if ($uint_dom) {
- next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
- $try_server));
- }
- ($spare_server, $lowest_load) =
- &compare_server_load($try_server, $spare_server, $lowest_load);
- }
+ my $found_server = ($spare_server ne '' && $lowest_load < 100);
+
+ if (!$found_server) {
+ if (ref($spareshash->{'default'}) eq 'ARRAY') {
+ foreach my $try_server (@{ $spareshash->{'default'} }) {
+ if ($uint_dom) {
+ next unless (&spare_can_host($udom,$uint_dom,
+ $remotesessions,$try_server));
+ }
+ ($spare_server, $lowest_load) =
+ &compare_server_load($try_server, $spare_server, $lowest_load);
+ }
+ }
+ }
}
if (!$want_server_name) {
@@ -841,7 +871,7 @@ sub compare_server_load {
my $userloadans = &reply('userload',$try_server);
if ($loadans !~ /\d/ && $userloadans !~ /\d/) {
- return ($spare_server, $lowest_load); #didn't get a number from the server
+ return ($spare_server, $lowest_load); #didn't get a number from the server
}
my $load;
@@ -867,9 +897,18 @@ sub compare_server_load {
# --------------------------- ask offload servers if user already has a session
sub find_existing_session {
my ($udom,$uname) = @_;
- foreach my $try_server (@{ $spareid{'primary'} },
- @{ $spareid{'default'} }) {
- return $try_server if (&has_user_session($try_server, $udom, $uname));
+ my $spareshash = &this_host_spares($udom);
+ if (ref($spareshash) eq 'HASH') {
+ if (ref($spareshash->{'primary'}) eq 'ARRAY') {
+ foreach my $try_server (@{ $spareshash->{'primary'} }) {
+ return $try_server if (&has_user_session($try_server, $udom, $uname));
+ }
+ }
+ if (ref($spareshash->{'default'}) eq 'ARRAY') {
+ foreach my $try_server (@{ $spareshash->{'default'} }) {
+ return $try_server if (&has_user_session($try_server, $udom, $uname));
+ }
+ }
}
return;
}
@@ -891,7 +930,7 @@ sub choose_server {
my %domconfhash = &Apache::loncommon::get_domainconf($udom);
my %servers = &get_servers($udom);
my $lowest_load = 30000;
- my ($login_host,$hostname,$portal_path,$isredirect);
+ my ($login_host,$hostname,$portal_path);
foreach my $lonhost (keys(%servers)) {
my $loginvia;
if ($checkloginvia) {
@@ -899,17 +938,15 @@ sub choose_server {
if ($loginvia) {
my ($server,$path) = split(/:/,$loginvia);
($login_host, $lowest_load) =
- &compare_server_load($lonhost, $login_host, $lowest_load);
+ &compare_server_load($server, $login_host, $lowest_load);
if ($login_host eq $server) {
$portal_path = $path;
- $isredirect = 1;
}
} else {
($login_host, $lowest_load) =
&compare_server_load($lonhost, $login_host, $lowest_load);
if ($login_host eq $lonhost) {
$portal_path = '';
- $isredirect = '';
}
}
} else {
@@ -920,7 +957,7 @@ sub choose_server {
if ($login_host ne '') {
$hostname = &hostname($login_host);
}
- return ($login_host,$hostname,$portal_path,$isredirect);
+ return ($login_host,$hostname,$portal_path);
}
# --------------------------------------------- Try to change a user's password
@@ -1104,6 +1141,302 @@ sub spare_can_host {
return $canhost;
}
+sub this_host_spares {
+ my ($dom) = @_;
+ my ($dom_in_use,$lonhost_in_use,$result);
+ my @hosts = ¤t_machine_ids();
+ foreach my $lonhost (@hosts) {
+ if (&host_domain($lonhost) eq $dom) {
+ $dom_in_use = $dom;
+ $lonhost_in_use = $lonhost;
+ last;
+ }
+ }
+ if ($dom_in_use ne '') {
+ $result = &spares_for_offload($dom_in_use,$lonhost_in_use);
+ }
+ if (ref($result) ne 'HASH') {
+ $lonhost_in_use = $perlvar{'lonHostID'};
+ $dom_in_use = &host_domain($lonhost_in_use);
+ $result = &spares_for_offload($dom_in_use,$lonhost_in_use);
+ if (ref($result) ne 'HASH') {
+ $result = \%spareid;
+ }
+ }
+ return $result;
+}
+
+sub spares_for_offload {
+ my ($dom_in_use,$lonhost_in_use) = @_;
+ my ($result,$cached)=&is_cached_new('spares',$dom_in_use);
+ if (defined($cached)) {
+ return $result;
+ } else {
+ my $cachetime = 60*60*24;
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['usersessions'],$dom_in_use);
+ if (ref($domconfig{'usersessions'}) eq 'HASH') {
+ if (ref($domconfig{'usersessions'}{'spares'}) eq 'HASH') {
+ if (ref($domconfig{'usersessions'}{'spares'}{$lonhost_in_use}) eq 'HASH') {
+ return &do_cache_new('spares',$dom_in_use,$domconfig{'usersessions'}{'spares'}{$lonhost_in_use},$cachetime);
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub get_lonbalancer_config {
+ my ($servers) = @_;
+ my ($currbalancer,$currtargets);
+ if (ref($servers) eq 'HASH') {
+ foreach my $server (keys(%{$servers})) {
+ my %what = (
+ spareid => 1,
+ perlvar => 1,
+ );
+ my ($result,$returnhash) = &get_remote_globals($server,\%what);
+ if ($result eq 'ok') {
+ if (ref($returnhash) eq 'HASH') {
+ if (ref($returnhash->{'perlvar'}) eq 'HASH') {
+ if ($returnhash->{'perlvar'}->{'lonBalancer'} eq 'yes') {
+ $currbalancer = $server;
+ $currtargets = {};
+ if (ref($returnhash->{'spareid'}) eq 'HASH') {
+ if (ref($returnhash->{'spareid'}->{'primary'}) eq 'ARRAY') {
+ $currtargets->{'primary'} = $returnhash->{'spareid'}->{'primary'};
+ }
+ if (ref($returnhash->{'spareid'}->{'default'}) eq 'ARRAY') {
+ $currtargets->{'default'} = $returnhash->{'spareid'}->{'default'};
+ }
+ }
+ last;
+ }
+ }
+ }
+ }
+ }
+ }
+ return ($currbalancer,$currtargets);
+}
+
+sub check_loadbalancing {
+ my ($uname,$udom) = @_;
+ my ($is_balancer,$dom_in_use,$homeintdom,$rule_in_effect,
+ $offloadto,$otherserver);
+ my $lonhost = $perlvar{'lonHostID'};
+ my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
+ my $uintdom = &Apache::lonnet::internet_dom($uprimary_id);
+ my $intdom = &Apache::lonnet::internet_dom($lonhost);
+ my $serverhomedom = &host_domain($lonhost);
+
+ my $cachetime = 60*60*24;
+
+ if (($uintdom ne '') && ($uintdom eq $intdom)) {
+ $dom_in_use = $udom;
+ $homeintdom = 1;
+ } else {
+ $dom_in_use = $serverhomedom;
+ }
+ my ($result,$cached)=&is_cached_new('loadbalancing',$dom_in_use);
+ unless (defined($cached)) {
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['loadbalancing'],$dom_in_use);
+ if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
+ $result = &do_cache_new('loadbalancing',$dom_in_use,$domconfig{'loadbalancing'},$cachetime);
+ }
+ }
+ if (ref($result) eq 'HASH') {
+ my $currbalancer = $result->{'lonhost'};
+ my $currtargets = $result->{'targets'};
+ my $currrules = $result->{'rules'};
+ if ($currbalancer ne '') {
+ my @hosts = ¤t_machine_ids();
+ if (grep(/^\Q$currbalancer\E$/,@hosts)) {
+ $is_balancer = 1;
+ }
+ }
+ if ($is_balancer) {
+ if (ref($currrules) eq 'HASH') {
+ if ($homeintdom) {
+ if ($uname ne '') {
+ if (($currrules->{'_LC_adv'} ne '') || ($currrules->{'_LC_author'} ne '')) {
+ my ($is_adv,$is_author) = &is_advanced_user($udom,$uname);
+ if (($currrules->{'_LC_author'} ne '') && ($is_author)) {
+ $rule_in_effect = $currrules->{'_LC_author'};
+ } elsif (($currrules->{'_LC_adv'} ne '') && ($is_adv)) {
+ $rule_in_effect = $currrules->{'_LC_adv'}
+ }
+ }
+ if ($rule_in_effect eq '') {
+ my %userenv = &userenvironment($udom,$uname,'inststatus');
+ if ($userenv{'inststatus'} ne '') {
+ my @statuses = map { &unescape($_); } split(/:/,$userenv{'inststatus'});
+ my ($othertitle,$usertypes,$types) =
+ &Apache::loncommon::sorted_inst_types($udom);
+ if (ref($types) eq 'ARRAY') {
+ foreach my $type (@{$types}) {
+ if (grep(/^\Q$type\E$/,@statuses)) {
+ if (exists($currrules->{$type})) {
+ $rule_in_effect = $currrules->{$type};
+ }
+ }
+ }
+ }
+ } else {
+ if (exists($currrules->{'default'})) {
+ $rule_in_effect = $currrules->{'default'};
+ }
+ }
+ }
+ } else {
+ if (exists($currrules->{'default'})) {
+ $rule_in_effect = $currrules->{'default'};
+ }
+ }
+ } else {
+ if ($currrules->{'_LC_external'} ne '') {
+ $rule_in_effect = $currrules->{'_LC_external'};
+ }
+ }
+ $offloadto = &get_loadbalancer_targets($rule_in_effect,$currtargets,
+ $uname,$udom);
+ }
+ }
+ } elsif (($homeintdom) && ($udom ne $serverhomedom)) {
+ my ($result,$cached)=&is_cached_new('loadbalancing',$serverhomedom);
+ unless (defined($cached)) {
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['loadbalancing'],$serverhomedom);
+ if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
+ $result = &do_cache_new('loadbalancing',$dom_in_use,$domconfig{'loadbalancing'},$cachetime);
+ }
+ }
+ if (ref($result) eq 'HASH') {
+ my $currbalancer = $result->{'lonhost'};
+ my $currtargets = $result->{'targets'};
+ my $currrules = $result->{'rules'};
+
+ if ($currbalancer eq $lonhost) {
+ $is_balancer = 1;
+ if (ref($currrules) eq 'HASH') {
+ if ($currrules->{'_LC_internetdom'} ne '') {
+ $rule_in_effect = $currrules->{'_LC_internetdom'};
+ }
+ }
+ $offloadto = &get_loadbalancer_targets($rule_in_effect,$currtargets,
+ $uname,$udom);
+ }
+ } else {
+ if ($perlvar{'lonBalancer'} eq 'yes') {
+ $is_balancer = 1;
+ $offloadto = &this_host_spares($dom_in_use);
+ }
+ }
+ } else {
+ if ($perlvar{'lonBalancer'} eq 'yes') {
+ $is_balancer = 1;
+ $offloadto = &this_host_spares($dom_in_use);
+ }
+ }
+ my $lowest_load = 30000;
+ if (ref($offloadto) eq 'HASH') {
+ if (ref($offloadto->{'primary'}) eq 'ARRAY') {
+ foreach my $try_server (@{$offloadto->{'primary'}}) {
+ ($otherserver,$lowest_load) =
+ &compare_server_load($try_server,$otherserver,$lowest_load);
+ }
+ }
+ my $found_server = ($otherserver ne '' && $lowest_load < 100);
+
+ if (!$found_server) {
+ if (ref($offloadto->{'default'}) eq 'ARRAY') {
+ foreach my $try_server (@{$offloadto->{'default'}}) {
+ ($otherserver,$lowest_load) =
+ &compare_server_load($try_server,$otherserver,$lowest_load);
+ }
+ }
+ }
+ } elsif (ref($offloadto) eq 'ARRAY') {
+ if (@{$offloadto} == 1) {
+ $otherserver = $offloadto->[0];
+ } elsif (@{$offloadto} > 1) {
+ foreach my $try_server (@{$offloadto}) {
+ ($otherserver,$lowest_load) =
+ &compare_server_load($try_server,$otherserver,$lowest_load);
+ }
+ }
+ }
+ return ($is_balancer,$otherserver);
+}
+
+sub get_loadbalancer_targets {
+ my ($rule_in_effect,$currtargets,$uname,$udom) = @_;
+ my $offloadto;
+ if ($rule_in_effect eq '') {
+ $offloadto = $currtargets;
+ } else {
+ if ($rule_in_effect eq 'homeserver') {
+ my $homeserver = &homeserver($uname,$udom);
+ if ($homeserver ne 'no_host') {
+ $offloadto = [$homeserver];
+ }
+ } elsif ($rule_in_effect eq 'externalbalancer') {
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['loadbalancing'],$udom);
+ if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
+ if ($domconfig{'loadbalancing'}{'lonhost'} ne '') {
+ if (&hostname($domconfig{'loadbalancing'}{'lonhost'}) ne '') {
+ $offloadto = [$domconfig{'loadbalancing'}{'lonhost'}];
+ }
+ }
+ } else {
+ my %servers = &dom_servers($udom);
+ my ($remotebalancer,$remotetargets) = &get_lonbalancer_config(\%servers);
+ if (&hostname($remotebalancer) ne '') {
+ $offloadto = [$remotebalancer];
+ }
+ }
+ } elsif (&hostname($rule_in_effect) ne '') {
+ $offloadto = [$rule_in_effect];
+ }
+ }
+ return $offloadto;
+}
+
+sub internet_dom_servers {
+ my ($dom) = @_;
+ my (%uniqservers,%servers);
+ my $primaryserver = &hostname(&domain($dom,'primary'));
+ my @machinedoms = &machine_domains($primaryserver);
+ foreach my $mdom (@machinedoms) {
+ my %currservers = %servers;
+ my %server = &get_servers($mdom);
+ %servers = (%currservers,%server);
+ }
+ my %by_hostname;
+ foreach my $id (keys(%servers)) {
+ push(@{$by_hostname{$servers{$id}}},$id);
+ }
+ foreach my $hostname (sort(keys(%by_hostname))) {
+ if (@{$by_hostname{$hostname}} > 1) {
+ my $match = 0;
+ foreach my $id (@{$by_hostname{$hostname}}) {
+ if (&host_domain($id) eq $dom) {
+ $uniqservers{$id} = $hostname;
+ $match = 1;
+ }
+ }
+ unless ($match) {
+ $uniqservers{$by_hostname{$hostname}[0]} = $hostname;
+ }
+ } else {
+ $uniqservers{$by_hostname{$hostname}[0]} = $hostname;
+ }
+ }
+ return %uniqservers;
+}
+
# ---------------------- Find the homebase for a user from domain's lib servers
my %homecache;
@@ -1587,7 +1920,6 @@ sub get_domain_defaults {
$domdefaults{'auth_arg_def'} = $domconfig{'defaults'}{'auth_arg_def'};
$domdefaults{'timezone_def'} = $domconfig{'defaults'}{'timezone_def'};
$domdefaults{'datelocale_def'} = $domconfig{'defaults'}{'datelocale_def'};
- $domdefaults{'portal_def'} = $domconfig{'defaults'}{'portal_def'};
} else {
$domdefaults{'lang_def'} = &domain($domain,'lang_def');
$domdefaults{'auth_def'} = &domain($domain,'auth_def');
@@ -1881,7 +2213,7 @@ sub is_cached_new {
my ($name,$id,$debug) = @_;
$id=&make_key($name,$id);
if (exists($remembered{$id})) {
- if ($debug) { &Apache::lonnet::logthis("Earyl return $id of $remembered{$id} "); }
+ if ($debug) { &Apache::lonnet::logthis("Early return $id of $remembered{$id} "); }
$accessed{$id}=[&gettimeofday()];
$hits++;
return ($remembered{$id},1);
@@ -2047,20 +2379,29 @@ sub getversion {
sub currentversion {
my $fname=shift;
- my ($result,$cached)=&is_cached_new('resversion',$fname);
- if (defined($cached)) { return $result; }
my $author=$fname;
$author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
my ($udom,$uname)=split(/\//,$author);
- my $home=homeserver($uname,$udom);
+ my $home=&homeserver($uname,$udom);
if ($home eq 'no_host') {
return -1;
}
- my $answer=reply("currentversion:$fname",$home);
+ my $answer=&reply("currentversion:$fname",$home);
if (($answer eq 'con_lost') || ($answer eq 'rejected')) {
return -1;
}
- return &do_cache_new('resversion',$fname,$answer,600);
+ return $answer;
+}
+
+#
+# Return special version number of resource if set by override, empty otherwise
+#
+sub usedversion {
+ my $fname=shift;
+ unless ($fname) { $fname=$env{'request.uri'}; }
+ my ($urlversion)=($fname=~/\.(\d+)\.\w+$/);
+ if ($urlversion) { return $urlversion; }
+ return '';
}
# ----------------------------- Subscribe to a resource, return URL if possible
@@ -2088,10 +2429,11 @@ sub subscribe {
sub repcopy {
my $filename=shift;
$filename=~s/\/+/\//g;
- if ($filename=~m|^/home/httpd/html/adm/|) { return 'ok'; }
- if ($filename=~m|^/home/httpd/html/lonUsers/|) { return 'ok'; }
- if ($filename=~m|^/home/httpd/html/userfiles/| or
- $filename=~m -^/*(uploaded|editupload)/-) {
+ my $londocroot = $perlvar{'lonDocRoot'};
+ if ($filename=~m{^\Q$londocroot/adm/\E}) { return 'ok'; }
+ if ($filename=~m{^\Q$londocroot/lonUsers/\E}) { return 'ok'; }
+ if ($filename=~m{^\Q$londocroot/userfiles/\E} or
+ $filename=~m{^/*(uploaded|editupload)/}) {
return &repcopy_userfile($filename);
}
$filename=~s/[\n\r]//g;
@@ -2118,7 +2460,7 @@ sub repcopy {
unless ($home eq $perlvar{'lonHostID'}) {
my @parts=split(/\//,$filename);
my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
- if ($path ne "$perlvar{'lonDocRoot'}/res") {
+ if ($path ne "$londocroot/res") {
&logthis("Malconfiguration for replication: $filename");
return 'bad_request';
}
@@ -2322,7 +2664,7 @@ sub process_coursefile {
}
if (ref($mimetype)) {
$$mimetype = $type;
- }
+ }
}
$fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file,
$home);
@@ -2440,11 +2782,11 @@ 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, or ''.
+# canceloverwrite, or ''.
# if 'coursedoc': upload to the current course
-# if 'existingfile': write file to tmp/overwrites directory
+# if 'existingfile': write file to tmp/overwrites directory
# if 'canceloverwrite': delete file written to tmp/overwrites directory
-# $context is passed as argument to &finishuserfileupload
+# $context is passed as argument to &finishuserfileupload
# $subdir - directory in userfile to store the file into
# $parser - instruction to parse file for objects ($parser = parse)
# $allfiles - reference to hash for embedded objects
@@ -2596,11 +2938,11 @@ sub finishuserfileupload {
return '/adm/notfound.html';
}
if ($context eq 'overwrite') {
- my $source = $perlvar{'lonDaemons'}.'/tmp/overwrites/'.$docudom.'/'.$docuname.'/'.$fname;
+ my $source = LONCAPA::tempdir().'/overwrites/'.$docudom.'/'.$docuname.'/'.$fname;
my $target = $filepath.'/'.$file;
if (-e $source) {
my @info = stat($source);
- if ($info[9] eq $env{'form.timestamp'}) {
+ if ($info[9] eq $env{'form.timestamp'}) {
unless (&File::Copy::move($source,$target)) {
&logthis('Failed to overwrite '.$filepath.'/'.$file);
return "Moving from $source failed";
@@ -2611,7 +2953,7 @@ sub finishuserfileupload {
} else {
return "Temporary file: $source missing";
}
- } elsif (!print FH ($env{'form.'.$formname})) {
+ } elsif (!print FH ($env{'form.'.$formname})) {
&logthis('Failed to write to '.$filepath.'/'.$file);
print STDERR ('Failed to write to '.$filepath.'/'.$file."\n");
return '/adm/notfound.html';
@@ -2996,7 +3338,7 @@ sub courseacclog {
my $fnsymb=shift;
unless ($env{'request.course.id'}) { return ''; }
my $what=$fnsymb.':'.$env{'user.name'}.':'.$env{'user.domain'};
- if ($fnsymb=~/(problem|exam|quiz|assess|survey|form|task|page)$/) {
+ if ($fnsymb=~/$LONCAPA::assess_re/) {
$what.=':POST';
# FIXME: Probably ought to escape things....
foreach my $key (keys(%env)) {
@@ -3172,7 +3514,7 @@ sub get_my_roles {
unless (defined($uname)) { $uname=$env{'user.name'}; }
unless (defined($udom)) { $udom=$env{'user.domain'}; }
my (%dumphash,%nothide);
- if ($context eq 'userroles') {
+ if ($context eq 'userroles') {
my $extra = &freeze_escape({'skipcheck' => 1});
%dumphash = &dump('roles',$udom,$uname,'.',undef,$extra);
} else {
@@ -3506,7 +3848,7 @@ sub get_domain_roles {
return %personnel;
}
-# ----------------------------------------------------------- Check out an item
+# ----------------------------------------------------------- Interval timing
sub get_first_access {
my ($type,$argsymb)=@_;
@@ -3542,91 +3884,6 @@ sub set_first_access {
return 'already_set';
}
-sub checkout {
- my ($symb,$tuname,$tudom,$tcrsid)=@_;
- my $now=time;
- my $lonhost=$perlvar{'lonHostID'};
- my $infostr=&escape(
- 'CHECKOUTTOKEN&'.
- $tuname.'&'.
- $tudom.'&'.
- $tcrsid.'&'.
- $symb.'&'.
- $now.'&'.$ENV{'REMOTE_ADDR'});
- my $token=&reply('tmpput:'.$infostr,$lonhost);
- if ($token=~/^error\:/) {
- &logthis("WARNING: ".
- "Checkout tmpput failed ".$tudom.' - '.$tuname.' - '.$symb.
- "");
- return '';
- }
-
- $token=~s/^(\d+)\_.*\_(\d+)$/$1\*$2\*$lonhost/;
- $token=~tr/a-z/A-Z/;
-
- my %infohash=('resource.0.outtoken' => $token,
- 'resource.0.checkouttime' => $now,
- 'resource.0.outremote' => $ENV{'REMOTE_ADDR'});
-
- unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
- return '';
- } else {
- &logthis("WARNING: ".
- "Checkout cstore failed ".$tudom.' - '.$tuname.' - '.$symb.
- "");
- }
-
- if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
- &escape('Checkout '.$infostr.' - '.
- $token)) ne 'ok') {
- return '';
- } else {
- &logthis("WARNING: ".
- "Checkout log failed ".$tudom.' - '.$tuname.' - '.$symb.
- "");
- }
- return $token;
-}
-
-# ------------------------------------------------------------ Check in an item
-
-sub checkin {
- my $token=shift;
- my $now=time;
- my ($ta,$tb,$lonhost)=split(/\*/,$token);
- $lonhost=~tr/A-Z/a-z/;
- my $dtoken=$ta.'_'.&hostname($lonhost).'_'.$tb;
- $dtoken=~s/\W/\_/g;
- my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=
- split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));
-
- unless (($tuname) && ($tudom)) {
- &logthis('Check in '.$token.' ('.$dtoken.') failed');
- return '';
- }
-
- unless (&allowed('mgr',$tcrsid)) {
- &logthis('Check in '.$token.' ('.$dtoken.') unauthorized: '.
- $env{'user.name'}.' - '.$env{'user.domain'});
- return '';
- }
-
- my %infohash=('resource.0.intoken' => $token,
- 'resource.0.checkintime' => $now,
- 'resource.0.inremote' => $ENV{'REMOTE_ADDR'});
-
- unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
- return '';
- }
-
- if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
- &escape('Checkin - '.$token)) ne 'ok') {
- return '';
- }
-
- return ($symb,$tuname,$tudom,$tcrsid);
-}
-
# --------------------------------------------- Set Expire Date for Spreadsheet
sub expirespread {
@@ -3731,7 +3988,7 @@ sub hashref2str {
$result.='=';
#print("Got a ref of ".(ref($key))." skipping.");
} else {
- if (defined($key)) {$result.=&escape($key).'=';} else { last; }
+ if (defined($key)) {$result.=&escape($key).'=';} else { last; }
}
if(ref($hashref->{$key}) eq 'ARRAY') {
@@ -3883,7 +4140,7 @@ sub tmpreset {
if ($domain eq 'public' && $stuname eq 'public') {
$stuname=$ENV{'REMOTE_ADDR'};
}
- my $path=$perlvar{'lonDaemons'}.'/tmp';
+ my $path=LONCAPA::tempdir();
my %hash;
if (tie(%hash,'GDBM_File',
$path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
@@ -3922,7 +4179,7 @@ sub tmpstore {
}
my $now=time;
my %hash;
- my $path=$perlvar{'lonDaemons'}.'/tmp';
+ my $path=LONCAPA::tempdir();
if (tie(%hash,'GDBM_File',
$path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
&GDBM_WRCREAT(),0640)) {
@@ -3968,7 +4225,7 @@ sub tmprestore {
$namespace=~s/\//\_/g;
$namespace=~s/\W//g;
my %hash;
- my $path=$perlvar{'lonDaemons'}.'/tmp';
+ my $path=LONCAPA::tempdir();
if (tie(%hash,'GDBM_File',
$path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
&GDBM_READER(),0640)) {
@@ -4105,6 +4362,8 @@ sub restore {
}
# ---------------------------------------------------------- Course Description
+#
+#
sub coursedescription {
my ($courseid,$args)=@_;
@@ -4134,7 +4393,8 @@ sub coursedescription {
return %returnhash;
}
- # get the data agin
+ # get the data again
+
if (!$args->{'one_time'}) {
$envhash{'course.'.$normalid.'.last_cache'}=time;
}
@@ -4142,6 +4402,10 @@ sub coursedescription {
if ($chome ne 'no_host') {
%returnhash=&dump('environment',$cdomain,$cnum);
if (!exists($returnhash{'con_lost'})) {
+ my $username = $env{'user.name'}; # Defult username
+ if(defined $args->{'user'}) {
+ $username = $args->{'user'};
+ }
$returnhash{'home'}= $chome;
$returnhash{'domain'} = $cdomain;
$returnhash{'num'} = $cnum;
@@ -4152,8 +4416,8 @@ sub coursedescription {
$envhash{'course.'.$normalid.'.'.$name}=$value;
}
$returnhash{'url'}=&clutter($returnhash{'url'});
- $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.
- $env{'user.name'}.'_'.$cdomain.'_'.$cnum;
+ $returnhash{'fn'}=LONCAPA::tempdir() .
+ $username.'_'.$cdomain.'_'.$cnum;
$envhash{'course.'.$normalid.'.home'}=$chome;
$envhash{'course.'.$normalid.'.domain'}=$cdomain;
$envhash{'course.'.$normalid.'.num'}=$cnum;
@@ -4371,7 +4635,7 @@ sub set_userprivs {
my $adv=0;
my %grouproles = ();
if (keys(%{$allgroups}) > 0) {
- my @groupkeys;
+ my @groupkeys;
foreach my $role (keys(%{$allroles})) {
push(@groupkeys,$role);
}
@@ -4447,7 +4711,7 @@ sub role_status {
my %userroles = (
'user.role.'.$$role.'.'.$$where => $$tstart.'.'.$$tend
);
- @rolecodes = ('cm');
+ @rolecodes = ('cm');
my $spec=$$role.'.'.$$where;
my ($tdummy,$tdomain,$trest)=split(/\//,$$where);
if ($$role =~ /^cr\//) {
@@ -4490,9 +4754,9 @@ sub role_status {
sub get_groups_roles {
my ($cdom,$rest,$cdom_courseroles,$rolecodes,$groups_roles) = @_;
- return unless((ref($cdom_courseroles) eq 'HASH') &&
- (ref($rolecodes) eq 'ARRAY') &&
- (ref($groups_roles) eq 'HASH'));
+ return unless((ref($cdom_courseroles) eq 'HASH') &&
+ (ref($rolecodes) eq 'ARRAY') &&
+ (ref($groups_roles) eq 'HASH'));
if (keys(%{$cdom_courseroles}) > 0) {
my ($cnum) = ($rest =~ /^($match_courseid)/);
if ($cdom ne '' && $cnum ne '') {
@@ -4642,15 +4906,18 @@ sub dump {
my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range:$extra",$uhome);
my @pairs=split(/\&/,$rep);
my %returnhash=();
- foreach my $item (@pairs) {
- my ($key,$value)=split(/=/,$item,2);
- $key = &unescape($key);
- next if ($key =~ /^error: 2 /);
- $returnhash{$key}=&thaw_unescape($value);
+ if (!($rep =~ /^error/ )) {
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/=/,$item,2);
+ $key = &unescape($key);
+ next if ($key =~ /^error: 2 /);
+ $returnhash{$key}=&thaw_unescape($value);
+ }
}
return %returnhash;
}
+
# --------------------------------------------------------- dumpstore interface
sub dumpstore {
@@ -4933,7 +5200,7 @@ sub tmpget {
return %returnhash;
}
-# ------------------------------------------------------------ tmpget interface
+# ------------------------------------------------------------ tmpdel interface
sub tmpdel {
my ($token,$server)=@_;
if (!defined($server)) { $server = $perlvar{'lonHostID'}; }
@@ -5184,7 +5451,7 @@ sub is_portfolio_file {
}
sub usertools_access {
- my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref) = @_;
+ my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref)=@_;
my ($access,%tools);
if ($context eq '') {
$context = 'tools';
@@ -5331,12 +5598,16 @@ sub is_advanced_user {
my ($udom,$uname) = @_;
if ($udom ne '' && $uname ne '') {
if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
- return $env{'user.adv'};
+ if (wantarray) {
+ return ($env{'user.adv'},$env{'user.author'});
+ } else {
+ return $env{'user.adv'};
+ }
}
}
my %roleshash = &get_my_roles($uname,$udom,'userroles',undef,undef,undef,1);
my %allroles;
- my $is_adv;
+ my ($is_adv,$is_author);
foreach my $role (keys(%roleshash)) {
my ($trest,$tdomain,$trole,$sec) = split(/:/,$role);
my $area = '/'.$tdomain.'/'.$trest;
@@ -5350,6 +5621,9 @@ sub is_advanced_user {
} elsif ($trole ne 'gr') {
&standard_roleprivs(\%allroles,$trole,$tdomain,$spec,$trest,$area);
}
+ if ($trole eq 'au') {
+ $is_author = 1;
+ }
}
}
foreach my $role (keys(%allroles)) {
@@ -5364,6 +5638,9 @@ sub is_advanced_user {
}
}
}
+ if (wantarray) {
+ return ($is_adv,$is_author);
+ }
return $is_adv;
}
@@ -5634,6 +5911,15 @@ sub allowed {
}
}
+# User who is not author or co-author might still be able to edit
+# resource of an author in the domain (e.g., if Domain Coordinator).
+ if (($priv eq 'eco') && ($thisallowed eq '') && ($env{'request.course.id'}) &&
+ (&allowed('mdc',$env{'request.course.id'}))) {
+ if ($env{"user.priv.cm./$uri/"}=~/\Q$priv\E\&([^\:]*)/) {
+ $thisallowed.=$1;
+ }
+ }
+
# Course: uri itself is a course
my $courseuri=$uri;
$courseuri=~s/\_(\d)/\/$1/;
@@ -5849,7 +6135,7 @@ sub allowed {
my $unamedom=$env{'user.name'}.':'.$env{'user.domain'};
if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.roles.denied'}
=~/\Q$rolecode\E/) {
- if (($priv ne 'pch') && ($priv ne 'plc')) {
+ if (($priv ne 'pch') && ($priv ne 'plc')) {
&logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
$env{'request.course.id'});
@@ -5859,7 +6145,7 @@ sub allowed {
if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}
=~/\Q$unamedom\E/) {
- if (($priv ne 'pch') && ($priv ne 'plc')) {
+ if (($priv ne 'pch') && ($priv ne 'plc')) {
&logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.
'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
$env{'request.course.id'});
@@ -5873,7 +6159,7 @@ sub allowed {
if ($thisallowed=~/R/) {
my $rolecode=(split(/\./,$env{'request.role'}))[0];
if (&metadata($uri,'roledeny')=~/\Q$rolecode\E/) {
- if (($priv ne 'pch') && ($priv ne 'plc')) {
+ if (($priv ne 'pch') && ($priv ne 'plc')) {
&logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode);
}
@@ -5904,7 +6190,27 @@ sub allowed {
}
return 'F';
}
-
+#
+# Removes the versino from a URI and
+# splits it in to its filename and path to the filename.
+# Seems like File::Basename could have done this more clearly.
+# Parameters:
+# $uri - input URI
+# Returns:
+# Two element list consisting of
+# $pathname - the URI up to and excluding the trailing /
+# $filename - The part of the URI following the last /
+# NOTE:
+# Another realization of this is simply:
+# use File::Basename;
+# ...
+# $uri = shift;
+# $filename = basename($uri);
+# $path = dirname($uri);
+# return ($filename, $path);
+#
+# The implementation below is probably faster however.
+#
sub split_uri_for_cond {
my $uri=&deversion(&declutter(shift));
my @uriparts=split(/\//,$uri);
@@ -6100,7 +6406,7 @@ sub fetch_enrollment_query {
$$replyref{$key} = $value;
}
} else {
- my $pathname = $perlvar{'lonDaemons'}.'/tmp';
+ my $pathname = LONCAPA::tempdir();
foreach my $line (@responses) {
my ($key,$value) = split(/=/,$line);
$$replyref{$key} = $value;
@@ -6130,7 +6436,7 @@ sub fetch_enrollment_query {
sub get_query_reply {
my $queryid=shift;
- my $replyfile=$perlvar{'lonDaemons'}.'/tmp/'.$queryid;
+ my $replyfile=LONCAPA::tempdir().$queryid;
my $reply='';
for (1..100) {
sleep 2;
@@ -6981,7 +7287,7 @@ sub modifyuser {
}
&logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.
$umode.', '.$first.', '.$middle.', '.
- $last.', '.$gene.'(forceid: '.$forceid.'; candelete: '.$showcandelete.')'.
+ $last.', '.$gene.'(forceid: '.$forceid.'; candelete: '.$showcandelete.')'.
(defined($desiredhome) ? ' desiredhome = '.$desiredhome :
' desiredhome not specified').
' by '.$env{'user.name'}.' at '.$env{'user.domain'}.
@@ -7127,7 +7433,7 @@ sub modifyuser {
return 'ok';
}
my $reply = &put('environment', \%names, $udom,$uname);
- if ($reply ne 'ok') {
+ if ($reply ne 'ok') {
return 'error: '.$reply;
}
if ($names{'permanentemail'} ne $oldnames{'permanentemail'}) {
@@ -7509,7 +7815,7 @@ sub is_locked {
my ($file_name, $domain, $user, $which) = @_;
my @check;
my $is_locked;
- push(@check,$file_name);
+ push (@check,$file_name);
my %locked = &get('file_permissions',\@check,
$env{'user.domain'},$env{'user.name'});
my ($tmp)=keys(%locked);
@@ -7574,7 +7880,7 @@ sub save_selected_files {
sub clear_selected_files {
my ($user) = @_;
my $filename = $user."savedfiles";
- open (OUT, '>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+ open (OUT, '>'.LONCAPA::tempdir().$filename);
print (OUT undef);
close (OUT);
return ("ok");
@@ -7584,7 +7890,7 @@ sub files_in_path {
my ($user, $path) = @_;
my $filename = $user."savedfiles";
my %return_files;
- open (IN, '<'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+ open (IN, '<'.LONCAPA::tempdir().$filename);
while (my $line_in = ) {
chomp ($line_in);
my @paths_and_file = split (m!/!, $line_in);
@@ -7606,7 +7912,7 @@ sub files_not_in_path {
my $filename = $user."savedfiles";
my @return_files;
my $path_part;
- open(IN, '<'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+ open(IN, '<'.LONCAPA::.$filename);
while (my $line = ) {
#ok, I know it's clunky, but I want it to work
my @paths_and_file = split(m|/|, $line);
@@ -7954,26 +8260,33 @@ sub dirlist {
if($udom) {
if($uname) {
+ my $uhome = &homeserver($uname,$udom);
+ if ($uhome eq 'no_host') {
+ return ([],'no_host');
+ }
$listing = &reply('ls3:'.&escape('/'.$uri).':'.$getpropath.':'
.$getuserdir.':'.&escape($dirRoot)
- .':'.&escape($uname).':'.&escape($udom),
- &homeserver($uname,$udom));
+ .':'.&escape($uname).':'.&escape($udom),$uhome);
if ($listing eq 'unknown_cmd') {
- $listing = &reply('ls2:'.$dirRoot.'/'.$uri,
- &homeserver($uname,$udom));
+ $listing = &reply('ls2:'.$dirRoot.'/'.$uri,$uhome);
} else {
@listing_results = map { &unescape($_); } split(/:/,$listing);
}
if ($listing eq 'unknown_cmd') {
- $listing = &reply('ls:'.$dirRoot.'/'.$uri,
- &homeserver($uname,$udom));
+ $listing = &reply('ls:'.$dirRoot.'/'.$uri,$uhome);
@listing_results = split(/:/,$listing);
} else {
@listing_results = map { &unescape($_); } split(/:/,$listing);
}
- return @listing_results;
+ if (($listing eq 'no_such_host') || ($listing eq 'con_lost') ||
+ ($listing eq 'rejected') || ($listing eq 'refused') ||
+ ($listing eq 'no_such_dir') || ($listing eq 'empty')) {
+ return ([],$listing);
+ } else {
+ return (\@listing_results);
+ }
} elsif(!$alternateRoot) {
- my %allusers;
+ my (%allusers,%listerror);
my %servers = &get_servers($udom,'library');
foreach my $tryserver (keys(%servers)) {
$listing = &reply('ls3:'.&escape("/res/$udom").':::::'.
@@ -7992,32 +8305,31 @@ sub dirlist {
@listing_results =
map { &unescape($_); } split(/:/,$listing);
}
- if ($listing_results[0] ne 'no_such_dir' &&
- $listing_results[0] ne 'empty' &&
- $listing_results[0] ne 'con_lost') {
+ if (($listing eq 'no_such_host') || ($listing eq 'con_lost') ||
+ ($listing eq 'rejected') || ($listing eq 'refused') ||
+ ($listing eq 'no_such_dir') || ($listing eq 'empty')) {
+ $listerror{$tryserver} = $listing;
+ } else {
foreach my $line (@listing_results) {
my ($entry) = split(/&/,$line,2);
$allusers{$entry} = 1;
}
}
}
- my $alluserstr='';
+ my @alluserslist=();
foreach my $user (sort(keys(%allusers))) {
- $alluserstr.=$user.'&user:';
+ push(@alluserslist,$user.'&user');
}
- $alluserstr=~s/:$//;
- return split(/:/,$alluserstr);
+ return (\@alluserslist);
} else {
- return ('missing user name');
+ return ([],'missing username');
}
} elsif(!defined($getpropath)) {
- my @all_domains = sort(&all_domains());
- foreach my $domain (@all_domains) {
- $domain = $perlvar{'lonDocRoot'}.'/res/'.$domain.'/&domain';
- }
- return @all_domains;
+ my $path = $perlvar{'lonDocRoot'}.'/res/';
+ my @all_domains = map { $path.$_.'/&domain'; } (sort(&all_domains()));
+ return (\@all_domains);
} else {
- return ('missing domain');
+ return ([],'missing domain');
}
}
@@ -8030,11 +8342,13 @@ sub GetFileTimestamp {
my ($studentDomain,$studentName,$filename,$getuserdir)=@_;
$studentDomain = &LONCAPA::clean_domain($studentDomain);
$studentName = &LONCAPA::clean_username($studentName);
- my ($fileStat) =
- &Apache::lonnet::dirlist($filename,$studentDomain,$studentName,
- undef,$getuserdir);
- my @stats = split('&', $fileStat);
- if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') {
+ my ($fileref,$error) = &dirlist($filename,$studentDomain,$studentName,
+ undef,$getuserdir);
+ if (($error eq 'empty') || ($error eq 'no_such_dir')) {
+ return -1;
+ }
+ if (ref($fileref) eq 'ARRAY') {
+ my @stats = split('&',$fileref->[0]);
# @stats contains first the filename, then the stat output
return $stats[10]; # so this is 10 instead of 9.
} else {
@@ -8066,12 +8380,15 @@ sub stat_file {
if ($file =~ /^userfiles\//) {
$getpropath = 1;
}
- my ($result) = &dirlist($file,$udom,$uname,$getpropath);
- my @stats = split('&', $result);
-
- if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') {
- shift(@stats); #filename is first
- return @stats;
+ my ($listref,$error) = &dirlist($file,$udom,$uname,$getpropath);
+ if (($error eq 'empty') || ($error eq 'no_such_dir')) {
+ return ();
+ } else {
+ if (ref($listref) eq 'ARRAY') {
+ my @stats = split('&',$listref->[0]);
+ shift(@stats); #filename is first
+ return @stats;
+ }
}
return ();
}
@@ -8392,15 +8709,7 @@ sub EXT {
} elsif ($realm eq 'request') {
# ------------------------------------------------------------- request.browser
if ($space eq 'browser') {
- if ($qualifier eq 'textremote') {
- if (&Apache::lonlocal::mt('textual_remote_display') eq 'on') {
- return 1;
- } else {
- return 0;
- }
- } else {
- return $env{'browser.'.$qualifier};
- }
+ return $env{'browser.'.$qualifier};
# ------------------------------------------------------------ request.filename
} else {
return $env{'request.'.$spacequalifierrest};
@@ -8673,6 +8982,7 @@ sub add_prefix_and_part {
# ---------------------------------------------------------------- Get metadata
my %metaentry;
+my %importedpartids;
sub metadata {
my ($uri,$what,$liburi,$prefix,$depthcount)=@_;
$uri=&declutter($uri);
@@ -8683,7 +8993,7 @@ sub metadata {
($uri =~ m|/$|) || ($uri =~ m|/.meta$|) || ($uri =~ m{^/*uploaded/.+\.sequence$})) {
return undef;
}
- if (($uri =~ /^~/ || $uri =~ m{home/$match_username/public_html/})
+ if (($uri =~ /^priv/ || $uri=~/home\/httpd\/html\/priv/)
&& &Apache::lonxml::get_state('target') =~ /^(|meta)$/) {
return undef;
}
@@ -8699,6 +9009,10 @@ sub metadata {
if (defined($cached)) { return $result->{':'.$what}; }
}
{
+# Imported parts would go here
+ my %importedids=();
+ my @origfileimportpartids=();
+ my $importedparts=0;
#
# Is this a recursive call for a library?
#
@@ -8716,7 +9030,7 @@ sub metadata {
my %metathesekeys=();
unless ($filename=~/\.meta$/) { $filename.='.meta'; }
my $metastring;
- if ($uri =~ /^~/ || $uri =~ m{home/$match_username/public_html/}) {
+ if ($uri =~ /^priv/ || $uri=~/home\/httpd\/html\/priv/) {
my $which = &hreflocation('','/'.($liburi || $uri));
$metastring =
&Apache::lonnet::ssi_body($which,
@@ -8783,27 +9097,55 @@ sub metadata {
# This is not a package - some other kind of start tag
#
my $entry=$token->[1];
- my $unikey;
- if ($entry eq 'import') {
- $unikey='';
- } else {
- $unikey=$entry;
- }
- $unikey.=&add_prefix_and_part($prefix,$token->[2]->{'part'});
-
- if (defined($token->[2]->{'id'})) {
- $unikey.='_'.$token->[2]->{'id'};
- }
+ my $unikey='';
if ($entry eq 'import') {
#
# Importing a library here
#
+ my $location=$parser->get_text('/import');
+ my $dir=$filename;
+ $dir=~s|[^/]*$||;
+ $location=&filelocation($dir,$location);
+
+ my $importmode=$token->[2]->{'importmode'};
+ if ($importmode eq 'problem') {
+# Import as problem/response
+ $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
+ } elsif ($importmode eq 'part') {
+# Import as part(s)
+ $importedparts=1;
+# We need to get the original file and the imported file to get the part order correct
+# Good news: we do not need to worry about nested libraries, since parts cannot be nested
+# Load and inspect original file
+ if ($#origfileimportpartids<0) {
+ undef(%importedpartids);
+ my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri);
+ my $origfile=&getfile($origfilelocation);
+ @origfileimportpartids=($origfile=~/<(part|import)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+ }
+
+# Load and inspect imported file
+ my $impfile=&getfile($location);
+ my @impfilepartids=($impfile=~/]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+ if ($#impfilepartids>=0) {
+# This problem had parts
+ $importedpartids{$token->[2]->{'id'}}=join(',',@impfilepartids);
+ } else {
+# Importing by turning a single problem into a problem part
+# It gets the import-tags ID as part-ID
+ $unikey=&add_prefix_and_part($prefix,$token->[2]->{'id'});
+ $importedpartids{$token->[2]->{'id'}}=$token->[2]->{'id'};
+ }
+ } else {
+# Normal import
+ $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
+ if (defined($token->[2]->{'id'})) {
+ $unikey.='_'.$token->[2]->{'id'};
+ }
+ }
+
if ($depthcount<20) {
- my $location=$parser->get_text('/import');
- my $dir=$filename;
- $dir=~s|[^/]*$||;
- $location=&filelocation($dir,$location);
my $metadata =
&metadata($uri,'keys', $location,$unikey,
$depthcount+1);
@@ -8811,8 +9153,16 @@ sub metadata {
$metaentry{':'.$meta}=$metaentry{':'.$meta};
$metathesekeys{$meta}=1;
}
- }
- } else {
+
+ }
+ } else {
+#
+# Not importing, some other kind of non-package, non-library start tag
+#
+ $unikey=$entry.&add_prefix_and_part($prefix,$token->[2]->{'part'});
+ if (defined($token->[2]->{'id'})) {
+ $unikey.='_'.$token->[2]->{'id'};
+ }
if (defined($token->[2]->{'name'})) {
$unikey.='_'.$token->[2]->{'name'};
}
@@ -8886,6 +9236,22 @@ sub metadata {
grep { ! $seen{$_} ++ } (split(',',$metaentry{':packages'}));
$metaentry{':packages'} = join(',',@uniq_packages);
+ if ($importedparts) {
+# We had imported parts and need to rebuild partorder
+ $metaentry{':partorder'}='';
+ $metathesekeys{'partorder'}=1;
+ for (my $index=0;$index<$#origfileimportpartids;$index+=2) {
+ if ($origfileimportpartids[$index] eq 'part') {
+# original part, part of the problem
+ $metaentry{':partorder'}.=','.$origfileimportpartids[$index+1];
+ } else {
+# we have imported parts at this position
+ $metaentry{':partorder'}.=','.$importedpartids{$origfileimportpartids[$index+1]};
+ }
+ }
+ $metaentry{':partorder'}=~s/^\,//;
+ }
+
$metaentry{':keys'} = join(',',keys(%metathesekeys));
&metadata_generate_part0(\%metathesekeys,\%metaentry,$uri);
$metaentry{':allpossiblekeys'}=join(',',keys %metathesekeys);
@@ -9070,9 +9436,9 @@ sub symbverify {
$thisurl =~ s/\?.+$//;
}
my $ids=$bighash{'ids_'.&clutter($thisurl)};
- unless ($ids) {
- my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl;
- $ids=$bighash{$idkey};
+ unless ($ids) {
+ my $idkey = 'ids_'.($thisurl =~ m{^/}? '' : '/').$thisurl;
+ $ids=$bighash{$idkey};
}
if ($ids) {
# ------------------------------------------------------------------- Has ID(s)
@@ -9086,7 +9452,7 @@ sub symbverify {
eq $symb) {
if (($env{'request.role.adv'}) ||
($bighash{'encrypted_'.$id} eq $env{'request.enc'}) ||
- ($thisurl eq '/adm/navmaps')) {
+ ($thisurl eq '/adm/navmaps')) {
$okay=1;
}
}
@@ -9361,19 +9727,44 @@ sub getCODE {
}
return undef;
}
-
+#
+# Determines the random seed for a specific context:
+#
+# parameters:
+# symb - in course context the symb for the seed.
+# course_id - The course id of the form domain_coursenum.
+# domain - Domain for the user.
+# course - Course for the user.
+# cenv - environment of the course.
+#
+# NOTE:
+# All parameters are picked out of the environment if missing
+# or not defined.
+# If a symb cannot be determined the current time is used instead.
+#
+# For a given well defined symb, courside, domain, username,
+# and course environment, the seed is reproducible.
+#
sub rndseed {
- my ($symb,$courseid,$domain,$username)=@_;
+ my ($symb,$courseid,$domain,$username, $cenv)=@_;
my ($wsymb,$wcourseid,$wdomain,$wusername)=&whichuser();
if (!defined($symb)) {
unless ($symb=$wsymb) { return time; }
}
- if (!$courseid) { $courseid=$wcourseid; }
- if (!$domain) { $domain=$wdomain; }
- if (!$username) { $username=$wusername }
- my $which=&get_rand_alg();
+ if (!defined $courseid) {
+ $courseid=$wcourseid;
+ }
+ if (!defined $domain) { $domain=$wdomain; }
+ if (!defined $username) { $username=$wusername }
+ my $which;
+ if (defined($cenv->{'rndseed'})) {
+ $which = $cenv->{'rndseed'};
+ } else {
+ $which =&get_rand_alg($courseid);
+ }
if (defined(&getCODE())) {
+
if ($which eq '64bit5') {
return &rndseed_CODE_64bit5($symb,$courseid,$domain,$username);
} elsif ($which eq '64bit4') {
@@ -9697,8 +10088,9 @@ sub getfile {
sub repcopy_userfile {
my ($file)=@_;
- if ($file =~ m -^/*(uploaded|editupload)/-) { $file=&filelocation("",$file); }
- if ($file =~ m|^/home/httpd/html/lonUsers/|) { return 'ok'; }
+ my $londocroot = $perlvar{'lonDocRoot'};
+ if ($file =~ m{^/*(uploaded|editupload)/}) { $file=&filelocation("",$file); }
+ if ($file =~ m{^\Q$londocroot/lonUsers/\E}) { return 'ok'; }
my ($cdom,$cnum,$filename) =
($file=~m|^\Q$perlvar{'lonDocRoot'}\E/+userfiles/+($match_domain)/+($match_name)/+(.*)|);
my $uri="/uploaded/$cdom/$cnum/$filename";
@@ -9827,13 +10219,7 @@ sub filelocation {
$file=~s-^/adm/coursedocs/showdoc/-/-;
}
- if ($file=~m:^/~:) { # is a contruction space reference
- $location = $file;
- $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:;
- } elsif ($file=~m{^/home/$match_username/public_html/}) {
- # is a correct contruction space reference
- $location = $file;
- } elsif ($file =~ m-^\Q$Apache::lonnet::perlvar{'lonTabDir'}\E/-) {
+ if ($file =~ m-^\Q$Apache::lonnet::perlvar{'lonTabDir'}\E/-) {
$location = $file;
} elsif ($file=~/^\/*(uploaded|editupload)/) { # is an uploaded file
my ($udom,$uname,$filename)=
@@ -9843,7 +10229,7 @@ sub filelocation {
my @ids=¤t_machine_ids();
foreach my $id (@ids) { if ($id eq $home) { $is_me=1; } }
if ($is_me) {
- $location=&propath($udom,$uname).'/userfiles/'.$filename;
+ $location=propath($udom,$uname).'/userfiles/'.$filename;
} else {
$location=$Apache::lonnet::perlvar{'lonDocRoot'}.'/userfiles/'.
$udom.'/'.$uname.'/'.$filename;
@@ -9852,11 +10238,12 @@ sub filelocation {
$location = $perlvar{'lonDocRoot'}.'/'.$file;
} else {
$file=~s/^\Q$perlvar{'lonDocRoot'}\E//;
- $file=~s:^/res/:/:;
+ $file=~s:^/(res|priv)/:/:;
+ my $space=$1;
if ( !( $file =~ m:^/:) ) {
$location = $dir. '/'.$file;
} else {
- $location = '/home/httpd/html/res'.$file;
+ $location = $perlvar{'lonDocRoot'}.'/'.$space.$file;
}
}
$location=~s://+:/:g; # remove duplicate /
@@ -9881,11 +10268,9 @@ sub hreflocation {
}
if ($file=~m-^\Q$perlvar{'lonDocRoot'}\E-) {
$file=~s-^\Q$perlvar{'lonDocRoot'}\E--;
- } elsif ($file=~m-/home/($match_username)/public_html/-) {
- $file=~s-^/home/($match_username)/public_html/-/~$1/-;
} elsif ($file=~m-^\Q$perlvar{'lonUsersDir'}\E-) {
- $file=~s-^/home/httpd/lonUsers/($match_domain)/./././($match_name)/userfiles/
- -/uploaded/$1/$2/-x;
+ $file=~s{^/home/httpd/lonUsers/($match_domain)/./././($match_name)/userfiles/}
+ {/uploaded/$1/$2/}x;
}
if ($file=~ m{^/userfiles/}) {
$file =~ s{^/userfiles/}{/uploaded/};
@@ -9893,6 +10278,10 @@ sub hreflocation {
return $file;
}
+
+
+
+
sub current_machine_domains {
return &machine_domains(&hostname($perlvar{'lonHostID'}));
}
@@ -10173,7 +10562,7 @@ sub get_dns {
foreach my $configline (@$file) {
next if ($configline =~ /^(\#|\s*$ )/x);
chomp($configline);
- if ($configline =~ /^\^/) {
+ if ($configline =~ /^\^/) {
if ($configline =~ /^\^([\w.\-]+)/) {
$LC_dns_serv{$1} = 1;
}
@@ -10260,7 +10649,7 @@ sub get_dns {
}
sub unique_library {
- #2x reverse removes all hostnames that appear more than once
+ #2x reverse removes all hostnames that appear more than once
my %unique = reverse &all_library();
return reverse %unique;
}
@@ -10290,7 +10679,7 @@ sub get_dns {
sub get_unique_servers {
my %unique = reverse &get_servers(@_);
- return reverse %unique;
+ return reverse %unique;
}
sub host_domain {
@@ -10615,7 +11004,7 @@ BEGIN {
# ------------- set up temporary directory
{
- $tmpdir = $perlvar{'lonDaemons'}.'/tmp/';
+ $tmpdir = LONCAPA::tempdir();
}
@@ -10850,7 +11239,7 @@ $checkdefauth is optional (value is 1 if
authenticate user using default authentication method, and allow
account creation if username does not have account in the domain).
$clientcancheckhost is optional (value is 1 if checking whether the
- server can host will occur on the client side in lonauth.pm).
+ server can host will occur on the client side in lonauth.pm).
=item *
X
@@ -11109,11 +11498,32 @@ revokecustomrole($udom,$uname,$url,$role
=item *
-coursedescription($courseid) : returns a hash of information about the
+coursedescription($courseid,$options) : returns a hash of information about the
specified course id, including all environment settings for the
course, the description of the course will be in the hash under the
key 'description'
+$options is an optional parameter that if supplied is a hash reference that controls
+what how this function works. It has the following key/values:
+
+=over 4
+
+=item freshen_cache
+
+If defined, and the environment cache for the course is valid, it is
+returned in the returned hash.
+
+=item one_time
+
+If defined, the last cache time is set to _now_
+
+=item user
+
+If defined, the supplied username is used instead of the current user.
+
+
+=back
+
=item *
resdata($name,$domain,$type,@which) : request for current parameter
@@ -11444,7 +11854,82 @@ or lonTabs/domain.tab.
=item *
-dirlist($uri) : return directory list based on URI
+dirlist() : return directory list based on URI (first arg).
+
+Inputs: 1 required, 5 optional.
+
+=over
+
+=item
+$uri - path to file in filesystem (starts: /res or /userfiles/). Required.
+
+=item
+$userdomain - domain of user/course to be listed. Extracted from $uri if absent.
+
+=item
+$username - username of user/course to be listed. Extracted from $uri if absent.
+
+=item
+$getpropath - boolean: 1 if prepend path using &propath().
+
+=item
+$getuserdir - boolean: 1 if prepend path for "userfiles".
+
+=item
+$alternateRoot - path to prepend in place of path from $uri.
+
+=back
+
+Returns: Array of up to two items.
+
+=over
+
+a reference to an array of files/subdirectories
+
+=over
+
+Each element in the array of files/subdirectories is a & separated list of
+item name and the result of running stat on the item. If dirlist was requested
+for a file instead of a directory, the item name will be ''. For a directory
+listing, if the item is a metadata file, the element will end &N&M
+(where N amd M are either 0 or 1, corresponding to obsolete set (1), or
+default copyright set (1).
+
+=back
+
+a scalar containing error condition (if encountered).
+
+=over
+
+=item
+no_host (no homeserver identified for $username:$domain).
+
+=item
+no_such_host (server contacted for listing not identified as valid host).
+
+=item
+con_lost (connection to remote server failed).
+
+=item
+refused (invalid $username:$domain received on lond side).
+
+=item
+no_such_dir (directory at specified path on lond side does not exist).
+
+=item
+empty (directory at specified path on lond side is empty).
+
+=over
+
+This is currently not encountered because the &ls3, &ls2,
+&ls (_handler) routines on the lond side do not filter out
+. and .. from a directory listing.
+
+=back
+
+=back
+
+=back
=item *
@@ -11506,6 +11991,7 @@ splitting on '&', supports elements that
=head2 Logging Routines
+
These routines allow one to make log messages in the lonnet.log and
lonnet.perm logfiles.
@@ -11526,6 +12012,7 @@ logperm() : append a permanent message t
file never gets deleted by any automated portion of the system, only
messages of critical importance should go in here.
+
=back
=head2 General File Helper Routines
@@ -11600,7 +12087,7 @@ userfileupload(): main rotine for puttin
the filename is in $env{'form.'.$formname.'.filename'} and the
contents of the file is located in $env{'form.'.$formname}
context - if coursedoc, store the file in the course of the active role
- of the current user;
+ of the current user;
if 'existingfile': store in 'overwrites' in /home/httpd/perl/tmp
if 'canceloverwrite': delete file in tmp/overwrites directory
subdir - required - subdirectory to put the file in under ../userfiles/
@@ -11646,7 +12133,7 @@ userspace, probably shouldn't be called
returns either the url of the uploaded file (/uploaded/....) if successful
and /adm/notfound.html if unsuccessful (or an error message if context
was 'overwrite').
-
+
=item *