--- loncom/lonnet/perl/lonnet.pm 2004/03/19 16:45:25 1.479 +++ loncom/lonnet/perl/lonnet.pm 2004/04/23 19:36:46 1.488 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.479 2004/03/19 16:45:25 albertel Exp $ +# $Id: lonnet.pm,v 1.488 2004/04/23 19:36:46 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -32,6 +32,8 @@ package Apache::lonnet; use strict; use LWP::UserAgent(); use HTTP::Headers; +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 @@ -615,6 +617,7 @@ sub idput { my ($udom,%ids)=@_; my %servers=(); foreach (keys %ids) { + &cput('environment',{'id'=>$ids{$_}},$udom,$_); my $uhom=&homeserver($_,$udom); if ($uhom ne 'no_host') { my $id=&escape($ids{$_}); @@ -625,7 +628,6 @@ sub idput { } else { $servers{$uhom}=$id.'='.$unam; } - &critical('put:'.$udom.':'.$unam.':environment:id='.$id,$uhom); } } foreach (keys %servers) { @@ -1185,7 +1187,8 @@ sub tokenwrapper { # --------- File operations in /home/httpd/html/userfiles/$domain/1/2/3/$course # input: action, courseID, current domain, home server for course, intended # path to file, source of file. -# output: ok if successful, diagnostic message otherwise +# 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. @@ -1200,6 +1203,13 @@ sub tokenwrapper { # 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,$docuhome,$file,$source)=@_; @@ -1207,7 +1217,7 @@ sub process_coursefile { if ($action eq 'propagate') { $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file ,$docuhome); - } elsif ($action eq 'copy') { + } else { my $fetchresult = ''; my $fpath = ''; my $fname = $file; @@ -1223,17 +1233,32 @@ sub process_coursefile { } } } - if ($source eq '') { - $fetchresult = 'no source file'; - } else { - my $destination = $filepath.'/'.$fname; - print STDERR "Getting ready to rename $source to $destination\n"; - rename($source,$destination); + 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, + $docuhome); + } + } elsif ($action eq 'uploaddoc') { + open(my $fh,'>'.$filepath.'/'.$fname); + print $fh $ENV{'form.'.$source}; + close($fh); $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file, $docuhome); + if ($fetchresult eq 'ok') { + return '/uploaded/'.$fpath.'/'.$fname; + } else { + &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file. + ' to host '.$docuhome.': '.$fetchresult); + return '/adm/notfound.html'; + } } } - unless ( ($fetchresult eq 'ok') || ($fetchresult eq 'no source file') ) { + unless ( $fetchresult eq 'ok') { &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file. ' to host '.$docuhome.': '.$fetchresult); } @@ -1266,13 +1291,18 @@ sub userfileupload { $docuname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; $docudom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; $docuhome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'}; + if ($ENV{'form.folder'} =~ m/^default/) { + return &finishuserfileupload($docuname,$docudom,$docuhome,$formname,$fname); + } else { + $fname=$ENV{'form.folder'}.'/'.$fname; + return &process_coursefile('uploaddoc',$docuname,$docudom,$docuhome,$fname,$formname); + } } else { $docuname=$ENV{'user.name'}; $docudom=$ENV{'user.domain'}; $docuhome=$ENV{'user.home'}; + return &finishuserfileupload($docuname,$docudom,$docuhome,$formname,$fname); } - return - &finishuserfileupload($docuname,$docudom,$docuhome,$formname,$fname); } sub finishuserfileupload { @@ -3277,9 +3307,10 @@ sub modify_student_enrollment { } my $fullname = &Apache::loncoursedata::ProcessFullName($last,$gene, $first,$middle); - my $value=&escape($uname.':'.$udom).'='. - &escape(join(':',$end,$start,$uid,$usec,$fullname,$type)); - my $reply=critical('put:'.$cdom.':'.$cnum.':classlist:'.$value,$chome); + my $reply=cput('classlist', + {"$uname:$udom" => + join(':',$end,$start,$uid,$usec,$fullname,$type) }, + $cdom,$cnum); unless (($reply eq 'ok') || ($reply eq 'delayed')) { return 'error: '.$reply; } @@ -4047,6 +4078,22 @@ sub metadata { # the next is the end of "start tag" } } + my ($extension) = ($uri =~ /\.(\w+)$/); + foreach my $key (sort(keys(%packagetab))) { + #&logthis("extsion1 $extension $key !!"); + #no specific packages #how's our extension + if ($key!~/^extension_\Q$extension\E&/) { next; } + &metadata_create_package_def($uri,$key,'extension_'.$extension, + \%metathesekeys); + } + if (!exists($metacache{$uri}->{':packages'})) { + foreach my $key (sort(keys(%packagetab))) { + #no specific packages well let's get default then + if ($key!~/^default&/) { next; } + &metadata_create_package_def($uri,$key,'default', + \%metathesekeys); + } + } # are there custom rights to evaluate if ($metacache{$uri}->{':copyright'} eq 'custom') { @@ -4075,6 +4122,30 @@ sub metadata { return $metacache{$uri}->{':'.$what}; } +sub metadata_create_package_def { + my ($uri,$key,$package,$metathesekeys)=@_; + my ($pack,$name,$subp)=split(/\&/,$key); + if ($subp eq 'default') { next; } + + if (defined($metacache{$uri}->{':packages'})) { + $metacache{$uri}->{':packages'}.=','.$package; + } else { + $metacache{$uri}->{':packages'}=$package; + } + my $value=$packagetab{$key}; + my $unikey; + $unikey='parameter_0_'.$name; + $metacache{$uri}->{':'.$unikey.'.part'}=0; + $$metathesekeys{$unikey}=1; + unless (defined($metacache{$uri}->{':'.$unikey.'.'.$subp})) { + $metacache{$uri}->{':'.$unikey.'.'.$subp}=$value; + } + if (defined($metacache{$uri}->{':'.$unikey.'.default'})) { + $metacache{$uri}->{':'.$unikey}= + $metacache{$uri}->{':'.$unikey.'.default'}; + } +} + sub metadata_generate_part0 { my ($metadata,$metacache,$uri) = @_; my %allnames; @@ -4267,9 +4338,13 @@ sub symbread { my %bighash; my $syval=''; if (($ENV{'request.course.fn'}) && ($thisfn)) { + my $targetfn = $thisfn; + if ( ($thisfn =~ m/^uploaded\//) && ($thisfn !~ m/\.(page|sequence)$/) ) { + $targetfn = 'adm/wrapper/'.$thisfn; + } if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { - $syval=$hash{$thisfn}; + $syval=$hash{$targetfn}; untie(%hash); } # ---------------------------------------------------------- There was an entry @@ -4321,7 +4396,7 @@ sub symbread { } } untie(%bighash) - } + } } if ($syval) { return &symbclean($syval.'___'.$thisfn); @@ -4345,6 +4420,21 @@ sub numval { return int($txt); } +sub numval2 { + my $txt=shift; + $txt=~tr/A-J/0-9/; + $txt=~tr/a-j/0-9/; + $txt=~tr/K-T/0-9/; + $txt=~tr/k-t/0-9/; + $txt=~tr/U-Z/0-5/; + $txt=~tr/u-z/0-5/; + $txt=~s/\D//g; + my @txts=split(/(\d\d\d\d\d\d\d\d\d)/,$txt); + my $total; + foreach my $val (@txts) { $total+=$val; } + return int($total); +} + sub latest_rnd_algorithm_id { return '64bit2'; } @@ -4360,9 +4450,9 @@ sub rndseed { if (!$domain) { $domain=$wdomain; } if (!$username) { $username=$wusername } my $which=$ENV{"course.$courseid.rndseed"}; - my $CODE=$ENV{'scantron.CODE'}; + my $CODE=$ENV{'form.CODE'}; if (defined($CODE)) { - &rndseed_CODE_64bit($symb,$courseid,$domain,$username); + return &rndseed_CODE_64bit($symb,$courseid,$domain,$username); } elsif ($which eq '64bit2') { return &rndseed_64bit2($symb,$courseid,$domain,$username); } elsif ($which eq '64bit') { @@ -4435,12 +4525,13 @@ sub rndseed_CODE_64bit { { use integer; my $symbchck=unpack("%32S*",$symb.' ') << 16; - my $symbseed=numval($symb); - my $CODEseed=numval($ENV{'scantron.CODE'}) << 16; + my $symbseed=numval2($symb); + my $CODEchck=unpack("%32S*",$ENV{'form.CODE'}.' ') << 16; + my $CODEseed=numval($ENV{'form.CODE'}); my $courseseed=unpack("%32S*",$courseid.' '); - my $num1=$symbseed+$CODEseed; - my $num2=$courseseed+$symbchck; - #&Apache::lonxml::debug("$symbseed:$CODEseed|$courseseed:$symbchck"); + my $num1=$symbseed+$CODEchck; + my $num2=$CODEseed+$courseseed+$symbchck; + #&Apache::lonxml::debug("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck"); #&Apache::lonxml::debug("rndseed :$num1:$num2:$symb"); return "$num1,$num2"; } @@ -4460,15 +4551,37 @@ sub latest_receipt_algorithm_id { return 'receipt2'; } +sub recunique { + my $fucourseid=shift; + my $unique; + if ($ENV{"course.$fucourseid.receiptalg"} eq 'receipt2') { + $unique=$ENV{"course.$fucourseid.internal.encseed"}; + } else { + $unique=$perlvar{'lonReceipt'}; + } + return unpack("%32C*",$unique); +} + +sub recprefix { + my $fucourseid=shift; + my $prefix; + if ($ENV{"course.$fucourseid.receiptalg"} eq 'receipt2') { + $prefix=$ENV{"course.$fucourseid.internal.encpref"}; + } else { + $prefix=$perlvar{'lonHostID'}; + } + return unpack("%32C*",$prefix); +} + sub ireceipt { my ($funame,$fudom,$fucourseid,$fusymb,$part)=@_; my $cuname=unpack("%32C*",$funame); my $cudom=unpack("%32C*",$fudom); my $cucourseid=unpack("%32C*",$fucourseid); my $cusymb=unpack("%32C*",$fusymb); - my $cunique=unpack("%32C*",$perlvar{'lonReceipt'}); + my $cunique=&recunique($fucourseid); my $cpart=unpack("%32S*",$part); - my $return =unpack("%32C*",$perlvar{'lonHostID'}).'-'; + my $return =&recprefix($fucourseid).'-'; if ($ENV{"course.$fucourseid.receiptalg"} eq 'receipt2' || $ENV{'request.state'} eq 'construct') { &Apache::lonxml::debug("doing receipt2 using parts $cpart, uname $cuname and udom $cudom gets ".($cpart%$cuname). @@ -4502,37 +4615,98 @@ sub receipt { # ------------------------------------------------------------ Serves up a file # returns either the contents of the file or # -1 if the file doesn't exist -# -2 if an error occured when trying to aqcuire the file +# +# if the target is a file that was uploaded via DOCS, +# a check will be made to see if a current copy exists on the local server, +# if it does this will be served, otherwise a copy will be retrieved from +# the home server for the course and stored in /home/httpd/html/userfiles on +# the local server. sub getfile { - my $file=shift; - if ($file=~/^\/*uploaded\//) { # user file - my $ua=new LWP::UserAgent; - my $request=new HTTP::Request('GET',&tokenwrapper($file)); - my $response=$ua->request($request); - if ($response->is_success()) { - return $response->content; - } else { - #&logthis("Return Code is ".$response->code." for $file ". - # &tokenwrapper($file)); - # 500 for ISE when tokenwrapper can't figure out what server to - # contact - # 503 when lonuploadacc can't contact the requested server - if ($response->code eq 503 || $response->code eq 500) { - return -2; - } else { - return -1; + my ($file,$caller) = @_; + + if ($file !~ m|^/*uploaded/(\w+)/(\w+)/(.+)$|) { + # normal file from res space + &repcopy($file); + return &readfile($file); + } + + my $info; + my $cdom = $1; + my $cnum = $2; + my $filename = $3; + my $path = $Apache::lonnet::perlvar{'lonDocRoot'}.'/userfiles'; + my ($lwpresp,$rtncode); + my $localfile = $path.'/'.$cdom.'/'.$cnum.'/'.$filename; + if (-e "$localfile") { + my @fileinfo = stat($localfile); + $lwpresp = &getuploaded('HEAD',$file,$cdom,$cnum,\$info,\$rtncode); + if ($lwpresp ne 'ok') { + if ($rtncode eq '404') { + unlink($localfile); } + return -1; } - } else { # normal file from res space - &repcopy($file); - if (! -e $file ) { return -1; }; - my $fh; - open($fh,"<$file"); - my $a=''; - while (<$fh>) { $a .=$_; } - return $a; + if ($info < $fileinfo[9]) { + return &readfile($localfile); + } + $info = ''; + $lwpresp = &getuploaded('GET',$file,$cdom,$cnum,\$info,\$rtncode); + if ($lwpresp ne 'ok') { + return -1; + } + } else { + $lwpresp = &getuploaded('GET',$file,$cdom,$cnum,\$info,\$rtncode); + if ($lwpresp ne 'ok') { + return -1; + } + my @parts = ($cdom,$cnum); + if ($filename =~ m|^(.+)/[^/]+$|) { + push @parts, split(/\//,$1); + } + foreach my $part (@parts) { + $path .= '/'.$part; + if (!-e $path) { + mkdir($path,0770); + } + } + } + open (FILE,">$localfile"); + print FILE $info; + close(FILE); + if ($caller eq 'uploadrep') { + return 'ok'; + } + return $info; +} + +sub getuploaded { + my ($reqtype,$uri,$cdom,$cnum,$info,$rtncode) = @_; + $uri=~s/^\///; + $uri = 'http://'.$hostname{ &homeserver($cnum,$cdom)}.'/raw/'.$uri; + my $ua=new LWP::UserAgent; + my $request=new HTTP::Request($reqtype,$uri); + my $response=$ua->request($request); + $$rtncode = $response->code; + if (! $response->is_success()) { + return 'failed'; + } + if ($reqtype eq 'HEAD') { + $$info = &HTTP::Date::str2time( $response->header('Last-modified') ); + } elsif ($reqtype eq 'GET') { + $$info = $response->content; } + return 'ok'; +} + +sub readfile { + my $file = shift; + if ( (! -e $file ) || ($file eq '') ) { return -1; }; + my $fh; + open($fh,"<$file"); + my $a=''; + while (<$fh>) { $a .=$_; } + return $a; } sub filelocation { @@ -4675,7 +4849,7 @@ BEGIN { open(my $config,") { - if ($configline =~ /^[^\#]*PerlSetVar/) { + if ($configline=~/\S/ && $configline =~ /^[^\#]*PerlSetVar/) { my ($dummy,$varname,$varvalue)=split(/\s+/,$configline); chomp($varvalue); $perlvar{$varname}=$varvalue; @@ -4793,6 +4967,7 @@ BEGIN { open(my $config,"<$perlvar{'lonTabDir'}/packages.tab"); while (my $configline=<$config>) { + if ($configline !~ /\S/ || $configline=~/^#/) { next; } chomp($configline); my ($short,$plain)=split(/:/,$configline); my ($pack,$name)=split(/\&/,$short); @@ -5538,8 +5713,29 @@ messages of critical importance should g =item * -getfile($file) : returns the entire contents of a file or -1; it -properly subscribes to and replicates the file if neccessary. +getfile($file,$caller) : two cases - requests for files in /res or in /uploaded. +(a) files in /uploaded + (i) If a local copy of the file exists - + compares modification date of local copy with last-modified date for + definitive version stored on home server for course. If local copy is + stale, requests a new version from the home server and stores it. + If the original has been removed from the home server, then local copy + is unlinked. + (ii) If local copy does not exist - + requests the file from the home server and stores it. + + If $caller is 'uploadrep': + This indicates a call from lonuploadrep.pm (PerlHeaderParserHandler phase) + for request for files originally uploaded via DOCS. + - returns 'ok' if fresh local copy now available, -1 otherwise. + + Otherwise: + This indicates a call from the content generation phase of the request. + - returns the entire contents of the file or -1. + +(b) files in /res + - returns the entire contents of a file or -1; + it properly subscribes to and replicates the file if neccessary. =item *