--- loncom/lonnet/perl/lonnet.pm 2004/10/12 20:37:04 1.545.2.3 +++ loncom/lonnet/perl/lonnet.pm 2004/11/10 22:20:50 1.568 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.545.2.3 2004/10/12 20:37:04 albertel Exp $ +# $Id: lonnet.pm,v 1.568 2004/11/10 22:20:50 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -36,23 +36,22 @@ use HTTP::Date; # use Date::Parse; use vars qw(%perlvar %hostname %homecache %badServerCache %hostip %iphost %spareid %hostdom - %libserv %pr %prp $metacache %packagetab %titlecache %courseresversioncache %resversioncache + %libserv %pr %prp %metacache %packagetab %titlecache %courseresversioncache %resversioncache %courselogs %accesshash %userrolehash $processmarker $dumpcount %coursedombuf %coursenumbuf %coursehombuf %coursedescrbuf %courseinstcodebuf %courseresdatacache - %userresdatacache %usectioncache %domaindescription %domain_auth_def %domain_auth_arg_def - %domain_lang_def %domain_city %domain_longi %domain_lati $tmpdir); + %userresdatacache %getsectioncache %domaindescription %domain_auth_def %domain_auth_arg_def + %domain_lang_def %domain_city %domain_longi %domain_lati $tmpdir $_64bit); use IO::Socket; use GDBM_File; use Apache::Constants qw(:common :http); use HTML::LCParser; use Fcntl qw(:flock); -use Apache::loncoursedata; use Apache::lonlocal; -use Storable qw(lock_store lock_nstore lock_retrieve freeze thaw); +use Storable qw(lock_store lock_nstore lock_retrieve freeze thaw nfreeze); use Time::HiRes qw( gettimeofday tv_interval ); -use Cache::Memcached; my $readit; +my $max_connection_retries = 10; # Or some such value. =pod @@ -117,14 +116,40 @@ sub logperm { 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); + # + # 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! + } + sleep(1); # Try again later if failed connection. + } + my $answer; + if ($client) { + print $client "$cmd\n"; + $answer=<$client>; + if (!$answer) { $answer="con_lost"; } + chomp($answer); + } else { + $answer = 'con_lost'; # Failed connection. + } return $answer; } @@ -435,7 +460,7 @@ sub overloaderror { if ($overload>0) { $r->err_headers_out->{'Retry-After'}=$overload; $r->log_error('Overload of '.$overload.' on '.$checkserver); - return 409; + return 413; } return ''; } @@ -772,6 +797,11 @@ sub getsection { my ($udom,$unam,$courseid)=@_; $courseid=~s/\_/\//g; $courseid=~s/^(\w)/\/$1/; + + my $hashid="$udom:$unam:$courseid"; + my ($result,$cached)=&is_cached(\%getsectioncache,$hashid,'getsection'); + if (defined($cached)) { return $result; } + my %Pending; my %Expired; # @@ -796,29 +826,29 @@ sub getsection { if ($key eq $courseid.'_st') { $section=''; } my ($dummy,$end,$start)=split(/\_/,&unescape($value)); my $now=time; - if (defined($end) && ($now > $end)) { + if (defined($end) && $end && ($now > $end)) { $Expired{$end}=$section; next; } - if (defined($start) && ($now < $start)) { + if (defined($start) && $start && ($now < $start)) { $Pending{$start}=$section; next; } - return $section; + return &do_cache(\%getsectioncache,$hashid,$section,'getsection'); } # # 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 $Pending{$time}; + return &do_cache(\%getsectioncache,$hashid,$Pending{$time},'getsection'); } if (scalar(keys(%Expired))) { my @sorted = sort {$a <=> $b} keys(%Expired); my $time = pop(@sorted); - return $Expired{$time}; + return &do_cache(\%getsectioncache,$hashid,$Expired{$time},'getsection'); } - return '-1'; + return &do_cache(\%getsectioncache,$hashid,'-1','getsection'); } @@ -874,7 +904,7 @@ sub is_cached { # &logthis("Upping $mtime - ".$$cache{$id.'.time'}. # "$id because of $filename"); } else { -# &logthis("Devalidating $filename $id - ".(time-($$cache{$id.'.time'}))); + &logthis("Devalidating $filename $id - ".(time-($$cache{$id.'.time'}))); &devalidate_cache($cache,$id,$name); return (undef,undef); } @@ -1009,84 +1039,6 @@ EVALBLOCK # &logthis("load_cache_item $name took ".(&Time::HiRes::time()-$starttime)); } -sub devalidate_cache_new { - my ($cache,$name,$id) = @_; - if (0) { &Apache::lonnet::logthis("deleting $name:$id"); } - $cache->delete(&escape($name.':'.$id)); -} - -my $lastone; -my $lastname; -sub is_cached_new { - my ($cache,$name,$id,$debug) = @_; - $debug=0; - $id=&escape($name.':'.$id); - if ($lastname eq $id) { - if ($debug) { &Apache::lonnet::logthis("Earyl return $id of $lastone <= $lastname "); } - return ($lastone,1); - } - undef($lastone); - undef($lastname); - my $value = $cache->get($id); - if (!(defined($value))) { - if ($debug) { &Apache::lonnet::logthis("getting $id is not defined"); } - return (undef,undef); - } - $lastname=$id; - if ($value eq '__undef__') { - if ($debug) { &Apache::lonnet::logthis("getting $id is __undef__"); } - return (undef,1); - } - if ($debug) { &Apache::lonnet::logthis("getting $id is $value"); } - $lastone=$value; - return ($value,1); -} - -sub do_cache_new { - my ($cache,$name,$id,$value,$time,$debug) = @_; - $debug=0; - $id=&escape($name.':'.$id); - my $setvalue=$value; - if (!defined($setvalue)) { - $setvalue='__undef__'; - } - if ($debug) { &Apache::lonnet::logthis("Setting $id to $value"); } - $cache->set($id,$setvalue,300); - return $value; -} - -sub usection { - my ($udom,$unam,$courseid)=@_; - my $hashid="$udom:$unam:$courseid"; - - my ($result,$cached)=&is_cached(\%usectioncache,$hashid,'usection'); - if (defined($cached)) { return $result; } - $courseid=~s/\_/\//g; - $courseid=~s/^(\w)/\/$1/; - foreach (split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles', - &homeserver($unam,$udom)))) { - my ($key,$value)=split(/\=/,$_); - $key=&unescape($key); - if ($key=~/^\Q$courseid\E(?:\/)*(\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 &do_cache(\%usectioncache,$hashid,$section,'usection'); - } - } - } - return &do_cache(\%usectioncache,$hashid,'-1','usection'); -} - # ------------------------------------- Read an entry from a user's environment sub userenvironment { @@ -1236,8 +1188,7 @@ sub ssi_body { my ($filelink,%form)=@_; my $output=($filelink=~/^http\:/?&externalssi($filelink): &ssi($filelink,%form)); - $output=~ - s/\/\/ BEGIN LON\-CAPA Internal.+\/\/ END LON\-CAPA Internal\s//gs; + $output=~s|//(\s*)?\s||gs; $output=~s/^.*?\]*\>//si; $output=~s/(.*)\<\/body\s*\>.*?$/$1/si; return $output; @@ -1920,6 +1871,7 @@ sub devalidate { # - 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', @@ -1934,6 +1886,7 @@ sub devalidate { $uname.' at '.$udom.' for '. $symb.': '.$status); } + &delenv('user.state.'.$cid); } } @@ -2253,6 +2206,7 @@ sub tmprestore { } # ----------------------------------------------------------------------- Store + sub store { my ($storehash,$symb,$namespace,$domain,$stuname) = @_; my $home=''; @@ -2266,6 +2220,7 @@ sub store { if (!$stuname) { $stuname=$ENV{'user.name'}; } &devalidate($symb,$stuname,$domain); + $symb=escape($symb); if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { @@ -2301,6 +2256,7 @@ sub cstore { if (!$stuname) { $stuname=$ENV{'user.name'}; } &devalidate($symb,$stuname,$domain); + $symb=escape($symb); if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { @@ -2343,7 +2299,6 @@ sub restore { 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=(); @@ -2432,10 +2387,8 @@ sub rolesinit { 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; if ($rolesdump ne '') { foreach (split(/&/,$rolesdump)) { @@ -2443,94 +2396,21 @@ sub rolesinit { my ($area,$role)=split(/=/,$_); $area=~s/\_\w\w$//; my ($trole,$tend,$tstart)=split(/_/,$role); - $userroles.='user.role.'.$trole.'.'.$area.'='. - $tstart.'.'.$tend."\n"; -# log the associated role with the area - &userrolelog($trole,$username,$domain,$area,$tstart,$tend); - if ($tend!=0) { - if ($tend<$now) { - $trole=''; - } - } - if ($tstart!=0) { - if ($tstart>$now) { - $trole=''; - } - } + $userroles.=&set_arearole($trole,$area,$tstart,$tend); + 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\//) { - 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)) { - $allroles{'cm./'}.=':'.$syspriv; - $allroles{$spec.'./'}.=':'.$syspriv; - } - if ($tdomain ne '') { - if (defined($dompriv)) { - $allroles{'cm./'.$tdomain.'/'}.=':'.$dompriv; - $allroles{$spec.'./'.$tdomain.'/'}.=':'.$dompriv; - } - if ($trest ne '') { - if (defined($coursepriv)) { - $allroles{'cm.'.$area}.=':'.$coursepriv; - $allroles{$spec.'.'.$area}.=':'.$coursepriv; - } - } - } - } - } + &custom_roleprivs(\%allroles,$trole,$tdomain,$trest,$spec,$area); } else { - 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 '') { - if (defined($pr{$trole.':c'})) { - $allroles{'cm.'.$area}.=':'.$pr{$trole.':c'}; - $allroles{$spec.'.'.$area}.=':'.$pr{$trole.':c'}; - } - } - } + &standard_roleprivs(\%allroles,$trole,$tdomain,$spec,$trest,$area); } } } } - my $adv=0; - my $author=0; - foreach (keys %allroles) { - %thesepriv=(); - if (($_!~/^st/) && ($_!~/^ta/) && ($_!~/^cm/)) { $adv=1; } - if (($_=~/^au/) || ($_=~/^ca/)) { $author=1; } - foreach (split(/:/,$allroles{$_})) { - if ($_ ne '') { - my ($privilege,$restrictions)=split(/&/,$_); - if ($restrictions eq '') { - $thesepriv{$privilege}='F'; - } else { - if ($thesepriv{$privilege} ne 'F') { - $thesepriv{$privilege}.=$restrictions; - } - } - } - } - $thesestr=''; - foreach (keys %thesepriv) { $thesestr.=':'.$_.'&'.$thesepriv{$_}; } - $userroles.='user.priv.'.$_.'='.$thesestr."\n"; - } + my ($author,$adv) = &set_userprivs(\$userroles,\%allroles); $userroles.='user.adv='.$adv."\n". 'user.author='.$author."\n"; $ENV{'user.adv'}=$adv; @@ -2538,6 +2418,84 @@ sub rolesinit { 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."\n"; +} + +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)) { + $$allroles{'cm./'}.=':'.$syspriv; + $$allroles{$spec.'./'}.=':'.$syspriv; + } + 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 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) = @_; + my $author=0; + my $adv=0; + foreach (keys %{$allroles}) { + my %thesepriv=(); + if (($_=~/^au/) || ($_=~/^ca/)) { $author=1; } + foreach (split(/:/,$$allroles{$_})) { + if ($_ ne '') { + my ($privilege,$restrictions)=split(/&/,$_); + if ($restrictions eq '') { + $thesepriv{$privilege}='F'; + } elsif ($thesepriv{$privilege} ne 'F') { + $thesepriv{$privilege}.=$restrictions; + } + if ($thesepriv{'adv'} eq 'F') { $adv=1; } + } + } + my $thesestr=''; + foreach (keys %thesepriv) { $thesestr.=':'.$_.'&'.$thesepriv{$_}; } + $$userroles.='user.priv.'.$_.'='.$thesestr."\n"; + } + return ($author,$adv); +} + # --------------------------------------------------------------- get interface sub get { @@ -2559,7 +2517,7 @@ sub get { my %returnhash=(); my $i=0; foreach (@$storearr) { - $returnhash{$_}=unescape($pairs[$i]); + $returnhash{$_}=&thaw_unescape($pairs[$i]); $i++; } return %returnhash; @@ -2598,7 +2556,7 @@ sub dump { my %returnhash=(); foreach (@pairs) { my ($key,$value)=split(/=/,$_); - $returnhash{unescape($key)}=unescape($value); + $returnhash{unescape($key)}=&thaw_unescape($value); } return %returnhash; } @@ -2644,7 +2602,7 @@ sub currentdump { my ($key,$value)=split(/=/,$_); my ($symb,$param) = split(/:/,$key); $returnhash{&unescape($symb)}->{&unescape($param)} = - &unescape($value); + &thaw_unescape($value); } } return %returnhash; @@ -2710,7 +2668,7 @@ sub put { my $uhome=&homeserver($uname,$udomain); my $items=''; foreach (keys %$storehash) { - $items.=&escape($_).'='.&escape($$storehash{$_}).'&'; + $items.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&'; } $items=~s/\&$//; return &reply("put:$udomain:$uname:$namespace:$items",$uhome); @@ -2749,7 +2707,7 @@ sub cput { my $uhome=&homeserver($uname,$udomain); my $items=''; foreach (keys %$storehash) { - $items.=escape($_).'='.escape($$storehash{$_}).'&'; + $items.=escape($_).'='.&freeze_escape($$storehash{$_}).'&'; } $items=~s/\&$//; return &critical("put:$udomain:$uname:$namespace:$items",$uhome); @@ -2772,7 +2730,7 @@ sub eget { my %returnhash=(); my $i=0; foreach (@$storearr) { - $returnhash{$_}=unescape($pairs[$i]); + $returnhash{$_}=&thaw_unescape($pairs[$i]); $i++; } return %returnhash; @@ -2830,12 +2788,14 @@ sub allowed { } # Free bre access to user's own portfolio contents - $uri=~m:([^/]+)/([^/]+)/([^/]+)/([^/]+)/:; - if (('uploaded' eq $1)&&($ENV{'user.name'} eq $3) && ($ENV{'user.domain'} eq $2) && ('portfolio' eq $4)) { + my ($space,$domain,$name,$dir)=split('/',$uri); + if (('uploaded' eq $space) && ($ENV{'user.name'} eq $name) && + ($ENV{'user.domain'} eq $domain) && ('portfolio' eq $dir)) { return 'F'; } # Free bre to public access + if ($priv eq 'bre') { my $copyright=&metadata($uri,'copyright'); if (($copyright eq 'public') && (!$ENV{'request.course.id'})) { @@ -3235,8 +3195,10 @@ sub log_query { sub fetch_enrollment_query { my ($context,$affiliatesref,$replyref,$dom,$cnum) = @_; my $homeserver; + my $maxtries = 1; if ($context eq 'automated') { $homeserver = $perlvar{'lonHostID'}; + $maxtries = 10; # will wait for up to 2000s for retrieval of classlist data before timeout } else { $homeserver = &homeserver($cnum,$dom); } @@ -3254,8 +3216,13 @@ sub fetch_enrollment_query { return 'error: '.$queryid; } my $reply = &get_query_reply($queryid); + my $tries = 1; + while (($reply=~/^timeout/) && ($tries < $maxtries)) { + $reply = &get_query_reply($queryid); + $tries ++; + } if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) { - &logthis('fetch_enrollment_query error: '.$reply.' for '.$dom.' '.$ENV{'user.name'}.' for '.$queryid.' context: '.$context.' '.$cnum); + &logthis('fetch_enrollment_query error: '.$reply.' for '.$dom.' '.$ENV{'user.name'}.' for '.$queryid.' context: '.$context.' '.$cnum.' maxtries: '.$maxtries.' tries: '.$tries); } else { my @responses = split/:/,$reply; if ($homeserver eq $perlvar{'lonHostID'}) { @@ -3662,8 +3629,7 @@ sub modify_student_enrollment { $gene = $tmp{'generation'} if (!defined($gene) || $gene eq ''); $uid = $tmp{'id'} if (!defined($uid) || $uid eq ''); } - my $fullname = &Apache::loncoursedata::ProcessFullName($last,$gene, - $first,$middle); + my $fullname = &format_name($first,$middle,$last,$gene,'lastname'); my $reply=cput('classlist', {"$uname:$udom" => join(':',$end,$start,$uid,$usec,$fullname,$type,$locktype) }, @@ -3680,6 +3646,25 @@ sub modify_student_enrollment { return &assignrole($udom,$uname,$uurl,'st',$end,$start); } +sub format_name { + my ($firstname,$middlename,$lastname,$generation,$first)=@_; + my $name; + if ($first ne 'lastname') { + $name=$firstname.' '.$middlename.' '.$lastname.' '.$generation; + } else { + if ($lastname=~/\S/) { + $name.= $lastname.' '.$generation.', '.$firstname.' '.$middlename; + $name=~s/\s+,/,/; + } else { + $name.= $firstname.' '.$middlename.' '.$generation; + } + } + $name=~s/^\s+//; + $name=~s/\s+$//; + $name=~s/\s+/ /g; + return $name; +} + # ------------------------------------------------- Write to course preferences sub writecoursepref { @@ -3798,6 +3783,82 @@ sub diskusage { return $listing; } +sub is_locked { + my ($file_name, $domain, $user) = @_; + my @check; + my $is_locked; + push @check, $file_name; + my %locked = &Apache::lonnet::get('file_permissions',\@check, + $ENV{'user.domain'},$ENV{'user.name'}); + if (ref($locked{$file_name}) eq 'ARRAY') { + $is_locked = 'true'; + } else { + $is_locked = 'false'; + } +} + +# ------------------------------------------------------------- Mark as Read Only + +sub mark_as_readonly { + my ($domain,$user,$files,$what) = @_; + my %current_permissions = &Apache::lonnet::dump('file_permissions',$domain,$user); + foreach my $file (@{$files}) { + push(@{$current_permissions{$file}},$what); + } + &Apache::lonnet::put('file_permissions',\%current_permissions,$domain,$user); + return; +} + +#--------------------------------------------------------------Get Marked as Read Only + +sub get_marked_as_readonly { + my ($domain,$user,$what) = @_; + my %current_permissions = &Apache::lonnet::dump('file_permissions',$domain,$user); + my @readonly_files; + while (my ($file_name,$value) = each(%current_permissions)) { + if (ref($value) eq "ARRAY"){ + foreach my $stored_what (@{$value}) { + if ($stored_what eq $what) { + push(@readonly_files, $file_name); + } elsif (!defined($what)) { + push(@readonly_files, $file_name); + } + } + } + } + return @readonly_files; +} + +# ------------------------------------------------------------ Unmark as Read Only + +sub unmark_as_readonly { + # unmarks all files locked by $what + # for portfolio submissions, $what contains $crsid and $symb + my ($domain,$user,$what) = @_; + my %current_permissions = &Apache::lonnet::dump('file_permissions',$domain,$user); + my @readonly_files = &Apache::lonnet::get_marked_as_readonly($domain,$user,$what); + foreach my $file(@readonly_files){ + my $current_locks = $current_permissions{$file}; + my @new_locks; + my @del_keys; + if (ref($current_locks) eq "ARRAY"){ + foreach my $locker (@{$current_locks}) { + unless ($locker eq $what) { + push(@new_locks, $what); + } + } + if (@new_locks > 0) { + $current_permissions{$file} = \@new_locks; + } else { + push(@del_keys, $file); + &Apache::lonnet::del('file_permissions',\@del_keys, $domain, $user); + delete $current_permissions{$file}; + } + } + } + &Apache::lonnet::put('file_permissions',\%current_permissions,$domain,$user); + return; +} # ------------------------------------------------------------ Directory lister @@ -3907,6 +3968,9 @@ sub GetFileTimestamp { sub directcondval { my $number=shift; + if (!defined($ENV{'user.state.'.$ENV{'request.course.id'}})) { + &Apache::lonuserstate::evalstate(); + } if ($ENV{'user.state.'.$ENV{'request.course.id'}}) { return substr($ENV{'user.state.'.$ENV{'request.course.id'}},$number,1); } else { @@ -4151,7 +4215,7 @@ sub EXT { $section=$ENV{'request.course.sec'}; } else { if (! defined($usection)) { - $section=&usection($udom,$uname,$courseid); + $section=&getsection($udom,$uname,$courseid); } else { $section = $usection; } @@ -4301,7 +4365,6 @@ sub add_prefix_and_part { # ---------------------------------------------------------------- Get metadata -my %metaentry; sub metadata { my ($uri,$what,$liburi,$prefix,$depthcount)=@_; $uri=&declutter($uri); @@ -4321,29 +4384,28 @@ sub metadata { # Everything is cached by the main uri, libraries are never directly cached # if (!defined($liburi)) { - my ($result,$cached)=&is_cached_new($metacache,'meta',$uri); + my ($result,$cached)=&is_cached(\%metacache,$uri,'meta'); if (defined($cached)) { return $result->{':'.$what}; } } { # # Is this a recursive call for a library? # -# if (! exists($metacache{$uri})) { -# $metacache{$uri}={}; -# } + if (! exists($metacache{$uri})) { + $metacache{$uri}={}; + } if ($liburi) { $liburi=&declutter($liburi); $filename=$liburi; } else { - &devalidate_cache_new($metacache,'meta',$uri); - undef(%metaentry); + &devalidate_cache(\%metacache,$uri,'meta'); } my %metathesekeys=(); unless ($filename=~/\.meta$/) { $filename.='.meta'; } my $metastring; if ($uri !~ m|^uploaded/|) { my $file=&filelocation('',&clutter($filename)); - #push(@{$metaentry{$uri.'.file'}},$file); + push(@{$metacache{$uri.'.file'}},$file); $metastring=&getfile($file); } my $parser=HTML::LCParser->new(\$metastring); @@ -4360,10 +4422,10 @@ sub metadata { if (defined($token->[2]->{'id'})) { $keyroot.='_'.$token->[2]->{'id'}; } - if ($metaentry{':packages'}) { - $metaentry{':packages'}.=','.$package.$keyroot; + if ($metacache{$uri}->{':packages'}) { + $metacache{$uri}->{':packages'}.=','.$package.$keyroot; } else { - $metaentry{':packages'}=$package.$keyroot; + $metacache{$uri}->{':packages'}=$package.$keyroot; } foreach (keys %packagetab) { my $part=$keyroot; @@ -4385,14 +4447,14 @@ sub metadata { if ($subp eq 'display') { $value.=' [Part: '.$part.']'; } - $metaentry{':'.$unikey.'.part'}=$part; + $metacache{$uri}->{':'.$unikey.'.part'}=$part; $metathesekeys{$unikey}=1; - unless (defined($metaentry{':'.$unikey.'.'.$subp})) { - $metaentry{':'.$unikey.'.'.$subp}=$value; + unless (defined($metacache{$uri}->{':'.$unikey.'.'.$subp})) { + $metacache{$uri}->{':'.$unikey.'.'.$subp}=$value; } - if (defined($metaentry{':'.$unikey.'.default'})) { - $metaentry{':'.$unikey}= - $metaentry{':'.$unikey.'.default'}; + if (defined($metacache{$uri}->{':'.$unikey.'.default'})) { + $metacache{$uri}->{':'.$unikey}= + $metacache{$uri}->{':'.$unikey.'.default'}; } } } @@ -4425,7 +4487,7 @@ sub metadata { foreach (sort(split(/\,/,&metadata($uri,'keys', $location,$unikey, $depthcount+1)))) { - $metaentry{':'.$_}=$metaentry{':'.$_}; + $metacache{$uri}->{':'.$_}=$metacache{$uri}->{':'.$_}; $metathesekeys{$_}=1; } } @@ -4436,18 +4498,18 @@ sub metadata { } $metathesekeys{$unikey}=1; foreach (@{$token->[3]}) { - $metaentry{':'.$unikey.'.'.$_}=$token->[2]->{$_}; + $metacache{$uri}->{':'.$unikey.'.'.$_}=$token->[2]->{$_}; } my $internaltext=&HTML::Entities::decode($parser->get_text('/'.$entry)); - my $default=$metaentry{':'.$unikey.'.default'}; + my $default=$metacache{$uri}->{':'.$unikey.'.default'}; if ( $internaltext =~ /^\s*$/ && $default !~ /^\s*$/) { # only ws inside the tag, and not in default, so use default # as value - $metaentry{':'.$unikey}=$default; + $metacache{$uri}->{':'.$unikey}=$default; } else { # either something interesting inside the tag or default # uninteresting - $metaentry{':'.$unikey}=$internaltext; + $metacache{$uri}->{':'.$unikey}=$internaltext; } # end of not-a-package not-a-library import } @@ -4464,7 +4526,7 @@ sub metadata { &metadata_create_package_def($uri,$key,'extension_'.$extension, \%metathesekeys); } - if (!exists($metaentry{':packages'})) { + if (!exists($metacache{$uri}->{':packages'})) { foreach my $key (sort(keys(%packagetab))) { #no specific packages well let's get default then if ($key!~/^default&/) { next; } @@ -4473,31 +4535,31 @@ sub metadata { } } # are there custom rights to evaluate - if ($metaentry{':copyright'} eq 'custom') { + if ($metacache{$uri}->{':copyright'} eq 'custom') { # # Importing a rights file here # unless ($depthcount) { - my $location=$metaentry{':customdistributionfile'}; + my $location=$metacache{$uri}->{':customdistributionfile'}; my $dir=$filename; $dir=~s|[^/]*$||; $location=&filelocation($dir,$location); foreach (sort(split(/\,/,&metadata($uri,'keys', $location,'_rights', $depthcount+1)))) { - #$metaentry{':'.$_}=$metacache{$uri}->{':'.$_}; + $metacache{$uri}->{':'.$_}=$metacache{$uri}->{':'.$_}; $metathesekeys{$_}=1; } } } - $metaentry{':keys'}=join(',',keys %metathesekeys); - &metadata_generate_part0(\%metathesekeys,\%metaentry,$uri); - $metaentry{':allpossiblekeys'}=join(',',keys %metathesekeys); - &do_cache_new($metacache,'meta',$uri,\%metaentry); + $metacache{$uri}->{':keys'}=join(',',keys %metathesekeys); + &metadata_generate_part0(\%metathesekeys,$metacache{$uri},$uri); + $metacache{$uri}->{':allpossiblekeys'}=join(',',keys %metathesekeys); + &do_cache(\%metacache,$uri,$metacache{$uri},'meta'); # this is the end of "was not already recently cached } - return $metaentry{':'.$what}; + return $metacache{$uri}->{':'.$what}; } sub metadata_create_package_def { @@ -4505,22 +4567,22 @@ sub metadata_create_package_def { my ($pack,$name,$subp)=split(/\&/,$key); if ($subp eq 'default') { next; } - if (defined($metaentry{':packages'})) { - $metaentry{':packages'}.=','.$package; + if (defined($metacache{$uri}->{':packages'})) { + $metacache{$uri}->{':packages'}.=','.$package; } else { - $metaentry{':packages'}=$package; + $metacache{$uri}->{':packages'}=$package; } my $value=$packagetab{$key}; my $unikey; $unikey='parameter_0_'.$name; - $metaentry{':'.$unikey.'.part'}=0; + $metacache{$uri}->{':'.$unikey.'.part'}=0; $$metathesekeys{$unikey}=1; - unless (defined($metaentry{':'.$unikey.'.'.$subp})) { - $metaentry{':'.$unikey.'.'.$subp}=$value; + unless (defined($metacache{$uri}->{':'.$unikey.'.'.$subp})) { + $metacache{$uri}->{':'.$unikey.'.'.$subp}=$value; } - if (defined($metaentry{':'.$unikey.'.default'})) { - $metaentry{':'.$unikey}= - $metaentry{':'.$unikey.'.default'}; + if (defined($metacache{$uri}->{':'.$unikey.'.default'})) { + $metacache{$uri}->{':'.$unikey}= + $metacache{$uri}->{':'.$unikey.'.default'}; } } @@ -4648,7 +4710,7 @@ sub symbverify { sub symbclean { my $symb=shift; - + if ($symb=~m|^/enc/|) { $symb=&Apache::lonenc::unencrypted($symb); } # remove version from map $symb=~s/\.(\d+)\.(\w+)\_\_\_/\.$2\_\_\_/; @@ -4669,7 +4731,9 @@ sub encode_symb { } sub decode_symb { - my ($map,$resid,$url)=split(/\_\_\_/,shift); + my $symb=shift; + if ($symb=~m|^/enc/|) { $symb=&Apache::lonenc::unencrypted($symb); } + my ($map,$resid,$url)=split(/___/,$symb); return (&fixversion($map),$resid,&fixversion($url)); } @@ -4808,6 +4872,7 @@ sub numval { $txt=~tr/U-Z/0-5/; $txt=~tr/u-z/0-5/; $txt=~s/\D//g; + if ($_64bit) { if ($txt > 2**32) { return -1; } } return int($txt); } @@ -4823,6 +4888,7 @@ sub numval2 { my @txts=split(/(\d\d\d\d\d\d\d\d\d)/,$txt); my $total; foreach my $val (@txts) { $total+=$val; } + if ($_64bit) { if ($total > 2**32) { return -1; } } return int($total); } @@ -4839,10 +4905,16 @@ sub get_rand_alg { return &latest_rnd_algorithm_id(); } +sub validCODE { + my ($CODE)=@_; + if (defined($CODE) && $CODE ne '' && $CODE =~ /^\w+$/) { return 1; } + return 0; +} + sub getCODE { - if (defined($ENV{'form.CODE'})) { return $ENV{'form.CODE'}; } + if (&validCODE($ENV{'form.CODE'})) { return $ENV{'form.CODE'}; } if (defined($Apache::lonhomework::parsing_a_problem) && - defined($Apache::lonhomework::history{'resource.CODE'})) { + &validCODE($Apache::lonhomework::history{'resource.CODE'})) { return $Apache::lonhomework::history{'resource.CODE'}; } return undef; @@ -4884,6 +4956,7 @@ sub rndseed_32bit { my $num=$symbseed+$nameseed+$domainseed+$courseseed+$namechck+$symbchck; #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); #&Apache::lonxml::debug("rndseed :$num:$symb"); + if ($_64bit) { $num=(($num<<32)>>32); } return $num; } } @@ -4904,6 +4977,8 @@ sub rndseed_64bit { my $num2=$nameseed+$domainseed+$courseseed; #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); #&Apache::lonxml::debug("rndseed :$num:$symb"); + if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); } + if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); } return "$num1,$num2"; } } @@ -4947,7 +5022,9 @@ sub rndseed_64bit3 { my $num1=$symbchck+$symbseed+$namechck; my $num2=$nameseed+$domainseed+$courseseed; #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); - #&Apache::lonxml::debug("rndseed :$num:$symb"); + #&Apache::lonxml::debug("rndseed :$num1:$num2:$_64bit"); + if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); } + return "$num1:$num2"; } } @@ -4965,6 +5042,8 @@ sub rndseed_CODE_64bit { my $num2=$CODEseed+$courseseed+$symbchck; #&Apache::lonxml::debug("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck"); #&Apache::lonxml::debug("rndseed :$num1:$num2:$symb"); + if ($_64bit) { $num1=(($num1<<32)>>32); } + if ($_64bit) { $num2=(($num2<<32)>>32); } return "$num1:$num2"; } } @@ -5129,13 +5208,15 @@ sub repcopy_userfile { sub tokenwrapper { my $uri=shift; - $uri=~s/^http\:\/\/([^\/]+)//; - $uri=~s/^\///; + $uri=~s|^http\://([^/]+)||; + $uri=~s|^/||; $ENV{'user.environment'}=~/\/([^\/]+)\.id/; my $token=$1; - if ($uri=~/^uploaded\/([^\/]+)\/([^\/]+)\/([^\/]+)(\?\.*)*$/) { - &appenv('userfile.'.$1.'/'.$2.'/'.$3 => $ENV{'request.course.id'}); - return 'http://'.$hostname{ &homeserver($2,$1)}.'/'.$uri. + my (undef,$udom,$uname,$file)=split('/',$uri,4); + if ($udom && $uname && $file) { + $file=~s|(\?\.*)*$||; + &appenv("userfile.$udom/$uname/$file" => $ENV{'request.course.id'}); + return 'http://'.$hostname{ &homeserver($uname,$udom)}.'/'.$uri. (($uri=~/\?/)?'&':'?').'token='.$token. '&tokenissued='.$perlvar{'lonHostID'}; } else { @@ -5268,6 +5349,15 @@ sub clutter { return $thisfn; } +sub freeze_escape { + my ($value)=@_; + if (ref($value)) { + $value=&nfreeze($value); + return '__FROZEN__'.&escape($value); + } + return &escape($value); +} + # -------------------------------------------------------- Escape Special Chars sub escape { @@ -5284,6 +5374,16 @@ sub unescape { return $str; } +sub thaw_unescape { + my ($value)=@_; + if ($value =~ /^__FROZEN__/) { + substr($value,0,10,undef); + $value=&unescape($value); + return &thaw($value); + } + return &unescape($value); +} + sub mod_perl_version { if (defined($perlvar{'MODPERL2'})) { return 2; @@ -5303,13 +5403,13 @@ sub goodbye { #not converted to using infrastruture and probably shouldn't be &logthis(sprintf("%-20s is %s",'%badServerCache',scalar(%badServerCache))); #converted -# &logthis(sprintf("%-20s is %s",'%metacache',scalar(%metacache))); + &logthis(sprintf("%-20s is %s",'%metacache',scalar(%metacache))); &logthis(sprintf("%-20s is %s",'%homecache',scalar(%homecache))); &logthis(sprintf("%-20s is %s",'%titlecache',scalar(%titlecache))); &logthis(sprintf("%-20s is %s",'%courseresdatacache',scalar(%courseresdatacache))); #1.1 only &logthis(sprintf("%-20s is %s",'%userresdatacache',scalar(%userresdatacache))); - &logthis(sprintf("%-20s is %s",'%usectioncache',scalar(%usectioncache))); + &logthis(sprintf("%-20s is %s",'%getsectioncache',scalar(%getsectioncache))); &logthis(sprintf("%-20s is %s",'%courseresversioncache',scalar(%courseresversioncache))); &logthis(sprintf("%-20s is %s",'%resversioncache',scalar(%resversioncache))); &flushcourselogs(); @@ -5456,7 +5556,7 @@ BEGIN { } -$metacache=new Cache::Memcached({'servers'=>['127.0.0.1:11211']}); +%metacache=(); $processmarker='_'.time.'_'.$perlvar{'lonHostID'}; $dumpcount=0; @@ -5464,6 +5564,12 @@ $dumpcount=0; &logtouch(); &logthis('INFO: Read configuration'); $readit=1; + { + use integer; + my $test=(2**32)+1; + if ($test != 0) { $_64bit=1; } else { $_64bit=0; } + &logthis(" Detected 64bit platform ($_64bit)"); + } } } @@ -5691,8 +5797,8 @@ X B: get user privileges =item * -X -B: finds the section of student in the +X +B: finds the section of student in the course $cname, return section name/number or '' for "not in course" and '-1' for "no section"