--- loncom/lonnet/perl/lonnet.pm 2006/02/21 22:39:28 1.712
+++ loncom/lonnet/perl/lonnet.pm 2006/04/26 14:50:56 1.731
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.712 2006/02/21 22:39:28 albertel Exp $
+# $Id: lonnet.pm,v 1.731 2006/04/26 14:50:56 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -45,7 +45,6 @@ qw(%perlvar %hostname %badServerCache %i
use IO::Socket;
use GDBM_File;
-use Apache::Constants qw(:common :http);
use HTML::LCParser;
use HTML::Parser;
use Fcntl qw(:flock);
@@ -86,6 +85,29 @@ delayed.
# --------------------------------------------------------------------- Logging
+{
+ my $logid;
+ sub instructor_log {
+ my ($hash_name,$storehash,$delflag,$uname,$udom)=@_;
+ $logid++;
+ my $id=time().'00000'.$$.'00000'.$logid;
+ return &Apache::lonnet::put('nohist_'.$hash_name,
+ { $id => {
+ 'exe_uname' => $env{'user.name'},
+ 'exe_udom' => $env{'user.domain'},
+ 'exe_time' => time(),
+ 'exe_ip' => $ENV{'REMOTE_ADDR'},
+ 'delflag' => $delflag,
+ 'logentry' => $storehash,
+ 'uname' => $uname,
+ 'udom' => $udom,
+ }
+ },
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'}
+ );
+ }
+}
sub logtouch {
my $execdir=$perlvar{'lonDaemons'};
@@ -166,6 +188,7 @@ sub reply {
my ($cmd,$server)=@_;
unless (defined($hostname{$server})) { return 'no_such_host'; }
my $answer=subreply($cmd,$server);
+ &Apache::lonnet::logthis("$cmd");
if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
&logthis("WARNING:".
" $cmd to $server returned $answer");
@@ -260,6 +283,13 @@ sub critical {
sub transfer_profile_to_env {
my ($lonidsdir,$handle)=@_;
+ if (!defined($lonidsdir)) {
+ $lonidsdir = $perlvar{'lonIDsDir'};
+ }
+ if (!defined($handle)) {
+ ($handle) = ($env{'user.environment'} =~m|/([^/]+)\.id$| );
+ }
+
my @profile;
{
open(my $idf,"$lonidsdir/$handle.id");
@@ -272,6 +302,8 @@ sub transfer_profile_to_env {
for ($envi=0;$envi<=$#profile;$envi++) {
chomp($profile[$envi]);
my ($envname,$envvalue)=split(/=/,$profile[$envi],2);
+ $envname=&unescape($envname);
+ $envvalue=&unescape($envvalue);
$env{$envname} = $envvalue;
if (my ($key,$time) = ($envname =~ /^(cgi\.(\d+)_\d+\.)/)) {
if ($time < time-300) {
@@ -324,6 +356,8 @@ sub appenv {
chomp($oldenv[$i]);
if ($oldenv[$i] ne '') {
my ($name,$value)=split(/=/,$oldenv[$i],2);
+ $name=&unescape($name);
+ $value=&unescape($value);
unless (defined($newenv{$name})) {
$newenv{$name}=$value;
}
@@ -336,7 +370,7 @@ sub appenv {
}
my $newname;
foreach $newname (keys %newenv) {
- print $fh "$newname=$newenv{$newname}\n";
+ print $fh &escape($newname).'='.&escape($newenv{$newname})."\n";
}
close($fh);
}
@@ -348,7 +382,6 @@ sub appenv {
sub delenv {
my $delthis=shift;
- my %newenv=();
if (($delthis=~/user\.role/) || ($delthis=~/user\.priv/)) {
&logthis("WARNING: ".
"Attempt to delete from environment ".$delthis);
@@ -381,8 +414,10 @@ sub delenv {
return 'error: '.$!;
}
foreach my $cur_key (@oldenv) {
- if ($cur_key=~/^$delthis/) {
- my ($key,undef) = split('=',$cur_key,2);
+ my $unescaped_cur_key = &unescape($cur_key);
+ if ($unescaped_cur_key=~/^$delthis/) {
+ my ($key) = split('=',$cur_key,2);
+ $key = &unescape($key);
delete($env{$key});
} else {
print $fh $cur_key;
@@ -840,11 +875,9 @@ sub getsection {
}
sub save_cache {
- my ($r)=@_;
- if (! $r->is_initial_req()) { return DECLINED; }
&purge_remembered();
+ #&Apache::loncommon::validate_page();
undef(%env);
- return OK;
}
my $to_remember=-1;
@@ -996,13 +1029,13 @@ sub retrievestudentphoto {
# -------------------------------------------------------------------- New chat
sub chatsend {
- my ($newentry,$anon)=@_;
+ 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)),$chome);
+ &escape($newentry)).':'.$group,$chome);
}
# ------------------------------------------ Find current version of a resource
@@ -1320,7 +1353,7 @@ sub clean_filename {
# --------------- 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 filenam is in $env{"form.$formname"}
+# the desired filenam is in $env{"form.$formname.filename"}
# $coursedoc - if true up to the current course
# if false
# $subdir - directory in userfile to store the file into
@@ -1331,7 +1364,7 @@ sub clean_filename {
sub userfileupload {
- my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase)=@_;
+ my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase,$destuname,$destudom)=@_;
if (!defined($subdir)) { $subdir='unknown'; }
my $fname=$env{'form.'.$formname.'.filename'};
$fname=&clean_filename($fname);
@@ -1354,6 +1387,7 @@ sub userfileupload {
close($fh);
return $fullpath.'/'.$fname;
}
+
# Create the directory if not present
$fname="$subdir/$fname";
if ($coursedoc) {
@@ -1369,9 +1403,19 @@ sub userfileupload {
$fname,$formname,$parser,
$allfiles,$codebase);
}
+ } elsif (defined($destuname)) {
+ my $docuname=$destuname;
+ my $docudom=$destudom;
+ return &finishuserfileupload($docuname,$docudom,$formname,
+ $fname,$parser,$allfiles,$codebase);
+
} else {
my $docuname=$env{'user.name'};
my $docudom=$env{'user.domain'};
+ if (exists($env{'form.group'})) {
+ $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
+ $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
+ }
return &finishuserfileupload($docuname,$docudom,$formname,
$fname,$parser,$allfiles,$codebase);
}
@@ -2557,7 +2601,7 @@ sub restore {
# ---------------------------------------------------------- Course Description
sub coursedescription {
- my $courseid=shift;
+ my ($courseid,$args)=@_;
$courseid=~s/^\///;
$courseid=~s/\_/\//g;
my ($cdomain,$cnum)=split(/\//,$courseid);
@@ -2567,7 +2611,27 @@ sub coursedescription {
# trying and trying and trying to get the course description.
my %envhash=();
my %returnhash=();
- $envhash{'course.'.$normalid.'.last_cache'}=time;
+
+ 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 agin
+ if (!$args->{'one_time'}) {
+ $envhash{'course.'.$normalid.'.last_cache'}=time;
+ }
if ($chome ne 'no_host') {
%returnhash=&dump('environment',$cdomain,$cnum);
if (!exists($returnhash{'con_lost'})) {
@@ -2585,7 +2649,9 @@ sub coursedescription {
$envhash{'course.'.$normalid.'.num'}=$cnum;
}
}
- &appenv(%envhash);
+ if (!$args->{'one_time'}) {
+ &appenv(%envhash);
+ }
return %returnhash;
}
@@ -2851,6 +2917,13 @@ sub dump {
return %returnhash;
}
+# --------------------------------------------------------- dumpstore interface
+
+sub dumpstore {
+ my ($namespace,$udomain,$uname,$regexp,$range)=@_;
+ return &dump($namespace,$udomain,$uname,$regexp,$range);
+}
+
# -------------------------------------------------------------- keys interface
sub getkeys {
@@ -2992,25 +3065,53 @@ sub newput {
# --------------------------------------------------------- putstore interface
sub putstore {
- my ($namespace,$storehash,$udomain,$uname)=@_;
+ 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='';
- my %allitems = ();
- foreach (keys %$storehash) {
- if ($_ =~ m/^([^\:]+):([^\:]+):([^\:]+)$/) {
- my $key = $1.':keys:'.$2;
- $allitems{$key} .= $3.':';
- }
- $items.=$_.'='.&freeze_escape($$storehash{$_}).'&';
- }
- foreach (keys %allitems) {
- $allitems{$_} =~ s/\:$//;
- $items.= $_.'='.$allitems{$_}.'&';
+ foreach my $key (keys(%$storehash)) {
+ $items.= &escape($key).'='.&freeze_escape($storehash->{$key}).'&';
}
$items=~s/\&$//;
- return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
+ 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 (keys %$storehash) {
+ my $key = $version.':'.&escape($symb).':'.$_;
+ $newstorehash{$key} = $storehash->{$_};
+ }
+ my $items='';
+ my %allitems = ();
+ foreach (keys %newstorehash) {
+ if ($_ =~ m/^([^\:]+):([^\:]+):([^\:]+)$/) {
+ my $key = $1.':keys:'.$2;
+ $allitems{$key} .= $3.':';
+ }
+ $items.=$_.'='.&freeze_escape($newstorehash{$_}).'&';
+ }
+ foreach (keys %allitems) {
+ $allitems{$_} =~ s/\:$//;
+ $items.= $_.'='.$allitems{$_}.'&';
+ }
+ $items=~s/\&$//;
+ return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
}
# ------------------------------------------------------ critical put interface
@@ -3022,7 +3123,7 @@ sub cput {
my $uhome=&homeserver($uname,$udomain);
my $items='';
foreach (keys %$storehash) {
- $items.=escape($_).'='.&freeze_escape($$storehash{$_}).'&';
+ $items.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&';
}
$items=~s/\&$//;
return &critical("put:$udomain:$uname:$namespace:$items",$uhome);
@@ -3133,12 +3234,29 @@ sub allowed {
}
# Free bre access to user's own portfolio contents
- my ($space,$domain,$name,$dir)=split('/',$uri);
+ my ($space,$domain,$name,@dir)=split('/',$uri);
if (($space=~/^(uploaded|editupload)$/) && ($env{'user.name'} eq $name) &&
- ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir)) {
+ ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir[0])) {
return 'F';
}
+# bre access to group if user has rgf priv for this group and 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;
+ }
+ }
+ }
+ }
+
# Free bre to public access
if ($priv eq 'bre') {
@@ -3353,7 +3471,7 @@ sub allowed {
my ($cdom,$cnum,$csec)=split(/\//,$courseid);
my $prefix='course.'.$cdom.'_'.$cnum.'.';
if ((time-$env{$prefix.'last_cache'})>$expiretime) {
- &coursedescription($courseid);
+ &coursedescription($courseid,{'freshen_cache' => 1});
}
if (($env{$prefix.'res.'.$uri.'.lock.sections'}=~/\,\Q$csec\E\,/)
|| ($env{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {
@@ -3916,7 +4034,7 @@ sub get_users_groups {
my $grouplist;
foreach my $key (keys %roleshash) {
if ($key =~ /^\Q$courseid\E\/(\w+)\_gr$/) {
- unless ($roleshash{$key} =~ /_1_1$/) { # deleted membership
+ unless ($roleshash{$key} =~ /_\d+_\-1$/) { # deleted membership
$grouplist .= $1.':';
}
}
@@ -4670,6 +4788,12 @@ sub GetFileTimestamp {
sub stat_file {
my ($uri) = @_;
$uri = &clutter($uri);
+
+ # we want just the url part without the unneeded accessor url bits
+ if ($uri =~ m-^/adm/-) {
+ $uri=~s-^/adm/wrapper/-/-;
+ $uri=~s-^/adm/coursedocs/showdoc/-/-;
+ }
my ($udom,$uname,$file,$dir);
if ($uri =~ m-^/(uploaded|editupload)/-) {
($udom,$uname,$file) =
@@ -4690,6 +4814,7 @@ sub stat_file {
my ($result) = &dirlist($file,$udom,$uname,$dir);
my @stats = split('&', $result);
+
if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') {
shift(@stats); #filename is first
return @stats;
@@ -4699,11 +4824,30 @@ sub stat_file {
# -------------------------------------------------------- Value of a Condition
+# gets the value of a specific preevaluated condition
+# stored in the string $env{user.state.}
+# or looks up a condition reference in the bighash and if if hasn't
+# already been evaluated recurses into docondval to get the value of
+# the condition, then memoizing it to
+# $env{user.state..}
sub directcondval {
my $number=shift;
if (!defined($env{'user.state.'.$env{'request.course.id'}})) {
&Apache::lonuserstate::evalstate();
}
+ if (exists($env{'user.state.'.$env{'request.course.id'}.".$number"})) {
+ return $env{'user.state.'.$env{'request.course.id'}.".$number"};
+ } elsif ($number =~ /^_/) {
+ my $sub_condition;
+ if (tie(my %bighash,'GDBM_File',$env{'request.course.fn'}.'.db',
+ &GDBM_READER(),0640)) {
+ $sub_condition=$bighash{'conditions'.$number};
+ untie(%bighash);
+ }
+ my $value = &docondval($sub_condition);
+ &appenv('user.state.'.$env{'request.course.id'}.".$number" => $value);
+ return $value;
+ }
if ($env{'user.state.'.$env{'request.course.id'}}) {
return substr($env{'user.state.'.$env{'request.course.id'}},$number,1);
} else {
@@ -4711,43 +4855,49 @@ sub directcondval {
}
}
+# get the collection of conditions for this resource
sub condval {
my $condidx=shift;
- my $result=0;
my $allpathcond='';
- foreach (split(/\|/,$condidx)) {
- if (defined($env{'acc.cond.'.$env{'request.course.id'}.'.'.$_})) {
- $allpathcond.=
- '('.$env{'acc.cond.'.$env{'request.course.id'}.'.'.$_}.')|';
- }
+ foreach my $cond (split(/\|/,$condidx)) {
+ if (defined($env{'acc.cond.'.$env{'request.course.id'}.'.'.$cond})) {
+ $allpathcond.=
+ '('.$env{'acc.cond.'.$env{'request.course.id'}.'.'.$cond}.')|';
+ }
}
$allpathcond=~s/\|$//;
- if ($env{'request.course.id'}) {
- if ($allpathcond) {
- my $operand='|';
- my @stack;
- foreach ($allpathcond=~/(\d+|\(|\)|\&|\|)/g) {
- if ($_ eq '(') {
- push @stack,($operand,$result)
- } elsif ($_ eq ')') {
- my $before=pop @stack;
- if (pop @stack eq '&') {
- $result=$result>$before?$before:$result;
- } else {
- $result=$result>$before?$result:$before;
- }
- } elsif (($_ eq '&') || ($_ eq '|')) {
- $operand=$_;
- } else {
- my $new=directcondval($_);
- if ($operand eq '&') {
- $result=$result>$new?$new:$result;
- } else {
- $result=$result>$new?$result:$new;
- }
- }
- }
- }
+ return &docondval($allpathcond);
+}
+
+#evaluates an expression of conditions
+sub docondval {
+ my ($allpathcond) = @_;
+ my $result=0;
+ if ($env{'request.course.id'}
+ && defined($allpathcond)) {
+ my $operand='|';
+ my @stack;
+ foreach my $chunk ($allpathcond=~/(\d+|_\d+\.\d+|\(|\)|\&|\|)/g) {
+ if ($chunk eq '(') {
+ push @stack,($operand,$result);
+ } elsif ($chunk eq ')') {
+ my $before=pop @stack;
+ if (pop @stack eq '&') {
+ $result=$result>$before?$before:$result;
+ } else {
+ $result=$result>$before?$result:$before;
+ }
+ } elsif (($chunk eq '&') || ($chunk eq '|')) {
+ $operand=$chunk;
+ } else {
+ my $new=directcondval($chunk);
+ if ($operand eq '&') {
+ $result=$result>$new?$new:$result;
+ } else {
+ $result=$result>$new?$result:$new;
+ }
+ }
+ }
}
return $result;
}
@@ -4864,8 +5014,8 @@ sub EXT_cache_set {
# --------------------------------------------------------- Value of a Variable
sub EXT {
- my ($varname,$symbparm,$udom,$uname,$usection,$recurse)=@_;
+ my ($varname,$symbparm,$udom,$uname,$usection,$recurse)=@_;
unless ($varname) { return ''; }
#get real user name/domain, courseid and symb
my $courseid;
@@ -5011,9 +5161,6 @@ sub EXT {
($env{'user.domain'} eq $udom)) {
$section=$env{'request.course.sec'};
@groups=&sort_course_groups($env{'request.course.groups'},$courseid);
- if (@groups > 0) {
- @groups = sort(@groups);
- }
} else {
if (! defined($usection)) {
$section=&getsection($udom,$uname,$courseid);
@@ -5148,10 +5295,7 @@ sub check_group_parms {
sub sort_course_groups { # Sort groups based on defined rankings. Default is sort().
my ($grouplist,$courseid) = @_;
- my @groups = split/:/,$grouplist;
- if (@groups > 1) {
- @groups = sort(@groups);
- }
+ my @groups = sort(split(/:/,$grouplist));
return @groups;
}
@@ -6355,7 +6499,7 @@ sub clutter {
&& $thisfn!~/\.(sequence|page)$/) {
$thisfn='/adm/coursedocs/showdoc'.$thisfn;
} else {
- &logthis("Got a blank emb style");
+# &logthis("Got a blank emb style");
}
}
}
@@ -6423,7 +6567,6 @@ sub goodbye {
&logthis(sprintf("%-20s is %s",'hits',$hits));
&flushcourselogs();
&logthis("Shutting down");
- return DONE;
}
BEGIN {
@@ -7176,6 +7319,27 @@ all args are optional
=item *
+dumpstore($namespace,$udom,$uname,$regexp,$range) :
+dumps the complete (or key matching regexp) namespace into a hash
+($udom, $uname, $regexp, $range are optional) for a namespace that is
+normally &store()ed into
+
+$range should be either an integer '100' (give me the first 100
+ matching records)
+ or be two integers sperated by a - with no spaces
+ '30-50' (give me the 30th through the 50th matching
+ records)
+
+
+=item *
+
+putstore($namespace,$symb,$version,$storehash,$udomain,$uname) :
+replaces a &store() version of data with a replacement set of data
+for a particular resource in a namespace passed in the $storehash hash
+reference
+
+=item *
+
tmpstore($storehash,$symb,$namespace,$udom,$uname) : storage that
works very similar to store/cstore, but all data is stored in a
temporary location and can be reset using tmpreset, $storehash should
@@ -7229,17 +7393,6 @@ put($namespace,$storehash,$udom,$uname)
=item *
-putstore($namespace,$storehash,$udomain,$uname) : stores hash in namesp
-keys used in storehash include version information (e.g., 1:$symb:message etc.) as
-used in records written by &store and retrieved by &restore. This function
-was created for use in editing discussion posts, without incrementing the
-version number included in the key for a particular post. The colon
-separated list of attribute names (e.g., the value associated with the key
-1:keys:$symb) is also generated and passed in the ampersand separated
-items sent to lonnet::reply().
-
-=item *
-
cput($namespace,$storehash,$udom,$uname) : critical put
($udom and $uname are optional)