--- loncom/publisher/loncfile.pm 2002/07/29 01:55:51 1.13 +++ loncom/publisher/loncfile.pm 2003/06/19 20:23:39 1.30 @@ -8,9 +8,8 @@ # and requests confirmation. The second phase commits the action # and displays a page showing the results of the action. # - # -# $Id: loncfile.pm,v 1.13 2002/07/29 01:55:51 foxr Exp $ +# $Id: loncfile.pm,v 1.30 2003/06/19 20:23:39 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -69,7 +68,7 @@ directory. =head1 INTRODUCTION loncfile is invoked when buttons in the top frame of the construction -space directory listing are clicked. All operations procede in two phases. +space directory listing are clicked. All operations proceed in two phases. The first phase describes to the user exactly what will be done. If the user confirms the operation, the second phase commits the operation and indicates completion. When the user dismisses the output of phase2, they are returned to @@ -86,10 +85,13 @@ package Apache::loncfile; use strict; use Apache::File; +use File::Basename; use File::Copy; +use HTML::Entities(); use Apache::Constants qw(:common :http :methods); use Apache::loncacc; use Apache::Log (); +use Apache::lonnet; my $DEBUG=0; my $r; # Needs to be global for some stuff RF. @@ -98,17 +100,17 @@ my $r; # Needs to be global for some =item Debug($request, $message) - If debugging is enabled puts out a debuggin message determined by the + If debugging is enabled puts out a debugging message determined by the caller. The debug message goes to the Apache error log file. Debugging - is enabled by ssetting the module global DEBUG variable to nonzero (TRUE). + is enabled by setting the module global DEBUG variable to nonzero (TRUE). Parameters: =over 4 -=item $request - The curretn request operation. +=item $request - The current request operation. -=item $message - The message to put inthe log file. +=item $message - The message to put in the log file. =back @@ -125,7 +127,7 @@ sub Debug { my $log = $r->log; my $message = shift; - # Put out the indicated message butonly if DEBUG is false. + # Put out the indicated message butonly if DEBUG is true. if ($DEBUG) { $log->debug($message); @@ -153,7 +155,7 @@ sub Debug { =over 4 -=item The corresponing file system path. +=item The corresponding file system path. =back @@ -180,7 +182,7 @@ sub URLToPath { =item PublicationPath($domain, $user, $dir, $file) - Determines the filesystem path corersponding to a published resource + Determines the filesystem path corresponding to a published resource specification. The returned value is the path. Parameters: @@ -191,7 +193,7 @@ Parameters: =item $user - string [in] Name of the user asking about the resource. -=item $dir - Directory pathr elatvie to the top of the resource space0 +=item $dir - Directory path relative to the top of the resource space. =item $file - name of the resource file itself without path info. @@ -219,7 +221,7 @@ sub PublicationPath =item ConstructionPath($domain, $user, $dir, $file) - Determines the filesystem path corersponding to a construction space + Determines the filesystem path corresponding to a construction space resource specification. The returned value is the path Parameters: @@ -227,7 +229,7 @@ Parameters: =item $user - string [in] Name of the user asking about the resource. -=item $dir - Directory path relatvie to the top of the resource space +=item $dir - Directory path relative to the top of the resource space. =item $file - name of the resource file itself without path info. @@ -285,7 +287,7 @@ sub ConstructionPathFromRelative { =item exists($user, $domain, $directory, $file) - Determine if a resource file name has been publisehd or exists + Determine if a resource file name has been published or exists in the construction space. Parameters: @@ -320,20 +322,24 @@ sub exists { my ($user, $domain, $dir, $file) = @_; # Create complete paths in publication and construction space. - - my $published = &PublicationPath($domain, $user, $dir, $file); - my $construct = &ConstructionPath($user, $dir, $file); + my $relativedir=$dir; + $relativedir=s|/home/\Q$user\E/public_html||; + my $published = &PublicationPath($domain, $user, $relativedir, $file); + my $construct = &ConstructionPath($user, $relativedir, $file); # If the resource exists in either space indicate this fact. # Note that the check for existence in resource space is stricter. my $result; + if ( -d $construct ) { + return 'Error: destination for operation is a directory.'; + } if ( -e $published) { - $result.='

Warning: target file exists, and has been published!

'; + $result.='

Warning: target file exists, and has been published!

'; } elsif ( -e $construct) { - $result.='

Warning: target file exists!

'; - } + $result.='

Warning: target file exists!

'; + } return $result; @@ -360,7 +366,7 @@ as a result of this operation. =over 4 -=item Empty string if everythikng worked. +=item Empty string if everything worked. =item String containing an error message if there was a problem. @@ -377,7 +383,7 @@ sub checksuffix { if ($old=~m:(.*)/+([^/]+)\.(\w+)$:) { $oldsuffix=$3; } if ($oldsuffix ne $newsuffix) { $result.= - '

Warning: change of MIME type!

'; + '

Warning: change of MIME type!

'; } return $result; } @@ -404,9 +410,9 @@ sub CloseForm1 { &Debug($request, "Cancel url is: ".$cancelurl); - $request->print('

'); + $request->print('

'); $request->print('

'); + '" method="POST">

'); } @@ -438,7 +444,7 @@ Parameters: sub CloseForm2 { my ($request, $user, $directory) = @_; - $request->print('

Done

'); + $request->print('

Done

'); } =pod @@ -488,12 +494,31 @@ sub Rename1 { if(-e $conspace) { if($ENV{'form.newfilename'}) { my $newfilename = $ENV{'form.newfilename'}; + if ($newfilename =~ m|/[^\.]+$|) { + #no extension add on orignal extension + if ($filename =~ m|/[^\.]*\.([^\.]+)$|) { + $newfilename.='.'.$1; + } + } $request->print(&checksuffix($filename, $newfilename)); - $request->print(&exists($user, $domain, $dir, $newfilename)); - $request->print('Cancel'); + return; + } + my $dest=&SimplifyDir($dir,$newfilename); + $request->print('

Rename '.$filename.' to '. - $dir.'/'.$newfilename.'?

'); + '" />

Rename '.$filename. + '
to '. + $dest.'?

'); &CloseForm1($request, $cancelurl); } else { $request->print('

No new filename specified

'); @@ -538,8 +563,8 @@ sub Delete1 { if( -e $filename) { - $request->print(''); + $request->print(''); $request->print('

Delete '.$filename.'?

'); &CloseForm1($request, $cancelurl); } else { @@ -552,8 +577,8 @@ sub Delete1 { =item Copy1($request, $user, $domain, $filename, $newfilename) Performs phase 1 processing of the construction space copy command. - Ensure that the source fil eexists. Ensure that a destination exists, - also warn if the detination already exists. + Ensure that the source file exists. Ensure that a destination exists, + also warn if the destination already exists. Parameters: @@ -584,14 +609,19 @@ sub Copy1 { $cancelurl =~ s/\/public_html//; - if(-e $filename) { $request->print(&checksuffix($filename,$newfilename)); - $request->print(&exists($user, $domain, $dir, $newfilename)); - $request->print('Cancel'); + return; + } + my $dest=&SimplifyDir($dir,$newfilename); + $request->print('

Copy '.$filename.' to'. - ''.$dir.'/'.$newfilename.'/?

'); + '" />

Copy '.$filename.'
to '. + ''.$dest.'?

'); &CloseForm1($request, $cancelurl); } else { $request->print('

No such file '.$filename.'

'); @@ -600,6 +630,34 @@ sub Copy1 { =pod +=item SimplifyDir + + Removes all extra / and all .. references + +Parameters: + +=over 4 + +=item $dir - string [in] a directory name + +=item $file - string [in] a file reference relative to $dir + +=back + +Results: the concatenated path. + +=cut + +sub SimplifyDir { + my ($dir,$file) = @_; + my $location = $dir. '/'.$file; + $location=~s://+:/:g; # remove duplicate / + while ($location=~m:/\.\./:) {$location=~s:/[^/]+/\.\./:/:g;}#remove dir/.. + return $location; +} + +=pod + =item NewDir1 Does all phase 1 processing of directory creation: @@ -611,11 +669,11 @@ Parameters: =over 4 =item $request - Apache Request Object [in] - Server request object for the - current url.. + current url. =item $username - Name of the user that is requesting the directory creation. -=item $path - current directory relative to construction spacee. +=item $path - current directory relative to construction space. =item $newdir - Name of the directory to be created; path relative to the top level of construction space. @@ -650,8 +708,8 @@ sub NewDir1 $request->print('

Directory exists.

'); } else { - $request->print('

Make new directory '. + $request->print('

Make new directory '. $path."/".$newdir.'?

'); &CloseForm1($request, $cancelurl); @@ -660,6 +718,88 @@ sub NewDir1 =pod +=item NewFile1 + + Does all phase 1 processing of file creation: + Ensures that the user provides a new filename, adds proper extension + if needed and that the file does not already exist, if it is a html, + problem, page, or sequence, it then creates a form link to hand the + actual creation off to the proper handler. + +Parameters: + +=over 4 + +=item $request - Apache Request Object [in] - Server request object for the + current url. + +=item $username - Name of the user that is requesting the directory creation. + +=item $domain - Name of the domain of the user + +=item $dir - current absolute diretory + +=item $newfilename + - Name of the file to be created; no path information +=back + +Side Effects: + +=over 4 + +=item 2 new forms are displayed. Clicking on the confirmation button +causes the browser to attempt to load the specfied URL, allowing the +proper handler to take care of file creation. There is also a Cancle +button which returns you to the driectory listing you came from + +=back + +=cut + + +sub NewFile1 { + my ($request, $user, $domain, $dir, $newfilename) = @_; + + &Debug($request, "Dir is : ".$dir); + &Debug($request, "Newfile is : ".$newfilename); + + my $cancelurl = "/priv/".$dir; + $cancelurl =~ s/\/home\///; + $cancelurl =~ s/\/public_html//; + + if ($ENV{'form.action'} =~ /new(.+)file/) { + my $extension=$1; + if ($newfilename !~ /\Q.$extension\E$/) { + if ($newfilename =~ m|^[^\.]*\.([^\.]+)$|) { + #already has an extension strip it and add in expected one + $newfilename =~ s|.([^\.]+)$||; + } + $newfilename.=".$extension"; + } + } + + my $fullpath = $dir.'/'.$newfilename; + + &Debug($request, "Full path is : ".$fullpath); + + if(-e $fullpath) { + $request->print('

File exists.

'); + } + else { + $request->print('

Make new file '.$dir.'/'.$newfilename.'?

'); + my $dest=&MakeFinalUrl($request,$fullpath); + &Debug($request, "Cancel url is: ".$cancelurl); + &Debug($request, "Dest url is: ".$dest); + $request->print(''); + $request->print('

'); + $request->print('

'); + } +} + +=pod + =item phaseone($r, $fn, $uname, $udom) Peforms phase one processing of the request. In phase one, error messages @@ -680,7 +820,7 @@ performed and reported to the user. =item $uname - string [in] Name of user logged in and doing this action. -=item $udom - string [in] Domain nmae under which the user logged in. +=item $udom - string [in] Domain name under which the user logged in. =back @@ -697,15 +837,17 @@ sub phaseone { # my $conspace=ConstructionPathFromRelative($uname, $fn); - $r->print('
'. - ''. - ''. - ''); + $r->print(''. + ''. + ''. + ''); if ($ENV{'form.action'} eq 'rename') { - - &Rename1($r, $fn, $uname, $udom, $dir); - + if (!defined($dir)) { + $fn=~m:(.*)/:; + $dir=$1; + } + &Rename1($r, $fn, $uname, $udom, $dir); } elsif ($ENV{'form.action'} eq 'delete') { &Delete1($r, $uname, $fn); @@ -719,15 +861,30 @@ sub phaseone { } } elsif ($ENV{'form.action'} eq 'newdir') { &NewDir1($r, $uname, $dir, $ENV{'form.newfilename'}); + } elsif ($ENV{'form.action'} eq 'newfile' || + $ENV{'form.action'} eq 'newhtmlfile' || + $ENV{'form.action'} eq 'newproblemfile' || + $ENV{'form.action'} eq 'newpagefile' || + $ENV{'form.action'} eq 'newsequencefile' || + $ENV{'form.action'} eq 'Select Action') { + if($ENV{'form.newfilename'}) { + my $newfilename = $ENV{'form.newfilename'}; + if (!defined($dir)) { + $fn=~m:(.*)/:; + $dir=$1; + } + &NewFile1($r, $uname, $udom, $dir, $newfilename); + } else { + $r->print('

No new filename specified.

'); + } } - } =pod =item Rename2($request, $user, $directory, $oldfile, $newfile) -Performs phase 2 procesing of a rename reequest. This is where the +Performs phase 2 processing of a rename reequest. This is where the actual rename is performed. Parameters @@ -767,9 +924,20 @@ sub Rename2 { $newfile); if(-e $oldfile) { - unless(rename($oldfile, - $directory.'/'.$newfile)) { - $request->print('Error: '.$!.''); + my $dest; + + if ($oldfile =~ m|/$|) { + #renaming a dir + $oldfile =~ s|/$||; + $dest=$directory; + $dest=~s|(/)([^/]*)$|$1|; + $dest.='/'.$newfile; + } else { + $dest=$directory.'/'.$newfile; + } + + unless(rename($oldfile,$dest)) { + $request->print('Error: '.$!.''); return 0; } else {} } else { @@ -797,8 +965,8 @@ Parameters: =item $user - string [in] The name of the user initiating the delete request. -=item $filename - string [in] The name of the file, relative to construction space, - to delete. +=item $filename - string [in] The name of the file, relative to construction + space, to delete. =back @@ -813,11 +981,11 @@ sub Delete2 { if(-e $filename) { unless(unlink($filename)) { - $request->print('Error: '.$!.''); + $request->print('Error: '.$!.''); return 0; } } else { - $request->print('

No such file. print('

No such file.

print(' Error: '.$!.''); + $request->print(' copy Error: '.$!.''); return 0; } else { + unless (chmod(0660, $newfile)) { + $request->print(' chmod error: '.$!.''); + return 0; + } return 1; } } else { @@ -893,11 +1065,11 @@ sub NewDir2 { my ($request, $user, $newdirectory) = @_; unless(mkdir($newdirectory, 02770)) { - $request->print('Error: '.$!.''); + $request->print('Error: '.$!.''); return 0; } unless(chmod(02770, ($newdirectory))) { - $request->print(' Error: '.$!.''); + $request->print(' Error: '.$!.''); return 0; } return 1; @@ -942,11 +1114,16 @@ sub phasetwo { # Break down the file into it's component pieces. - $fn=~/(.*)\/([^\/]+)\.(\w+)$/; - my $dir=$1; # Directory path - my $main=$2; # Filename. - my $suffix=$3; # Extension. - + my $dir; # Directory path + my $main; # Filename. + my $suffix; # Extension. + + if ($fn=~m:(.*)/([^/]+)\.(\w+)$:) { + $dir=$1; # Directory path + $main=$2; # Filename. + $suffix=$3; # Extension. + } + my $dest; # On success this is where we'll go. &Debug($r, @@ -966,12 +1143,19 @@ sub phasetwo { if ($ENV{'form.action'} eq 'rename') { # Rename. if($ENV{'form.newfilename'}) { + if (!defined($dir)) { + $fn=~m:^(.*)/:; + $dir=$1; + } if(!&Rename2($r, $uname, $dir, $fn, $ENV{'form.newfilename'})) { return; } # Prepend the directory to the new name to form the basis of the # url of the new resource. # + #renaming a dir + #remove last element for current dir + if ($fn =~ m|/$|) { $dir =~ s|/[^/]*$||; } $dest = $dir."/".$ENV{'form.newfilename'}; } } elsif ($ENV{'form.action'} eq 'delete') { @@ -981,7 +1165,7 @@ sub phasetwo { # Once a resource is deleted, we just list the directory that # previously held it. # - $dest = $dir."/"; # Parent dir. + $dest = $dir."/."; # Parent dir. } elsif ($ENV{'form.action'} eq 'copy') { if($ENV{'form.newfilename'}) { if(!&Copy2($r, $uname, $dir, $fn, $ENV{'form.newfilename'})) { @@ -990,7 +1174,7 @@ sub phasetwo { $dest = $ENV{'form.newfilename'}; } else { - $r->print('

No New filename specified'); + $r->print('

No New filename specified

'); return; } @@ -1009,14 +1193,26 @@ sub phasetwo { # Substitute for priv for the first home in $dir to get our # construction space path. # - &Debug($r, "Final url is: $dest"); - $dest =~ s/\/home\//\/priv\//; - $dest =~ s/\/public_html//; - &Debug($r, "Final url after rewrite: $dest"); + $dest=&MakeFinalUrl($r,$dest); $r->print('

Done

'); } +sub MakeFinalUrl { + my($r,$dest)=@_; + &Debug($r, "Final url is: $dest"); + $dest =~ s|/home/|/priv/|; + $dest =~ s|/public_html||; + + my $base = &File::Basename::basename($dest); + my $dpath= &File::Basename::dirname($dest); + if ($base eq '.') { $base=''; } + $dest = &HTML::Entities::encode($dpath.'/'.$base); + + &Debug($r, "Final url after rewrite: $dest"); + return $dest; +} + sub handler { $r=shift; @@ -1029,7 +1225,7 @@ sub handler { my $fn; if ($ENV{'form.filename'}) { - $fn=$ENV{'form.filename'}; + $fn=&Apache::lonnet::unescape($ENV{'form.filename'}); &Debug($r, "loncfile::handler - raw url: $fn"); # $fn=~s/^http\:\/\/[^\/]+\/\~(\w+)/\/home\/$1\/public_html/; # $fn=~s/^http\:\/\/[^\/]+//; @@ -1075,13 +1271,13 @@ sub handler { $r->print('LON-CAPA Construction Space'); $r->print( - ''); + ''); $r->print('

Construction Space '.$fn.'

'); if (($uname ne $ENV{'user.name'}) || ($udom ne $ENV{'user.domain'})) { - $r->print('

Co-Author: '.$uname.' at '.$udom. + $r->print('

Co-Author: '.$uname.' at '.$udom. '

'); } @@ -1096,8 +1292,15 @@ sub handler { $r->print('

New Directory

'); } elsif ($ENV{'form.action'} eq 'copy') { $r->print('

Copy

'); + } elsif ($ENV{'form.action'} eq 'newfile' || + $ENV{'form.action'} eq 'newhtmlfile' || + $ENV{'form.action'} eq 'newproblemfile' || + $ENV{'form.action'} eq 'newpagefile' || + $ENV{'form.action'} eq 'newsequencefile' || + $ENV{'form.action'} eq 'Select Action' ) { + $r->print('

New Resource

'); } else { - $r->print('

Unknown Action'); + $r->print('

Unknown Action '.$ENV{'form.action'}.'

'); return OK; } if ($ENV{'form.phase'} eq 'two') {