--- loncom/lonnet/perl/lonnet.pm	2010/08/25 16:34:36	1.1083
+++ loncom/lonnet/perl/lonnet.pm	2010/11/10 14:44:19	1.1093
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1083 2010/08/25 16:34:36 raeburn Exp $
+# $Id: lonnet.pm,v 1.1093 2010/11/10 14:44:19 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -95,6 +95,7 @@ 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.
@@ -1733,7 +1734,8 @@ sub getsection {
     # If there is a role which has expired, return it.
     #
     $courseid = &courseid_to_courseurl($courseid);
-    my %roleshash = &dump('roles',$udom,$unam,$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;
@@ -2350,9 +2352,13 @@ sub resizeImage {
 
 # --------------- 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.filename"}
-#        $coursedoc - if true up to the current course
-#                     if false
+#                    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
@@ -2368,32 +2374,54 @@ sub resizeImage {
 #             or /adm/notfound.html if failure to upload occurse
 
 sub userfileupload {
-    my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase,$destuname,
+    my ($formname,$context,$subdir,$parser,$allfiles,$codebase,$destuname,
         $destudom,$thumbwidth,$thumbheight,$resizewidth,$resizeheight)=@_;
     if (!defined($subdir)) { $subdir='unknown'; }
     my $fname=$env{'form.'.$formname.'.filename'};
     $fname=&clean_filename($fname);
-# See if there is anything left
+    # See if there is anything left
     unless ($fname) { return 'error: no uploaded file'; }
     chop($env{'form.'.$formname});
-    if (($formname eq 'screenshot') && ($subdir eq 'helprequests')) { #files uploaded to help request form are handled differently
+    # Files uploaded to help request form, or uploaded to "create course" page are handled differently
+    if ((($formname eq 'screenshot') && ($subdir eq 'helprequests')) ||
+        (($formname eq 'coursecreatorxml') && ($subdir eq 'batchupload')) ||
+         ($context eq 'existingfile') || ($context eq 'canceloverwrite')) {
         my $now = time;
-        my $filepath = 'tmp/helprequests/'.$now;
-        my @parts=split(/\//,$filepath);
-        my $fullpath = $perlvar{'lonDaemons'};
-        for (my $i=0;$i<@parts;$i++) {
-            $fullpath .= '/'.$parts[$i];
-            if ((-e $fullpath)!=1) {
-                mkdir($fullpath,0777);
+        my $filepath;
+        if (($formname eq 'screenshot') && ($subdir eq 'helprequests')) { 
+             $filepath = 'tmp/helprequests/'.$now;
+        } elsif (($formname eq 'coursecreatorxml') && ($subdir eq 'batchupload')) {
+             $filepath = 'tmp/addcourse/'.$destudom.'/web/'.$env{'user.name'}.
+                         '_'.$env{'user.domain'}.'/pending';
+        } elsif (($context eq 'existingfile') || ($context eq 'canceloverwrite')) {
+            my ($docuname,$docudom);
+            if ($destudom) {
+                $docudom = $destudom;
+            } else {
+                $docudom = $env{'user.domain'};
+            }
+            if ($destuname) {
+                $docuname = $destuname;
+            } else {
+                $docuname = $env{'user.name'};
+            }
+            if (exists($env{'form.group'})) {
+                $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
+                $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
+            }
+            $filepath = 'tmp/overwrites/'.$docudom.'/'.$docuname.'/'.$subdir;
+            if ($context eq 'canceloverwrite') {
+                my $tempfile =  $perlvar{'lonDaemons'}.'/'.$filepath.'/'.$fname;
+                if (-e  $tempfile) {
+                    my @info = stat($tempfile);
+                    if ($info[9] eq $env{'form.timestamp'}) {
+                        unlink($tempfile);
+                    }
+                }
+                return;
             }
         }
-        open(my $fh,'>'.$fullpath.'/'.$fname);
-        print $fh $env{'form.'.$formname};
-        close($fh);
-        return $fullpath.'/'.$fname;
-    } elsif (($formname eq 'coursecreatorxml') && ($subdir eq 'batchupload')) { #files uploaded to create course page are handled differently
-        my $filepath = 'tmp/addcourse/'.$destudom.'/web/'.$env{'user.name'}.
-                       '_'.$env{'user.domain'}.'/pending';
+        # Create the directory if not present
         my @parts=split(/\//,$filepath);
         my $fullpath = $perlvar{'lonDaemons'};
         for (my $i=0;$i<@parts;$i++) {
@@ -2405,22 +2433,26 @@ sub userfileupload {
         open(my $fh,'>'.$fullpath.'/'.$fname);
         print $fh $env{'form.'.$formname};
         close($fh);
-        return $fullpath.'/'.$fname;
+        if ($context eq 'existingfile') {
+            my @info = stat($fullpath.'/'.$fname);
+            return ($fullpath.'/'.$fname,$info[9]);
+        } else {
+            return $fullpath.'/'.$fname;
+        }
     }
     if ($subdir eq 'scantron') {
         $fname = 'scantron_orig_'.$fname;
-    } else {   
-# Create the directory if not present
+    } else {
         $fname="$subdir/$fname";
     }
-    if ($coursedoc) {
+    if ($context eq 'coursedoc') {
 	my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
 	my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
         if ($env{'form.folder'} =~ m/^(default|supplemental)/) {
             return &finishuserfileupload($docuname,$docudom,
 					 $formname,$fname,$parser,$allfiles,
 					 $codebase,$thumbwidth,$thumbheight,
-                                         $resizewidth,$resizeheight);
+                                         $resizewidth,$resizeheight,$context);
         } else {
             $fname=$env{'form.folder'}.'/'.$fname;
             return &process_coursefile('uploaddoc',$docuname,$docudom,
@@ -2433,8 +2465,7 @@ sub userfileupload {
 	return &finishuserfileupload($docuname,$docudom,$formname,$fname,
 				     $parser,$allfiles,$codebase,
                                      $thumbwidth,$thumbheight,
-                                     $resizewidth,$resizeheight);
-        
+                                     $resizewidth,$resizeheight,$context);
     } else {
         my $docuname=$env{'user.name'};
         my $docudom=$env{'user.domain'};
@@ -2445,13 +2476,13 @@ sub userfileupload {
 	return &finishuserfileupload($docuname,$docudom,$formname,$fname,
 				     $parser,$allfiles,$codebase,
                                      $thumbwidth,$thumbheight,
-                                     $resizewidth,$resizeheight);
+                                     $resizewidth,$resizeheight,$context);
     }
 }
 
 sub finishuserfileupload {
     my ($docuname,$docudom,$formname,$fname,$parser,$allfiles,$codebase,
-        $thumbwidth,$thumbheight,$resizewidth,$resizeheight) = @_;
+        $thumbwidth,$thumbheight,$resizewidth,$resizeheight,$context) = @_;
     my $path=$docudom.'/'.$docuname.'/';
     my $filepath=$perlvar{'lonDocRoot'};
   
@@ -2477,7 +2508,23 @@ sub finishuserfileupload {
 	    print STDERR ('Failed to create '.$filepath.'/'.$file."\n");
 	    return '/adm/notfound.html';
 	}
-	if (!print FH ($env{'form.'.$formname})) {
+        if ($context eq 'overwrite') {
+            my $source =  $perlvar{'lonDaemons'}.'/tmp/overwrites/'.$docudom.'/'.$docuname.'/'.$fname;
+            my $target = $filepath.'/'.$file;
+            if (-e $source) {
+                my @info = stat($source);
+                if ($info[9] eq $env{'form.timestamp'}) {   
+                    unless (&File::Copy::move($source,$target)) {
+                        &logthis('Failed to overwrite '.$filepath.'/'.$file);
+                        return "Moving from $source failed";
+                    }
+                } else {
+                    return "Temporary file: $source had unexpected date/time for last modification";
+                }
+            } else {
+                return "Temporary file: $source missing";
+            }
+        } elsif (!print FH ($env{'form.'.$formname})) {
 	    &logthis('Failed to write to '.$filepath.'/'.$file);
 	    print STDERR ('Failed to write to '.$filepath.'/'.$file."\n");
 	    return '/adm/notfound.html';
@@ -3035,8 +3082,9 @@ sub get_my_roles {
     unless (defined($uname)) { $uname=$env{'user.name'}; }
     unless (defined($udom)) { $udom=$env{'user.domain'}; }
     my (%dumphash,%nothide);
-    if ($context eq 'userroles') { 
-        %dumphash = &dump('roles',$udom,$uname);
+    if ($context eq 'userroles') {
+        my $extra = &freeze_escape({'skipcheck' => 1});
+        %dumphash = &dump('roles',$udom,$uname,'.',undef,$extra);
     } else {
         %dumphash=
             &dump('nohist_userroles',$udom,$uname);
@@ -4015,7 +4063,7 @@ sub rolesinit {
     my ($domain,$username,$authhost)=@_;
     my $now=time;
     my %userroles = ('user.login.time' => $now);
-    my $extra = &freeze_escape({'clientcheckrole' => 1});
+    my $extra = &freeze_escape({'skipcheck' => 1});
     my $rolesdump=reply("dump:$domain:$username:roles:.::$extra",$authhost);
     if (($rolesdump eq 'con_lost') || ($rolesdump eq '') || 
         ($rolesdump =~ /^error:/)) {
@@ -4285,22 +4333,22 @@ sub role_status {
 }
 
 sub check_adhoc_privs {
-    my ($cdom,$cnum,$then,$refresh,$now,$checkrole) = @_;
+    my ($cdom,$cnum,$then,$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,$then,$refresh,$now,\$role,\$where,\$trolecode,\$tstatus,\$tstart,\$tend);
         unless (($tstatus eq 'is') || ($tstatus eq 'will_not')) {
-            &set_adhoc_privileges($cdom,$cnum,$checkrole);
+            &set_adhoc_privileges($cdom,$cnum,$checkrole,$caller);
         }
     } else {
-        &set_adhoc_privileges($cdom,$cnum,$checkrole);
+        &set_adhoc_privileges($cdom,$cnum,$checkrole,$caller);
     }
 }
 
 sub set_adhoc_privileges {
 # role can be cc or ca
-    my ($dcdom,$pickedcourse,$role) = @_;
+    my ($dcdom,$pickedcourse,$role,$caller) = @_;
     my $area = '/'.$dcdom.'/'.$pickedcourse;
     my $spec = $role.'.'.$area;
     my %userroles = &set_arearole($role,$area,'','',$env{'user.domain'},
@@ -4310,14 +4358,16 @@ sub set_adhoc_privileges {
     my ($author,$adv)= &set_userprivs(\%userroles,\%ccrole);
     &appenv(\%userroles,[$role,'cm']);
     &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$role);
-    &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});
+    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});
+    }
 }
 
 # --------------------------------------------------------------- get interface
@@ -4366,7 +4416,7 @@ sub del {
 # -------------------------------------------------------------- dump interface
 
 sub dump {
-    my ($namespace,$udomain,$uname,$regexp,$range)=@_;
+    my ($namespace,$udomain,$uname,$regexp,$range,$extra)=@_;
     if (!$udomain) { $udomain=$env{'user.domain'}; }
     if (!$uname) { $uname=$env{'user.name'}; }
     my $uhome=&homeserver($uname,$udomain);
@@ -4375,7 +4425,7 @@ sub dump {
     } else {
 	$regexp='.';
     }
-    my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
+    my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range:$extra",$uhome);
     my @pairs=split(/\&/,$rep);
     my %returnhash=();
     foreach my $item (@pairs) {
@@ -4920,7 +4970,7 @@ sub is_portfolio_file {
 }
 
 sub usertools_access {
-    my ($uname,$udom,$tool,$action,$context) = @_;
+    my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref)=@_;
     my ($access,%tools);
     if ($context eq '') {
         $context = 'tools';
@@ -4962,9 +5012,14 @@ sub usertools_access {
         $toolstatus = $env{'environment.'.$context.'.'.$tool};
         $inststatus = $env{'environment.inststatus'};
     } else {
-        my %userenv = &userenvironment($udom,$uname,$context.'.'.$tool,'inststatus');
-        $toolstatus = $userenv{$context.'.'.$tool};
-        $inststatus = $userenv{'inststatus'};
+        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 '') {
@@ -4976,8 +5031,17 @@ sub usertools_access {
         return $access;
     }
 
-    my $is_adv = &is_advanced_user($udom,$uname);
-    my %domdef = &get_domain_defaults($udom);
+    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 '') {
@@ -5051,6 +5115,11 @@ sub is_course_owner {
 
 sub is_advanced_user {
     my ($udom,$uname) = @_;
+    if ($udom ne '' && $uname ne '') {
+        if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
+            return $env{'user.adv'};  
+        }
+    }
     my %roleshash = &get_my_roles($uname,$udom,'userroles',undef,undef,undef,1);
     my %allroles;
     my $is_adv;
@@ -6336,7 +6405,8 @@ sub get_users_groups {
     } else {  
         $grouplist = '';
         my $courseurl = &courseid_to_courseurl($courseid);
-        my %roleshash = &dump('roles',$udom,$uname,$courseurl);
+        my $extra = &freeze_escape({'skipcheck' => 1});
+        my %roleshash = &dump('roles',$udom,$uname,$courseurl,undef,$extra);
         my $access_end = $env{'course.'.$courseid.
                               '.default_enrollment_end_date'};
         my $now = time;
@@ -6839,6 +6909,9 @@ sub modifyuser {
     if ($reply ne 'ok') { 
         return 'error: '.$reply;
     }
+    if ($names{'permanentemail'} ne $oldnames{'permanentemail'}) {
+        &Apache::lonnet::devalidate_cache_new('emailscache',$uname.':'.$udom);
+    }
     my $sqlresult = &update_allusers_table($uname,$udom,\%names);
     &devalidate_cache_new('namescache',$uname.':'.$udom);
     $logmsg = 'Success modifying user '.$logmsg;
@@ -7215,7 +7288,7 @@ sub is_locked {
     my ($file_name, $domain, $user) = @_;
     my @check;
     my $is_locked;
-    push @check, $file_name;
+    push (@check,$file_name);
     my %locked = &get('file_permissions',\@check,
 		      $env{'user.domain'},$env{'user.name'});
     my ($tmp)=keys(%locked);
@@ -7232,6 +7305,7 @@ sub is_locked {
     } else {
         $is_locked = 'false';
     }
+    return $is_locked;
 }
 
 sub declutter_portfile {
@@ -8382,7 +8456,7 @@ sub metadata {
     if (($uri eq '') || 
 	(($uri =~ m|^/*adm/|) && 
 	     ($uri !~ m|^adm/includes|) && ($uri !~ m|/bulletinboard$|)) ||
-        ($uri =~ m|/$|) || ($uri =~ m|/.meta$|) ) {
+        ($uri =~ m|/$|) || ($uri =~ m|/.meta$|) || ($uri =~ /^\*uploaded\/.+\.sequence$/) ) {
 	return undef;
     }
     if (($uri =~ /^~/ || $uri =~ m{home/$match_username/public_html/}) 
@@ -11323,8 +11397,10 @@ userfileupload(): main rotine for puttin
            filename, and the contents of the file to create/modifed exist
            the filename is in $env{'form.'.$formname.'.filename'} and the
            contents of the file is located in $env{'form.'.$formname}
- coursedoc - if true, store the file in the course of the active role
-             of the current user
+ context - if coursedoc, store the file in the course of the active role
+             of the current user; 
+           if 'existingfile': store in 'overwrites' in /home/httpd/perl/tmp
+           if 'canceloverwrite': delete file in tmp/overwrites directory
  subdir - required - subdirectory to put the file in under ../userfiles/
          if undefined, it will be placed in "unknown"
 
@@ -11346,16 +11422,27 @@ returns: the new clean filename
 
 =item *
 
-finishuserfileupload(): routine that creaes and sends the file to
+finishuserfileupload(): routine that creates and sends the file to
 userspace, probably shouldn't be called directly
 
   docuname: username or courseid of destination for the file
   docudom: domain of user/course of destination for the file
   formname: same as for userfileupload()
-  fname: filename (inculding subdirectories) for the file
+  fname: filename (including subdirectories) for the file
+  parser: if 'parse', will parse (html) file to extract references to objects, links etc.
+  allfiles: reference to hash used to store objects found by parser
+  codebase: reference to hash used for codebases of java objects found by parser
+  thumbwidth: width (pixels) of thumbnail to be created for uploaded image
+  thumbheight: height (pixels) of thumbnail to be created for uploaded image
+  resizewidth: width to be used to resize image using resizeImage from ImageMagick
+  resizeheight: height to be used to resize image using resizeImage from ImageMagick
+  context: if 'overwrite', will move the uploaded file from its temporary location to
+            userfiles to facilitate overwriting a previously uploaded file with same name.
 
  returns either the url of the uploaded file (/uploaded/....) if successful
- and /adm/notfound.html if unsuccessful
+ and /adm/notfound.html if unsuccessful (or an error message if context 
+ was 'overwrite').
+ 
 
 =item *