--- loncom/lonnet/perl/lonnet.pm 2000/10/28 19:26:07 1.53 +++ loncom/lonnet/perl/lonnet.pm 2000/11/22 12:14:56 1.68 @@ -24,6 +24,7 @@ # 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 @@ -41,8 +42,7 @@ # directcondval(index) : reading condition value of single condition from # state string # condval(index) : value of condition index based on state -# varval(name) : value of a variable -# refreshstate() : refresh the state information string +# 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 @@ -66,8 +66,8 @@ # 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 -# Gerd Kortemeyer +# 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 Gerd Kortemeyer package Apache::lonnet; @@ -122,8 +122,7 @@ sub reply { my ($cmd,$server)=@_; my $answer=subreply($cmd,$server); if ($answer eq 'con_lost') { $answer=subreply($cmd,$server); } - if (($answer=~/^error:/) || ($answer=~/^refused/) || - ($answer=~/^rejected/)) { + if (($answer=~/^refused/) || ($answer=~/^rejected/)) { &logthis("WARNING:". " $cmd to $server returned $answer"); } @@ -250,6 +249,35 @@ sub appenv { } 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 @oldenv; + { + my $fh; + unless ($fh=Apache::File->new("$ENV{'user.environment'}")) { + return 'error'; + } + @oldenv=<$fh>; + } + { + my $fh; + unless ($fh=Apache::File->new(">$ENV{'user.environment'}")) { + return 'error'; + } + map { + unless ($_=~/^$delthis/) { print $fh $_; } + } @oldenv; + } + return 'ok'; +} # ------------------------------ Find server with least workload from spare.tab @@ -340,6 +368,9 @@ sub subscribe { return 'not_found'; } my $answer=reply("sub:$fname",$home); + if (($answer eq 'con_lost') || ($answer eq 'rejected')) { + $answer.=' by '.$home; + } return $answer; } @@ -351,14 +382,14 @@ sub repcopy { my $transname="$filename.in.transfer"; if ((-e $filename) || (-e $transname)) { return OK; } my $remoteurl=subscribe($filename); - if ($remoteurl eq 'con_lost') { - &logthis("Subscribe returned con_lost: $filename"); + if ($remoteurl =~ /^con_lost by/) { + &logthis("Subscribe returned $remoteurl: $filename"); return HTTP_SERVICE_UNAVAILABLE; } elsif ($remoteurl eq 'not_found') { &logthis("Subscribe returned not_found: $filename"); return HTTP_NOT_FOUND; - } elsif ($remoteurl eq 'rejected') { - &logthis("Subscribe returned rejected: $filename"); + } elsif ($remoteurl =~ /^rejected by/) { + &logthis("Subscribe returned $remoteurl: $filename"); return FORBIDDEN; } elsif ($remoteurl eq 'directory') { return OK; @@ -499,6 +530,8 @@ sub coursedescription { if ($chome ne 'no_host') { my $rep=reply("dump:$cdomain:$cnum:environment",$chome); if ($rep ne 'con_lost') { + my $normalid=$courseid; + $normalid=~s/\//\_/g; my %envhash=(); my %returnhash=('home' => $chome, 'domain' => $cdomain, @@ -508,13 +541,15 @@ sub coursedescription { $name=&unescape($name); $value=&unescape($value); $returnhash{$name}=$value; - my $normalid=$courseid; - $normalid=~s/\//\_/g; $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; + $envhash{'course.'.$normalid.'.home'}=$chome; + $envhash{'course.'.$normalid.'.domain'}=$cdomain; + $envhash{'course.'.$normalid.'.num'}=$cnum; &appenv(%envhash); return %returnhash; } @@ -717,9 +752,9 @@ sub allowed { my ($priv,$uri)=@_; $uri=&declutter($uri); -# Free bre access to adm resources +# Free bre access to adm and meta resources - if (($uri=~/^adm\//) && ($priv eq 'bre')) { + if ((($uri=~/^adm\//) || ($uri=~/\.meta$/)) && ($priv eq 'bre')) { return 'F'; } @@ -741,8 +776,9 @@ sub allowed { } # Course: uri itself is a course - - if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$uri} + my $courseuri=$uri; + $courseuri=~s/\_(\d)/\/$1/; + if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseuri} =~/$priv\&([^\:]*)/) { $thisallowed.=$1; } @@ -776,7 +812,7 @@ sub allowed { my $pathname=$uri; $pathname=~s/\/$filename$//; if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~ - /\&$filename\:(\d+)\&/) { + /\&$filename\:([\d\|]+)\&/) { $statecond=$1; if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid} =~/$priv\&([^\:]*)/) { @@ -784,16 +820,19 @@ sub allowed { $checkreferer=0; } } + if (($ENV{'HTTP_REFERER'}) && ($checkreferer)) { - my $refuri=&declutter($ENV{'HTTP_REFERER'}); + 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(/\./,$filename); + my @filenameparts=split(/\./,$uri); if (&fileembstyle($filenameparts[$#filenameparts]) ne 'ssi') { if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~ - /\&$filename\:(\d+)\&/) { + /\&$filename\:([\d\|]+)\&/) { my $refstatecond=$1; if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid} =~/$priv\&([^\:]*)/) { @@ -830,37 +869,43 @@ sub allowed { # # Possibly locked functionality, check all courses +# Locks might take effect only after 10 minutes cache expiration for other +# courses, and 2 minutes for current course my $envkey; if ($thisallowed=~/L/) { foreach $envkey (keys %ENV) { - if ($envkey=~/^user\.role\.st\.([^\.]*)/) { - my ($cdom,$cnum,$csec)=split(/\//,$1); - my %locks=(); - map { - my ($name,$value)=split(/\=/,$_); - $locks{&unescape($name)}=&unescape($value); - } split(/\&/,&reply('get:'.$cdom.':'.$cnum. - ':environment:'.&escape('priv.'.$priv.'.lock.sections'). - ':'.&escape('priv.'.$priv.'.lock.expire'). - ':'.&escape('res.'.$uri.'.lock.sections'). - ':'.&escape('res.'.$uri.'.lock.expire'), - &homeserver($cnum,$cdom))); - if (($locks{'res.'.$uri.'.lock.sections'}=~/\,$csec\,/) || - ($locks{'res.'.$uri.'.lock.sections'} eq 'all')) { - if ($locks{'res.'.$uri.'.lock.expire'}>time) { - &log('Locked by res: '.$priv.' for '.$uri.' due to '. + if ($envkey=~/^user\.role\.(st|ta)\.([^\.]*)/) { + my $courseid=$2; + my $roleid=$1.'.'.$2; + my $expiretime=600; + 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 (($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'}, + 'Locked by res: '.$priv.' for '.$uri.' due to '. $cdom.'/'.$cnum.'/'.$csec.' expire '. - $locks{'priv.'.$priv.'.lock.expire'}); + $ENV{$prefix.'priv.'.$priv.'.lock.expire'}); return ''; } } - if (($locks{'priv.'.$priv.'.lock.sections'}=~/\,$csec\,/) || - ($locks{'priv.'.$priv.'.lock.sections'} eq 'all')) { - if ($locks{'priv.'.$priv.'.lock.expire'}>time) { - &log('Locked by priv: '.$priv.' for '.$uri.' due to '. + 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'}, + 'Locked by priv: '.$priv.' for '.$uri.' due to '. $cdom.'/'.$cnum.'/'.$csec.' expire '. - $locks{'priv.'.$priv.'.lock.expire'}); + $ENV{$prefix.'priv.'.$priv.'.lock.expire'}); return ''; } } @@ -884,17 +929,35 @@ sub allowed { # Course preferences if ($thisallowed=~/C/) { -# -# Registered course preferences from environment -# + 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'}); + return ''; + } } # Resource preferences if ($thisallowed=~/R/) { -# -# Resource Metadata -# + 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>; + } + if (join('',@content)=~ + /\]*\>[^\<]*$rolecode[^\<]*\<\/roledeny\>/) { + &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'}, + 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode); + return ''; + + } + } } # Restricted by state? @@ -910,11 +973,6 @@ sub allowed { return 'F'; } -# ---------------------------------------------------------- Refresh State Info - -sub refreshstate { -} - # ----------------------------------------------------------------- Define Role sub definerole { @@ -1094,8 +1152,16 @@ sub directcondval { sub condval { my $condidx=shift; my $result=0; + my $allpathcond=''; + map { + if (defined($ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$_})) { + $allpathcond.= + '('.$ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$_}.')|'; + } + } split(/\|/,$condidx); + $allpathcond=~s/\|$//; if ($ENV{'request.course.id'}) { - if (defined($ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$condidx})) { + if ($allpathcond) { my $operand='|'; my @stack; map { @@ -1118,8 +1184,7 @@ sub condval { $result=$result>$new?$result:$new; } } - } ($ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$condidx}=~ - /(\d+|\(|\)|\&|\|)/g); + } ($allpathcond=~/(\d+|\(|\)|\&|\|)/g); } } return $result; @@ -1127,8 +1192,9 @@ sub condval { # --------------------------------------------------------- Value of a Variable -sub varval { +sub EXT { my $varname=shift; + unless ($varname) { return ''; } my ($realm,$space,$qualifier,@therest)=split(/\./,$varname); my $rest; if ($therest[0]) { @@ -1136,15 +1202,21 @@ sub varval { } else { $rest=''; } + my $qualifierrest=$qualifier; + if ($rest) { $qualifierrest.='.'.$rest; } + my $spacequalifierrest=$space; + if ($qualifierrest) { $spacequalifierrest.='.'.$qualifierrest; } if ($realm eq 'user') { # --------------------------------------------------------------- user.resource if ($space eq 'resource') { + my %restored=&restore; + return $restored{$qualifierrest}; # ----------------------------------------------------------------- user.access } elsif ($space eq 'access') { return &allowed($qualifier,$rest); # ------------------------------------------ user.preferences, user.environment } elsif (($space eq 'preferences') || ($space eq 'environment')) { - return $ENV{join('.',('environment',$qualifier,$rest))}; + return $ENV{join('.',('environment',$qualifierrest))}; # ----------------------------------------------------------------- user.course } elsif ($space eq 'course') { return $ENV{join('.',('request.course',$qualifier))}; @@ -1172,33 +1244,87 @@ sub varval { # ------------------------------------------------------------- request.browser if ($space eq 'browser') { return $ENV{'browser.'.$qualifier}; - } elsif ($space eq 'filename') { - return $ENV{'request.filename'}; +# ------------------------------------------------------------ request.filename + } else { + return $ENV{'request.'.$spacequalifierrest}; } } elsif ($realm eq 'course') { # ---------------------------------------------------------- course.description - if ($space eq 'description') { - my %reply=&coursedescription($ENV{'request.course.id'}); - return $reply{'description'}; -# ------------------------------------------------------------------- course.id - } elsif ($space eq 'id') { - return $ENV{'request.course.id'}; -# -------------------------------------------------- Any other course namespace - } else { - my ($cdom,$cnam)=split(/\_/,$ENV{'request.course.id'}); - my $chome=&homeserver($cnam,$cdom); - my $item=join('.',($qualifier,$rest)); - return &unescape - (&reply('get:'.$cdom.':'.$cnam.':'.&escape($space).':'. - &escape($item),$chome)); + my $section=''; + if ($ENV{'request.course.sec'}) { + $section='_'.$ENV{'request.course.sec'}; + } + return $ENV{'course.'.$ENV{'request.course.id'}.$section.'.'. + $spacequalifierrest}; + } elsif ($realm eq 'resource') { + if ($ENV{'request.course.id'}) { +# ----------------------------------------------------- Cascading lookup scheme + my $symbparm=&symbread().'.'.$spacequalifierrest; + my $reslevel= + $ENV{'request.course.id'}.'.'.$symbparm; + my $seclevel= + $ENV{'request.course.id'}.'.'. + $ENV{'request.course.sec'}.'.'.$spacequalifierrest; + my $courselevel= + $ENV{'request.course.id'}.'.'.$spacequalifierrest; + +# ----------------------------------------------------------- first, check user + my %resourcedata=get('resourcedata',($reslevel,$seclevel,$courselevel)); + if ($resourcedata{$reslevel}!~/^error\:/) { + if ($resourcedata{$reslevel}) { return $resourcedata{$reslevel}; } + if ($resourcedata{$seclevel}) { return $resourcedata{$seclevel}; } + if ($resourcedata{$courselevel}) { return $resourcedata{$courselevel}; } + } +# -------------------------------------------------------- second, check course + my $section=''; + if ($ENV{'request.course.sec'}) { + $section='_'.$ENV{'request.course.sec'}; + } + my $reply=&reply('get:'. + $ENV{'course.'.$ENV{'request.course.id'}.$section.'.domain'}.':'. + $ENV{'course.'.$ENV{'request.course.id'}.$section.'.num'}. + ':resourcedata:'. + escape($reslevel).':'.escape($seclevel).':'.escape($courselevel), + $ENV{'course.'.$ENV{'request.course.id'}.$section.'.home'}); + if ($reply!~/^error\:/) { + map { + my ($name,$value)=split(/\=/,$_); + $resourcedata{unescape($name)}=unescape($value); + } split(/\&/,$reply); + if ($resourcedata{$reslevel}) { return $resourcedata{$reslevel}; } + if ($resourcedata{$seclevel}) { return $resourcedata{$seclevel}; } + if ($resourcedata{$courselevel}) { return $resourcedata{$courselevel}; } + } + +# ------------------------------------------------------ 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 + my $uri=&declutter($ENV{'request.filename'}); + my $filename=$perlvar{'lonDocRoot'}.'/res/'.$uri.'.meta'; + if (-e $filename) { + my @content; + { + my $fh=Apache::File->new($filename); + @content=<$fh>; + } + if (join('',@content)=~ + /\<$space[^\>]*\>([^\<]*)\<\/$space\>/) { + return $1; + } } - } elsif ($realm eq 'userdata') { - my $uhome=&homeserver($qualifier,$space); -# ----------------------------------------------- userdata.domain.name.resource # ---------------------------------------------------- Any other user namespace } elsif ($realm eq 'environment') { # ----------------------------------------------------------------- environment - return $ENV{join('.',($space,$qualifier,$rest))}; + return $ENV{$spacequalifierrest}; } elsif ($realm eq 'system') { # ----------------------------------------------------------------- system.time if ($space eq 'time') { @@ -1260,6 +1386,9 @@ sub symbread { &GDBM_READER,0640)) { # ---------------------------------------------- Get ID(s) for current resource my $ids=$bighash{'ids_/res/'.$thisfn}; + unless ($ids) { + $ids=$bighash{'ids_/'.$thisfn}; + } if ($ids) { # ------------------------------------------------------------------- Has ID(s) my @possibilities=split(/\,/,$ids); @@ -1287,7 +1416,9 @@ sub symbread { untie(%bighash) } } - if ($syval) { return $syval.'___'.$thisfn; } + if ($syval) { + return $syval.'___'.$thisfn; + } } &appenv('request.ambiguous' => $thisfn); return ''; @@ -1338,12 +1469,17 @@ sub filelocation { my ($dir,$file) = @_; my $location; $file=~ s/^\s*(\S+)\s*$/$1/; ## strip off leading and trailing spaces - $file=~s/^$perlvar{'lonDocRoot'}//; - $file=~s:^/*res::; - if ( !( $file =~ m:^/:) ) { - $location = $dir. '/'.$file; + if ($file=~m:^/~:) { # is a contruction space reference + $location = $file; + $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:; } else { - $location = '/home/httpd/html/res'.$file; + $file=~s/^$perlvar{'lonDocRoot'}//; + $file=~s:^/*res::; + if ( !( $file =~ m:^/:) ) { + $location = $dir. '/'.$file; + } else { + $location = '/home/httpd/html/res'.$file; + } } $location=~s://+:/:g; # remove duplicate / while ($location=~m:/\.\./:) {$location=~ s:/[^/]+/\.\./:/:g;} #remove dir/..