--- loncom/lonnet/perl/lonnet.pm 2001/01/11 11:11:27 1.97 +++ loncom/lonnet/perl/lonnet.pm 2011/08/17 00:32:27 1.1130 @@ -1,115 +1,159 @@ # The LearningOnline Network # TCP networking package # -# Functions for use by content handlers: +# $Id: lonnet.pm,v 1.1130 2011/08/17 00:32:27 raeburn Exp $ # -# plaintext(short) : plain text explanation of short term -# fileembstyle(ext) : embed style in page for file extension -# filedescription(ext) : descriptor text for file extension -# allowed(short,url) : returns codes for allowed actions -# F: full access -# U,I,K: authentication modes (cxx only) -# '': forbidden -# 1: user needs to choose course -# 2: browse allowed -# definerole(rolename,sys,dom,cou) : define a custom role rolename -# set priviledges in format of lonTabs/roles.tab for -# system, domain and course level, -# assignrole(udom,uname,url,role,end,start) : give a role to a user for the -# level given by url. Optional start and end dates -# (leave empty string or zero for "no date") -# assigncustomrole (udom,uname,url,rdom,rnam,rolename,end,start) : give a -# custom role to a user for the level given by url. -# Specify name and domain of role author, and role name -# revokerole (udom,uname,url,role) : Revoke a role for url -# revokecustomrole (udom,uname,url,rdom,rnam,rolename) : Revoke a custom role -# appenv(hash) : adds hash to session environment -# delenv(varname) : deletes all environment entries starting with varname -# store(hash) : stores hash permanently for this url -# cstore(hash) : critical store -# restore : returns hash for this url -# eget(namesp,array) : returns hash with keys from array filled in from namesp -# get(namesp,array) : returns hash with keys from array filled in from namesp -# del(namesp,array) : deletes keys out of array from namesp -# put(namesp,hash) : stores hash in namesp -# cput(namesp,hash) : critical put -# dump(namesp) : dumps the complete namespace into a hash -# ssi(url,hash) : does a complete request cycle on url to localhost, posts -# hash -# coursedescription(id) : returns and caches course description for id -# repcopy(filename) : replicate file -# dirlist(url) : gets a directory listing -# directcondval(index) : reading condition value of single condition from -# state string -# condval(index) : value of condition index based on state -# EXT(name) : value of a variable -# symblist(map,hash) : Updates symbolic storage links -# symbread([filename]) : returns the data handle (filename optional) -# rndseed() : returns a random seed -# receipt() : returns a receipt to be given out to users -# getfile(filename) : returns the contents of filename, or a -1 if it can't -# be found, replicates and subscribes to the file -# filelocation(dir,file) : returns a farily clean absolute reference to file -# from the directory dir -# hreflocation(dir,file) : same as filelocation, but for hrefs -# log(domain,user,home,msg) : write to permanent log for user -# usection(domain,user,courseid) : output of section name/number or '' for -# "not in course" and '-1' for "no section" -# userenvironment(domain,user,what) : puts out any environment parameter -# for a user -# idput(domain,hash) : writes IDs for users from hash (name=>id,name=>id) -# idget(domain,array): returns hash with usernames (id=>name,id=>name) for -# an array of IDs -# idrget(domain,array): returns hash with IDs for usernames (name=>id,...) for -# an array of names -# metadata(file,entry): returns the metadata entry for a file. entry='keys' -# returns a comma separated list of keys -# -# 6/1/99,6/2,6/10,6/11,6/12,6/14,6/26,6/28,6/29,6/30, -# 7/1,7/2,7/9,7/10,7/12,7/14,7/15,7/19, -# 11/8,11/16,11/18,11/22,11/23,12/22, -# 01/06,01/13,02/24,02/28,02/29, -# 03/01,03/02,03/06,03/07,03/13, -# 04/05,05/29,05/31,06/01, -# 06/05,06/26 Gerd Kortemeyer -# 06/26 Ben Tyszka -# 06/30,07/15,07/17,07/18,07/20,07/21,07/22,07/25 Gerd Kortemeyer -# 08/14 Ben Tyszka -# 08/22,08/28,08/31,09/01,09/02,09/04,09/05,09/25,09/28,09/30 Gerd Kortemeyer -# 10/04 Gerd Kortemeyer -# 10/04 Guy Albertelli -# 10/06,10/09,10/10,10/11,10/14,10/20,10/23,10/25,10/26,10/27,10/28,10/29, -# 10/30,10/31, -# 11/2,11/14,11/15,11/16,11/20,11/21,11/22,11/25,11/27, -# 12/02,12/12,12/13,12/14,12/28,12/29 Gerd Kortemeyer -# 05/01/01 Guy Albertelli -# 05/01,06/01,09/01 Gerd Kortemeyer -# 09/01 Guy Albertelli -# 09/01,10/01,11/01 Gerd Kortemeyer +# Copyright Michigan State University Board of Trustees +# +# This file is part of the LearningOnline Network with CAPA (LON-CAPA). +# +# LON-CAPA is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# LON-CAPA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with LON-CAPA; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# /home/httpd/html/adm/gpl.txt +# +# http://www.lon-capa.org/ +# +### + +=pod + +=head1 NAME + +Apache::lonnet.pm + +=head1 SYNOPSIS + +This file is an interface to the lonc processes of +the LON-CAPA network as well as set of elaborated functions for handling information +necessary for navigating through a given cluster of LON-CAPA machines within a +domain. There are over 40 specialized functions in this module which handle the +reading and transmission of metadata, user information (ids, names, environments, roles, +logs), file information (storage, reading, directories, extensions, replication, embedded +styles and descriptors), educational resources (course descriptions, section names and +numbers), url hashing (to assign roles on a url basis), and translating abbreviated symbols to +and from more descriptive phrases or explanations. + +This is part of the LearningOnline Network with CAPA project +described at http://www.lon-capa.org. + +=head1 Package Variables + +These are largely undocumented, so if you decipher one please note it here. + +=over 4 + +=item $processmarker + +Contains the time this process was started and this servers host id. + +=item $dumpcount + +Counts the number of times a message log flush has been attempted (regardless +of success) by this process. Used as part of the filename when messages are +delayed. + +=back + +=cut package Apache::lonnet; use strict; -use Apache::File; use LWP::UserAgent(); -use HTTP::Headers; -use vars -qw(%perlvar %hostname %homecache %spareid %hostdom %libserv %pr %prp %fe %fd $readit %metacache); +use HTTP::Date; +use Image::Magick; + +use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir + $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease); + +my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash, + %userrolehash, $processmarker, $dumpcount, %coursedombuf, + %coursenumbuf, %coursehombuf, %coursedescrbuf, %courseinstcodebuf, + %courseownerbuf, %coursetypebuf,$locknum); + use IO::Socket; use GDBM_File; -use Apache::Constants qw(:common :http); -use HTML::TokeParser; +use HTML::LCParser; use Fcntl qw(:flock); +use Storable qw(thaw nfreeze); +use Time::HiRes qw( gettimeofday tv_interval ); +use Cache::Memcached; +use Digest::MD5; +use Math::Random; +use File::MMagic; +use LONCAPA qw(:DEFAULT :match); +use LONCAPA::Configuration; + +use File::Copy; + +my $readit; +my $max_connection_retries = 10; # Or some such value. + +require Exporter; + +our @ISA = qw (Exporter); +our @EXPORT = qw(%env); + # --------------------------------------------------------------------- Logging +{ + my $logid; + sub instructor_log { + my ($hash_name,$storehash,$delflag,$uname,$udom,$cnum,$cdom)=@_; + if (($cnum eq '') || ($cdom eq '')) { + $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + } + $logid++; + my $now = time(); + my $id=$now.'00000'.$$.'00000'.$logid; + return &Apache::lonnet::put('nohist_'.$hash_name, + { $id => { + 'exe_uname' => $env{'user.name'}, + 'exe_udom' => $env{'user.domain'}, + 'exe_time' => $now, + 'exe_ip' => $ENV{'REMOTE_ADDR'}, + 'delflag' => $delflag, + 'logentry' => $storehash, + 'uname' => $uname, + 'udom' => $udom, + } + },$cdom,$cnum); + } +} + +sub logtouch { + my $execdir=$perlvar{'lonDaemons'}; + unless (-e "$execdir/logs/lonnet.log") { + open(my $fh,">>$execdir/logs/lonnet.log"); + close $fh; + } + my ($wwwuid,$wwwgid)=(getpwnam('www'))[2,3]; + chown($wwwuid,$wwwgid,$execdir.'/logs/lonnet.log'); +} sub logthis { my $message=shift; my $execdir=$perlvar{'lonDaemons'}; my $now=time; my $local=localtime($now); - my $fh=Apache::File->new(">>$execdir/logs/lonnet.log"); - print $fh "$local ($$): $message\n"; + if (open(my $fh,">>$execdir/logs/lonnet.log")) { + my $logstring = $local. " ($$): ".$message."\n"; # Keep any \'s in string. + print $fh $logstring; + close($fh); + } return 1; } @@ -118,32 +162,245 @@ sub logperm { my $execdir=$perlvar{'lonDaemons'}; my $now=time; my $local=localtime($now); - my $fh=Apache::File->new(">>$execdir/logs/lonnet.perm.log"); - print $fh "$now:$message:$local\n"; + if (open(my $fh,">>$execdir/logs/lonnet.perm.log")) { + print $fh "$now:$message:$local\n"; + close($fh); + } return 1; } +sub create_connection { + my ($hostname,$lonid) = @_; + my $client=IO::Socket::UNIX->new(Peer => $perlvar{'lonSockCreate'}, + Type => SOCK_STREAM, + Timeout => 10); + return 0 if (!$client); + print $client (join(':',$hostname,$lonid,&machine_ids($hostname))."\n"); + my $result = <$client>; + chomp($result); + return 1 if ($result eq 'done'); + return 0; +} + +sub get_server_timezone { + my ($cnum,$cdom) = @_; + my $home=&homeserver($cnum,$cdom); + if ($home ne 'no_host') { + my $cachetime = 24*3600; + my ($timezone,$cached)=&is_cached_new('servertimezone',$home); + if (defined($cached)) { + return $timezone; + } else { + my $timezone = &reply('servertimezone',$home); + return &do_cache_new('servertimezone',$home,$timezone,$cachetime); + } + } +} + +sub get_server_distarch { + my ($lonhost,$ignore_cache) = @_; + if (defined($lonhost)) { + if (!defined(&hostname($lonhost))) { + return; + } + my $cachetime = 12*3600; + if (!$ignore_cache) { + my ($distarch,$cached)=&is_cached_new('serverdistarch',$lonhost); + if (defined($cached)) { + return $distarch; + } + } + my $rep = &reply('serverdistarch',$lonhost); + unless ($rep eq 'unknown_command' || $rep eq 'no_such_host' || + $rep eq 'con_lost' || $rep eq 'rejected' || $rep eq 'refused' || + $rep eq '') { + return &do_cache_new('serverdistarch',$lonhost,$rep,$cachetime); + } + } + return; +} + +sub get_server_loncaparev { + my ($dom,$lonhost,$ignore_cache,$caller) = @_; + if (defined($lonhost)) { + if (!defined(&hostname($lonhost))) { + undef($lonhost); + } + } + if (!defined($lonhost)) { + if (defined(&domain($dom,'primary'))) { + $lonhost=&domain($dom,'primary'); + if ($lonhost eq 'no_host') { + undef($lonhost); + } + } + } + if (defined($lonhost)) { + my $cachetime = 12*3600; + if (!$ignore_cache) { + my ($loncaparev,$cached)=&is_cached_new('serverloncaparev',$lonhost); + if (defined($cached)) { + return $loncaparev; + } + } + my ($answer,$loncaparev); + my @ids=¤t_machine_ids(); + if (grep(/^\Q$lonhost\E$/,@ids)) { + $answer = $perlvar{'lonVersion'}; + if ($answer =~ /^[\'\"]?([\w.\-]+)[\'\"]?$/) { + $loncaparev = $1; + } + } else { + $answer = &reply('serverloncaparev',$lonhost); + if (($answer eq 'unknown_cmd') || ($answer eq 'con_lost')) { + if ($caller eq 'loncron') { + my $ua=new LWP::UserAgent; + $ua->timeout(4); + my $protocol = $protocol{$lonhost}; + $protocol = 'http' if ($protocol ne 'https'); + my $url = $protocol.'://'.&hostname($lonhost).'/adm/about.html'; + my $request=new HTTP::Request('GET',$url); + my $response=$ua->request($request); + unless ($response->is_error()) { + my $content = $response->content; + if ($content =~ /
VERSION\:\s*([\w.\-]+)<\/p>/) {
+ $loncaparev = $1;
+ }
+ }
+ } else {
+ $loncaparev = $loncaparevs{$lonhost};
+ }
+ } elsif ($answer =~ /^[\'\"]?([\w.\-]+)[\'\"]?$/) {
+ $loncaparev = $1;
+ }
+ }
+ return &do_cache_new('serverloncaparev',$lonhost,$loncaparev,$cachetime);
+ }
+}
+
+sub get_server_homeID {
+ my ($hostname,$ignore_cache,$caller) = @_;
+ unless ($ignore_cache) {
+ my ($serverhomeID,$cached)=&is_cached_new('serverhomeID',$hostname);
+ if (defined($cached)) {
+ return $serverhomeID;
+ }
+ }
+ my $cachetime = 12*3600;
+ my $serverhomeID;
+ if ($caller eq 'loncron') {
+ my @machine_ids = &machine_ids($hostname);
+ foreach my $id (@machine_ids) {
+ my $response = &reply('serverhomeID',$id);
+ unless (($response eq 'unknown_cmd') || ($response eq 'con_lost')) {
+ $serverhomeID = $response;
+ last;
+ }
+ }
+ if ($serverhomeID eq '') {
+ $serverhomeID = $machine_ids[-1];
+ }
+ } else {
+ $serverhomeID = $serverhomeIDs{$hostname};
+ }
+ 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)=@_;
- my $peerfile="$perlvar{'lonSockDir'}/$server";
- my $client=IO::Socket::UNIX->new(Peer =>"$peerfile",
- Type => SOCK_STREAM,
- Timeout => 10)
- or return "con_lost";
- print $client "$cmd\n";
- my $answer=<$client>;
- if (!$answer) { $answer="con_lost"; }
- chomp($answer);
+ my $peerfile="$perlvar{'lonSockDir'}/".&hostname($server);
+ #
+ # With loncnew process trimming, there's a timing hole between lonc server
+ # process exit and the master server picking up the listen on the AF_UNIX
+ # socket. In that time interval, a lock file will exist:
+
+ my $lockfile=$peerfile.".lock";
+ while (-e $lockfile) { # Need to wait for the lockfile to disappear.
+ sleep(1);
+ }
+ # At this point, either a loncnew parent is listening or an old lonc
+ # or loncnew child is listening so we can connect or everything's dead.
+ #
+ # We'll give the connection a few tries before abandoning it. If
+ # connection is not possible, we'll con_lost back to the client.
+ #
+ my $client;
+ for (my $retries = 0; $retries < $max_connection_retries; $retries++) {
+ $client=IO::Socket::UNIX->new(Peer =>"$peerfile",
+ Type => SOCK_STREAM,
+ Timeout => 10);
+ if ($client) {
+ last; # Connected!
+ } else {
+ &create_connection(&hostname($server),$server);
+ }
+ sleep(1); # Try again later if failed connection.
+ }
+ my $answer;
+ if ($client) {
+ print $client "sethost:$server:$cmd\n";
+ $answer=<$client>;
+ if (!$answer) { $answer="con_lost"; }
+ chomp($answer);
+ } else {
+ $answer = 'con_lost'; # Failed connection.
+ }
return $answer;
}
sub reply {
my ($cmd,$server)=@_;
+ unless (defined(&hostname($server))) { return 'no_such_host'; }
my $answer=subreply($cmd,$server);
- if ($answer eq 'con_lost') { $answer=subreply($cmd,$server); }
if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
- &logthis("WARNING:".
+ &logthis("WARNING:".
" $cmd to $server returned $answer");
}
return $answer;
@@ -152,29 +409,40 @@ sub reply {
# ----------------------------------------------------------- Send USR1 to lonc
sub reconlonc {
- my $peerfile=shift;
- &logthis("Trying to reconnect for $peerfile");
+ my ($lonid) = @_;
+ my $hostname = &hostname($lonid);
+ if ($lonid) {
+ my $peerfile="$perlvar{'lonSockDir'}/$hostname";
+ if ($hostname && -e $peerfile) {
+ &logthis("Trying to reconnect lonc for $lonid ($hostname)");
+ my $client=IO::Socket::UNIX->new(Peer => $peerfile,
+ Type => SOCK_STREAM,
+ Timeout => 10);
+ if ($client) {
+ print $client ("reset_retries\n");
+ my $answer=<$client>;
+ #reset just this one.
+ }
+ }
+ return;
+ }
+
+ &logthis("Trying to reconnect lonc");
my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
- if (my $fh=Apache::File->new("$loncfile")) {
+ if (open(my $fh,"<$loncfile")) {
my $loncpid=<$fh>;
chomp($loncpid);
if (kill 0 => $loncpid) {
&logthis("lonc at pid $loncpid responding, sending USR1");
kill USR1 => $loncpid;
sleep 1;
- if (-e "$peerfile") { return; }
- &logthis("$peerfile still not there, give it another try");
- sleep 5;
- if (-e "$peerfile") { return; }
- &logthis(
- "WARNING: $peerfile still not there, giving up");
- } else {
+ } else {
&logthis(
- "WARNING:".
+ "WARNING:".
" lonc at pid $loncpid not responding, giving up");
}
} else {
- &logthis('WARNING: lonc not running, giving up');
+ &logthis('WARNING: lonc not running, giving up');
}
}
@@ -182,47 +450,47 @@ sub reconlonc {
sub critical {
my ($cmd,$server)=@_;
- unless ($hostname{$server}) {
- &logthis("WARNING:".
+ unless (&hostname($server)) {
+ &logthis("WARNING:".
" Critical message to unknown server ($server)");
return 'no_such_host';
}
my $answer=reply($cmd,$server);
if ($answer eq 'con_lost') {
- my $pingreply=reply('ping',$server);
&reconlonc("$perlvar{'lonSockDir'}/$server");
- my $pongreply=reply('pong',$server);
- &logthis("Ping/Pong for $server: $pingreply/$pongreply");
- $answer=reply($cmd,$server);
+ my $answer=reply($cmd,$server);
if ($answer eq 'con_lost') {
my $now=time;
my $middlename=$cmd;
$middlename=substr($middlename,0,16);
$middlename=~s/\W//g;
my $dfilename=
- "$perlvar{'lonSockDir'}/delayed/$now.$middlename.$server";
+ "$perlvar{'lonSockDir'}/delayed/$now.$dumpcount.$$.$middlename.$server";
+ $dumpcount++;
{
- my $dfh;
- if ($dfh=Apache::File->new(">$dfilename")) {
- print $dfh "$cmd\n";
- }
+ my $dfh;
+ if (open($dfh,">$dfilename")) {
+ print $dfh "$cmd\n";
+ close($dfh);
+ }
}
sleep 2;
my $wcmd='';
{
- my $dfh;
- if ($dfh=Apache::File->new("$dfilename")) {
- $wcmd=<$dfh>;
- }
+ my $dfh;
+ if (open($dfh,"<$dfilename")) {
+ $wcmd=<$dfh>;
+ close($dfh);
+ }
}
chomp($wcmd);
if ($wcmd eq $cmd) {
- &logthis("WARNING: ".
+ &logthis("WARNING: ".
"Connection buffer $dfilename: $cmd");
&logperm("D:$server:$cmd");
return 'con_delayed';
} else {
- &logthis("CRITICAL:"
+ &logthis("CRITICAL:"
." Critical connection failed: $server $cmd");
&logperm("F:$server:$cmd");
return 'con_failed';
@@ -232,181 +500,963 @@ sub critical {
return $answer;
}
-# ---------------------------------------------------------- Append Environment
+# ------------------------------------------- check if return value is an error
-sub appenv {
- my %newenv=@_;
- map {
- if (($newenv{$_}=~/^user\.role/) || ($newenv{$_}=~/^user\.priv/)) {
- &logthis("WARNING: ".
- "Attempt to modify environment ".$_." to ".$newenv{$_});
- delete($newenv{$_});
- } else {
- $ENV{$_}=$newenv{$_};
- }
- } keys %newenv;
+sub error {
+ my ($result) = @_;
+ if ($result =~ /^(con_lost|no_such_host|error: (\d+) (.*))/) {
+ if ($2 == 2) { return undef; }
+ return $1;
+ }
+ return undef;
+}
+
+sub convert_and_load_session_env {
+ my ($lonidsdir,$handle)=@_;
+ my @profile;
+ {
+ my $opened = open(my $idf,'+<',"$lonidsdir/$handle.id");
+ if (!$opened) {
+ return 0;
+ }
+ flock($idf,LOCK_SH);
+ @profile=<$idf>;
+ close($idf);
+ }
+ my %temp_env;
+ foreach my $line (@profile) {
+ if ($line !~ m/=/) {
+ return 0;
+ }
+ chomp($line);
+ my ($envname,$envvalue)=split(/=/,$line,2);
+ $temp_env{&unescape($envname)} = &unescape($envvalue);
+ }
+ unlink("$lonidsdir/$handle.id");
+ if (tie(my %disk_env,'GDBM_File',"$lonidsdir/$handle.id",&GDBM_WRCREAT(),
+ 0640)) {
+ %disk_env = %temp_env;
+ @env{keys(%temp_env)} = @disk_env{keys(%temp_env)};
+ untie(%disk_env);
+ }
+ return 1;
+}
+
+# ------------------------------------------- Transfer profile into environment
+my $env_loaded;
+sub transfer_profile_to_env {
+ my ($lonidsdir,$handle,$force_transfer) = @_;
+ if (!$force_transfer && $env_loaded) { return; }
- my $lockfh;
- unless ($lockfh=Apache::File->new("$ENV{'user.environment'}")) {
- return 'error: '.$!;
+ if (!defined($lonidsdir)) {
+ $lonidsdir = $perlvar{'lonIDsDir'};
}
- unless (flock($lockfh,LOCK_EX)) {
- &logthis("WARNING: ".
- 'Could not obtain exclusive lock in appenv: '.$!);
- $lockfh->close();
- return 'error: '.$!;
+ if (!defined($handle)) {
+ ($handle) = ($env{'user.environment'} =~m|/([^/]+)\.id$| );
}
- my @oldenv;
+ my $convert;
{
- my $fh;
- unless ($fh=Apache::File->new("$ENV{'user.environment'}")) {
- return 'error: '.$!;
- }
- @oldenv=<$fh>;
- $fh->close();
- }
- for (my $i=0; $i<=$#oldenv; $i++) {
- chomp($oldenv[$i]);
- if ($oldenv[$i] ne '') {
- my ($name,$value)=split(/=/,$oldenv[$i]);
- unless (defined($newenv{$name})) {
- $newenv{$name}=$value;
- }
+ my $opened = open(my $idf,'+<',"$lonidsdir/$handle.id");
+ if (!$opened) {
+ return;
+ }
+ flock($idf,LOCK_SH);
+ if (tie(my %disk_env,'GDBM_File',"$lonidsdir/$handle.id",
+ &GDBM_READER(),0640)) {
+ @env{keys(%disk_env)} = @disk_env{keys(%disk_env)};
+ untie(%disk_env);
+ } else {
+ $convert = 1;
+ }
+ }
+ if ($convert) {
+ if (!&convert_and_load_session_env($lonidsdir,$handle)) {
+ &logthis("Failed to load session, or convert session.");
+ }
+ }
+
+ my %remove;
+ while ( my $envname = each(%env) ) {
+ if (my ($key,$time) = ($envname =~ /^(cgi\.(\d+)_\d+\.)/)) {
+ if ($time < time-300) {
+ $remove{$key}++;
+ }
}
}
- {
- my $fh;
- unless ($fh=Apache::File->new(">$ENV{'user.environment'}")) {
- return 'error';
- }
- my $newname;
- foreach $newname (keys %newenv) {
- print $fh "$newname=$newenv{$newname}\n";
- }
- $fh->close();
+
+ $env{'user.environment'} = "$lonidsdir/$handle.id";
+ $env_loaded=1;
+ foreach my $expired_key (keys(%remove)) {
+ &delenv($expired_key);
}
+}
- $lockfh->close();
+# ---------------------------------------------------- Check for valid session
+sub check_for_valid_session {
+ my ($r) = @_;
+ my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
+ my $lonid=$cookies{'lonID'};
+ return undef if (!$lonid);
+
+ my $handle=&LONCAPA::clean_handle($lonid->value);
+ my $lonidsdir=$r->dir_config('lonIDsDir');
+ return undef if (!-e "$lonidsdir/$handle.id");
+
+ my $opened = open(my $idf,'+<',"$lonidsdir/$handle.id");
+ return undef if (!$opened);
+
+ flock($idf,LOCK_SH);
+ my %disk_env;
+ if (!tie(%disk_env,'GDBM_File',"$lonidsdir/$handle.id",
+ &GDBM_READER(),0640)) {
+ return undef;
+ }
+
+ if (!defined($disk_env{'user.name'})
+ || !defined($disk_env{'user.domain'})) {
+ return undef;
+ }
+ return $handle;
+}
+
+sub timed_flock {
+ my ($file,$lock_type) = @_;
+ my $failed=0;
+ eval {
+ local $SIG{__DIE__}='DEFAULT';
+ local $SIG{ALRM}=sub {
+ $failed=1;
+ die("failed lock");
+ };
+ alarm(13);
+ flock($file,$lock_type);
+ alarm(0);
+ };
+ if ($failed) {
+ return undef;
+ } else {
+ return 1;
+ }
+}
+
+# ---------------------------------------------------------- Append Environment
+
+sub appenv {
+ my ($newenv,$roles) = @_;
+ if (ref($newenv) eq 'HASH') {
+ foreach my $key (keys(%{$newenv})) {
+ my $refused = 0;
+ if (($key =~ /^user\.role/) || ($key =~ /^user\.priv/)) {
+ $refused = 1;
+ if (ref($roles) eq 'ARRAY') {
+ my ($type,$role) = ($key =~ /^user\.(role|priv)\.([^.]+)\./);
+ if (grep(/^\Q$role\E$/,@{$roles})) {
+ $refused = 0;
+ }
+ }
+ }
+ if ($refused) {
+ &logthis("WARNING: ".
+ "Attempt to modify environment ".$key." to ".$newenv->{$key}
+ .'');
+ delete($newenv->{$key});
+ } else {
+ $env{$key}=$newenv->{$key};
+ }
+ }
+ my $opened = open(my $env_file,'+<',$env{'user.environment'});
+ if ($opened
+ && &timed_flock($env_file,LOCK_EX)
+ &&
+ tie(my %disk_env,'GDBM_File',$env{'user.environment'},
+ (&GDBM_WRITER()|&GDBM_NOLOCK()),0640)) {
+ while (my ($key,$value) = each(%{$newenv})) {
+ $disk_env{$key} = $value;
+ }
+ untie(%disk_env);
+ }
+ }
return 'ok';
}
# ----------------------------------------------------- Delete from Environment
sub delenv {
- my $delthis=shift;
- my %newenv=();
- if (($delthis=~/user\.role/) || ($delthis=~/user\.priv/)) {
- &logthis("WARNING: ".
- "Attempt to delete from environment ".$delthis);
- return 'error';
+ my ($delthis,$regexp,$roles) = @_;
+ if (($delthis=~/^user\.role/) || ($delthis=~/^user\.priv/)) {
+ my $refused = 1;
+ if (ref($roles) eq 'ARRAY') {
+ my ($type,$role) = ($delthis =~ /^user\.(role|priv)\.([^.]+)\./);
+ if (grep(/^\Q$role\E$/,@{$roles})) {
+ $refused = 0;
+ }
+ }
+ if ($refused) {
+ &logthis("WARNING: ".
+ "Attempt to delete from environment ".$delthis);
+ return 'error';
+ }
}
- my @oldenv;
- {
- my $fh;
- unless ($fh=Apache::File->new("$ENV{'user.environment'}")) {
- return 'error';
- }
- unless (flock($fh,LOCK_SH)) {
- &logthis("WARNING: ".
- 'Could not obtain shared lock in delenv: '.$!);
- $fh->close();
- return 'error: '.$!;
- }
- @oldenv=<$fh>;
- $fh->close();
+ my $opened = open(my $env_file,'+<',$env{'user.environment'});
+ if ($opened
+ && &timed_flock($env_file,LOCK_EX)
+ &&
+ tie(my %disk_env,'GDBM_File',$env{'user.environment'},
+ (&GDBM_WRITER()|&GDBM_NOLOCK()),0640)) {
+ foreach my $key (keys(%disk_env)) {
+ if ($regexp) {
+ if ($key=~/^$delthis/) {
+ delete($env{$key});
+ delete($disk_env{$key});
+ }
+ } else {
+ if ($key=~/^\Q$delthis\E/) {
+ delete($env{$key});
+ delete($disk_env{$key});
+ }
+ }
+ }
+ untie(%disk_env);
+ }
+ return 'ok';
+}
+
+sub get_env_multiple {
+ my ($name) = @_;
+ my @values;
+ if (defined($env{$name})) {
+ # exists is it an array
+ if (ref($env{$name})) {
+ @values=@{ $env{$name} };
+ } else {
+ $values[0]=$env{$name};
+ }
}
+ return(@values);
+}
+
+# ------------------------------------------------------------------- Locking
+
+sub set_lock {
+ my ($text)=@_;
+ $locknum++;
+ my $id=$$.'-'.$locknum;
+ &appenv({'session.locks' => $env{'session.locks'}.','.$id,
+ 'session.lock.'.$id => $text});
+ return $id;
+}
+
+sub get_locks {
+ my $num=0;
+ my %texts=();
+ foreach my $lock (split(/\,/,$env{'session.locks'})) {
+ if ($lock=~/\w/) {
+ $num++;
+ $texts{$lock}=$env{'session.lock.'.$lock};
+ }
+ }
+ return ($num,%texts);
+}
+
+sub remove_lock {
+ my ($id)=@_;
+ my $newlocks='';
+ foreach my $lock (split(/\,/,$env{'session.locks'})) {
+ if (($lock=~/\w/) && ($lock ne $id)) {
+ $newlocks.=','.$lock;
+ }
+ }
+ &appenv({'session.locks' => $newlocks});
+ &delenv('session.lock.'.$id);
+}
+
+sub remove_all_locks {
+ my $activelocks=$env{'session.locks'};
+ foreach my $lock (split(/\,/,$env{'session.locks'})) {
+ if ($lock=~/\w/) {
+ &remove_lock($lock);
+ }
+ }
+}
+
+
+# ------------------------------------------ Find out current server userload
+sub userload {
+ my $numusers=0;
{
- my $fh;
- unless ($fh=Apache::File->new(">$ENV{'user.environment'}")) {
- return 'error';
- }
- unless (flock($fh,LOCK_EX)) {
- &logthis("WARNING: ".
- 'Could not obtain exclusive lock in delenv: '.$!);
- $fh->close();
- return 'error: '.$!;
- }
- map {
- unless ($_=~/^$delthis/) { print $fh $_; }
- } @oldenv;
- $fh->close();
+ opendir(LONIDS,$perlvar{'lonIDsDir'});
+ my $filename;
+ my $curtime=time;
+ while ($filename=readdir(LONIDS)) {
+ next if ($filename eq '.' || $filename eq '..');
+ next if ($filename =~ /publicuser_\d+\.id/);
+ my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9];
+ if ($curtime-$mtime < 1800) { $numusers++; }
+ }
+ closedir(LONIDS);
}
- return 'ok';
+ my $userloadpercent=0;
+ my $maxuserload=$perlvar{'lonUserLoadLim'};
+ if ($maxuserload) {
+ $userloadpercent=100*$numusers/$maxuserload;
+ }
+ $userloadpercent=sprintf("%.2f",$userloadpercent);
+ return $userloadpercent;
}
# ------------------------------ Find server with least workload from spare.tab
sub spareserver {
- my $tryserver;
- my $spareserver='';
- my $lowestserver=100;
- foreach $tryserver (keys %spareid) {
- my $answer=reply('load',$tryserver);
- if (($answer =~ /\d/) && ($answer<$lowestserver)) {
- $spareserver="http://$hostname{$tryserver}";
- $lowestserver=$answer;
- }
- }
- return $spareserver;
+ my ($loadpercent,$userloadpercent,$want_server_name,$udom) = @_;
+ my $spare_server;
+ if ($userloadpercent !~ /\d/) { $userloadpercent=0; }
+ my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent
+ : $userloadpercent;
+ my ($uint_dom,$remotesessions);
+ if (($udom ne '') && (&domain($udom) ne '')) {
+ my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
+ $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
+ my %udomdefaults = &Apache::lonnet::get_domain_defaults($udom);
+ $remotesessions = $udomdefaults{'remotesessions'};
+ }
+ 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);
+ }
+ }
+
+ 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) {
+ 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)) {
+ $spare_server = $protocol.'://'.$hostname;
+ }
+ }
+ }
+ return $spare_server;
}
-# --------- Try to authenticate user from domain's lib servers (first this one)
+sub compare_server_load {
+ my ($try_server, $spare_server, $lowest_load) = @_;
-sub authenticate {
- my ($uname,$upass,$udom)=@_;
- $upass=escape($upass);
- if (($perlvar{'lonRole'} eq 'library') &&
- ($udom eq $perlvar{'lonDefDomain'})) {
- my $answer=reply("encrypt:auth:$udom:$uname:$upass",$perlvar{'lonHostID'});
- if ($answer =~ /authorized/) {
- if ($answer eq 'authorized') {
- &logthis("User $uname at $udom authorized by local server");
- return $perlvar{'lonHostID'};
- }
- if ($answer eq 'non_authorized') {
- &logthis("User $uname at $udom rejected by local server");
- return 'no_host';
- }
+ my $loadans = &reply('load', $try_server);
+ my $userloadans = &reply('userload',$try_server);
+
+ if ($loadans !~ /\d/ && $userloadans !~ /\d/) {
+ return ($spare_server, $lowest_load); #didn't get a number from the server
+ }
+
+ my $load;
+ if ($loadans =~ /\d/) {
+ if ($userloadans =~ /\d/) {
+ #both are numbers, pick the bigger one
+ $load = ($loadans > $userloadans) ? $loadans
+ : $userloadans;
+ } else {
+ $load = $loadans;
}
+ } else {
+ $load = $userloadans;
}
- my $tryserver;
- foreach $tryserver (keys %libserv) {
- if ($hostdom{$tryserver} eq $udom) {
- my $answer=reply("encrypt:auth:$udom:$uname:$upass",$tryserver);
- if ($answer =~ /authorized/) {
- if ($answer eq 'authorized') {
- &logthis("User $uname at $udom authorized by $tryserver");
- return $tryserver;
- }
- if ($answer eq 'non_authorized') {
- &logthis("User $uname at $udom rejected by $tryserver");
- return 'no_host';
- }
- }
- }
+ if (($load =~ /\d/) && ($load < $lowest_load)) {
+ $spare_server = $try_server;
+ $lowest_load = $load;
+ }
+ return ($spare_server,$lowest_load);
+}
+
+# --------------------------- ask offload servers if user already has a session
+sub find_existing_session {
+ my ($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;
+}
+
+# -------------------------------- ask if server already has a session for user
+sub has_user_session {
+ my ($lonid,$udom,$uname) = @_;
+ my $result = &reply(join(':','userhassession',
+ map {&escape($_)} ($udom,$uname)),$lonid);
+ return 1 if ($result eq 'ok');
+
+ return 0;
+}
+
+# --------- determine least loaded server in a user's domain which allows login
+
+sub choose_server {
+ my ($udom,$checkloginvia) = @_;
+ my %domconfhash = &Apache::loncommon::get_domainconf($udom);
+ my %servers = &get_servers($udom);
+ my $lowest_load = 30000;
+ my ($login_host,$hostname,$portal_path);
+ foreach my $lonhost (keys(%servers)) {
+ my $loginvia;
+ if ($checkloginvia) {
+ $loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost};
+ if ($loginvia) {
+ my ($server,$path) = split(/:/,$loginvia);
+ ($login_host, $lowest_load) =
+ &compare_server_load($server, $login_host, $lowest_load);
+ if ($login_host eq $server) {
+ $portal_path = $path;
+ }
+ } else {
+ ($login_host, $lowest_load) =
+ &compare_server_load($lonhost, $login_host, $lowest_load);
+ if ($login_host eq $lonhost) {
+ $portal_path = '';
+ }
+ }
+ } else {
+ ($login_host, $lowest_load) =
+ &compare_server_load($lonhost, $login_host, $lowest_load);
+ }
+ }
+ if ($login_host ne '') {
+ $hostname = &hostname($login_host);
}
- &logthis("User $uname at $udom could not be authenticated");
+ return ($login_host,$hostname,$portal_path);
+}
+
+# --------------------------------------------- Try to change a user's password
+
+sub changepass {
+ my ($uname,$udom,$currentpass,$newpass,$server,$context)=@_;
+ $currentpass = &escape($currentpass);
+ $newpass = &escape($newpass);
+ my $lonhost = $perlvar{'lonHostID'};
+ my $answer = reply("encrypt:passwd:$udom:$uname:$currentpass:$newpass:$context:$lonhost",
+ $server);
+ if (! $answer) {
+ &logthis("No reply on password change request to $server ".
+ "by $uname in domain $udom.");
+ } elsif ($answer =~ "^ok") {
+ &logthis("$uname in $udom successfully changed their password ".
+ "on $server.");
+ } elsif ($answer =~ "^pwchange_failure") {
+ &logthis("$uname in $udom was unable to change their password ".
+ "on $server. The action was blocked by either lcpasswd ".
+ "or pwchange");
+ } elsif ($answer =~ "^non_authorized") {
+ &logthis("$uname in $udom did not get their password correct when ".
+ "attempting to change it on $server.");
+ } elsif ($answer =~ "^auth_mode_error") {
+ &logthis("$uname in $udom attempted to change their password despite ".
+ "not being locally or internally authenticated on $server.");
+ } elsif ($answer =~ "^unknown_user") {
+ &logthis("$uname in $udom attempted to change their password ".
+ "on $server but were unable to because $server is not ".
+ "their home server.");
+ } elsif ($answer =~ "^refused") {
+ &logthis("$server refused to change $uname in $udom password because ".
+ "it was sent an unencrypted request to change the password.");
+ } 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.");
+ }
+ return $answer;
+}
+
+# ----------------------- Try to determine user's current authentication scheme
+
+sub queryauthenticate {
+ my ($uname,$udom)=@_;
+ my $uhome=&homeserver($uname,$udom);
+ if (!$uhome) {
+ &logthis("User $uname at $udom is unknown when looking for authentication mechanism");
+ return 'no_host';
+ }
+ my $answer=reply("encrypt:currentauth:$udom:$uname",$uhome);
+ if ($answer =~ /^(unknown_user|refused|con_lost)/) {
+ &logthis("User $uname at $udom threw error $answer when checking authentication mechanism");
+ }
+ return $answer;
+}
+
+# --------- Try to authenticate user from domain's lib servers (first this one)
+
+sub authenticate {
+ my ($uname,$upass,$udom,$checkdefauth,$clientcancheckhost)=@_;
+ $upass=&escape($upass);
+ $uname= &LONCAPA::clean_username($uname);
+ my $uhome=&homeserver($uname,$udom,1);
+ my $newhome;
+ if ((!$uhome) || ($uhome eq 'no_host')) {
+# Maybe the machine was offline and only re-appeared again recently?
+ &reconlonc();
+# One more
+ $uhome=&homeserver($uname,$udom,1);
+ if (($uhome eq 'no_host') && $checkdefauth) {
+ if (defined(&domain($udom,'primary'))) {
+ $newhome=&domain($udom,'primary');
+ }
+ if ($newhome ne '') {
+ $uhome = $newhome;
+ }
+ }
+ if ((!$uhome) || ($uhome eq 'no_host')) {
+ &logthis("User $uname at $udom is unknown in authenticate");
+ return 'no_host';
+ }
+ }
+ my $answer=reply("encrypt:auth:$udom:$uname:$upass:$checkdefauth:$clientcancheckhost",$uhome);
+ if ($answer eq 'authorized') {
+ if ($newhome) {
+ &logthis("User $uname at $udom authorized by $uhome, but needs account");
+ return 'no_account_on_host';
+ } else {
+ &logthis("User $uname at $udom authorized by $uhome");
+ return $uhome;
+ }
+ }
+ if ($answer eq 'non_authorized') {
+ &logthis("User $uname at $udom rejected by $uhome");
+ return 'no_host';
+ }
+ &logthis("User $uname at $udom threw error $answer when checking authentication mechanism");
return 'no_host';
}
+sub can_host_session {
+ my ($udom,$lonhost,$remoterev,$remotesessions,$hostedsessions) = @_;
+ my $canhost = 1;
+ my $host_idn = &Apache::lonnet::internet_dom($lonhost);
+ if (ref($remotesessions) eq 'HASH') {
+ if (ref($remotesessions->{'excludedomain'}) eq 'ARRAY') {
+ if (grep(/^\Q$host_idn\E$/,@{$remotesessions->{'excludedomain'}})) {
+ $canhost = 0;
+ } else {
+ $canhost = 1;
+ }
+ }
+ if (ref($remotesessions->{'includedomain'}) eq 'ARRAY') {
+ if (grep(/^\Q$host_idn\E$/,@{$remotesessions->{'includedomain'}})) {
+ $canhost = 1;
+ } else {
+ $canhost = 0;
+ }
+ }
+ if ($canhost) {
+ if ($remotesessions->{'version'} ne '') {
+ my ($reqmajor,$reqminor) = ($remotesessions->{'version'} =~ /^(\d+)\.(\d+)$/);
+ if ($reqmajor ne '' && $reqminor ne '') {
+ if ($remoterev =~ /^\'?(\d+)\.(\d+)/) {
+ my $major = $1;
+ my $minor = $2;
+ if (($major < $reqmajor ) ||
+ (($major == $reqmajor) && ($minor < $reqminor))) {
+ $canhost = 0;
+ }
+ } else {
+ $canhost = 0;
+ }
+ }
+ }
+ }
+ }
+ if ($canhost) {
+ if (ref($hostedsessions) eq 'HASH') {
+ my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
+ my $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
+ if (ref($hostedsessions->{'excludedomain'}) eq 'ARRAY') {
+ if (($uint_dom ne '') &&
+ (grep(/^\Q$uint_dom\E$/,@{$hostedsessions->{'excludedomain'}}))) {
+ $canhost = 0;
+ } else {
+ $canhost = 1;
+ }
+ }
+ if (ref($hostedsessions->{'includedomain'}) eq 'ARRAY') {
+ if (($uint_dom ne '') &&
+ (grep(/^\Q$uint_dom\E$/,@{$hostedsessions->{'includedomain'}}))) {
+ $canhost = 1;
+ } else {
+ $canhost = 0;
+ }
+ }
+ }
+ }
+ return $canhost;
+}
+
+sub spare_can_host {
+ my ($udom,$uint_dom,$remotesessions,$try_server)=@_;
+ my $canhost=1;
+ my @intdoms;
+ my $internet_names = &Apache::lonnet::get_internet_names($try_server);
+ if (ref($internet_names) eq 'ARRAY') {
+ @intdoms = @{$internet_names};
+ }
+ unless (grep(/^\Q$uint_dom\E$/,@intdoms)) {
+ my $serverhomeID = &Apache::lonnet::get_server_homeID($try_server);
+ my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
+ my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
+ my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$try_server);
+ $canhost = &can_host_session($udom,$try_server,$remoterev,
+ $remotesessions,
+ $defdomdefaults{'hostedsessions'});
+ }
+ 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;
sub homeserver {
- my ($uname,$udom)=@_;
-
+ my ($uname,$udom,$ignoreBadCache)=@_;
my $index="$uname:$udom";
- if ($homecache{$index}) { return "$homecache{$index}"; }
- my $tryserver;
- foreach $tryserver (keys %libserv) {
- if ($hostdom{$tryserver} eq $udom) {
- my $answer=reply("home:$udom:$uname",$tryserver);
- if ($answer eq 'found') {
- $homecache{$index}=$tryserver;
- return $tryserver;
- }
- }
+ if (exists($homecache{$index})) { return $homecache{$index}; }
+
+ my %servers = &get_servers($udom,'library');
+ foreach my $tryserver (keys(%servers)) {
+ next if ($ignoreBadCache ne 'true' &&
+ exists($badServerCache{$tryserver}));
+
+ my $answer=reply("home:$udom:$uname",$tryserver);
+ if ($answer eq 'found') {
+ delete($badServerCache{$tryserver});
+ return $homecache{$index}=$tryserver;
+ } elsif ($answer eq 'no_host') {
+ $badServerCache{$tryserver}=1;
+ }
}
return 'no_host';
}
@@ -417,24 +1467,22 @@ sub idget {
my ($udom,@ids)=@_;
my %returnhash=();
- my $tryserver;
- foreach $tryserver (keys %libserv) {
- if ($hostdom{$tryserver} eq $udom) {
- my $idlist=join('&',@ids);
- $idlist=~tr/A-Z/a-z/;
- my $reply=&reply("idget:$udom:".$idlist,$tryserver);
- my @answer=();
- if (($reply ne 'con_lost') && ($reply!~/^error\:/)) {
- @answer=split(/\&/,$reply);
- } ;
- my $i;
- for ($i=0;$i<=$#ids;$i++) {
- if ($answer[$i]) {
- $returnhash{$ids[$i]}=$answer[$i];
- }
- }
- }
- }
+ my %servers = &get_servers($udom,'library');
+ foreach my $tryserver (keys(%servers)) {
+ my $idlist=join('&',@ids);
+ $idlist=~tr/A-Z/a-z/;
+ my $reply=&reply("idget:$udom:".$idlist,$tryserver);
+ my @answer=();
+ if (($reply ne 'con_lost') && ($reply!~/^error\:/)) {
+ @answer=split(/\&/,$reply);
+ } ;
+ my $i;
+ for ($i=0;$i<=$#ids;$i++) {
+ if ($answer[$i]) {
+ $returnhash{$ids[$i]}=$answer[$i];
+ }
+ }
+ }
return %returnhash;
}
@@ -443,9 +1491,9 @@ sub idget {
sub idrget {
my ($udom,@unames)=@_;
my %returnhash=();
- map {
- $returnhash{$_}=(&userenvironment($udom,$_,'id'))[1];
- } @unames;
+ foreach my $uname (@unames) {
+ $returnhash{$uname}=(&userenvironment($udom,$uname,'id'))[1];
+ }
return %returnhash;
}
@@ -454,78 +1502,919 @@ sub idrget {
sub idput {
my ($udom,%ids)=@_;
my %servers=();
- map {
- my $uhom=&homeserver($_,$udom);
+ foreach my $uname (keys(%ids)) {
+ &cput('environment',{'id'=>$ids{$uname}},$udom,$uname);
+ my $uhom=&homeserver($uname,$udom);
if ($uhom ne 'no_host') {
- my $id=&escape($ids{$_});
+ my $id=&escape($ids{$uname});
$id=~tr/A-Z/a-z/;
- my $unam=&escape($_);
+ my $esc_unam=&escape($uname);
if ($servers{$uhom}) {
- $servers{$uhom}.='&'.$id.'='.$unam;
+ $servers{$uhom}.='&'.$id.'='.$esc_unam;
} else {
- $servers{$uhom}=$id.'='.$unam;
+ $servers{$uhom}=$id.'='.$esc_unam;
}
- &critical('put:'.$udom.':'.$unam.':environment:id='.$id,$uhom);
}
- } keys %ids;
- map {
- &critical('idput:'.$udom.':'.$servers{$_},$_);
- } keys %servers;
+ }
+ foreach my $server (keys(%servers)) {
+ &critical('idput:'.$udom.':'.$servers{$server},$server);
+ }
+}
+
+# ------------------------------dump from db file owned by domainconfig user
+sub dump_dom {
+ my ($namespace,$udom,$regexp,$range)=@_;
+ if (!$udom) {
+ $udom=$env{'user.domain'};
+ }
+ my %returnhash;
+ if ($udom) {
+ my $uname = &get_domainconfiguser($udom);
+ %returnhash = &dump($namespace,$udom,$uname,$regexp,$range);
+ }
+ return %returnhash;
+}
+
+# ------------------------------------------ get items from domain db files
+
+sub get_dom {
+ my ($namespace,$storearr,$udom,$uhome)=@_;
+ my $items='';
+ foreach my $item (@$storearr) {
+ $items.=&escape($item).'&';
+ }
+ $items=~s/\&$//;
+ if (!$udom) {
+ $udom=$env{'user.domain'};
+ if (defined(&domain($udom,'primary'))) {
+ $uhome=&domain($udom,'primary');
+ } else {
+ undef($uhome);
+ }
+ } else {
+ if (!$uhome) {
+ if (defined(&domain($udom,'primary'))) {
+ $uhome=&domain($udom,'primary');
+ }
+ }
+ }
+ if ($udom && $uhome && ($uhome ne 'no_host')) {
+ my $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
+ my %returnhash;
+ if ($rep eq '' || $rep =~ /^error: 2 /) {
+ return %returnhash;
+ }
+ my @pairs=split(/\&/,$rep);
+ if ( $#pairs==0 && $pairs[0] =~ /^(con_lost|error|no_such_host)/i) {
+ return @pairs;
+ }
+ my $i=0;
+ foreach my $item (@$storearr) {
+ $returnhash{$item}=&thaw_unescape($pairs[$i]);
+ $i++;
+ }
+ return %returnhash;
+ } else {
+ &logthis("get_dom failed - no homeserver and/or domain ($udom) ($uhome)");
+ }
+}
+
+# -------------------------------------------- put items in domain db files
+
+sub put_dom {
+ my ($namespace,$storehash,$udom,$uhome)=@_;
+ if (!$udom) {
+ $udom=$env{'user.domain'};
+ if (defined(&domain($udom,'primary'))) {
+ $uhome=&domain($udom,'primary');
+ } else {
+ undef($uhome);
+ }
+ } else {
+ if (!$uhome) {
+ if (defined(&domain($udom,'primary'))) {
+ $uhome=&domain($udom,'primary');
+ }
+ }
+ }
+ if ($udom && $uhome && ($uhome ne 'no_host')) {
+ my $items='';
+ foreach my $item (keys(%$storehash)) {
+ $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&';
+ }
+ $items=~s/\&$//;
+ return &reply("putdom:$udom:$namespace:$items",$uhome);
+ } else {
+ &logthis("put_dom failed - no homeserver and/or domain");
+ }
+}
+
+# --------------------- newput for items in db file owned by domainconfig user
+sub newput_dom {
+ my ($namespace,$storehash,$udom) = @_;
+ my $result;
+ if (!$udom) {
+ $udom=$env{'user.domain'};
+ }
+ if ($udom) {
+ my $uname = &get_domainconfiguser($udom);
+ $result = &newput($namespace,$storehash,$udom,$uname);
+ }
+ return $result;
+}
+
+# --------------------- delete for items in db file owned by domainconfig user
+sub del_dom {
+ my ($namespace,$storearr,$udom)=@_;
+ if (ref($storearr) eq 'ARRAY') {
+ if (!$udom) {
+ $udom=$env{'user.domain'};
+ }
+ if ($udom) {
+ my $uname = &get_domainconfiguser($udom);
+ return &del($namespace,$storearr,$udom,$uname);
+ }
+ }
+}
+
+# ----------------------------------construct domainconfig user for a domain
+sub get_domainconfiguser {
+ my ($udom) = @_;
+ return $udom.'-domainconfig';
+}
+
+sub retrieve_inst_usertypes {
+ my ($udom) = @_;
+ my (%returnhash,@order);
+ my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
+ if ((ref($domdefs{'inststatustypes'}) eq 'HASH') &&
+ (ref($domdefs{'inststatusorder'}) eq 'ARRAY')) {
+ %returnhash = %{$domdefs{'inststatustypes'}};
+ @order = @{$domdefs{'inststatusorder'}};
+ } else {
+ if (defined(&domain($udom,'primary'))) {
+ my $uhome=&domain($udom,'primary');
+ my $rep=&reply("inst_usertypes:$udom",$uhome);
+ if ($rep =~ /^(con_lost|error|no_such_host|refused)/) {
+ &logthis("get_dom failed - $rep returned from $uhome in domain: $udom");
+ return (\%returnhash,\@order);
+ }
+ my ($hashitems,$orderitems) = split(/:/,$rep);
+ my @pairs=split(/\&/,$hashitems);
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/=/,$item,2);
+ $key = &unescape($key);
+ next if ($key =~ /^error: 2 /);
+ $returnhash{$key}=&thaw_unescape($value);
+ }
+ my @esc_order = split(/\&/,$orderitems);
+ foreach my $item (@esc_order) {
+ push(@order,&unescape($item));
+ }
+ } else {
+ &logthis("get_dom failed - no primary domain server for $udom");
+ }
+ }
+ return (\%returnhash,\@order);
+}
+
+sub is_domainimage {
+ my ($url) = @_;
+ if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo)/+-) {
+ if (&domain($1) ne '') {
+ return '1';
+ }
+ }
+ return;
+}
+
+sub inst_directory_query {
+ my ($srch) = @_;
+ my $udom = $srch->{'srchdomain'};
+ my %results;
+ my $homeserver = &domain($udom,'primary');
+ my $outcome;
+ if ($homeserver ne '') {
+ my $queryid=&reply("querysend:instdirsearch:".
+ &escape($srch->{'srchby'}).':'.
+ &escape($srch->{'srchterm'}).':'.
+ &escape($srch->{'srchtype'}),$homeserver);
+ my $host=&hostname($homeserver);
+ if ($queryid !~/^\Q$host\E\_/) {
+ &logthis('instituional directory search invalid queryid: '.$queryid.' for host: '.$homeserver.'in domain '.$udom);
+ return;
+ }
+ my $response = &get_query_reply($queryid);
+ my $maxtries = 5;
+ my $tries = 1;
+ while (($response=~/^timeout/) && ($tries < $maxtries)) {
+ $response = &get_query_reply($queryid);
+ $tries ++;
+ }
+
+ if (!&error($response) && $response ne 'refused') {
+ if ($response eq 'unavailable') {
+ $outcome = $response;
+ } else {
+ $outcome = 'ok';
+ my @matches = split(/\n/,$response);
+ foreach my $match (@matches) {
+ my ($key,$value) = split(/=/,$match);
+ $results{&unescape($key).':'.$udom} = &thaw_unescape($value);
+ }
+ }
+ }
+ }
+ return ($outcome,%results);
+}
+
+sub usersearch {
+ my ($srch) = @_;
+ my $dom = $srch->{'srchdomain'};
+ my %results;
+ my %libserv = &all_library();
+ my $query = 'usersearch';
+ foreach my $tryserver (keys(%libserv)) {
+ if (&host_domain($tryserver) eq $dom) {
+ my $host=&hostname($tryserver);
+ my $queryid=
+ &reply("querysend:".&escape($query).':'.
+ &escape($srch->{'srchby'}).':'.
+ &escape($srch->{'srchtype'}).':'.
+ &escape($srch->{'srchterm'}),$tryserver);
+ if ($queryid !~/^\Q$host\E\_/) {
+ &logthis('usersearch: invalid queryid: '.$queryid.' for host: '.$host.'in domain '.$dom.' and server: '.$tryserver);
+ next;
+ }
+ my $reply = &get_query_reply($queryid);
+ my $maxtries = 1;
+ my $tries = 1;
+ while (($reply=~/^timeout/) && ($tries < $maxtries)) {
+ $reply = &get_query_reply($queryid);
+ $tries ++;
+ }
+ if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) {
+ &logthis('usersrch error: '.$reply.' for '.$dom.' - searching for : '.$srch->{'srchterm'}.' by '.$srch->{'srchby'}.' ('.$srch->{'srchtype'}.') - maxtries: '.$maxtries.' tries: '.$tries);
+ } else {
+ my @matches;
+ if ($reply =~ /\n/) {
+ @matches = split(/\n/,$reply);
+ } else {
+ @matches = split(/\&/,$reply);
+ }
+ foreach my $match (@matches) {
+ my ($uname,$udom,%userhash);
+ foreach my $entry (split(/:/,$match)) {
+ my ($key,$value) =
+ map {&unescape($_);} split(/=/,$entry);
+ $userhash{$key} = $value;
+ if ($key eq 'username') {
+ $uname = $value;
+ } elsif ($key eq 'domain') {
+ $udom = $value;
+ }
+ }
+ $results{$uname.':'.$udom} = \%userhash;
+ }
+ }
+ }
+ }
+ return %results;
+}
+
+sub get_instuser {
+ my ($udom,$uname,$id) = @_;
+ my $homeserver = &domain($udom,'primary');
+ my ($outcome,%results);
+ if ($homeserver ne '') {
+ my $queryid=&reply("querysend:getinstuser:".&escape($uname).':'.
+ &escape($id).':'.&escape($udom),$homeserver);
+ my $host=&hostname($homeserver);
+ if ($queryid !~/^\Q$host\E\_/) {
+ &logthis('get_instuser invalid queryid: '.$queryid.' for host: '.$homeserver.'in domain '.$udom);
+ return;
+ }
+ my $response = &get_query_reply($queryid);
+ my $maxtries = 5;
+ my $tries = 1;
+ while (($response=~/^timeout/) && ($tries < $maxtries)) {
+ $response = &get_query_reply($queryid);
+ $tries ++;
+ }
+ if (!&error($response) && $response ne 'refused') {
+ if ($response eq 'unavailable') {
+ $outcome = $response;
+ } else {
+ $outcome = 'ok';
+ my @matches = split(/\n/,$response);
+ foreach my $match (@matches) {
+ my ($key,$value) = split(/=/,$match);
+ $results{&unescape($key)} = &thaw_unescape($value);
+ }
+ }
+ }
+ }
+ my %userinfo;
+ if (ref($results{$uname}) eq 'HASH') {
+ %userinfo = %{$results{$uname}};
+ }
+ return ($outcome,%userinfo);
+}
+
+sub inst_rulecheck {
+ my ($udom,$uname,$id,$item,$rules) = @_;
+ my %returnhash;
+ if ($udom ne '') {
+ if (ref($rules) eq 'ARRAY') {
+ @{$rules} = map {&escape($_);} (@{$rules});
+ my $rulestr = join(':',@{$rules});
+ my $homeserver=&domain($udom,'primary');
+ if (($homeserver ne '') && ($homeserver ne 'no_host')) {
+ my $response;
+ if ($item eq 'username') {
+ $response=&unescape(&reply('instrulecheck:'.&escape($udom).
+ ':'.&escape($uname).':'.$rulestr,
+ $homeserver));
+ } elsif ($item eq 'id') {
+ $response=&unescape(&reply('instidrulecheck:'.&escape($udom).
+ ':'.&escape($id).':'.$rulestr,
+ $homeserver));
+ } elsif ($item eq 'selfcreate') {
+ $response=&unescape(&reply('instselfcreatecheck:'.
+ &escape($udom).':'.&escape($uname).
+ ':'.$rulestr,$homeserver));
+ }
+ if ($response ne 'refused') {
+ my @pairs=split(/\&/,$response);
+ 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;
+}
+
+sub inst_userrules {
+ my ($udom,$check) = @_;
+ my (%ruleshash,@ruleorder);
+ if ($udom ne '') {
+ my $homeserver=&domain($udom,'primary');
+ if (($homeserver ne '') && ($homeserver ne 'no_host')) {
+ my $response;
+ if ($check eq 'id') {
+ $response=&reply('instidrules:'.&escape($udom),
+ $homeserver);
+ } elsif ($check eq 'email') {
+ $response=&reply('instemailrules:'.&escape($udom),
+ $homeserver);
+ } else {
+ $response=&reply('instuserrules:'.&escape($udom),
+ $homeserver);
+ }
+ if (($response ne 'refused') && ($response ne 'error') &&
+ ($response ne 'unknown_cmd') &&
+ ($response ne 'no_such_host')) {
+ my ($hashitems,$orderitems) = split(/:/,$response);
+ my @pairs=split(/\&/,$hashitems);
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/=/,$item,2);
+ $key = &unescape($key);
+ next if ($key =~ /^error: 2 /);
+ $ruleshash{$key}=&thaw_unescape($value);
+ }
+ my @esc_order = split(/\&/,$orderitems);
+ foreach my $item (@esc_order) {
+ push(@ruleorder,&unescape($item));
+ }
+ }
+ }
+ }
+ return (\%ruleshash,\@ruleorder);
+}
+
+# ------------- Get Authentication, Language and User Tools Defaults for Domain
+
+sub get_domain_defaults {
+ my ($domain) = @_;
+ my $cachetime = 60*60*24;
+ my ($result,$cached)=&is_cached_new('domdefaults',$domain);
+ if (defined($cached)) {
+ if (ref($result) eq 'HASH') {
+ return %{$result};
+ }
+ }
+ my %domdefaults;
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['defaults','quotas',
+ 'requestcourses','inststatus',
+ 'coursedefaults','usersessions'],$domain);
+ if (ref($domconfig{'defaults'}) eq 'HASH') {
+ $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'};
+ $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'};
+ $domdefaults{'auth_arg_def'} = $domconfig{'defaults'}{'auth_arg_def'};
+ $domdefaults{'timezone_def'} = $domconfig{'defaults'}{'timezone_def'};
+ $domdefaults{'datelocale_def'} = $domconfig{'defaults'}{'datelocale_def'};
+ } else {
+ $domdefaults{'lang_def'} = &domain($domain,'lang_def');
+ $domdefaults{'auth_def'} = &domain($domain,'auth_def');
+ $domdefaults{'auth_arg_def'} = &domain($domain,'auth_arg_def');
+ }
+ if (ref($domconfig{'quotas'}) eq 'HASH') {
+ if (ref($domconfig{'quotas'}{'defaultquota'}) eq 'HASH') {
+ $domdefaults{'defaultquota'} = $domconfig{'quotas'}{'defaultquota'};
+ } else {
+ $domdefaults{'defaultquota'} = $domconfig{'quotas'};
+ }
+ my @usertools = ('aboutme','blog','portfolio');
+ foreach my $item (@usertools) {
+ if (ref($domconfig{'quotas'}{$item}) eq 'HASH') {
+ $domdefaults{$item} = $domconfig{'quotas'}{$item};
+ }
+ }
+ }
+ if (ref($domconfig{'requestcourses'}) eq 'HASH') {
+ foreach my $item ('official','unofficial','community') {
+ $domdefaults{$item} = $domconfig{'requestcourses'}{$item};
+ }
+ }
+ if (ref($domconfig{'inststatus'}) eq 'HASH') {
+ foreach my $item ('inststatustypes','inststatusorder') {
+ $domdefaults{$item} = $domconfig{'inststatus'}{$item};
+ }
+ }
+ if (ref($domconfig{'coursedefaults'}) eq 'HASH') {
+ foreach my $item ('canuse_pdfforms') {
+ $domdefaults{$item} = $domconfig{'coursedefaults'}{$item};
+ }
+ }
+ if (ref($domconfig{'usersessions'}) eq 'HASH') {
+ if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {
+ $domdefaults{'remotesessions'} = $domconfig{'usersessions'}{'remote'};
+ }
+ if (ref($domconfig{'usersessions'}{'hosted'}) eq 'HASH') {
+ $domdefaults{'hostedsessions'} = $domconfig{'usersessions'}{'hosted'};
+ }
+ }
+ &Apache::lonnet::do_cache_new('domdefaults',$domain,\%domdefaults,
+ $cachetime);
+ return %domdefaults;
+}
+
+# --------------------------------------------------- Assign a key to a student
+
+sub assign_access_key {
+#
+# a valid key looks like uname:udom#comments
+# comments are being appended
+#
+ my ($ckey,$kdom,$knum,$cdom,$cnum,$udom,$uname,$logentry)=@_;
+ $kdom=
+ $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($kdom));
+ $knum=
+ $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($knum));
+ $cdom=
+ $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom));
+ $cnum=
+ $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum));
+ $udom=$env{'user.name'} unless (defined($udom));
+ $uname=$env{'user.domain'} unless (defined($uname));
+ my %existing=&get('accesskeys',[$ckey],$kdom,$knum);
+ if (($existing{$ckey}=~/^\#(.*)$/) || # - new key
+ ($existing{$ckey}=~/^\Q$uname\E\:\Q$udom\E\#(.*)$/)) {
+ # assigned to this person
+ # - this should not happen,
+ # unless something went wrong
+ # the first time around
+# ready to assign
+ $logentry=$1.'; '.$logentry;
+ if (&put('accesskeys',{$ckey=>$uname.':'.$udom.'#'.$logentry},
+ $kdom,$knum) eq 'ok') {
+# key now belongs to user
+ my $envkey='key.'.$cdom.'_'.$cnum;
+ if (&put('environment',{$envkey => $ckey}) eq 'ok') {
+ &appenv({'environment.'.$envkey => $ckey});
+ return 'ok';
+ } else {
+ return
+ 'error: Count not permanently assign key, will need to be re-entered later.';
+ }
+ } else {
+ return 'error: Could not assign key, try again later.';
+ }
+ } elsif (!$existing{$ckey}) {
+# the key does not exist
+ return 'error: The key does not exist';
+ } else {
+# the key is somebody else's
+ return 'error: The key is already in use';
+ }
+}
+
+# ------------------------------------------ put an additional comment on a key
+
+sub comment_access_key {
+#
+# a valid key looks like uname:udom#comments
+# comments are being appended
+#
+ my ($ckey,$cdom,$cnum,$logentry)=@_;
+ $cdom=
+ $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom));
+ $cnum=
+ $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum));
+ my %existing=&get('accesskeys',[$ckey],$cdom,$cnum);
+ if ($existing{$ckey}) {
+ $existing{$ckey}.='; '.$logentry;
+# ready to assign
+ if (&put('accesskeys',{$ckey=>$existing{$ckey}},
+ $cdom,$cnum) eq 'ok') {
+ return 'ok';
+ } else {
+ return 'error: Count not store comment.';
+ }
+ } else {
+# the key does not exist
+ return 'error: The key does not exist';
+ }
+}
+
+# ------------------------------------------------------ Generate a set of keys
+
+sub generate_access_keys {
+ my ($number,$cdom,$cnum,$logentry)=@_;
+ $cdom=
+ $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom));
+ $cnum=
+ $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum));
+ unless (&allowed('mky',$cdom)) { return 0; }
+ unless (($cdom) && ($cnum)) { return 0; }
+ if ($number>10000) { return 0; }
+ sleep(2); # make sure don't get same seed twice
+ srand(time()^($$+($$<<15))); # from "Programming Perl"
+ my $total=0;
+ for (my $i=1;$i<=$number;$i++) {
+ my $newkey=sprintf("%lx",int(100000*rand)).'-'.
+ sprintf("%lx",int(100000*rand)).'-'.
+ sprintf("%lx",int(100000*rand));
+ $newkey=~s/1/g/g; # folks mix up 1 and l
+ $newkey=~s/0/h/g; # and also 0 and O
+ my %existing=&get('accesskeys',[$newkey],$cdom,$cnum);
+ if ($existing{$newkey}) {
+ $i--;
+ } else {
+ if (&put('accesskeys',
+ { $newkey => '# generated '.localtime().
+ ' by '.$env{'user.name'}.'@'.$env{'user.domain'}.
+ '; '.$logentry },
+ $cdom,$cnum) eq 'ok') {
+ $total++;
+ }
+ }
+ }
+ &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},
+ 'Generated '.$total.' keys for '.$cnum.' at '.$cdom);
+ return $total;
+}
+
+# ------------------------------------------------------- Validate an accesskey
+
+sub validate_access_key {
+ my ($ckey,$cdom,$cnum,$udom,$uname)=@_;
+ $cdom=
+ $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom));
+ $cnum=
+ $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum));
+ $udom=$env{'user.domain'} unless (defined($udom));
+ $uname=$env{'user.name'} unless (defined($uname));
+ my %existing=&get('accesskeys',[$ckey],$cdom,$cnum);
+ return ($existing{$ckey}=~/^\Q$uname\E\:\Q$udom\E\#/);
}
# ------------------------------------- Find the section of student in a course
+sub devalidate_getsection_cache {
+ my ($udom,$unam,$courseid)=@_;
+ my $hashid="$udom:$unam:$courseid";
+ &devalidate_cache_new('getsection',$hashid);
+}
+
+sub courseid_to_courseurl {
+ my ($courseid) = @_;
+ #already url style courseid
+ return $courseid if ($courseid =~ m{^/});
+
+ if (exists($env{'course.'.$courseid.'.num'})) {
+ my $cnum = $env{'course.'.$courseid.'.num'};
+ my $cdom = $env{'course.'.$courseid.'.domain'};
+ return "/$cdom/$cnum";
+ }
+
+ my %courseinfo=&Apache::lonnet::coursedescription($courseid);
+ if (exists($courseinfo{'num'})) {
+ return "/$courseinfo{'domain'}/$courseinfo{'num'}";
+ }
+
+ return undef;
+}
-sub usection {
+sub getsection {
my ($udom,$unam,$courseid)=@_;
- $courseid=~s/\_/\//g;
- $courseid=~s/^(\w)/\/$1/;
- map {
- my ($key,$value)=split(/\=/,$_);
- $key=&unescape($key);
- if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) {
- my $section=$1;
- if ($key eq $courseid.'_st') { $section=''; }
- my ($dummy,$end,$start)=split(/\_/,&unescape($value));
- my $now=time;
- my $notactive=0;
- if ($start) {
- if ($now<$start) { $notactive=1; }
- }
- if ($end) {
- if ($now>$end) { $notactive=1; }
- }
- unless ($notactive) { return $section; }
+ my $cachetime=1800;
+
+ my $hashid="$udom:$unam:$courseid";
+ my ($result,$cached)=&is_cached_new('getsection',$hashid);
+ if (defined($cached)) { return $result; }
+
+ my %Pending;
+ my %Expired;
+ #
+ # Each role can either have not started yet (pending), be active,
+ # or have expired.
+ #
+ # If there is an active role, we are done.
+ #
+ # If there is more than one role which has not started yet,
+ # choose the one which will start sooner
+ # If there is one role which has not started yet, return it.
+ #
+ # If there is more than one expired role, choose the one which ended last.
+ # If there is a role which has expired, return it.
+ #
+ $courseid = &courseid_to_courseurl($courseid);
+ my $extra = &freeze_escape({'skipcheck' => 1});
+ my %roleshash = &dump('roles',$udom,$unam,$courseid,undef,$extra);
+ foreach my $key (keys(%roleshash)) {
+ next if ($key !~/^\Q$courseid\E(?:\/)*(\w+)*\_st$/);
+ my $section=$1;
+ if ($key eq $courseid.'_st') { $section=''; }
+ my ($dummy,$end,$start)=split(/\_/,&unescape($roleshash{$key}));
+ my $now=time;
+ if (defined($end) && $end && ($now > $end)) {
+ $Expired{$end}=$section;
+ next;
+ }
+ if (defined($start) && $start && ($now < $start)) {
+ $Pending{$start}=$section;
+ next;
}
- } split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles',
- &homeserver($unam,$udom)));
- return '-1';
+ return &do_cache_new('getsection',$hashid,$section,$cachetime);
+ }
+ #
+ # Presumedly there will be few matching roles from the above
+ # loop and the sorting time will be negligible.
+ if (scalar(keys(%Pending))) {
+ my ($time) = sort {$a <=> $b} keys(%Pending);
+ return &do_cache_new('getsection',$hashid,$Pending{$time},$cachetime);
+ }
+ if (scalar(keys(%Expired))) {
+ my @sorted = sort {$a <=> $b} keys(%Expired);
+ my $time = pop(@sorted);
+ return &do_cache_new('getsection',$hashid,$Expired{$time},$cachetime);
+ }
+ return &do_cache_new('getsection',$hashid,'-1',$cachetime);
+}
+
+sub save_cache {
+ &purge_remembered();
+ #&Apache::loncommon::validate_page();
+ undef(%env);
+ undef($env_loaded);
+}
+
+my $to_remember=-1;
+my %remembered;
+my %accessed;
+my $kicks=0;
+my $hits=0;
+sub make_key {
+ my ($name,$id) = @_;
+ if (length($id) > 65
+ && length(&escape($id)) > 200) {
+ $id=length($id).':'.&Digest::MD5::md5_hex($id);
+ }
+ return &escape($name.':'.$id);
+}
+
+sub devalidate_cache_new {
+ my ($name,$id,$debug) = @_;
+ if ($debug) { &Apache::lonnet::logthis("deleting $name:$id"); }
+ $id=&make_key($name,$id);
+ $memcache->delete($id);
+ delete($remembered{$id});
+ delete($accessed{$id});
+}
+
+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} "); }
+ $accessed{$id}=[&gettimeofday()];
+ $hits++;
+ return ($remembered{$id},1);
+ }
+ my $value = $memcache->get($id);
+ if (!(defined($value))) {
+ if ($debug) { &Apache::lonnet::logthis("getting $id is not defined"); }
+ return (undef,undef);
+ }
+ if ($value eq '__undef__') {
+ if ($debug) { &Apache::lonnet::logthis("getting $id is __undef__"); }
+ $value=undef;
+ }
+ &make_room($id,$value,$debug);
+ if ($debug) { &Apache::lonnet::logthis("getting $id is $value"); }
+ return ($value,1);
+}
+
+sub do_cache_new {
+ my ($name,$id,$value,$time,$debug) = @_;
+ $id=&make_key($name,$id);
+ my $setvalue=$value;
+ if (!defined($setvalue)) {
+ $setvalue='__undef__';
+ }
+ if (!defined($time) ) {
+ $time=600;
+ }
+ if ($debug) { &Apache::lonnet::logthis("Setting $id to $value"); }
+ my $result = $memcache->set($id,$setvalue,$time);
+ if (! $result) {
+ &logthis("caching of id -> $id failed");
+ $memcache->disconnect_all();
+ }
+ # need to make a copy of $value
+ &make_room($id,$value,$debug);
+ return $value;
+}
+
+sub make_room {
+ my ($id,$value,$debug)=@_;
+
+ $remembered{$id}= (ref($value)) ? &Storable::dclone($value)
+ : $value;
+ if ($to_remember<0) { return; }
+ $accessed{$id}=[&gettimeofday()];
+ if (scalar(keys(%remembered)) <= $to_remember) { return; }
+ my $to_kick;
+ my $max_time=0;
+ foreach my $other (keys(%accessed)) {
+ if (&tv_interval($accessed{$other}) > $max_time) {
+ $to_kick=$other;
+ $max_time=&tv_interval($accessed{$other});
+ }
+ }
+ delete($remembered{$to_kick});
+ delete($accessed{$to_kick});
+ $kicks++;
+ if ($debug) { &logthis("kicking $to_kick $max_time $kicks\n"); }
+ return;
}
+sub purge_remembered {
+ #&logthis("Tossing ".scalar(keys(%remembered)));
+ #&logthis(sprintf("%-20s is %s",'%remembered',length(&freeze(\%remembered))));
+ undef(%remembered);
+ undef(%accessed);
+}
# ------------------------------------- Read an entry from a user's environment
sub userenvironment {
my ($udom,$unam,@what)=@_;
+ my $items;
+ foreach my $item (@what) {
+ $items.=&escape($item).'&';
+ }
+ $items=~s/\&$//;
my %returnhash=();
- my @answer=split(/\&/,
- &reply('get:'.$udom.':'.$unam.':environment:'.join('&',@what),
- &homeserver($unam,$udom)));
- my $i;
- for ($i=0;$i<=$#what;$i++) {
- $returnhash{$what[$i]}=&unescape($answer[$i]);
+ my $uhome = &homeserver($unam,$udom);
+ unless ($uhome eq 'no_host') {
+ my @answer=split(/\&/,
+ &reply('get:'.$udom.':'.$unam.':environment:'.$items,$uhome));
+ if ($#answer==0 && $answer[0] =~ /^(con_lost|error:|no_such_host)/i) {
+ return %returnhash;
+ }
+ my $i;
+ for ($i=0;$i<=$#what;$i++) {
+ $returnhash{$what[$i]}=&unescape($answer[$i]);
+ }
}
return %returnhash;
}
+# ---------------------------------------------------------- Get a studentphoto
+sub studentphoto {
+ my ($udom,$unam,$ext) = @_;
+ my $home=&Apache::lonnet::homeserver($unam,$udom);
+ if (defined($env{'request.course.id'})) {
+ if ($env{'course.'.$env{'request.course.id'}.'.internal.showphoto'}) {
+ if ($udom eq $env{'course.'.$env{'request.course.id'}.'.domain'}) {
+ return(&retrievestudentphoto($udom,$unam,$ext));
+ } else {
+ my ($result,$perm_reqd)=
+ &Apache::lonnet::auto_photo_permission($unam,$udom);
+ if ($result eq 'ok') {
+ if (!($perm_reqd eq 'yes')) {
+ return(&retrievestudentphoto($udom,$unam,$ext));
+ }
+ }
+ }
+ }
+ } else {
+ my ($result,$perm_reqd) =
+ &Apache::lonnet::auto_photo_permission($unam,$udom);
+ if ($result eq 'ok') {
+ if (!($perm_reqd eq 'yes')) {
+ return(&retrievestudentphoto($udom,$unam,$ext));
+ }
+ }
+ }
+ return '/adm/lonKaputt/lonlogo_broken.gif';
+}
+
+sub retrievestudentphoto {
+ my ($udom,$unam,$ext,$type) = @_;
+ my $home=&Apache::lonnet::homeserver($unam,$udom);
+ my $ret=&Apache::lonnet::reply("studentphoto:$udom:$unam:$ext:$type",$home);
+ if ($ret eq 'ok') {
+ my $url="/uploaded/$udom/$unam/internal/studentphoto.$ext";
+ if ($type eq 'thumbnail') {
+ $url="/uploaded/$udom/$unam/internal/studentphoto_tn.$ext";
+ }
+ my $tokenurl=&Apache::lonnet::tokenwrapper($url);
+ return $tokenurl;
+ } else {
+ if ($type eq 'thumbnail') {
+ return '/adm/lonKaputt/genericstudent_tn.gif';
+ } else {
+ return '/adm/lonKaputt/lonlogo_broken.gif';
+ }
+ }
+}
+
+# -------------------------------------------------------------------- New chat
+
+sub chatsend {
+ my ($newentry,$anon,$group)=@_;
+ my $cnum=$env{'course.'.$env{'request.course.id'}.'.num'};
+ my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $chome=$env{'course.'.$env{'request.course.id'}.'.home'};
+ &reply('chatsend:'.$cdom.':'.$cnum.':'.
+ &escape($env{'user.domain'}.':'.$env{'user.name'}.':'.$anon.':'.
+ &escape($newentry)).':'.$group,$chome);
+}
+
+# ------------------------------------------ Find current version of a resource
+
+sub getversion {
+ my $fname=&clutter(shift);
+ unless ($fname=~/^\/res\//) { return -1; }
+ return ¤tversion(&filelocation('',$fname));
+}
+
+sub currentversion {
+ my $fname=shift;
+ 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') {
+ return -1;
+ }
+ my $answer=&reply("currentversion:$fname",$home);
+ if (($answer eq 'con_lost') || ($answer eq 'rejected')) {
+ return -1;
+ }
+ 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
sub subscribe {
my $fname=shift;
+ if ($fname=~/\/(aboutme|syllabus|bulletinboard|smppg)$/) { return ''; }
+ $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') || ($home eq $perlvar{'lonHostID'})) {
- return 'not_found';
+ if ($home eq 'no_host') {
+ return 'not_found';
}
my $answer=reply("sub:$fname",$home);
if (($answer eq 'con_lost') || ($answer eq 'rejected')) {
@@ -539,26 +2428,39 @@ 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)/-) {
+ return &repcopy_userfile($filename);
+ }
+ $filename=~s/[\n\r]//g;
my $transname="$filename.in.transfer";
- if ((-e $filename) || (-e $transname)) { return OK; }
+# FIXME: this should flock
+ if ((-e $filename) || (-e $transname)) { return 'ok'; }
my $remoteurl=subscribe($filename);
if ($remoteurl =~ /^con_lost by/) {
&logthis("Subscribe returned $remoteurl: $filename");
- return HTTP_SERVICE_UNAVAILABLE;
+ return 'unavailable';
} elsif ($remoteurl eq 'not_found') {
- &logthis("Subscribe returned not_found: $filename");
- return HTTP_NOT_FOUND;
+ #&logthis("Subscribe returned not_found: $filename");
+ return 'not_found';
} elsif ($remoteurl =~ /^rejected by/) {
&logthis("Subscribe returned $remoteurl: $filename");
- return FORBIDDEN;
+ return 'forbidden';
} elsif ($remoteurl eq 'directory') {
- return OK;
+ return 'ok';
} else {
+ my $author=$filename;
+ $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
+ my ($udom,$uname)=split(/\//,$author);
+ my $home=homeserver($uname,$udom);
+ 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") {
&logthis("Malconfiguration for replication: $filename");
- return HTTP_BAD_REQUEST;
+ return 'bad_request';
}
my $count;
for ($count=5;$count<$#parts;$count++) {
@@ -573,9 +2475,9 @@ sub repcopy {
if ($response->is_error()) {
unlink($transname);
my $message=$response->status_line;
- &logthis("WARNING:"
+ &logthis("WARNING:"
." LWP get: $message: $filename");
- return HTTP_SERVICE_UNAVAILABLE;
+ return 'unavailable';
} else {
if ($remoteurl!~/\.meta$/) {
my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta');
@@ -583,36 +2485,695 @@ sub repcopy {
if ($mresponse->is_error()) {
unlink($filename.'.meta');
&logthis(
- "INFO: No metadata: $filename");
+ "INFO: No metadata: $filename");
}
}
rename($transname,$filename);
- return OK;
+ return 'ok';
}
+ }
+ }
+}
+
+# ------------------------------------------------ Get server side include body
+sub ssi_body {
+ my ($filelink,%form)=@_;
+ if (! exists($form{'LONCAPA_INTERNAL_no_discussion'})) {
+ $form{'LONCAPA_INTERNAL_no_discussion'}='true';
+ }
+ my $output='';
+ my $response;
+ if ($filelink=~/^https?\:/) {
+ ($output,$response)=&externalssi($filelink);
+ } else {
+ $filelink .= $filelink=~/\?/ ? '&' : '?';
+ $filelink .= 'inhibitmenu=yes';
+ ($output,$response)=&ssi($filelink,%form);
+ }
+ $output=~s|//(\s*)?\s||gs;
+ $output=~s/^.*?\]*\>//si;
+ $output=~s/\<\/body\s*\>.*?$//si;
+ if (wantarray) {
+ return ($output, $response);
+ } else {
+ return $output;
}
}
# --------------------------------------------------------- Server Side Include
+sub absolute_url {
+ my ($host_name) = @_;
+ my $protocol = ($ENV{'SERVER_PORT'} == 443?'https://':'http://');
+ if ($host_name eq '') {
+ $host_name = $ENV{'SERVER_NAME'};
+ }
+ return $protocol.$host_name;
+}
+
+#
+# Server side include.
+# Parameters:
+# fn Possibly encrypted resource name/id.
+# form Hash that describes how the rendering should be done
+# and other things.
+# Returns:
+# Scalar context: The content of the response.
+# Array context: 2 element list of the content and the full response object.
+#
sub ssi {
my ($fn,%form)=@_;
-
my $ua=new LWP::UserAgent;
-
my $request;
-
+
+ $form{'no_update_last_known'}=1;
+ &Apache::lonenc::check_encrypt(\$fn);
if (%form) {
- $request=new HTTP::Request('POST',"http://".$ENV{'HTTP_HOST'}.$fn);
- $request->content(join '&', map { "$_=$form{$_}" } keys %form);
+ $request=new HTTP::Request('POST',&absolute_url().$fn);
+ $request->content(join('&',map { &escape($_).'='.&escape($form{$_}) } keys(%form)));
} else {
- $request=new HTTP::Request('GET',"http://".$ENV{'HTTP_HOST'}.$fn);
+ $request=new HTTP::Request('GET',&absolute_url().$fn);
}
$request->header(Cookie => $ENV{'HTTP_COOKIE'});
my $response=$ua->request($request);
- return $response->content;
+ if (wantarray) {
+ return ($response->content, $response);
+ } else {
+ return $response->content;
+ }
+}
+
+sub externalssi {
+ my ($url)=@_;
+ my $ua=new LWP::UserAgent;
+ my $request=new HTTP::Request('GET',$url);
+ my $response=$ua->request($request);
+ if (wantarray) {
+ return ($response->content, $response);
+ } else {
+ return $response->content;
+ }
+}
+
+# -------------------------------- Allow a /uploaded/ URI to be vouched for
+
+sub allowuploaded {
+ my ($srcurl,$url)=@_;
+ $url=&clutter(&declutter($url));
+ my $dir=$url;
+ $dir=~s/\/[^\/]+$//;
+ my %httpref=();
+ my $httpurl=&hreflocation('',$url);
+ $httpref{'httpref.'.$httpurl}=$srcurl;
+ &Apache::lonnet::appenv(\%httpref);
+}
+
+# --------- File operations in /home/httpd/html/userfiles/$domain/1/2/3/$course
+# input: action, courseID, current domain, intended
+# path to file, source of file, instruction to parse file for objects,
+# ref to hash for embedded objects,
+# ref to hash for codebase of java objects.
+# reference to scalar to accommodate mime type determined
+# from File::MMagic if $parser = parse.
+#
+# output: url to file (if action was uploaddoc),
+# ok if successful, or diagnostic message otherwise (if action was propagate or copy)
+#
+# Allows directory structure to be used within lonUsers/../userfiles/ for a
+# course.
+#
+# action = propagate - /home/httpd/html/userfiles/$domain/1/2/3/$course/$file
+# will be copied to /home/httpd/lonUsers/1/2/3/$course/userfiles in
+# course's home server.
+#
+# action = copy - /home/httpd/html/userfiles/$domain/1/2/3/$course/$file will
+# be copied from $source (current location) to
+# /home/httpd/html/userfiles/$domain/1/2/3/$course/$file
+# and will then be copied to
+# /home/httpd/lonUsers/$domain/1/2/3/$course/userfiles/$file in
+# course's home server.
+#
+# action = uploaddoc - /home/httpd/html/userfiles/$domain/1/2/3/$course/$file
+# will be retrived from $env{form.uploaddoc} (from DOCS interface) to
+# /home/httpd/html/userfiles/$domain/1/2/3/$course/$file
+# and will then be copied to /home/httpd/lonUsers/1/2/3/$course/userfiles/$file
+# in course's home server.
+#
+
+sub process_coursefile {
+ my ($action,$docuname,$docudom,$file,$source,$parser,$allfiles,$codebase,
+ $mimetype)=@_;
+ my $fetchresult;
+ my $home=&homeserver($docuname,$docudom);
+ if ($action eq 'propagate') {
+ $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file,
+ $home);
+ } else {
+ my $fpath = '';
+ my $fname = $file;
+ ($fpath,$fname) = ($file =~ m|^(.*)/([^/]+)$|);
+ $fpath=$docudom.'/'.$docuname.'/'.$fpath;
+ my $filepath = &build_filepath($fpath);
+ if ($action eq 'copy') {
+ if ($source eq '') {
+ $fetchresult = 'no source file';
+ return $fetchresult;
+ } else {
+ my $destination = $filepath.'/'.$fname;
+ rename($source,$destination);
+ $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file,
+ $home);
+ }
+ } elsif ($action eq 'uploaddoc') {
+ open(my $fh,'>'.$filepath.'/'.$fname);
+ print $fh $env{'form.'.$source};
+ close($fh);
+ if ($parser eq 'parse') {
+ my $mm = new File::MMagic;
+ my $type = $mm->checktype_filename($filepath.'/'.$fname);
+ if ($type eq 'text/html') {
+ my $parse_result = &extract_embedded_items($filepath.'/'.$fname,$allfiles,$codebase);
+ unless ($parse_result eq 'ok') {
+ &logthis('Failed to parse '.$filepath.'/'.$fname.' for embedded media: '.$parse_result);
+ }
+ }
+ if (ref($mimetype)) {
+ $$mimetype = $type;
+ }
+ }
+ $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file,
+ $home);
+ if ($fetchresult eq 'ok') {
+ return '/uploaded/'.$fpath.'/'.$fname;
+ } else {
+ &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file.
+ ' to host '.$home.': '.$fetchresult);
+ return '/adm/notfound.html';
+ }
+ }
+ }
+ unless ( $fetchresult eq 'ok') {
+ &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file.
+ ' to host '.$home.': '.$fetchresult);
+ }
+ return $fetchresult;
+}
+
+sub build_filepath {
+ my ($fpath) = @_;
+ my $filepath=$perlvar{'lonDocRoot'}.'/userfiles';
+ unless ($fpath eq '') {
+ my @parts=split('/',$fpath);
+ foreach my $part (@parts) {
+ $filepath.= '/'.$part;
+ if ((-e $filepath)!=1) {
+ mkdir($filepath,0777);
+ }
+ }
+ }
+ return $filepath;
+}
+
+sub store_edited_file {
+ my ($primary_url,$content,$docudom,$docuname,$fetchresult) = @_;
+ my $file = $primary_url;
+ $file =~ s#^/uploaded/$docudom/$docuname/##;
+ my $fpath = '';
+ my $fname = $file;
+ ($fpath,$fname) = ($file =~ m|^(.*)/([^/]+)$|);
+ $fpath=$docudom.'/'.$docuname.'/'.$fpath;
+ my $filepath = &build_filepath($fpath);
+ open(my $fh,'>'.$filepath.'/'.$fname);
+ print $fh $content;
+ close($fh);
+ my $home=&homeserver($docuname,$docudom);
+ $$fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file,
+ $home);
+ if ($$fetchresult eq 'ok') {
+ return '/uploaded/'.$fpath.'/'.$fname;
+ } else {
+ &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file.
+ ' to host '.$home.': '.$$fetchresult);
+ return '/adm/notfound.html';
+ }
+}
+
+sub clean_filename {
+ my ($fname,$args)=@_;
+# Replace Windows backslashes by forward slashes
+ $fname=~s/\\/\//g;
+ if (!$args->{'keep_path'}) {
+ # Get rid of everything but the actual filename
+ $fname=~s/^.*\/([^\/]+)$/$1/;
+ }
+# Replace spaces by underscores
+ $fname=~s/\s+/\_/g;
+# Replace all other weird characters by nothing
+ $fname=~s{[^/\w\.\-]}{}g;
+# Replace all .\d. sequences with _\d. so they no longer look like version
+# numbers
+ $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
+# not exceed $resizewidth and $resizeheight.
+
+sub resizeImage {
+ my ($img_path,$resizewidth,$resizeheight) = @_;
+ my $ima = Image::Magick->new;
+ my $resized;
+ if (-e $img_path) {
+ $ima->Read($img_path);
+ if (($resizewidth =~ /^\d+$/) && ($resizeheight > 0)) {
+ my $width = $ima->Get('width');
+ my $height = $ima->Get('height');
+ if ($width > $resizewidth) {
+ my $factor = $width/$resizewidth;
+ my $newheight = $height/$factor;
+ $ima->Scale(width=>$resizewidth,height=>$newheight);
+ $resized = 1;
+ }
+ }
+ if (($resizeheight =~ /^\d+$/) && ($resizeheight > 0)) {
+ my $width = $ima->Get('width');
+ my $height = $ima->Get('height');
+ if ($height > $resizeheight) {
+ my $factor = $height/$resizeheight;
+ my $newwidth = $width/$factor;
+ $ima->Scale(width=>$newwidth,height=>$resizeheight);
+ $resized = 1;
+ }
+ }
+ if ($resized) {
+ $ima->Write($img_path);
+ }
+ }
+ return;
+}
+
+# --------------- Take an uploaded file and put it into the userfiles directory
+# 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 ''.
+# if 'coursedoc': upload to the current course
+# if 'existingfile': write file to tmp/overwrites directory
+# if 'canceloverwrite': delete file written to tmp/overwrites directory
+# $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
+# $codebase - reference to hash for codebase of java objects
+# $desuname - username for permanent storage of uploaded file
+# $dsetudom - domain for permanaent storage of uploaded file
+# $thumbwidth - width (pixels) of thumbnail to make for uploaded image
+# $thumbheight - height (pixels) of thumbnail to make for uploaded image
+# $resizewidth - width (pixels) to which to resize uploaded image
+# $resizeheight - height (pixels) to which to resize uploaded image
+# $mimetype - reference to scalar to accommodate mime type determined
+# from File::MMagic if $parser = parse.
+#
+# output: url of file in userspace, or error: '.
+ '
';
+ } else {
+ return '';
+ }
+ } else {
+ return '';
+ }
+}
+
+# ---------------------------------------------------------- Course ID routines
+# Deal with domain's nohist_courseid.db files
+#
+
+sub courseidput {
+ my ($domain,$storehash,$coursehome,$caller) = @_;
+ return unless (ref($storehash) eq 'HASH');
+ my $outcome;
+ if ($caller eq 'timeonly') {
+ my $cids = '';
+ foreach my $item (keys(%$storehash)) {
+ $cids.=&escape($item).'&';
+ }
+ $cids=~s/\&$//;
+ $outcome = &reply('courseidputhash:'.$domain.':'.$caller.':'.$cids,
+ $coursehome);
+ } else {
+ my $items = '';
+ foreach my $item (keys(%$storehash)) {
+ $items.= &escape($item).'='.
+ &freeze_escape($$storehash{$item}).'&';
+ }
+ $items=~s/\&$//;
+ $outcome = &reply('courseidputhash:'.$domain.':'.$caller.':'.$items,
+ $coursehome);
+ }
+ if ($outcome eq 'unknown_cmd') {
+ my $what;
+ foreach my $cid (keys(%$storehash)) {
+ $what .= &escape($cid).'=';
+ foreach my $item ('description','inst_code','owner','type') {
+ $what .= &escape($storehash->{$cid}{$item}).':';
+ }
+ $what =~ s/\:$/&/;
+ }
+ $what =~ s/\&$//;
+ return &reply('courseidput:'.$domain.':'.$what,$coursehome);
+ } else {
+ return $outcome;
+ }
+}
+
+sub courseiddump {
+ my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,
+ $coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok,
+ $selfenrollonly,$catfilter,$showhidden,$caller,$cloner,$cc_clone,
+ $cloneonly,$createdbefore,$createdafter,$creationcontext,$domcloner)=@_;
+ my $as_hash = 1;
+ my %returnhash;
+ if (!$domfilter) { $domfilter=''; }
+ my %libserv = &all_library();
+ foreach my $tryserver (keys(%libserv)) {
+ if ( ( $hostidflag == 1
+ && grep(/^\Q$tryserver\E$/,@{$hostidref}) )
+ || (!defined($hostidflag)) ) {
+
+ if (($domfilter eq '') ||
+ (&host_domain($tryserver) eq $domfilter)) {
+ my $rep =
+ &reply('courseiddump:'.&host_domain($tryserver).':'.
+ $sincefilter.':'.&escape($descfilter).':'.
+ &escape($instcodefilter).':'.&escape($ownerfilter).
+ ':'.&escape($coursefilter).':'.&escape($typefilter).
+ ':'.&escape($regexp_ok).':'.$as_hash.':'.
+ &escape($selfenrollonly).':'.&escape($catfilter).':'.
+ $showhidden.':'.$caller.':'.&escape($cloner).':'.
+ &escape($cc_clone).':'.$cloneonly.':'.
+ &escape($createdbefore).':'.&escape($createdafter).':'.
+ &escape($creationcontext).':'.$domcloner,
+ $tryserver);
+ my @pairs=split(/\&/,$rep);
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/\=/,$item,2);
+ $key = &unescape($key);
+ next if ($key =~ /^error: 2 /);
+ my $result = &thaw_unescape($value);
+ if (ref($result) eq 'HASH') {
+ $returnhash{$key}=$result;
+ } else {
+ my @responses = split(/:/,$value);
+ my @items = ('description','inst_code','owner','type');
+ for (my $i=0; $i<@responses; $i++) {
+ $returnhash{$key}{$items[$i]} = &unescape($responses[$i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ return %returnhash;
+}
+
+sub courselastaccess {
+ my ($cdom,$cnum,$hostidref) = @_;
+ my %returnhash;
+ if ($cdom && $cnum) {
+ my $chome = &homeserver($cnum,$cdom);
+ if ($chome ne 'no_host') {
+ my $rep = &reply('courselastaccess:'.$cdom.':'.$cnum,$chome);
+ &extract_lastaccess(\%returnhash,$rep);
+ }
+ } else {
+ if (!$cdom) { $cdom=''; }
+ my %libserv = &all_library();
+ foreach my $tryserver (keys(%libserv)) {
+ if (ref($hostidref) eq 'ARRAY') {
+ next unless (grep(/^\Q$tryserver\E$/,@{$hostidref}));
+ }
+ if (($cdom eq '') || (&host_domain($tryserver) eq $cdom)) {
+ my $rep = &reply('courselastaccess:'.&host_domain($tryserver).':',$tryserver);
+ &extract_lastaccess(\%returnhash,$rep);
+ }
+ }
+ }
+ return %returnhash;
+}
+
+sub extract_lastaccess {
+ my ($returnhash,$rep) = @_;
+ if (ref($returnhash) eq 'HASH') {
+ unless ($rep eq 'unknown_command' || $rep eq 'no_such_host' ||
+ $rep eq 'con_lost' || $rep eq 'rejected' || $rep eq 'refused' ||
+ $rep eq '') {
+ my @pairs=split(/\&/,$rep);
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/\=/,$item,2);
+ $key = &unescape($key);
+ next if ($key =~ /^error: 2 /);
+ $returnhash->{$key} = &thaw_unescape($value);
+ }
+ }
+ }
+ return;
+}
+
+# ---------------------------------------------------------- DC e-mail
+
+sub dcmailput {
+ my ($domain,$msgid,$message,$server)=@_;
+ my $status = &Apache::lonnet::critical(
+ 'dcmailput:'.$domain.':'.&escape($msgid).'='.
+ &escape($message),$server);
+ return $status;
+}
+
+sub dcmaildump {
+ my ($dom,$startdate,$enddate,$senders) = @_;
+ my %returnhash=();
+
+ if (defined(&domain($dom,'primary'))) {
+ my $cmd='dcmaildump:'.$dom.':'.&escape($startdate).':'.
+ &escape($enddate).':';
+ my @esc_senders=map { &escape($_)} @$senders;
+ $cmd.=&escape(join('&',@esc_senders));
+ foreach my $line (split(/\&/,&reply($cmd,&domain($dom,'primary')))) {
+ my ($key,$value) = split(/\=/,$line,2);
+ if (($key) && ($value)) {
+ $returnhash{&unescape($key)} = &unescape($value);
+ }
+ }
+ }
+ return %returnhash;
+}
+# ---------------------------------------------------------- Domain roles
+
+sub get_domain_roles {
+ my ($dom,$roles,$startdate,$enddate)=@_;
+ if ((!defined($startdate)) || ($startdate eq '')) {
+ $startdate = '.';
+ }
+ if ((!defined($enddate)) || ($enddate eq '')) {
+ $enddate = '.';
+ }
+ my $rolelist;
+ if (ref($roles) eq 'ARRAY') {
+ $rolelist = join(':',@{$roles});
+ }
+ my %personnel = ();
+
+ my %servers = &get_servers($dom,'library');
+ foreach my $tryserver (keys(%servers)) {
+ %{$personnel{$tryserver}}=();
+ foreach my $line (split(/\&/,&reply('domrolesdump:'.$dom.':'.
+ &escape($startdate).':'.
+ &escape($enddate).':'.
+ &escape($rolelist), $tryserver))) {
+ my ($key,$value) = split(/\=/,$line,2);
+ if (($key) && ($value)) {
+ $personnel{$tryserver}{&unescape($key)} = &unescape($value);
+ }
+ }
+ }
+ return %personnel;
+}
+
+# ----------------------------------------------------------- Interval timing
+
+sub get_first_access {
+ my ($type,$argsymb)=@_;
+ my ($symb,$courseid,$udom,$uname)=&whichuser();
+ if ($argsymb) { $symb=$argsymb; }
+ my ($map,$id,$res)=&decode_symb($symb);
+ if ($type eq 'course') {
+ $res='course';
+ } elsif ($type eq 'map') {
+ $res=&symbread($map);
+ } else {
+ $res=$symb;
+ }
+ my %times=&get('firstaccesstimes',["$courseid\0$res"],$udom,$uname);
+ return $times{"$courseid\0$res"};
+}
+
+sub set_first_access {
+ my ($type)=@_;
+ my ($symb,$courseid,$udom,$uname)=&whichuser();
+ my ($map,$id,$res)=&decode_symb($symb);
+ if ($type eq 'course') {
+ $res='course';
+ } elsif ($type eq 'map') {
+ $res=&symbread($map);
+ } else {
+ $res=$symb;
+ }
+ my $firstaccess=&get_first_access($type,$symb);
+ if (!$firstaccess) {
+ return &put('firstaccesstimes',{"$courseid\0$res"=>time},$udom,$uname);
+ }
+ return 'already_set';
+}
+
+# --------------------------------------------- Set Expire Date for Spreadsheet
+
+sub expirespread {
+ my ($uname,$udom,$stype,$usymb)=@_;
+ my $cid=$env{'request.course.id'};
+ if ($cid) {
+ my $now=time;
+ my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
+ return &reply('put:'.$env{'course.'.$cid.'.domain'}.':'.
+ $env{'course.'.$cid.'.num'}.
+ ':nohist_expirationdates:'.
+ &escape($key).'='.$now,
+ $env{'course.'.$cid.'.home'})
+ }
+ return 'ok';
+}
+
+# ----------------------------------------------------- Devalidate Spreadsheets
+
+sub devalidate {
+ my ($symb,$uname,$udom)=@_;
+ my $cid=$env{'request.course.id'};
+ if ($cid) {
+ # delete the stored spreadsheets for
+ # - the student level sheet of this user in course's homespace
+ # - the assessment level sheet for this resource
+ # for this user in user's homespace
+ # - current conditional state info
+ my $key=$uname.':'.$udom.':';
+ my $status=
+ &del('nohist_calculatedsheets',
+ [$key.'studentcalc:'],
+ $env{'course.'.$cid.'.domain'},
+ $env{'course.'.$cid.'.num'})
+ .' '.
+ &del('nohist_calculatedsheets_'.$cid,
+ [$key.'assesscalc:'.$symb],$udom,$uname);
+ unless ($status eq 'ok ok') {
+ &logthis('Could not devalidate spreadsheet '.
+ $uname.' at '.$udom.' for '.
+ $symb.': '.$status);
+ }
+ &delenv('user.state.'.$cid);
+ }
+}
+
+sub get_scalar {
+ my ($string,$end) = @_;
+ my $value;
+ if ($$string =~ s/^([^&]*?)($end)/$2/) {
+ $value = $1;
+ } elsif ($$string =~ s/^([^&]*?)&//) {
+ $value = $1;
+ }
+ return &unescape($value);
+}
+
+sub array2str {
+ my (@array) = @_;
+ my $result=&arrayref2str(\@array);
+ $result=~s/^__ARRAY_REF__//;
+ $result=~s/__END_ARRAY_REF__$//;
+ return $result;
+}
+
+sub arrayref2str {
+ my ($arrayref) = @_;
+ my $result='__ARRAY_REF__';
+ foreach my $elem (@$arrayref) {
+ if(ref($elem) eq 'ARRAY') {
+ $result.=&arrayref2str($elem).'&';
+ } elsif(ref($elem) eq 'HASH') {
+ $result.=&hashref2str($elem).'&';
+ } elsif(ref($elem)) {
+ #print("Got a ref of ".(ref($elem))." skipping.");
+ } else {
+ $result.=&escape($elem).'&';
+ }
+ }
+ $result=~s/\&$//;
+ $result .= '__END_ARRAY_REF__';
+ return $result;
+}
+
+sub hash2str {
+ my (%hash) = @_;
+ my $result=&hashref2str(\%hash);
+ $result=~s/^__HASH_REF__//;
+ $result=~s/__END_HASH_REF__$//;
+ return $result;
+}
+
+sub hashref2str {
+ my ($hashref)=@_;
+ my $result='__HASH_REF__';
+ foreach my $key (sort(keys(%$hashref))) {
+ if (ref($key) eq 'ARRAY') {
+ $result.=&arrayref2str($key).'=';
+ } elsif (ref($key) eq 'HASH') {
+ $result.=&hashref2str($key).'=';
+ } elsif (ref($key)) {
+ $result.='=';
+ #print("Got a ref of ".(ref($key))." skipping.");
+ } else {
+ if ($key) {$result.=&escape($key).'=';} else { last; }
+ }
+
+ if(ref($hashref->{$key}) eq 'ARRAY') {
+ $result.=&arrayref2str($hashref->{$key}).'&';
+ } elsif(ref($hashref->{$key}) eq 'HASH') {
+ $result.=&hashref2str($hashref->{$key}).'&';
+ } elsif(ref($hashref->{$key})) {
+ $result.='&';
+ #print("Got a ref of ".(ref($hashref->{$key}))." skipping.");
+ } else {
+ $result.=&escape($hashref->{$key}).'&';
+ }
+ }
+ $result=~s/\&$//;
+ $result .= '__END_HASH_REF__';
+ return $result;
+}
+
+sub str2hash {
+ my ($string)=@_;
+ my ($hash)=&str2hashref('__HASH_REF__'.$string.'__END_HASH_REF__');
+ return %$hash;
+}
+
+sub str2hashref {
+ my ($string) = @_;
+
+ my %hash;
+
+ if($string !~ /^__HASH_REF__/) {
+ if (! ($string eq '' || !defined($string))) {
+ $hash{'error'}='Not hash reference';
+ }
+ return (\%hash, $string);
+ }
+
+ $string =~ s/^__HASH_REF__//;
+
+ while($string !~ /^__END_HASH_REF__/) {
+ #key
+ my $key='';
+ if($string =~ /^__HASH_REF__/) {
+ ($key, $string)=&str2hashref($string);
+ if(defined($key->{'error'})) {
+ $hash{'error'}='Bad data';
+ return (\%hash, $string);
+ }
+ } elsif($string =~ /^__ARRAY_REF__/) {
+ ($key, $string)=&str2arrayref($string);
+ if($key->[0] eq 'Array reference error') {
+ $hash{'error'}='Bad data';
+ return (\%hash, $string);
+ }
+ } else {
+ $string =~ s/^(.*?)=//;
+ $key=&unescape($1);
+ }
+ $string =~ s/^=//;
+
+ #value
+ my $value='';
+ if($string =~ /^__HASH_REF__/) {
+ ($value, $string)=&str2hashref($string);
+ if(defined($value->{'error'})) {
+ $hash{'error'}='Bad data';
+ return (\%hash, $string);
+ }
+ } elsif($string =~ /^__ARRAY_REF__/) {
+ ($value, $string)=&str2arrayref($string);
+ if($value->[0] eq 'Array reference error') {
+ $hash{'error'}='Bad data';
+ return (\%hash, $string);
+ }
+ } else {
+ $value=&get_scalar(\$string,'__END_HASH_REF__');
+ }
+ $string =~ s/^&//;
+
+ $hash{$key}=$value;
+ }
+
+ $string =~ s/^__END_HASH_REF__//;
+
+ return (\%hash, $string);
+}
+
+sub str2array {
+ my ($string)=@_;
+ my ($array)=&str2arrayref('__ARRAY_REF__'.$string.'__END_ARRAY_REF__');
+ return @$array;
+}
+
+sub str2arrayref {
+ my ($string) = @_;
+ my @array;
+
+ if($string !~ /^__ARRAY_REF__/) {
+ if (! ($string eq '' || !defined($string))) {
+ $array[0]='Array reference error';
+ }
+ return (\@array, $string);
+ }
+
+ $string =~ s/^__ARRAY_REF__//;
+
+ while($string !~ /^__END_ARRAY_REF__/) {
+ my $value='';
+ if($string =~ /^__HASH_REF__/) {
+ ($value, $string)=&str2hashref($string);
+ if(defined($value->{'error'})) {
+ $array[0] ='Array reference error';
+ return (\@array, $string);
+ }
+ } elsif($string =~ /^__ARRAY_REF__/) {
+ ($value, $string)=&str2arrayref($string);
+ if($value->[0] eq 'Array reference error') {
+ $array[0] ='Array reference error';
+ return (\@array, $string);
+ }
+ } else {
+ $value=&get_scalar(\$string,'__END_ARRAY_REF__');
+ }
+ $string =~ s/^&//;
+
+ push(@array, $value);
+ }
+
+ $string =~ s/^__END_ARRAY_REF__//;
+
+ return (\@array, $string);
+}
+
+# -------------------------------------------------------------------Temp Store
+
+sub tmpreset {
+ my ($symb,$namespace,$domain,$stuname) = @_;
+ if (!$symb) {
+ $symb=&symbread();
+ if (!$symb) { $symb= $env{'request.url'}; }
+ }
+ $symb=escape($symb);
+
+ if (!$namespace) { $namespace=$env{'request.state'}; }
+ $namespace=~s/\//\_/g;
+ $namespace=~s/\W//g;
+
+ if (!$domain) { $domain=$env{'user.domain'}; }
+ if (!$stuname) { $stuname=$env{'user.name'}; }
+ if ($domain eq 'public' && $stuname eq 'public') {
+ $stuname=$ENV{'REMOTE_ADDR'};
+ }
+ my $path=LONCAPA::tempdir();
+ my %hash;
+ if (tie(%hash,'GDBM_File',
+ $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
+ &GDBM_WRCREAT(),0640)) {
+ foreach my $key (keys(%hash)) {
+ if ($key=~ /:$symb/) {
+ delete($hash{$key});
+ }
+ }
+ }
+}
+
+sub tmpstore {
+ my ($storehash,$symb,$namespace,$domain,$stuname) = @_;
+
+ if (!$symb) {
+ $symb=&symbread();
+ if (!$symb) { $symb= $env{'request.url'}; }
+ }
+ $symb=escape($symb);
+
+ if (!$namespace) {
+ # I don't think we would ever want to store this for a course.
+ # it seems this will only be used if we don't have a course.
+ #$namespace=$env{'request.course.id'};
+ #if (!$namespace) {
+ $namespace=$env{'request.state'};
+ #}
+ }
+ $namespace=~s/\//\_/g;
+ $namespace=~s/\W//g;
+ if (!$domain) { $domain=$env{'user.domain'}; }
+ if (!$stuname) { $stuname=$env{'user.name'}; }
+ if ($domain eq 'public' && $stuname eq 'public') {
+ $stuname=$ENV{'REMOTE_ADDR'};
+ }
+ my $now=time;
+ my %hash;
+ my $path=LONCAPA::tempdir();
+ if (tie(%hash,'GDBM_File',
+ $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
+ &GDBM_WRCREAT(),0640)) {
+ $hash{"version:$symb"}++;
+ my $version=$hash{"version:$symb"};
+ my $allkeys='';
+ foreach my $key (keys(%$storehash)) {
+ $allkeys.=$key.':';
+ $hash{"$version:$symb:$key"}=&freeze_escape($$storehash{$key});
+ }
+ $hash{"$version:$symb:timestamp"}=$now;
+ $allkeys.='timestamp';
+ $hash{"$version:keys:$symb"}=$allkeys;
+ if (untie(%hash)) {
+ return 'ok';
+ } else {
+ return "error:$!";
+ }
+ } else {
+ return "error:$!";
+ }
+}
+
+# -----------------------------------------------------------------Temp Restore
+
+sub tmprestore {
+ my ($symb,$namespace,$domain,$stuname) = @_;
+
+ if (!$symb) {
+ $symb=&symbread();
+ if (!$symb) { $symb= $env{'request.url'}; }
+ }
+ $symb=escape($symb);
+
+ if (!$namespace) { $namespace=$env{'request.state'}; }
+
+ if (!$domain) { $domain=$env{'user.domain'}; }
+ if (!$stuname) { $stuname=$env{'user.name'}; }
+ if ($domain eq 'public' && $stuname eq 'public') {
+ $stuname=$ENV{'REMOTE_ADDR'};
+ }
+ my %returnhash;
+ $namespace=~s/\//\_/g;
+ $namespace=~s/\W//g;
+ my %hash;
+ my $path=LONCAPA::tempdir();
+ if (tie(%hash,'GDBM_File',
+ $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db',
+ &GDBM_READER(),0640)) {
+ my $version=$hash{"version:$symb"};
+ $returnhash{'version'}=$version;
+ my $scope;
+ for ($scope=1;$scope<=$version;$scope++) {
+ my $vkeys=$hash{"$scope:keys:$symb"};
+ my @keys=split(/:/,$vkeys);
+ my $key;
+ $returnhash{"$scope:keys"}=$vkeys;
+ foreach $key (@keys) {
+ $returnhash{"$scope:$key"}=&thaw_unescape($hash{"$scope:$symb:$key"});
+ $returnhash{"$key"}=&thaw_unescape($hash{"$scope:$symb:$key"});
+ }
+ }
+ if (!(untie(%hash))) {
+ return "error:$!";
+ }
+ } else {
+ return "error:$!";
+ }
+ return %returnhash;
+}
+
# ----------------------------------------------------------------------- Store
sub store {
- my %storehash=@_;
- my $symb;
- unless ($symb=escape(&symbread())) { return ''; }
- my $namespace;
- unless ($namespace=$ENV{'request.course.id'}) { return ''; }
+ my ($storehash,$symb,$namespace,$domain,$stuname) = @_;
+ my $home='';
+
+ if ($stuname) { $home=&homeserver($stuname,$domain); }
+
+ $symb=&symbclean($symb);
+ if (!$symb) { unless ($symb=&symbread()) { return ''; } }
+
+ if (!$domain) { $domain=$env{'user.domain'}; }
+ if (!$stuname) { $stuname=$env{'user.name'}; }
+
+ &devalidate($symb,$stuname,$domain);
+
+ $symb=escape($symb);
+ if (!$namespace) {
+ unless ($namespace=$env{'request.course.id'}) {
+ return '';
+ }
+ }
+ if (!$home) { $home=$env{'user.home'}; }
+
+ $$storehash{'ip'}=$ENV{'REMOTE_ADDR'};
+ $$storehash{'host'}=$perlvar{'lonHostID'};
+
my $namevalue='';
- map {
- $namevalue.=escape($_).'='.escape($storehash{$_}).'&';
- } keys %storehash;
+ foreach my $key (keys(%$storehash)) {
+ $namevalue.=&escape($key).'='.&freeze_escape($$storehash{$key}).'&';
+ }
$namevalue=~s/\&$//;
- return reply(
- "store:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$symb:$namevalue",
- "$ENV{'user.home'}");
+ &courselog($symb.':'.$stuname.':'.$domain.':STORE:'.$namevalue);
+ return reply("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
}
# -------------------------------------------------------------- Critical Store
sub cstore {
- my %storehash=@_;
- my $symb;
- unless ($symb=escape(&symbread())) { return ''; }
- my $namespace;
- unless ($namespace=$ENV{'request.course.id'}) { return ''; }
+ my ($storehash,$symb,$namespace,$domain,$stuname) = @_;
+ my $home='';
+
+ if ($stuname) { $home=&homeserver($stuname,$domain); }
+
+ $symb=&symbclean($symb);
+ if (!$symb) { unless ($symb=&symbread()) { return ''; } }
+
+ if (!$domain) { $domain=$env{'user.domain'}; }
+ if (!$stuname) { $stuname=$env{'user.name'}; }
+
+ &devalidate($symb,$stuname,$domain);
+
+ $symb=escape($symb);
+ if (!$namespace) {
+ unless ($namespace=$env{'request.course.id'}) {
+ return '';
+ }
+ }
+ if (!$home) { $home=$env{'user.home'}; }
+
+ $$storehash{'ip'}=$ENV{'REMOTE_ADDR'};
+ $$storehash{'host'}=$perlvar{'lonHostID'};
+
my $namevalue='';
- map {
- $namevalue.=escape($_).'='.escape($storehash{$_}).'&';
- } keys %storehash;
+ foreach my $key (keys(%$storehash)) {
+ $namevalue.=&escape($key).'='.&freeze_escape($$storehash{$key}).'&';
+ }
$namevalue=~s/\&$//;
- return critical(
- "store:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$symb:$namevalue",
- "$ENV{'user.home'}");
+ &courselog($symb.':'.$stuname.':'.$domain.':CSTORE:'.$namevalue);
+ return critical
+ ("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
}
# --------------------------------------------------------------------- Restore
sub restore {
- my $symb;
- unless ($symb=escape(&symbread())) { return ''; }
- my $namespace;
- unless ($namespace=$ENV{'request.course.id'}) { return ''; }
- my $answer=reply(
- "restore:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$symb",
- "$ENV{'user.home'}");
+ my ($symb,$namespace,$domain,$stuname) = @_;
+ my $home='';
+
+ if ($stuname) { $home=&homeserver($stuname,$domain); }
+
+ if (!$symb) {
+ unless ($symb=escape(&symbread())) { return ''; }
+ } else {
+ $symb=&escape(&symbclean($symb));
+ }
+ if (!$namespace) {
+ unless ($namespace=$env{'request.course.id'}) {
+ return '';
+ }
+ }
+ if (!$domain) { $domain=$env{'user.domain'}; }
+ if (!$stuname) { $stuname=$env{'user.name'}; }
+ if (!$home) { $home=$env{'user.home'}; }
+ my $answer=&reply("restore:$domain:$stuname:$namespace:$symb","$home");
+
my %returnhash=();
- map {
- my ($name,$value)=split(/\=/,$_);
- $returnhash{&unescape($name)}=&unescape($value);
- } split(/\&/,$answer);
+ foreach my $line (split(/\&/,$answer)) {
+ my ($name,$value)=split(/\=/,$line);
+ $returnhash{&unescape($name)}=&thaw_unescape($value);
+ }
my $version;
for ($version=1;$version<=$returnhash{'version'};$version++) {
- map {
- $returnhash{$_}=$returnhash{$version.':'.$_};
- } split(/\:/,$returnhash{$version.':keys'});
+ foreach my $item (split(/\:/,$returnhash{$version.':keys'})) {
+ $returnhash{$item}=$returnhash{$version.':'.$item};
+ }
}
return %returnhash;
}
# ---------------------------------------------------------- Course Description
+#
+#
sub coursedescription {
- my $courseid=shift;
+ my ($courseid,$args)=@_;
$courseid=~s/^\///;
$courseid=~s/\_/\//g;
my ($cdomain,$cnum)=split(/\//,$courseid);
- my $chome=homeserver($cnum,$cdomain);
+ my $chome=&homeserver($cnum,$cdomain);
+ my $normalid=$cdomain.'_'.$cnum;
+ # need to always cache even if we get errors otherwise we keep
+ # trying and trying and trying to get the course description.
+ my %envhash=();
+ my %returnhash=();
+
+ my $expiretime=600;
+ if ($env{'request.course.id'} eq $normalid) {
+ $expiretime=120;
+ }
+
+ my $prefix='course.'.$cdomain.'_'.$cnum.'.';
+ if (!$args->{'freshen_cache'}
+ && ((time-$env{$prefix.'last_cache'}) < $expiretime) ) {
+ foreach my $key (keys(%env)) {
+ next if ($key !~ /^\Q$prefix\E(.*)/);
+ my ($setting) = $1;
+ $returnhash{$setting} = $env{$key};
+ }
+ return %returnhash;
+ }
+
+ # get the data again
+
+ if (!$args->{'one_time'}) {
+ $envhash{'course.'.$normalid.'.last_cache'}=time;
+ }
+
if ($chome ne 'no_host') {
- my $rep=reply("dump:$cdomain:$cnum:environment",$chome);
- if ($rep ne 'con_lost') {
- my $normalid=$cdomain.'_'.$cnum;
- my %envhash=();
- my %returnhash=('home' => $chome,
- 'domain' => $cdomain,
- 'num' => $cnum);
- map {
- my ($name,$value)=split(/\=/,$_);
- $name=&unescape($name);
- $value=&unescape($value);
- $returnhash{$name}=$value;
+ %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;
+ if (!defined($returnhash{'type'})) {
+ $returnhash{'type'} = 'Course';
+ }
+ while (my ($name,$value) = each %returnhash) {
$envhash{'course.'.$normalid.'.'.$name}=$value;
- } split(/\&/,$rep);
- $returnhash{'url'}='/res/'.declutter($returnhash{'url'});
- $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.
- $ENV{'user.name'}.'_'.$cdomain.'_'.$cnum;
- $envhash{'course.'.$normalid.'.last_cache'}=time;
+ }
+ $returnhash{'url'}=&clutter($returnhash{'url'});
+ $returnhash{'fn'}=LONCAPA::tempdir() .
+ $username.'_'.$cdomain.'_'.$cnum;
$envhash{'course.'.$normalid.'.home'}=$chome;
$envhash{'course.'.$normalid.'.domain'}=$cdomain;
$envhash{'course.'.$normalid.'.num'}=$cnum;
- &appenv(%envhash);
- return %returnhash;
}
}
- return ();
+ if (!$args->{'one_time'}) {
+ &appenv(\%envhash);
+ }
+ return %returnhash;
}
-# -------------------------------------------------------- Get user priviledges
+sub update_released_required {
+ my ($needsrelease,$cdom,$cnum,$chome,$cid) = @_;
+ if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
+ $cid = $env{'request.course.id'};
+ $cdom = $env{'course.'.$cid.'.domain'};
+ $cnum = $env{'course.'.$cid.'.num'};
+ $chome = $env{'course.'.$cid.'.home'};
+ }
+ if ($needsrelease) {
+ my %curr_reqd_hash = &userenvironment($cdom,$cnum,'internal.releaserequired');
+ my $needsupdate;
+ if ($curr_reqd_hash{'internal.releaserequired'} eq '') {
+ $needsupdate = 1;
+ } else {
+ my ($currmajor,$currminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
+ my ($needsmajor,$needsminor) = split(/\./,$needsrelease);
+ if (($currmajor < $needsmajor) || ($currmajor == $needsmajor && $currminor < $needsminor)) {
+ $needsupdate = 1;
+ }
+ }
+ if ($needsupdate) {
+ my %needshash = (
+ 'internal.releaserequired' => $needsrelease,
+ );
+ my $putresult = &put('environment',\%needshash,$cdom,$cnum);
+ if ($putresult eq 'ok') {
+ &appenv({'course.'.$cid.'.internal.releaserequired' => $needsrelease});
+ my %crsinfo = &courseiddump($cdom,'.',1,'.','.',$cnum,undef,undef,'.');
+ if (ref($crsinfo{$cid}) eq 'HASH') {
+ $crsinfo{$cid}{'releaserequired'} = $needsrelease;
+ &courseidput($cdom,\%crsinfo,$chome,'notime');
+ }
+ }
+ }
+ }
+ return;
+}
+
+# -------------------------------------------------See if a user is privileged
+
+sub privileged {
+ my ($username,$domain)=@_;
+ my $rolesdump=&reply("dump:$domain:$username:roles",
+ &homeserver($username,$domain));
+ if (($rolesdump eq 'con_lost') || ($rolesdump eq '') ||
+ ($rolesdump =~ /^error:/)) {
+ return 0;
+ }
+ my $now=time;
+ if ($rolesdump ne '') {
+ foreach my $entry (split(/&/,$rolesdump)) {
+ if ($entry!~/^rolesdef_/) {
+ my ($area,$role)=split(/=/,$entry);
+ $area=~s/\_\w\w$//;
+ my ($trole,$tend,$tstart)=split(/_/,$role);
+ if (($trole eq 'dc') || ($trole eq 'su')) {
+ my $active=1;
+ if ($tend) {
+ if ($tend<$now) { $active=0; }
+ }
+ if ($tstart) {
+ if ($tstart>$now) { $active=0; }
+ }
+ if ($active) { return 1; }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+# -------------------------------------------------------- Get user privileges
sub rolesinit {
my ($domain,$username,$authhost)=@_;
- my $rolesdump=reply("dump:$domain:$username:roles",$authhost);
- if (($rolesdump eq 'con_lost') || ($rolesdump eq '')) { return ''; }
- my %allroles=();
- my %thesepriv=();
my $now=time;
- my $userroles="user.login.time=$now\n";
- my $thesestr;
+ my %userroles = ('user.login.time' => $now);
+ my $extra = &freeze_escape({'skipcheck' => 1});
+ my $rolesdump=reply("dump:$domain:$username:roles:.::$extra",$authhost);
+ if (($rolesdump eq 'con_lost') || ($rolesdump eq '') ||
+ ($rolesdump =~ /^error:/)) {
+ return \%userroles;
+ }
+ my %allroles=();
+ my %allgroups=();
if ($rolesdump ne '') {
- map {
- if ($_!~/^rolesdef\&/) {
- my ($area,$role)=split(/=/,$_);
- $area=~s/\_\w\w$//;
- my ($trole,$tend,$tstart)=split(/_/,$role);
- $userroles.='user.role.'.$trole.'.'.$area.'='.
- $tstart.'.'.$tend."\n";
- if ($tend!=0) {
- if ($tend<$now) {
- $trole='';
- }
+ foreach my $entry (split(/&/,$rolesdump)) {
+ if ($entry!~/^rolesdef_/) {
+ my ($area,$role)=split(/=/,$entry);
+ $area=~s/\_\w\w$//;
+ my ($trole,$tend,$tstart,$group_privs);
+ if ($role=~/^cr/) {
+ if ($role=~m|^(cr/$match_domain/$match_username/[a-zA-Z0-9]+)_(.*)$|) {
+ ($trole,my $trest)=($role=~m|^(cr/$match_domain/$match_username/[a-zA-Z0-9]+)_(.*)$|);
+ ($tend,$tstart)=split('_',$trest);
+ } else {
+ $trole=$role;
+ }
+ } elsif ($role =~ m|^gr/|) {
+ ($trole,$tend,$tstart) = split(/_/,$role);
+ next if ($tstart eq '-1');
+ ($trole,$group_privs) = split(/\//,$trole);
+ $group_privs = &unescape($group_privs);
+ } else {
+ ($trole,$tend,$tstart)=split(/_/,$role);
+ }
+ my %new_role = &set_arearole($trole,$area,$tstart,$tend,$domain,
+ $username);
+ @userroles{keys(%new_role)} = @new_role{keys(%new_role)};
+ if (($tend!=0) && ($tend<$now)) { $trole=''; }
+ if (($tstart!=0) && ($tstart>$now)) { $trole=''; }
+ if (($area ne '') && ($trole ne '')) {
+ my $spec=$trole.'.'.$area;
+ my ($tdummy,$tdomain,$trest)=split(/\//,$area);
+ if ($trole =~ /^cr\//) {
+ &custom_roleprivs(\%allroles,$trole,$tdomain,$trest,$spec,$area);
+ } elsif ($trole eq 'gr') {
+ &group_roleprivs(\%allgroups,$area,$group_privs,$tend,$tstart);
+ } else {
+ &standard_roleprivs(\%allroles,$trole,$tdomain,$spec,$trest,$area);
+ }
}
- if ($tstart!=0) {
- if ($tstart>$now) {
- $trole='';
+ }
+ }
+ my ($author,$adv) = &set_userprivs(\%userroles,\%allroles,\%allgroups);
+ $userroles{'user.adv'} = $adv;
+ $userroles{'user.author'} = $author;
+ $env{'user.adv'}=$adv;
+ }
+ return \%userroles;
+}
+
+sub set_arearole {
+ my ($trole,$area,$tstart,$tend,$domain,$username) = @_;
+# log the associated role with the area
+ &userrolelog($trole,$username,$domain,$area,$tstart,$tend);
+ return ('user.role.'.$trole.'.'.$area => $tstart.'.'.$tend);
+}
+
+sub custom_roleprivs {
+ my ($allroles,$trole,$tdomain,$trest,$spec,$area) = @_;
+ my ($rdummy,$rdomain,$rauthor,$rrole)=split(/\//,$trole);
+ my $homsvr=homeserver($rauthor,$rdomain);
+ if (&hostname($homsvr) ne '') {
+ my ($rdummy,$roledef)=
+ &get('roles',["rolesdef_$rrole"],$rdomain,$rauthor);
+ if (($rdummy ne 'con_lost') && ($roledef ne '')) {
+ my ($syspriv,$dompriv,$coursepriv)=split(/\_/,$roledef);
+ if (defined($syspriv)) {
+ if ($trest =~ /^$match_community$/) {
+ $syspriv =~ s/bre\&S//;
}
+ $$allroles{'cm./'}.=':'.$syspriv;
+ $$allroles{$spec.'./'}.=':'.$syspriv;
}
- if (($area ne '') && ($trole ne '')) {
- my $spec=$trole.'.'.$area;
- my ($tdummy,$tdomain,$trest)=split(/\//,$area);
- if ($trole =~ /^cr\//) {
- my ($rdummy,$rdomain,$rauthor,$rrole)=split(/\//,$trole);
- my $homsvr=homeserver($rauthor,$rdomain);
- if ($hostname{$homsvr} ne '') {
- my $roledef=
- reply("get:$rdomain:$rauthor:roles:rolesdef_$rrole",
- $homsvr);
- if (($roledef ne 'con_lost') && ($roledef ne '')) {
- my ($syspriv,$dompriv,$coursepriv)=
- split(/\_/,unescape($roledef));
- $allroles{'cm./'}.=':'.$syspriv;
- $allroles{$spec.'./'}.=':'.$syspriv;
- if ($tdomain ne '') {
- $allroles{'cm./'.$tdomain.'/'}.=':'.$dompriv;
- $allroles{$spec.'./'.$tdomain.'/'}.=':'.$dompriv;
- if ($trest ne '') {
- $allroles{'cm.'.$area}.=':'.$coursepriv;
- $allroles{$spec.'.'.$area}.=':'.$coursepriv;
- }
- }
- }
- }
- } else {
- $allroles{'cm./'}.=':'.$pr{$trole.':s'};
- $allroles{$spec.'./'}.=':'.$pr{$trole.':s'};
- if ($tdomain ne '') {
- $allroles{'cm./'.$tdomain.'/'}.=':'.$pr{$trole.':d'};
- $allroles{$spec.'./'.$tdomain.'/'}.=':'.$pr{$trole.':d'};
- if ($trest ne '') {
- $allroles{'cm.'.$area}.=':'.$pr{$trole.':c'};
- $allroles{$spec.'.'.$area}.=':'.$pr{$trole.':c'};
- }
- }
- }
+ if ($tdomain ne '') {
+ if (defined($dompriv)) {
+ $$allroles{'cm./'.$tdomain.'/'}.=':'.$dompriv;
+ $$allroles{$spec.'./'.$tdomain.'/'}.=':'.$dompriv;
+ }
+ if (($trest ne '') && (defined($coursepriv))) {
+ $$allroles{'cm.'.$area}.=':'.$coursepriv;
+ $$allroles{$spec.'.'.$area}.=':'.$coursepriv;
+ }
+ }
+ }
+ }
+}
+
+sub group_roleprivs {
+ my ($allgroups,$area,$group_privs,$tend,$tstart) = @_;
+ my $access = 1;
+ my $now = time;
+ if (($tend!=0) && ($tend<$now)) { $access = 0; }
+ if (($tstart!=0) && ($tstart>$now)) { $access=0; }
+ if ($access) {
+ my ($course,$group) = ($area =~ m|(/$match_domain/$match_courseid)/([^/]+)$|);
+ $$allgroups{$course}{$group} .=':'.$group_privs;
+ }
+}
+
+sub standard_roleprivs {
+ my ($allroles,$trole,$tdomain,$spec,$trest,$area) = @_;
+ if (defined($pr{$trole.':s'})) {
+ $$allroles{'cm./'}.=':'.$pr{$trole.':s'};
+ $$allroles{$spec.'./'}.=':'.$pr{$trole.':s'};
+ }
+ if ($tdomain ne '') {
+ if (defined($pr{$trole.':d'})) {
+ $$allroles{'cm./'.$tdomain.'/'}.=':'.$pr{$trole.':d'};
+ $$allroles{$spec.'./'.$tdomain.'/'}.=':'.$pr{$trole.':d'};
+ }
+ if (($trest ne '') && (defined($pr{$trole.':c'}))) {
+ $$allroles{'cm.'.$area}.=':'.$pr{$trole.':c'};
+ $$allroles{$spec.'.'.$area}.=':'.$pr{$trole.':c'};
+ }
+ }
+}
+
+sub set_userprivs {
+ my ($userroles,$allroles,$allgroups,$groups_roles) = @_;
+ my $author=0;
+ my $adv=0;
+ my %grouproles = ();
+ if (keys(%{$allgroups}) > 0) {
+ my @groupkeys;
+ foreach my $role (keys(%{$allroles})) {
+ push(@groupkeys,$role);
+ }
+ if (ref($groups_roles) eq 'HASH') {
+ foreach my $key (keys(%{$groups_roles})) {
+ unless (grep(/^\Q$key\E$/,@groupkeys)) {
+ push(@groupkeys,$key);
+ }
+ }
+ }
+ if (@groupkeys > 0) {
+ foreach my $role (@groupkeys) {
+ my ($trole,$area,$sec,$extendedarea);
+ if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)\.-) {
+ $trole = $1;
+ $area = $2;
+ $sec = $3;
+ $extendedarea = $area.$sec;
+ if (exists($$allgroups{$area})) {
+ foreach my $group (keys(%{$$allgroups{$area}})) {
+ my $spec = $trole.'.'.$extendedarea;
+ $grouproles{$spec.'.'.$area.'/'.$group} =
+ $$allgroups{$area}{$group};
+ }
+ }
+ }
+ }
+ }
+ }
+ foreach my $group (keys(%grouproles)) {
+ $$allroles{$group} = $grouproles{$group};
+ }
+ foreach my $role (keys(%{$allroles})) {
+ my %thesepriv;
+ if (($role=~/^au/) || ($role=~/^ca/) || ($role=~/^aa/)) { $author=1; }
+ foreach my $item (split(/:/,$$allroles{$role})) {
+ if ($item ne '') {
+ my ($privilege,$restrictions)=split(/&/,$item);
+ if ($restrictions eq '') {
+ $thesepriv{$privilege}='F';
+ } elsif ($thesepriv{$privilege} ne 'F') {
+ $thesepriv{$privilege}.=$restrictions;
+ }
+ if ($thesepriv{'adv'} eq 'F') { $adv=1; }
+ }
+ }
+ my $thesestr='';
+ foreach my $priv (sort(keys(%thesepriv))) {
+ $thesestr.=':'.$priv.'&'.$thesepriv{$priv};
+ }
+ $userroles->{'user.priv.'.$role} = $thesestr;
+ }
+ return ($author,$adv);
+}
+
+sub role_status {
+ my ($rolekey,$update,$refresh,$now,$role,$where,$trolecode,$tstatus,$tstart,$tend) = @_;
+ my @pwhere = ();
+ if (exists($env{$rolekey}) && $env{$rolekey} ne '') {
+ (undef,undef,$$role,@pwhere)=split(/\./,$rolekey);
+ unless (!defined($$role) || $$role eq '') {
+ $$where=join('.',@pwhere);
+ $$trolecode=$$role.'.'.$$where;
+ ($$tstart,$$tend)=split(/\./,$env{$rolekey});
+ $$tstatus='is';
+ if ($$tstart && $$tstart>$update) {
+ $$tstatus='future';
+ if ($$tstart<$now) {
+ if ($$tstart && $$tstart>$refresh) {
+ if (($$where ne '') && ($$role ne '')) {
+ my (%allroles,%allgroups,$group_privs,
+ %groups_roles,@rolecodes);
+ my %userroles = (
+ 'user.role.'.$$role.'.'.$$where => $$tstart.'.'.$$tend
+ );
+ @rolecodes = ('cm');
+ my $spec=$$role.'.'.$$where;
+ my ($tdummy,$tdomain,$trest)=split(/\//,$$where);
+ if ($$role =~ /^cr\//) {
+ &custom_roleprivs(\%allroles,$$role,$tdomain,$trest,$spec,$$where);
+ push(@rolecodes,'cr');
+ } elsif ($$role eq 'gr') {
+ push(@rolecodes,$$role);
+ my %rolehash = &get('roles',[$$where.'_'.$$role],$env{'user.domain'},
+ $env{'user.name'});
+ my ($trole) = split('_',$rolehash{$$where.'_'.$$role},2);
+ (undef,my $group_privs) = split(/\//,$trole);
+ $group_privs = &unescape($group_privs);
+ &group_roleprivs(\%allgroups,$$where,$group_privs,$$tend,$$tstart);
+ my %course_roles = &get_my_roles($env{'user.name'},$env{'user.domain'},'userroles',['active'],['cc','co','in','ta','ep','ad','st','cr'],[$tdomain],1);
+ &get_groups_roles($tdomain,$trest,
+ \%course_roles,\@rolecodes,
+ \%groups_roles);
+ } else {
+ push(@rolecodes,$$role);
+ &standard_roleprivs(\%allroles,$$role,$tdomain,$spec,$trest,$$where);
+ }
+ my ($author,$adv)= &set_userprivs(\%userroles,\%allroles,\%allgroups,\%groups_roles);
+ &appenv(\%userroles,\@rolecodes);
+ &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$role);
+ }
+ }
+ $$tstatus = 'is';
+ }
}
- }
- } split(/&/,$rolesdump);
- map {
- %thesepriv=();
- map {
- if ($_ ne '') {
- my ($priviledge,$restrictions)=split(/&/,$_);
- if ($restrictions eq '') {
- $thesepriv{$priviledge}='F';
+ if ($$tend) {
+ if ($$tend<$update) {
+ $$tstatus='expired';
+ } elsif ($$tend<$now) {
+ $$tstatus='will_not';
+ }
+ }
+ }
+ }
+}
+
+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'));
+ if (keys(%{$cdom_courseroles}) > 0) {
+ my ($cnum) = ($rest =~ /^($match_courseid)/);
+ if ($cdom ne '' && $cnum ne '') {
+ foreach my $key (keys(%{$cdom_courseroles})) {
+ if ($key =~ /^\Q$cnum\E:\Q$cdom\E:([^:]+):?([^:]*)/) {
+ my $crsrole = $1;
+ my $crssec = $2;
+ if ($crsrole =~ /^cr/) {
+ unless (grep(/^cr$/,@{$rolecodes})) {
+ push(@{$rolecodes},'cr');
+ }
} else {
- if ($thesepriv{$priviledge} ne 'F') {
- $thesepriv{$priviledge}.=$restrictions;
+ unless(grep(/^\Q$crsrole\E$/,@{$rolecodes})) {
+ push(@{$rolecodes},$crsrole);
}
}
+ my $rolekey = "$crsrole./$cdom/$cnum";
+ if ($crssec ne '') {
+ $rolekey .= "/$crssec";
+ }
+ $rolekey .= './';
+ $groups_roles->{$rolekey} = $rolecodes;
}
- } split(/:/,$allroles{$_});
- $thesestr='';
- map { $thesestr.=':'.$_.'&'.$thesepriv{$_}; } keys %thesepriv;
- $userroles.='user.priv.'.$_.'='.$thesestr."\n";
- } keys %allroles;
+ }
+ }
+ }
+ return;
+}
+
+sub delete_env_groupprivs {
+ my ($where,$courseroles,$possroles) = @_;
+ return unless((ref($courseroles) eq 'HASH') && (ref($possroles) eq 'ARRAY'));
+ my ($dummy,$udom,$uname,$group) = split(/\//,$where);
+ unless (ref($courseroles->{$udom}) eq 'HASH') {
+ %{$courseroles->{$udom}} =
+ &get_my_roles('','','userroles',['active'],
+ $possroles,[$udom],1);
+ }
+ if (ref($courseroles->{$udom}) eq 'HASH') {
+ foreach my $item (keys(%{$courseroles->{$udom}})) {
+ my ($cnum,$cdom,$crsrole,$crssec) = split(/:/,$item);
+ my $area = '/'.$cdom.'/'.$cnum;
+ my $privkey = "user.priv.$crsrole.$area";
+ if ($crssec ne '') {
+ $privkey .= '/'.$crssec;
+ }
+ $privkey .= ".$area/$group";
+ &Apache::lonnet::delenv($privkey,undef,[$crsrole]);
+ }
+ }
+ return;
+}
+
+sub check_adhoc_privs {
+ my ($cdom,$cnum,$update,$refresh,$now,$checkrole,$caller) = @_;
+ my $cckey = 'user.role.'.$checkrole.'./'.$cdom.'/'.$cnum;
+ if ($env{$cckey}) {
+ my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend);
+ &role_status($cckey,$update,$refresh,$now,\$role,\$where,\$trolecode,\$tstatus,\$tstart,\$tend);
+ unless (($tstatus eq 'is') || ($tstatus eq 'will_not')) {
+ &set_adhoc_privileges($cdom,$cnum,$checkrole,$caller);
+ }
+ } else {
+ &set_adhoc_privileges($cdom,$cnum,$checkrole,$caller);
+ }
+}
+
+sub set_adhoc_privileges {
+# role can be cc or ca
+ my ($dcdom,$pickedcourse,$role,$caller) = @_;
+ my $area = '/'.$dcdom.'/'.$pickedcourse;
+ my $spec = $role.'.'.$area;
+ my %userroles = &set_arearole($role,$area,'','',$env{'user.domain'},
+ $env{'user.name'});
+ my %ccrole = ();
+ &standard_roleprivs(\%ccrole,$role,$dcdom,$spec,$pickedcourse,$area);
+ my ($author,$adv)= &set_userprivs(\%userroles,\%ccrole);
+ &appenv(\%userroles,[$role,'cm']);
+ &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$role);
+ unless ($caller eq 'constructaccess' && $env{'request.course.id'}) {
+ &appenv( {'request.role' => $spec,
+ 'request.role.domain' => $dcdom,
+ 'request.course.sec' => ''
+ }
+ );
+ my $tadv=0;
+ if (&allowed('adv') eq 'F') { $tadv=1; }
+ &appenv({'request.role.adv' => $tadv});
}
- return $userroles;
}
# --------------------------------------------------------------- get interface
sub get {
- my ($namespace,@storearr)=@_;
+ my ($namespace,$storearr,$udomain,$uname)=@_;
my $items='';
- map {
- $items.=escape($_).'&';
- } @storearr;
+ foreach my $item (@$storearr) {
+ $items.=&escape($item).'&';
+ }
$items=~s/\&$//;
- my $rep=reply("get:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
- $ENV{'user.home'});
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+
+ my $rep=&reply("get:$udomain:$uname:$namespace:$items",$uhome);
my @pairs=split(/\&/,$rep);
+ if ( $#pairs==0 && $pairs[0] =~ /^(con_lost|error|no_such_host)/i) {
+ return @pairs;
+ }
my %returnhash=();
my $i=0;
- map {
- $returnhash{$_}=unescape($pairs[$i]);
+ foreach my $item (@$storearr) {
+ $returnhash{$item}=&thaw_unescape($pairs[$i]);
$i++;
- } @storearr;
+ }
return %returnhash;
}
# --------------------------------------------------------------- del interface
sub del {
- my ($namespace,@storearr)=@_;
+ my ($namespace,$storearr,$udomain,$uname)=@_;
my $items='';
- map {
- $items.=escape($_).'&';
- } @storearr;
+ foreach my $item (@$storearr) {
+ $items.=&escape($item).'&';
+ }
+
$items=~s/\&$//;
- return reply("del:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
- $ENV{'user.home'});
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ return &reply("del:$udomain:$uname:$namespace:$items",$uhome);
}
# -------------------------------------------------------------- dump interface
sub dump {
- my $namespace=shift;
- my $rep=reply("dump:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace",
- $ENV{'user.home'});
+ my ($namespace,$udomain,$uname,$regexp,$range,$extra)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ if ($regexp) {
+ $regexp=&escape($regexp);
+ } else {
+ $regexp='.';
+ }
+ my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range:$extra",$uhome);
+ my @pairs=split(/\&/,$rep);
+ my %returnhash=();
+ 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 {
+ my ($namespace,$udomain,$uname,$regexp,$range)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ if ($regexp) {
+ $regexp=&escape($regexp);
+ } else {
+ $regexp='.';
+ }
+ my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
my @pairs=split(/\&/,$rep);
my %returnhash=();
- map {
- my ($key,$value)=split(/=/,$_);
- $returnhash{unescape($key)}=unescape($value);
- } @pairs;
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/=/,$item,2);
+ next if ($key =~ /^error: 2 /);
+ $returnhash{$key}=&thaw_unescape($value);
+ }
return %returnhash;
}
+# -------------------------------------------------------------- keys interface
+
+sub getkeys {
+ my ($namespace,$udomain,$uname)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ my $rep=reply("keys:$udomain:$uname:$namespace",$uhome);
+ my @keyarray=();
+ foreach my $key (split(/\&/,$rep)) {
+ next if ($key =~ /^error: 2 /);
+ push(@keyarray,&unescape($key));
+ }
+ return @keyarray;
+}
+
+# --------------------------------------------------------------- currentdump
+sub currentdump {
+ my ($courseid,$sdom,$sname)=@_;
+ $courseid = $env{'request.course.id'} if (! defined($courseid));
+ $sdom = $env{'user.domain'} if (! defined($sdom));
+ $sname = $env{'user.name'} if (! defined($sname));
+ my $uhome = &homeserver($sname,$sdom);
+ my $rep=reply('currentdump:'.$sdom.':'.$sname.':'.$courseid,$uhome);
+ return if ($rep =~ /^(error:|no_such_host)/);
+ #
+ my %returnhash=();
+ #
+ 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,'.');
+ return if ($tmp[0] =~ /^(error:|no_such_host)/);
+ my %hash = @tmp;
+ @tmp=();
+ %returnhash = %{&convert_dump_to_currentdump(\%hash)};
+ } else {
+ my @pairs=split(/\&/,$rep);
+ foreach my $pair (@pairs) {
+ my ($key,$value)=split(/=/,$pair,2);
+ my ($symb,$param) = split(/:/,$key);
+ $returnhash{&unescape($symb)}->{&unescape($param)} =
+ &thaw_unescape($value);
+ }
+ }
+ return %returnhash;
+}
+
+sub convert_dump_to_currentdump{
+ my %hash = %{shift()};
+ my %returnhash;
+ # Code ripped from lond, essentially. The only difference
+ # here is the unescaping done by lonnet::dump(). Conceivably
+ # we might run in to problems with parameter names =~ /^v\./
+ while (my ($key,$value) = each(%hash)) {
+ my ($v,$symb,$param) = split(/:/,$key);
+ $symb = &unescape($symb);
+ $param = &unescape($param);
+ next if ($v eq 'version' || $symb eq 'keys');
+ next if (exists($returnhash{$symb}) &&
+ exists($returnhash{$symb}->{$param}) &&
+ $returnhash{$symb}->{'v.'.$param} > $v);
+ $returnhash{$symb}->{$param}=$value;
+ $returnhash{$symb}->{'v.'.$param}=$v;
+ }
+ #
+ # Remove all of the keys in the hashes which keep track of
+ # the version of the parameter.
+ while (my ($symb,$param_hash) = each(%returnhash)) {
+ # use a foreach because we are going to delete from the hash.
+ foreach my $key (keys(%$param_hash)) {
+ delete($param_hash->{$key}) if ($key =~ /^v\./);
+ }
+ }
+ return \%returnhash;
+}
+
+# ------------------------------------------------------ critical inc interface
+
+sub cinc {
+ return &inc(@_,'critical');
+}
+
+# --------------------------------------------------------------- inc interface
+
+sub inc {
+ my ($namespace,$store,$udomain,$uname,$critical) = @_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ my $items='';
+ if (! ref($store)) {
+ # got a single value, so use that instead
+ $items = &escape($store).'=&';
+ } elsif (ref($store) eq 'SCALAR') {
+ $items = &escape($$store).'=&';
+ } elsif (ref($store) eq 'ARRAY') {
+ $items = join('=&',map {&escape($_);} @{$store});
+ } elsif (ref($store) eq 'HASH') {
+ while (my($key,$value) = each(%{$store})) {
+ $items.= &escape($key).'='.&escape($value).'&';
+ }
+ }
+ $items=~s/\&$//;
+ if ($critical) {
+ return &critical("inc:$udomain:$uname:$namespace:$items",$uhome);
+ } else {
+ return &reply("inc:$udomain:$uname:$namespace:$items",$uhome);
+ }
+}
+
# --------------------------------------------------------------- put interface
sub put {
- my ($namespace,%storehash)=@_;
+ my ($namespace,$storehash,$udomain,$uname)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ my $items='';
+ foreach my $item (keys(%$storehash)) {
+ $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&';
+ }
+ $items=~s/\&$//;
+ return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
+}
+
+# ------------------------------------------------------------ newput interface
+
+sub newput {
+ my ($namespace,$storehash,$udomain,$uname)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ my $items='';
+ foreach my $key (keys(%$storehash)) {
+ $items.=&escape($key).'='.&freeze_escape($$storehash{$key}).'&';
+ }
+ $items=~s/\&$//;
+ return &reply("newput:$udomain:$uname:$namespace:$items",$uhome);
+}
+
+# --------------------------------------------------------- putstore interface
+
+sub putstore {
+ my ($namespace,$symb,$version,$storehash,$udomain,$uname)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
my $items='';
- map {
- $items.=escape($_).'='.escape($storehash{$_}).'&';
- } keys %storehash;
+ foreach my $key (keys(%$storehash)) {
+ $items.= &escape($key).'='.&freeze_escape($storehash->{$key}).'&';
+ }
$items=~s/\&$//;
- return reply("put:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
- $ENV{'user.home'});
+ my $esc_symb=&escape($symb);
+ my $esc_v=&escape($version);
+ my $reply =
+ &reply("putstore:$udomain:$uname:$namespace:$esc_symb:$esc_v:$items",
+ $uhome);
+ if ($reply eq 'unknown_cmd') {
+ # gfall back to way things use to be done
+ return &old_putstore($namespace,$symb,$version,$storehash,$udomain,
+ $uname);
+ }
+ return $reply;
+}
+
+sub old_putstore {
+ my ($namespace,$symb,$version,$storehash,$udomain,$uname)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ my %newstorehash;
+ foreach my $item (keys(%$storehash)) {
+ my $key = $version.':'.&escape($symb).':'.$item;
+ $newstorehash{$key} = $storehash->{$item};
+ }
+ my $items='';
+ my %allitems = ();
+ foreach my $item (keys(%newstorehash)) {
+ if ($item =~ m/^([^\:]+):([^\:]+):([^\:]+)$/) {
+ my $key = $1.':keys:'.$2;
+ $allitems{$key} .= $3.':';
+ }
+ $items.=$item.'='.&freeze_escape($newstorehash{$item}).'&';
+ }
+ foreach my $item (keys(%allitems)) {
+ $allitems{$item} =~ s/\:$//;
+ $items.= $item.'='.$allitems{$item}.'&';
+ }
+ $items=~s/\&$//;
+ return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
}
# ------------------------------------------------------ critical put interface
sub cput {
- my ($namespace,%storehash)=@_;
+ my ($namespace,$storehash,$udomain,$uname)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
my $items='';
- map {
- $items.=escape($_).'='.escape($storehash{$_}).'&';
- } keys %storehash;
+ foreach my $item (keys(%$storehash)) {
+ $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&';
+ }
$items=~s/\&$//;
- return critical
- ("put:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
- $ENV{'user.home'});
+ return &critical("put:$udomain:$uname:$namespace:$items",$uhome);
}
# -------------------------------------------------------------- eget interface
sub eget {
- my ($namespace,@storearr)=@_;
+ my ($namespace,$storearr,$udomain,$uname)=@_;
my $items='';
- map {
- $items.=escape($_).'&';
- } @storearr;
+ foreach my $item (@$storearr) {
+ $items.=&escape($item).'&';
+ }
$items=~s/\&$//;
- my $rep=reply("eget:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
- $ENV{'user.home'});
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ my $rep=&reply("eget:$udomain:$uname:$namespace:$items",$uhome);
my @pairs=split(/\&/,$rep);
my %returnhash=();
my $i=0;
- map {
- $returnhash{$_}=unescape($pairs[$i]);
+ foreach my $item (@$storearr) {
+ $returnhash{$item}=&thaw_unescape($pairs[$i]);
$i++;
- } @storearr;
+ }
return %returnhash;
}
-# ------------------------------------------------- Check for a user priviledge
+# ------------------------------------------------------------ tmpput interface
+sub tmpput {
+ my ($storehash,$server,$context)=@_;
+ my $items='';
+ foreach my $item (keys(%$storehash)) {
+ $items.=&escape($item).'='.&freeze_escape($$storehash{$item}).'&';
+ }
+ $items=~s/\&$//;
+ if (defined($context)) {
+ $items .= ':'.&escape($context);
+ }
+ return &reply("tmpput:$items",$server);
+}
+
+# ------------------------------------------------------------ tmpget interface
+sub tmpget {
+ my ($token,$server)=@_;
+ if (!defined($server)) { $server = $perlvar{'lonHostID'}; }
+ my $rep=&reply("tmpget:$token",$server);
+ my %returnhash;
+ foreach my $item (split(/\&/,$rep)) {
+ my ($key,$value)=split(/=/,$item);
+ next if ($key =~ /^error: 2 /);
+ $returnhash{&unescape($key)}=&thaw_unescape($value);
+ }
+ return %returnhash;
+}
-sub allowed {
+# ------------------------------------------------------------ tmpdel interface
+sub tmpdel {
+ my ($token,$server)=@_;
+ if (!defined($server)) { $server = $perlvar{'lonHostID'}; }
+ return &reply("tmpdel:$token",$server);
+}
+
+# -------------------------------------------------- portfolio access checking
+
+sub portfolio_access {
+ my ($requrl) = @_;
+ my (undef,$udom,$unum,$file_name,$group) = &parse_portfolio_url($requrl);
+ my $result = &get_portfolio_access($udom,$unum,$file_name,$group);
+ if ($result) {
+ my %setters;
+ if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
+ my ($startblock,$endblock) =
+ &Apache::loncommon::blockcheck(\%setters,'port',$unum,$udom);
+ if ($startblock && $endblock) {
+ return 'B';
+ }
+ } else {
+ my ($startblock,$endblock) =
+ &Apache::loncommon::blockcheck(\%setters,'port');
+ if ($startblock && $endblock) {
+ return 'B';
+ }
+ }
+ }
+ if ($result eq 'ok') {
+ return 'F';
+ } elsif ($result =~ /^[^:]+:guest_/) {
+ return 'A';
+ }
+ return '';
+}
+
+sub get_portfolio_access {
+ my ($udom,$unum,$file_name,$group,$access_hash) = @_;
+
+ if (!ref($access_hash)) {
+ my $current_perms = &get_portfile_permissions($udom,$unum);
+ my %access_controls = &get_access_controls($current_perms,$group,
+ $file_name);
+ $access_hash = $access_controls{$file_name};
+ }
+
+ my ($public,$guest,@domains,@users,@courses,@groups);
+ my $now = time;
+ if (ref($access_hash) eq 'HASH') {
+ foreach my $key (keys(%{$access_hash})) {
+ my ($num,$scope,$end,$start) = ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
+ if ($start > $now) {
+ next;
+ }
+ if ($end && $end<$now) {
+ next;
+ }
+ if ($scope eq 'public') {
+ $public = $key;
+ last;
+ } elsif ($scope eq 'guest') {
+ $guest = $key;
+ } elsif ($scope eq 'domains') {
+ push(@domains,$key);
+ } elsif ($scope eq 'users') {
+ push(@users,$key);
+ } elsif ($scope eq 'course') {
+ push(@courses,$key);
+ } elsif ($scope eq 'group') {
+ push(@groups,$key);
+ }
+ }
+ if ($public) {
+ return 'ok';
+ }
+ if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
+ if ($guest) {
+ return $guest;
+ }
+ } else {
+ if (@domains > 0) {
+ foreach my $domkey (@domains) {
+ if (ref($access_hash->{$domkey}{'dom'}) eq 'ARRAY') {
+ if (grep(/^\Q$env{'user.domain'}\E$/,@{$access_hash->{$domkey}{'dom'}})) {
+ return 'ok';
+ }
+ }
+ }
+ }
+ if (@users > 0) {
+ foreach my $userkey (@users) {
+ if (ref($access_hash->{$userkey}{'users'}) eq 'ARRAY') {
+ foreach my $item (@{$access_hash->{$userkey}{'users'}}) {
+ if (ref($item) eq 'HASH') {
+ if (($item->{'uname'} eq $env{'user.name'}) &&
+ ($item->{'udom'} eq $env{'user.domain'})) {
+ return 'ok';
+ }
+ }
+ }
+ }
+ }
+ }
+ my %roleshash;
+ my @courses_and_groups = @courses;
+ push(@courses_and_groups,@groups);
+ if (@courses_and_groups > 0) {
+ my (%allgroups,%allroles);
+ my ($start,$end,$role,$sec,$group);
+ foreach my $envkey (%env) {
+ if ($envkey =~ m-^user\.role\.(gr|cc|co|in|ta|ep|ad|st)\./($match_domain)/($match_courseid)/?([^/]*)$-) {
+ my $cid = $2.'_'.$3;
+ if ($1 eq 'gr') {
+ $group = $4;
+ $allgroups{$cid}{$group} = $env{$envkey};
+ } else {
+ if ($4 eq '') {
+ $sec = 'none';
+ } else {
+ $sec = $4;
+ }
+ $allroles{$cid}{$1}{$sec} = $env{$envkey};
+ }
+ } elsif ($envkey =~ m-^user\.role\./cr/($match_domain/$match_username/\w*)./($match_domain)/($match_courseid)/?([^/]*)$-) {
+ my $cid = $2.'_'.$3;
+ if ($4 eq '') {
+ $sec = 'none';
+ } else {
+ $sec = $4;
+ }
+ $allroles{$cid}{$1}{$sec} = $env{$envkey};
+ }
+ }
+ if (keys(%allroles) == 0) {
+ return;
+ }
+ foreach my $key (@courses_and_groups) {
+ my %content = %{$$access_hash{$key}};
+ my $cnum = $content{'number'};
+ my $cdom = $content{'domain'};
+ my $cid = $cdom.'_'.$cnum;
+ if (!exists($allroles{$cid})) {
+ next;
+ }
+ foreach my $role_id (keys(%{$content{'roles'}})) {
+ my @sections = @{$content{'roles'}{$role_id}{'section'}};
+ my @groups = @{$content{'roles'}{$role_id}{'group'}};
+ my @status = @{$content{'roles'}{$role_id}{'access'}};
+ my @roles = @{$content{'roles'}{$role_id}{'role'}};
+ foreach my $role (keys(%{$allroles{$cid}})) {
+ if ((grep/^all$/,@roles) || (grep/^\Q$role\E$/,@roles)) {
+ foreach my $sec (keys(%{$allroles{$cid}{$role}})) {
+ if (&course_group_datechecker($allroles{$cid}{$role}{$sec},$now,\@status) eq 'ok') {
+ if (grep/^all$/,@sections) {
+ return 'ok';
+ } else {
+ if (grep/^$sec$/,@sections) {
+ return 'ok';
+ }
+ }
+ }
+ }
+ if (keys(%{$allgroups{$cid}}) == 0) {
+ if (grep/^none$/,@groups) {
+ return 'ok';
+ }
+ } else {
+ if (grep/^all$/,@groups) {
+ return 'ok';
+ }
+ foreach my $group (keys(%{$allgroups{$cid}})) {
+ if (grep/^$group$/,@groups) {
+ return 'ok';
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if ($guest) {
+ return $guest;
+ }
+ }
+ }
+ return;
+}
+
+sub course_group_datechecker {
+ my ($dates,$now,$status) = @_;
+ my ($start,$end) = split(/\./,$dates);
+ if (!$start && !$end) {
+ return 'ok';
+ }
+ if (grep/^active$/,@{$status}) {
+ if (((!$start) || ($start && $start <= $now)) && ((!$end) || ($end && $end >= $now))) {
+ return 'ok';
+ }
+ }
+ if (grep/^previous$/,@{$status}) {
+ if ($end > $now ) {
+ return 'ok';
+ }
+ }
+ if (grep/^future$/,@{$status}) {
+ if ($start > $now) {
+ return 'ok';
+ }
+ }
+ return;
+}
+
+sub parse_portfolio_url {
+ my ($url) = @_;
+
+ my ($type,$udom,$unum,$group,$file_name);
+
+ if ($url =~ m-^/*(?:uploaded|editupload)/($match_domain)/($match_username)/portfolio(/.+)$-) {
+ $type = 1;
+ $udom = $1;
+ $unum = $2;
+ $file_name = $3;
+ } elsif ($url =~ m-^/*(?:uploaded|editupload)/($match_domain)/($match_courseid)/groups/([^/]+)/portfolio/(.+)$-) {
+ $type = 2;
+ $udom = $1;
+ $unum = $2;
+ $group = $3;
+ $file_name = $3.'/'.$4;
+ }
+ if (wantarray) {
+ return ($type,$udom,$unum,$file_name,$group);
+ }
+ return $type;
+}
+
+sub is_portfolio_url {
+ my ($url) = @_;
+ return scalar(&parse_portfolio_url($url));
+}
+
+sub is_portfolio_file {
+ my ($file) = @_;
+ if (($file =~ /^portfolio/) || ($file =~ /^groups\/\w+\/portfolio/)) {
+ return 1;
+ }
+ return;
+}
+
+sub usertools_access {
+ my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref)=@_;
+ my ($access,%tools);
+ if ($context eq '') {
+ $context = 'tools';
+ }
+ if ($context eq 'requestcourses') {
+ %tools = (
+ official => 1,
+ unofficial => 1,
+ community => 1,
+ );
+ } else {
+ %tools = (
+ aboutme => 1,
+ blog => 1,
+ portfolio => 1,
+ );
+ }
+ return if (!defined($tools{$tool}));
+
+ if ((!defined($udom)) || (!defined($uname))) {
+ $udom = $env{'user.domain'};
+ $uname = $env{'user.name'};
+ }
+
+ if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
+ if ($action ne 'reload') {
+ if ($context eq 'requestcourses') {
+ return $env{'environment.canrequest.'.$tool};
+ } else {
+ return $env{'environment.availabletools.'.$tool};
+ }
+ }
+ }
+
+ my ($toolstatus,$inststatus);
+
+ if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'}) &&
+ ($action ne 'reload')) {
+ $toolstatus = $env{'environment.'.$context.'.'.$tool};
+ $inststatus = $env{'environment.inststatus'};
+ } else {
+ if (ref($userenvref) eq 'HASH') {
+ $toolstatus = $userenvref->{$context.'.'.$tool};
+ $inststatus = $userenvref->{'inststatus'};
+ } else {
+ my %userenv = &userenvironment($udom,$uname,$context.'.'.$tool,'inststatus');
+ $toolstatus = $userenv{$context.'.'.$tool};
+ $inststatus = $userenv{'inststatus'};
+ }
+ }
+
+ if ($toolstatus ne '') {
+ if ($toolstatus) {
+ $access = 1;
+ } else {
+ $access = 0;
+ }
+ return $access;
+ }
+
+ my ($is_adv,%domdef);
+ if (ref($is_advref) eq 'HASH') {
+ $is_adv = $is_advref->{'is_adv'};
+ } else {
+ $is_adv = &is_advanced_user($udom,$uname);
+ }
+ if (ref($domdefref) eq 'HASH') {
+ %domdef = %{$domdefref};
+ } else {
+ %domdef = &get_domain_defaults($udom);
+ }
+ if (ref($domdef{$tool}) eq 'HASH') {
+ if ($is_adv) {
+ if ($domdef{$tool}{'_LC_adv'} ne '') {
+ if ($domdef{$tool}{'_LC_adv'}) {
+ $access = 1;
+ } else {
+ $access = 0;
+ }
+ return $access;
+ }
+ }
+ if ($inststatus ne '') {
+ my ($hasaccess,$hasnoaccess);
+ foreach my $affiliation (split(/:/,$inststatus)) {
+ if ($domdef{$tool}{$affiliation} ne '') {
+ if ($domdef{$tool}{$affiliation}) {
+ $hasaccess = 1;
+ } else {
+ $hasnoaccess = 1;
+ }
+ }
+ }
+ if ($hasaccess || $hasnoaccess) {
+ if ($hasaccess) {
+ $access = 1;
+ } elsif ($hasnoaccess) {
+ $access = 0;
+ }
+ return $access;
+ }
+ } else {
+ if ($domdef{$tool}{'default'} ne '') {
+ if ($domdef{$tool}{'default'}) {
+ $access = 1;
+ } elsif ($domdef{$tool}{'default'} == 0) {
+ $access = 0;
+ }
+ return $access;
+ }
+ }
+ } else {
+ if ($context eq 'tools') {
+ $access = 1;
+ } else {
+ $access = 0;
+ }
+ return $access;
+ }
+}
+
+sub is_course_owner {
+ my ($cdom,$cnum,$udom,$uname) = @_;
+ if (($udom eq '') || ($uname eq '')) {
+ $udom = $env{'user.domain'};
+ $uname = $env{'user.name'};
+ }
+ unless (($udom eq '') || ($uname eq '')) {
+ if (exists($env{'course.'.$cdom.'_'.$cnum.'.internal.courseowner'})) {
+ if ($env{'course.'.$cdom.'_'.$cnum.'.internal.courseowner'} eq $uname.':'.$udom) {
+ return 1;
+ } else {
+ my %courseinfo = &Apache::lonnet::coursedescription($cdom.'/'.$cnum);
+ if ($courseinfo{'internal.courseowner'} eq $uname.':'.$udom) {
+ return 1;
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub is_advanced_user {
+ my ($udom,$uname) = @_;
+ if ($udom ne '' && $uname ne '') {
+ if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
+ 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,$is_author);
+ foreach my $role (keys(%roleshash)) {
+ my ($trest,$tdomain,$trole,$sec) = split(/:/,$role);
+ my $area = '/'.$tdomain.'/'.$trest;
+ if ($sec ne '') {
+ $area .= '/'.$sec;
+ }
+ if (($area ne '') && ($trole ne '')) {
+ my $spec=$trole.'.'.$area;
+ if ($trole =~ /^cr\//) {
+ &custom_roleprivs(\%allroles,$trole,$tdomain,$trest,$spec,$area);
+ } elsif ($trole ne 'gr') {
+ &standard_roleprivs(\%allroles,$trole,$tdomain,$spec,$trest,$area);
+ }
+ if ($trole eq 'au') {
+ $is_author = 1;
+ }
+ }
+ }
+ foreach my $role (keys(%allroles)) {
+ last if ($is_adv);
+ foreach my $item (split(/:/,$allroles{$role})) {
+ if ($item ne '') {
+ my ($privilege,$restrictions)=split(/&/,$item);
+ if ($privilege eq 'adv') {
+ $is_adv = 1;
+ last;
+ }
+ }
+ }
+ }
+ if (wantarray) {
+ return ($is_adv,$is_author);
+ }
+ return $is_adv;
+}
+
+sub check_can_request {
+ my ($dom,$can_request,$request_domains) = @_;
+ my $canreq = 0;
+ my ($types,$typename) = &Apache::loncommon::course_types();
+ my @options = ('approval','validate','autolimit');
+ my $optregex = join('|',@options);
+ if ((ref($can_request) eq 'HASH') && (ref($types) eq 'ARRAY')) {
+ foreach my $type (@{$types}) {
+ if (&usertools_access($env{'user.name'},
+ $env{'user.domain'},
+ $type,undef,'requestcourses')) {
+ $canreq ++;
+ if (ref($request_domains) eq 'HASH') {
+ push(@{$request_domains->{$type}},$env{'user.domain'});
+ }
+ if ($dom eq $env{'user.domain'}) {
+ $can_request->{$type} = 1;
+ }
+ }
+ if ($env{'environment.reqcrsotherdom.'.$type} ne '') {
+ my @curr = split(',',$env{'environment.reqcrsotherdom.'.$type});
+ if (@curr > 0) {
+ foreach my $item (@curr) {
+ if (ref($request_domains) eq 'HASH') {
+ my ($otherdom) = ($item =~ /^($match_domain):($optregex)(=?\d*)$/);
+ if ($otherdom ne '') {
+ if (ref($request_domains->{$type}) eq 'ARRAY') {
+ unless (grep(/^\Q$otherdom\E$/,@{$request_domains->{$type}})) {
+ push(@{$request_domains->{$type}},$otherdom);
+ }
+ } else {
+ push(@{$request_domains->{$type}},$otherdom);
+ }
+ }
+ }
+ }
+ unless($dom eq $env{'user.domain'}) {
+ $canreq ++;
+ if (grep(/^\Q$dom\E:($optregex)(=?\d*)$/,@curr)) {
+ $can_request->{$type} = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ return $canreq;
+}
+
+# ---------------------------------------------- Custom access rule evaluation
+
+sub customaccess {
my ($priv,$uri)=@_;
+ my ($urole,$urealm)=split(/\./,$env{'request.role'},2);
+ my (undef,$udom,$ucrs,$usec)=split(/\//,$urealm);
+ $udom = &LONCAPA::clean_domain($udom);
+ $ucrs = &LONCAPA::clean_username($ucrs);
+ my $access=0;
+ foreach my $right (split(/\s*\,\s*/,&metadata($uri,'rule_rights'))) {
+ my ($effect,$realm,$role,$type)=split(/\:/,$right);
+ if ($type eq 'user') {
+ foreach my $scope (split(/\s*\,\s*/,$realm)) {
+ my ($tdom,$tuname)=split(m{/},$scope);
+ if ($tdom) {
+ if ($tdom ne $env{'user.domain'}) { next; }
+ }
+ if ($tuname) {
+ if ($tuname ne $env{'user.name'}) { next; }
+ }
+ $access=($effect eq 'allow');
+ last;
+ }
+ } else {
+ if ($role) {
+ if ($role ne $urole) { next; }
+ }
+ foreach my $scope (split(/\s*\,\s*/,$realm)) {
+ my ($tdom,$tcrs,$tsec)=split(/\_/,$scope);
+ if ($tdom) {
+ if ($tdom ne $udom) { next; }
+ }
+ if ($tcrs) {
+ if ($tcrs ne $ucrs) { next; }
+ }
+ if ($tsec) {
+ if ($tsec ne $usec) { next; }
+ }
+ $access=($effect eq 'allow');
+ last;
+ }
+ if ($realm eq '' && $role eq '') {
+ $access=($effect eq 'allow');
+ }
+ }
+ }
+ return $access;
+}
+
+# ------------------------------------------------- Check for a user privilege
+
+sub allowed {
+ my ($priv,$uri,$symb,$role)=@_;
+ my $ver_orguri=$uri;
+ $uri=&deversion($uri);
+ my $orguri=$uri;
$uri=&declutter($uri);
-# Free bre access to adm and meta resources
+ if ($priv eq 'evb') {
+# Evade communication block restrictions for specified role in a course
+ if ($env{'user.priv.'.$role} =~/evb\&([^\:]*)/) {
+ return $1;
+ } else {
+ return;
+ }
+ }
- if ((($uri=~/^adm\//) || ($uri=~/\.meta$/)) && ($priv eq 'bre')) {
+ if (defined($env{'allowed.'.$priv})) { return $env{'allowed.'.$priv}; }
+# Free bre access to adm and meta resources
+ if (((($uri=~/^adm\//) && ($uri !~ m{/(?:smppg|bulletinboard)$}))
+ || (($uri=~/\.meta$/) && ($uri!~m|^uploaded/|) ))
+ && ($priv eq 'bre')) {
return 'F';
}
+# Free bre access to user's own portfolio contents
+ my ($space,$domain,$name,@dir)=split('/',$uri);
+ if (($space=~/^(uploaded|editupload)$/) && ($env{'user.name'} eq $name) &&
+ ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir[0])) {
+ my %setters;
+ my ($startblock,$endblock) =
+ &Apache::loncommon::blockcheck(\%setters,'port');
+ if ($startblock && $endblock) {
+ return 'B';
+ } else {
+ return 'F';
+ }
+ }
+
+# bre access to group portfolio for rgf priv in group, or mdg or vcg in course.
+ if (($space=~/^(uploaded|editupload)$/) && ($dir[0] eq 'groups')
+ && ($dir[2] eq 'portfolio') && ($priv eq 'bre')) {
+ if (exists($env{'request.course.id'})) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ if (($domain eq $cdom) && ($name eq $cnum)) {
+ my $courseprivid=$env{'request.course.id'};
+ $courseprivid=~s/\_/\//;
+ if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid
+ .'/'.$dir[1]} =~/rgf\&([^\:]*)/) {
+ return $1;
+ } else {
+ if ($env{'request.course.sec'}) {
+ $courseprivid.='/'.$env{'request.course.sec'};
+ }
+ if ($env{'user.priv.'.$env{'request.role'}.'./'.
+ $courseprivid} =~/(mdg|vcg)\&([^\:]*)/) {
+ return $2;
+ }
+ }
+ }
+ }
+ }
+
+# Free bre to public access
+
+ if ($priv eq 'bre') {
+ my $copyright=&metadata($uri,'copyright');
+ if (($copyright eq 'public') && (!$env{'request.course.id'})) {
+ return 'F';
+ }
+ if ($copyright eq 'priv') {
+ $uri=~/([^\/]+)\/([^\/]+)\//;
+ unless (($env{'user.name'} eq $2) && ($env{'user.domain'} eq $1)) {
+ return '';
+ }
+ }
+ if ($copyright eq 'domain') {
+ $uri=~/([^\/]+)\/([^\/]+)\//;
+ unless (($env{'user.domain'} eq $1) ||
+ ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $1)) {
+ return '';
+ }
+ }
+ if ($env{'request.role'}=~ /li\.\//) {
+ # Library role, so allow browsing of resources in this domain.
+ return 'F';
+ }
+ if ($copyright eq 'custom') {
+ unless (&customaccess($priv,$uri)) { return ''; }
+ }
+ }
+ # Domain coordinator is trying to create a course
+ if (($priv eq 'ccc') && ($env{'request.role'} =~ /^dc\./)) {
+ # uri is the requested domain in this case.
+ # comparison to 'request.role.domain' shows if the user has selected
+ # a role of dc for the domain in question.
+ return 'F' if ($uri eq $env{'request.role.domain'});
+ }
+
my $thisallowed='';
my $statecond=0;
my $courseprivid='';
+ my $ownaccess;
+ # Community Coordinator or Assistant Co-author browsing resource space.
+ if (($priv eq 'bro') && ($env{'user.author'})) {
+ if ($uri eq '') {
+ $ownaccess = 1;
+ } else {
+ if (($env{'user.domain'} ne '') && ($env{'user.name'} ne '')) {
+ my $udom = $env{'user.domain'};
+ my $uname = $env{'user.name'};
+ if ($uri =~ m{^\Q$udom\E/?$}) {
+ $ownaccess = 1;
+ } elsif ($uri =~ m{^\Q$udom\E/\Q$uname\E/?}) {
+ unless ($uri =~ m{\.\./}) {
+ $ownaccess = 1;
+ }
+ } elsif (($udom ne 'public') && ($uname ne 'public')) {
+ my $now = time;
+ if ($uri =~ m{^([^/]+)/?$}) {
+ my $adom = $1;
+ foreach my $key (keys(%env)) {
+ if ($key =~ m{^user\.role\.(ca|aa)/\Q$adom\E}) {
+ my ($start,$end) = split('.',$env{$key});
+ if (($now >= $start) && (!$end || $end < $now)) {
+ $ownaccess = 1;
+ last;
+ }
+ }
+ }
+ } elsif ($uri =~ m{^([^/]+)/([^/]+)/?}) {
+ my $adom = $1;
+ my $aname = $2;
+ foreach my $role ('ca','aa') {
+ if ($env{"user.role.$role./$adom/$aname"}) {
+ my ($start,$end) =
+ split('.',$env{"user.role.$role./$adom/$aname"});
+ if (($now >= $start) && (!$end || $end < $now)) {
+ $ownaccess = 1;
+ last;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
# Course
- if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'}=~/$priv\&([^\:]*)/) {
- $thisallowed.=$1;
+ if ($env{'user.priv.'.$env{'request.role'}.'./'}=~/\Q$priv\E\&([^\:]*)/) {
+ unless (($priv eq 'bro') && (!$ownaccess)) {
+ $thisallowed.=$1;
+ }
}
# Domain
- if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.(split(/\//,$uri))[0].'/'}
- =~/$priv\&([^\:]*)/) {
- $thisallowed.=$1;
+ if ($env{'user.priv.'.$env{'request.role'}.'./'.(split(/\//,$uri))[0].'/'}
+ =~/\Q$priv\E\&([^\:]*)/) {
+ unless (($priv eq 'bro') && (!$ownaccess)) {
+ $thisallowed.=$1;
+ }
}
# Course: uri itself is a course
@@ -942,75 +5914,123 @@ sub allowed {
$courseuri=~s/\_(\d)/\/$1/;
$courseuri=~s/^([^\/])/\/$1/;
- if ($ENV{'user.priv.'.$ENV{'request.role'}.'.'.$courseuri}
- =~/$priv\&([^\:]*)/) {
- $thisallowed.=$1;
+ if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseuri}
+ =~/\Q$priv\E\&([^\:]*)/) {
+ unless (($priv eq 'bro') && (!$ownaccess)) {
+ $thisallowed.=$1;
+ }
}
-# Full access at system, domain or course-wide level? Exit.
+# URI is an uploaded document for this course, default permissions don't matter
+# not allowing 'edit' access (editupload) to uploaded course docs
+ if (($priv eq 'bre') && ($uri=~m|^uploaded/|)) {
+ $thisallowed='';
+ my ($match)=&is_on_map($uri);
+ if ($match) {
+ if ($env{'user.priv.'.$env{'request.role'}.'./'}
+ =~/\Q$priv\E\&([^\:]*)/) {
+ $thisallowed.=$1;
+ }
+ } else {
+ my $refuri = $env{'httpref.'.$orguri} || $env{'httpref.'.$ver_orguri};
+ if ($refuri) {
+ if ($refuri =~ m|^/adm/|) {
+ $thisallowed='F';
+ } else {
+ $refuri=&declutter($refuri);
+ my ($match) = &is_on_map($refuri);
+ if ($match) {
+ $thisallowed='F';
+ }
+ }
+ }
+ }
+ }
+ if ($priv eq 'bre'
+ && $thisallowed ne 'F'
+ && $thisallowed ne '2'
+ && &is_portfolio_url($uri)) {
+ $thisallowed = &portfolio_access($uri);
+ }
+
+# Full access at system, domain or course-wide level? Exit.
if ($thisallowed=~/F/) {
return 'F';
}
# If this is generating or modifying users, exit with special codes
- if (':csu:cdc:ccc:cin:cta:cep:ccr:cst:cad:cli:cau:cdg:'=~/\:$priv\:/) {
+ if (':csu:cdc:ccc:cin:cta:cep:ccr:cst:cad:cli:cau:cdg:cca:caa:'=~/\:\Q$priv\E\:/) {
+ if (($priv eq 'cca') || ($priv eq 'caa')) {
+ my ($audom,$auname)=split('/',$uri);
+# no author name given, so this just checks on the general right to make a co-author in this domain
+ unless ($auname) { return $thisallowed; }
+# an author name is given, so we are about to actually make a co-author for a certain account
+ if (($auname ne $env{'user.name'} && $env{'request.role'} !~ /^dc\./) ||
+ (($audom ne $env{'user.domain'} && $env{'request.role'} !~ /^dc\./) &&
+ ($audom ne $env{'request.role.domain'}))) { return ''; }
+ }
return $thisallowed;
}
#
-# Gathered so far: system, domain and course wide priviledges
+# Gathered so far: system, domain and course wide privileges
#
# Course: See if uri or referer is an individual resource that is part of
# the course
- if ($ENV{'request.course.id'}) {
- $courseprivid=$ENV{'request.course.id'};
- if ($ENV{'request.course.sec'}) {
- $courseprivid.='/'.$ENV{'request.course.sec'};
+ if ($env{'request.course.id'}) {
+
+ $courseprivid=$env{'request.course.id'};
+ if ($env{'request.course.sec'}) {
+ $courseprivid.='/'.$env{'request.course.sec'};
}
$courseprivid=~s/\_/\//;
my $checkreferer=1;
- my @uriparts=split(/\//,$uri);
- my $filename=$uriparts[$#uriparts];
- my $pathname=$uri;
- $pathname=~s/\/$filename$//;
- if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
- /\&$filename\:([\d\|]+)\&/) {
- $statecond=$1;
- if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
- =~/$priv\&([^\:]*)/) {
+ my ($match,$cond)=&is_on_map($uri);
+ if ($match) {
+ $statecond=$cond;
+ if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid}
+ =~/\Q$priv\E\&([^\:]*)/) {
$thisallowed.=$1;
$checkreferer=0;
}
}
- if (($ENV{'HTTP_REFERER'}) && ($checkreferer)) {
- my $refuri=$ENV{'HTTP_REFERER'};
- $refuri=~s/^http\:\/\/$ENV{'request.host'}//i;
- $refuri=&declutter($refuri);
- my @uriparts=split(/\//,$refuri);
- my $filename=$uriparts[$#uriparts];
- my $pathname=$refuri;
- $pathname=~s/\/$filename$//;
- my @filenameparts=split(/\./,$uri);
- if (&fileembstyle($filenameparts[$#filenameparts]) ne 'ssi') {
- if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
- /\&$filename\:([\d\|]+)\&/) {
- my $refstatecond=$1;
- if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
- =~/$priv\&([^\:]*)/) {
+ if ($checkreferer) {
+ my $refuri=$env{'httpref.'.$orguri};
+ unless ($refuri) {
+ foreach my $key (keys(%env)) {
+ if ($key=~/^httpref\..*\*/) {
+ my $pattern=$key;
+ $pattern=~s/^httpref\.\/res\///;
+ $pattern=~s/\*/\[\^\/\]\+/g;
+ $pattern=~s/\//\\\//g;
+ if ($orguri=~/$pattern/) {
+ $refuri=$env{$key};
+ }
+ }
+ }
+ }
+
+ if ($refuri) {
+ $refuri=&declutter($refuri);
+ my ($match,$cond)=&is_on_map($refuri);
+ if ($match) {
+ my $refstatecond=$cond;
+ if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid}
+ =~/\Q$priv\E\&([^\:]*)/) {
$thisallowed.=$1;
$uri=$refuri;
$statecond=$refstatecond;
}
- }
}
+ }
}
}
#
-# Gathered now: all priviledges that could apply, and condition number
+# Gathered now: all privileges that could apply, and condition number
#
#
# Full or no access?
@@ -1038,39 +6058,39 @@ sub allowed {
my $envkey;
if ($thisallowed=~/L/) {
- foreach $envkey (keys %ENV) {
+ foreach $envkey (keys(%env)) {
if ($envkey=~/^user\.role\.(st|ta)\.([^\.]*)/) {
my $courseid=$2;
my $roleid=$1.'.'.$2;
$courseid=~s/^\///;
my $expiretime=600;
- if ($ENV{'request.role'} eq $roleid) {
+ if ($env{'request.role'} eq $roleid) {
$expiretime=120;
}
my ($cdom,$cnum,$csec)=split(/\//,$courseid);
my $prefix='course.'.$cdom.'_'.$cnum.'.';
- if ((time-$ENV{$prefix.'last_cache'})>$expiretime) {
- &coursedescription($courseid);
+ if ((time-$env{$prefix.'last_cache'})>$expiretime) {
+ &coursedescription($courseid,{'freshen_cache' => 1});
}
- if (($ENV{$prefix.'res.'.$uri.'.lock.sections'}=~/\,$csec\,/)
- || ($ENV{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {
- if ($ENV{$prefix.'res.'.$uri.'.lock.expire'}>time) {
- &log($ENV{'user.domain'},$ENV{'user.name'},
- $ENV{'user.host'},
+ if (($env{$prefix.'res.'.$uri.'.lock.sections'}=~/\,\Q$csec\E\,/)
+ || ($env{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {
+ if ($env{$prefix.'res.'.$uri.'.lock.expire'}>time) {
+ &log($env{'user.domain'},$env{'user.name'},
+ $env{'user.home'},
'Locked by res: '.$priv.' for '.$uri.' due to '.
$cdom.'/'.$cnum.'/'.$csec.' expire '.
- $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
+ $env{$prefix.'priv.'.$priv.'.lock.expire'});
return '';
}
}
- if (($ENV{$prefix.'priv.'.$priv.'.lock.sections'}=~/\,$csec\,/)
- || ($ENV{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {
- if ($ENV{'priv.'.$priv.'.lock.expire'}>time) {
- &log($ENV{'user.domain'},$ENV{'user.name'},
- $ENV{'user.host'},
+ if (($env{$prefix.'priv.'.$priv.'.lock.sections'}=~/\,\Q$csec\E\,/)
+ || ($env{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {
+ if ($env{'priv.'.$priv.'.lock.expire'}>time) {
+ &log($env{'user.domain'},$env{'user.name'},
+ $env{'user.home'},
'Locked by priv: '.$priv.' for '.$uri.' due to '.
$cdom.'/'.$cnum.'/'.$csec.' expire '.
- $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
+ $env{$prefix.'priv.'.$priv.'.lock.expire'});
return '';
}
}
@@ -1082,8 +6102,14 @@ sub allowed {
# Rest of the restrictions depend on selected course
#
- unless ($ENV{'request.course.id'}) {
- return '1';
+ unless ($env{'request.course.id'}) {
+ if ($thisallowed eq 'A') {
+ return 'A';
+ } elsif ($thisallowed eq 'B') {
+ return 'B';
+ } else {
+ return '1';
+ }
}
#
@@ -1094,12 +6120,25 @@ sub allowed {
# Course preferences
if ($thisallowed=~/C/) {
- my $rolecode=(split(/\./,$ENV{'request.role'}))[0];
- if ($ENV{'course.'.$ENV{'request.course.id'}.'.'.$priv.'.roles.denied'}
- =~/\,$rolecode\,/) {
- &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},
- 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
- $ENV{'request.course.id'});
+ my $rolecode=(split(/\./,$env{'request.role'}))[0];
+ 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')) {
+ &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
+ 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
+ $env{'request.course.id'});
+ }
+ return '';
+ }
+
+ if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}
+ =~/\Q$unamedom\E/) {
+ 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'});
+ }
return '';
}
}
@@ -1107,27 +6146,25 @@ sub allowed {
# Resource preferences
if ($thisallowed=~/R/) {
- my $rolecode=(split(/\./,$ENV{'request.role'}))[0];
- my $filename=$perlvar{'lonDocRoot'}.'/res/'.$uri.'.meta';
- if (-e $filename) {
- my @content;
- {
- my $fh=Apache::File->new($filename);
- @content=<$fh>;
+ my $rolecode=(split(/\./,$env{'request.role'}))[0];
+ if (&metadata($uri,'roledeny')=~/\Q$rolecode\E/) {
+ 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);
}
- if (join('',@content)=~
- /\'.$announcement.'
'.$space.' - '.$qualifier.' - '.$spacequalifierrest;
+
# ----------------------------------------------------- Cascading lookup scheme
- my $symbp=&symbread();
- my $mapp=(split(/\_\_\_/,$symbp))[0];
+ my $symbp=$symbparm;
+ my $mapp=&deversion((&decode_symb($symbp))[0]);
- my $symbparm=$symbp.'.'.$spacequalifierrest;
- my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
+ my $symbparm=$symbp.'.'.$spacequalifierrest;
+ my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
- my $seclevel=
- $ENV{'request.course.id'}.'.['.
- $ENV{'request.course.sec'}.'].'.$spacequalifierrest;
- my $seclevelr=
- $ENV{'request.course.id'}.'.['.
- $ENV{'request.course.sec'}.'].'.$symbparm;
- my $seclevelm=
- $ENV{'request.course.id'}.'.['.
- $ENV{'request.course.sec'}.'].'.$mapparm;
-
- my $courselevel=
- $ENV{'request.course.id'}.'.'.$spacequalifierrest;
- my $courselevelr=
- $ENV{'request.course.id'}.'.'.$symbparm;
- my $courselevelm=
- $ENV{'request.course.id'}.'.'.$mapparm;
+ if (($env{'user.name'} eq $uname) &&
+ ($env{'user.domain'} eq $udom)) {
+ $section=$env{'request.course.sec'};
+ @groups = split(/:/,$env{'request.course.groups'});
+ @groups=&sort_course_groups($courseid,@groups);
+ } else {
+ if (! defined($usection)) {
+ $section=&getsection($udom,$uname,$courseid);
+ } else {
+ $section = $usection;
+ }
+ @groups = &get_users_groups($udom,$uname,$courseid);
+ }
+
+ my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest;
+ my $seclevelr=$courseid.'.['.$section.'].'.$symbparm;
+ my $seclevelm=$courseid.'.['.$section.'].'.$mapparm;
+
+ $courselevel=$courseid.'.'.$spacequalifierrest;
+ my $courselevelr=$courseid.'.'.$symbparm;
+ $courselevelm=$courseid.'.'.$mapparm;
# ----------------------------------------------------------- first, check user
- my %resourcedata=get('resourcedata',
- ($courselevelr,$courselevelm,$courselevel));
- if (($resourcedata{$courselevelr}!~/^error\:/) &&
- ($resourcedata{$courselevelr}!~/^con_lost/)) {
-
- if ($resourcedata{$courselevelr}) {
- return $resourcedata{$courselevelr}; }
- if ($resourcedata{$courselevelm}) {
- return $resourcedata{$courselevelm}; }
- if ($resourcedata{$courselevel}) { return $resourcedata{$courselevel}; }
- } else {
- if ($resourcedata{$courselevelr}!~/No such file/) {
- &logthis("WARNING:".
- " Trying to get resource data for ".$ENV{'user.name'}." at "
- .$ENV{'user.domain'}.": ".$resourcedata{$courselevelr}.
- "");
- }
- }
+ my $userreply=&resdata($uname,$udom,'user',
+ ([$courselevelr,'resource'],
+ [$courselevelm,'map' ],
+ [$courselevel, 'course' ]));
+ if (defined($userreply)) { return &get_reply($userreply); }
+
+# ------------------------------------------------ second, check some of course
+ my $coursereply;
+ if (@groups > 0) {
+ $coursereply = &check_group_parms($courseid,\@groups,$symbparm,
+ $mapparm,$spacequalifierrest);
+ if (defined($coursereply)) { return &get_reply($coursereply); }
+ }
-# -------------------------------------------------------- second, check course
+ $coursereply=&resdata($env{'course.'.$courseid.'.num'},
+ $env{'course.'.$courseid.'.domain'},
+ 'course',
+ ([$seclevelr, 'resource'],
+ [$seclevelm, 'map' ],
+ [$seclevel, 'course' ],
+ [$courselevelr,'resource']));
+ if (defined($coursereply)) { return &get_reply($coursereply); }
- my $reply=&reply('get:'.
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'.
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.
- ':resourcedata:'.
- &escape($seclevelr).'&'.&escape($seclevelm).'&'.&escape($seclevel).'&'.
- &escape($courselevelr).'&'.&escape($courselevelm).'&'.&escape($courselevel),
- $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
- if ($reply!~/^error\:/) {
- map {
- if ($_) { return &unescape($_); }
- } split(/\&/,$reply);
- }
- if (($reply=~/^con_lost/) || ($reply=~/^error\:/)) {
- &logthis("WARNING:".
- " Getting ".$reply." asking for ".$varname." for ".
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.
- ' at '.
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.
- ' from '.
- $ENV{'course.'.$ENV{'request.course.id'}.'.home'}.
- "");
- }
# ------------------------------------------------------ third, check map parms
- my %parmhash=();
- my $thisparm='';
- if (tie(%parmhash,'GDBM_File',
- $ENV{'request.course.fn'}.'_parms.db',&GDBM_READER,0640)) {
- $thisparm=$parmhash{$symbparm};
- untie(%parmhash);
- }
- if ($thisparm) { return $thisparm; }
- }
-
-# --------------------------------------------- last, look in resource metadata
-
- $spacequalifierrest=~s/\./\_/;
- my $metadata=&metadata($ENV{'request.filename'},$spacequalifierrest);
- if ($metadata) { return $metadata; }
- $metadata=&metadata($ENV{'request.filename'},
- 'parameter_'.$spacequalifierrest);
- if ($metadata) { return $metadata; }
+ my %parmhash=();
+ my $thisparm='';
+ if (tie(%parmhash,'GDBM_File',
+ $env{'request.course.fn'}.'_parms.db',
+ &GDBM_READER(),0640)) {
+ $thisparm=$parmhash{$symbparm};
+ untie(%parmhash);
+ }
+ if ($thisparm) { return &get_reply([$thisparm,'resource']); }
+ }
+# ------------------------------------------ fourth, look in resource metadata
+ $spacequalifierrest=~s/\./\_/;
+ my $filename;
+ if (!$symbparm) { $symbparm=&symbread(); }
+ if ($symbparm) {
+ $filename=(&decode_symb($symbparm))[2];
+ } else {
+ $filename=$env{'request.filename'};
+ }
+ my $metadata=&metadata($filename,$spacequalifierrest);
+ if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
+ $metadata=&metadata($filename,'parameter_'.$spacequalifierrest);
+ if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
+
+# ---------------------------------------------- fourth, look in rest of course
+ if ($symbparm && defined($courseid) &&
+ $courseid eq $env{'request.course.id'}) {
+ my $coursereply=&resdata($env{'course.'.$courseid.'.num'},
+ $env{'course.'.$courseid.'.domain'},
+ 'course',
+ ([$courselevelm,'map' ],
+ [$courselevel, 'course']));
+ if (defined($coursereply)) { return &get_reply($coursereply); }
+ }
+# ------------------------------------------------------------------ Cascade up
+ unless ($space eq '0') {
+ my @parts=split(/_/,$space);
+ my $id=pop(@parts);
+ my $part=join('_',@parts);
+ if ($part eq '') { $part='0'; }
+ my @partgeneral=&EXT('resource.'.$part.'.'.$qualifierrest,
+ $symbparm,$udom,$uname,$section,1);
+ if (defined($partgeneral[0])) { return &get_reply(\@partgeneral); }
+ }
+ if ($recurse) { return undef; }
+ my $pack_def=&packages_tab_default($filename,$varname);
+ if (defined($pack_def)) { return &get_reply([$pack_def,'resource']); }
# ---------------------------------------------------- Any other user namespace
} elsif ($realm eq 'environment') {
# ----------------------------------------------------------------- environment
- return $ENV{$spacequalifierrest};
+ if (($uname eq $env{'user.name'})&&($udom eq $env{'user.domain'})) {
+ return $env{'environment.'.$spacequalifierrest};
+ } else {
+ if ($uname eq 'anonymous' && $udom eq '') {
+ return '';
+ }
+ my %returnhash=&userenvironment($udom,$uname,
+ $spacequalifierrest);
+ return $returnhash{$spacequalifierrest};
+ }
} elsif ($realm eq 'system') {
# ----------------------------------------------------------------- system.time
if ($space eq 'time') {
return time;
}
+ } elsif ($realm eq 'server') {
+# ----------------------------------------------------------------- system.time
+ if ($space eq 'name') {
+ return $ENV{'SERVER_NAME'};
+ }
}
return '';
}
+sub get_reply {
+ my ($reply_value) = @_;
+ if (ref($reply_value) eq 'ARRAY') {
+ if (wantarray) {
+ return @$reply_value;
+ }
+ return $reply_value->[0];
+ } else {
+ return $reply_value;
+ }
+}
+
+sub check_group_parms {
+ my ($courseid,$groups,$symbparm,$mapparm,$what) = @_;
+ my @groupitems = ();
+ my $resultitem;
+ my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$what,'course']);
+ foreach my $group (@{$groups}) {
+ foreach my $level (@levels) {
+ my $item = $courseid.'.['.$group.'].'.$level->[0];
+ push(@groupitems,[$item,$level->[1]]);
+ }
+ }
+ my $coursereply = &resdata($env{'course.'.$courseid.'.num'},
+ $env{'course.'.$courseid.'.domain'},
+ 'course',@groupitems);
+ return $coursereply;
+}
+
+sub sort_course_groups { # Sort groups based on defined rankings. Default is sort().
+ my ($courseid,@groups) = @_;
+ @groups = sort(@groups);
+ return @groups;
+}
+
+sub packages_tab_default {
+ my ($uri,$varname)=@_;
+ my (undef,$part,$name)=split(/\./,$varname);
+
+ my (@extension,@specifics,$do_default);
+ foreach my $package (split(/,/,&metadata($uri,'packages'))) {
+ my ($pack_type,$pack_part)=split(/_/,$package,2);
+ if ($pack_type eq 'default') {
+ $do_default=1;
+ } elsif ($pack_type eq 'extension') {
+ push(@extension,[$package,$pack_type,$pack_part]);
+ } elsif ($pack_part eq $part || $pack_type eq 'part') {
+ # only look at packages defaults for packages that this id is
+ push(@specifics,[$package,$pack_type,$pack_part]);
+ }
+ }
+ # first look for a package that matches the requested part id
+ foreach my $package (@specifics) {
+ my (undef,$pack_type,$pack_part)=@{$package};
+ next if ($pack_part ne $part);
+ if (defined($packagetab{"$pack_type&$name&default"})) {
+ return $packagetab{"$pack_type&$name&default"};
+ }
+ }
+ # look for any possible matching non extension_ package
+ foreach my $package (@specifics) {
+ my (undef,$pack_type,$pack_part)=@{$package};
+ if (defined($packagetab{"$pack_type&$name&default"})) {
+ return $packagetab{"$pack_type&$name&default"};
+ }
+ if ($pack_type eq 'part') { $pack_part='0'; }
+ if (defined($packagetab{$pack_type."_".$pack_part."&$name&default"})) {
+ return $packagetab{$pack_type."_".$pack_part."&$name&default"};
+ }
+ }
+ # look for any posible extension_ match
+ foreach my $package (@extension) {
+ my ($package,$pack_type)=@{$package};
+ if (defined($packagetab{"$pack_type&$name&default"})) {
+ return $packagetab{"$pack_type&$name&default"};
+ }
+ if (defined($packagetab{$package."&$name&default"})) {
+ return $packagetab{$package."&$name&default"};
+ }
+ }
+ # look for a global default setting
+ if ($do_default && defined($packagetab{"default&$name&default"})) {
+ return $packagetab{"default&$name&default"};
+ }
+ return undef;
+}
+
+sub add_prefix_and_part {
+ my ($prefix,$part)=@_;
+ my $keyroot;
+ if (defined($prefix) && $prefix !~ /^__/) {
+ # prefix that has a part already
+ $keyroot=$prefix;
+ } elsif (defined($prefix)) {
+ # prefix that is missing a part
+ if (defined($part)) { $keyroot='_'.$part.substr($prefix,1); }
+ } else {
+ # no prefix at all
+ if (defined($part)) { $keyroot='_'.$part; }
+ }
+ return $keyroot;
+}
+
# ---------------------------------------------------------------- Get metadata
+my %metaentry;
+my %importedpartids;
sub metadata {
- my ($uri,$what)=@_;
-
+ my ($uri,$what,$liburi,$prefix,$depthcount)=@_;
$uri=&declutter($uri);
+ # if it is a non metadata possible uri return quickly
+ if (($uri eq '') ||
+ (($uri =~ m|^/*adm/|) &&
+ ($uri !~ m|^adm/includes|) && ($uri !~ m|/bulletinboard$|)) ||
+ ($uri =~ m|/$|) || ($uri =~ m|/.meta$|) || ($uri =~ m{^/*uploaded/.+\.sequence$})) {
+ return undef;
+ }
+ if (($uri =~ /^~/ || $uri =~ m{home/$match_username/public_html/})
+ && &Apache::lonxml::get_state('target') =~ /^(|meta)$/) {
+ return undef;
+ }
my $filename=$uri;
$uri=~s/\.meta$//;
- unless ($metacache{$uri.':keys'}) {
+#
+# Is the metadata already cached?
+# Look at timestamp of caching
+# Everything is cached by the main uri, libraries are never directly cached
+#
+ if (!defined($liburi)) {
+ my ($result,$cached)=&is_cached_new('meta',$uri);
+ 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?
+#
+# if (! exists($metacache{$uri})) {
+# $metacache{$uri}={};
+# }
+ my $cachetime = 60*60;
+ if ($liburi) {
+ $liburi=&declutter($liburi);
+ $filename=$liburi;
+ } else {
+ &devalidate_cache_new('meta',$uri);
+ undef(%metaentry);
+ }
+ my %metathesekeys=();
unless ($filename=~/\.meta$/) { $filename.='.meta'; }
- my $metastring=&getfile($perlvar{'lonDocRoot'}.'/res/'.$filename);
- my $parser=HTML::TokeParser->new(\$metastring);
+ my $metastring;
+ if ($uri =~ /^~/ || $uri =~ m{home/$match_username/public_html/}) {
+ my $which = &hreflocation('','/'.($liburi || $uri));
+ $metastring =
+ &Apache::lonnet::ssi_body($which,
+ ('grade_target' => 'meta'));
+ $cachetime = 1; # only want this cached in the child not long term
+ } elsif (($uri !~ m -^(editupload)/-) &&
+ ($uri !~ m{^/*uploaded/$match_domain/$match_courseid/docs/})) {
+ my $file=&filelocation('',&clutter($filename));
+ #push(@{$metaentry{$uri.'.file'}},$file);
+ $metastring=&getfile($file);
+ }
+ my $parser=HTML::LCParser->new(\$metastring);
my $token;
+ undef %metathesekeys;
while ($token=$parser->get_token) {
- if ($token->[0] eq 'S') {
- my $entry=$token->[1];
- my $unikey=$entry;
- if (defined($token->[2]->{'part'})) {
- $unikey.='_'.$token->[2]->{'part'};
- }
- if (defined($token->[2]->{'name'})) {
- $unikey.='_'.$token->[2]->{'name'};
- }
- if ($metacache{$uri.':keys'}) {
- $metacache{$uri.':keys'}.=','.$unikey;
- } else {
- $metacache{$uri.':keys'}=$unikey;
- }
- map {
- $metacache{$uri.':'.$unikey.'.'.$_}=$token->[2]->{$_};
- } @{$token->[3]};
- unless (
- $metacache{$uri.':'.$unikey}=$parser->get_text('/'.$entry)
- ) { $metacache{$uri.':'.$unikey}=
- $metacache{$uri.':'.$unikey.'.default'};
- }
- }
- }
+ if ($token->[0] eq 'S') {
+ if (defined($token->[2]->{'package'})) {
+#
+# This is a package - get package info
+#
+ my $package=$token->[2]->{'package'};
+ my $keyroot=&add_prefix_and_part($prefix,$token->[2]->{'part'});
+ if (defined($token->[2]->{'id'})) {
+ $keyroot.='_'.$token->[2]->{'id'};
+ }
+ if ($metaentry{':packages'}) {
+ $metaentry{':packages'}.=','.$package.$keyroot;
+ } else {
+ $metaentry{':packages'}=$package.$keyroot;
+ }
+ foreach my $pack_entry (keys(%packagetab)) {
+ my $part=$keyroot;
+ $part=~s/^\_//;
+ if ($pack_entry=~/^\Q$package\E\&/ ||
+ $pack_entry=~/^\Q$package\E_0\&/) {
+ my ($pack,$name,$subp)=split(/\&/,$pack_entry);
+ # ignore package.tab specified default values
+ # here &package_tab_default() will fetch those
+ if ($subp eq 'default') { next; }
+ my $value=$packagetab{$pack_entry};
+ my $unikey;
+ if ($pack =~ /_0$/) {
+ $unikey='parameter_0_'.$name;
+ $part=0;
+ } else {
+ $unikey='parameter'.$keyroot.'_'.$name;
+ }
+ if ($subp eq 'display') {
+ $value.=' [Part: '.$part.']';
+ }
+ $metaentry{':'.$unikey.'.part'}=$part;
+ $metathesekeys{$unikey}=1;
+ unless (defined($metaentry{':'.$unikey.'.'.$subp})) {
+ $metaentry{':'.$unikey.'.'.$subp}=$value;
+ }
+ if (defined($metaentry{':'.$unikey.'.default'})) {
+ $metaentry{':'.$unikey}=
+ $metaentry{':'.$unikey.'.default'};
+ }
+ }
+ }
+ } else {
+#
+# This is not a package - some other kind of start tag
+#
+ my $entry=$token->[1];
+ 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=~/