[system] / trunk / webwork2 / lib / WeBWorK / Upload.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/Upload.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1613 - (download) (as text) (annotate)
Tue Nov 4 02:23:07 2003 UTC (9 years, 6 months ago) by sh002i
File size: 9596 byte(s)
Finished Upload module. See POD.

Added sendFile routine to ContentGenerator. To use, store a hash
reference in $self->{sendFile} containing keys:
	source: full path to file to send
	type: content type of file to send
	name: file name to suggest to client
This must be done BEFORE the HTTP header is sent, i.e. in
pre_header_initialize. It will not work is &go or &header is overridden.

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::Upload;
    7 
    8 =head1 NAME
    9 
   10 WeBWorK::Upload - store uploads securely across requests.
   11 
   12 =head1 SYNOPSIS
   13 
   14 Given C<$u>, an Apache::Upload object
   15 
   16  my $upload = WeBWorK::Upload->store($u,
   17   dir => $ce->{webworkDirs}->{DATA}
   18  );
   19  my $id = $upload->id;
   20  my $hash = $upload->hash;
   21 
   22 Later...
   23 
   24  my $upload = WeBWorK::Upload->retrieve($id, $hash,
   25   dir => $ce->{webworkDirs}->{uploadCache}
   26  );
   27  my $fh = $upload->fileHandle;
   28  my $path = $upload->filePath;
   29 
   30  # get rid of the upload -- $upload is useless after this!
   31  $upload->dispose;
   32 
   33  # ...or move it somewhere before disposal
   34  $upload->disposeTo($path);
   35 
   36 =head1 DESCRIPTION
   37 
   38 WeBWorK::Upload provides a method for securely storing uploaded files until such
   39 time as they are needed. This is useful for situations in which an upload cannot
   40 be handled by the system until some later request, such as the case where a user
   41 is not yet authenticated, and a login page must be returned. Since a file upload
   42 should not be sent back to the client and then uploaded again with the user
   43 provides his login information, some proxy must be sent in its place.
   44 WeBWorK::Upload generates a unique ID which can be used to retrieve the original
   45 file.
   46 
   47 =cut
   48 
   49 use strict;
   50 use warnings;
   51 use Carp qw(croak);
   52 use Data::UUID; # this is probably overkill ;)
   53 use Digest::MD5 qw(md5_hex);
   54 use File::Copy qw(copy move);
   55 
   56 =head1 STORING UPLOADS
   57 
   58 Uploads represented as Apache::Uploads objects can be stored in an upload cache
   59 and later retrieved, given the proper ID and hash. The hash is used to confirm
   60 the authenticity of the ID.
   61 
   62 =head2 CONSTRUCTOR
   63 
   64 =over
   65 
   66 =item store($u, %options)
   67 
   68 Stores the Apache::Upload C<$u> securely. The following keys must be defined in
   69 %options:
   70 
   71  dir => the directory in which to store the uploaded file
   72 
   73 =cut
   74 
   75 sub store {
   76   my ($invocant, $apacheUpload, %options) = @_;
   77 
   78   croak "no Apache::Upload specified" unless $apacheUpload;
   79 
   80   # generate UUID
   81   my $ug = new Data::UUID;
   82   my $uuid = $ug->create_str;
   83 
   84   # generate one-time secret
   85   my $secret = sprintf("%X"x4, map { int rand 2**32 } 1..4);
   86 
   87   # generate hash from $uuid and $secret
   88   my $hash = md5_hex($uuid, $secret);
   89 
   90   # get information about uploaded file
   91   my $realFileName = $apacheUpload->filename;
   92   my $fh = $apacheUpload->fh;
   93 
   94   my $infoName = "$uuid.info";
   95   my $infoPath = "$options{dir}/$infoName";
   96 
   97   my $fileName = "$uuid.file";
   98   my $filePath = "$options{dir}/$fileName";
   99 
  100   # write info file
  101   open my $infoFH, ">", $infoPath
  102     or die "failed to write upload info file $infoPath: $!";
  103   print $infoFH "$realFileName\n$secret\n";
  104   close $infoFH;
  105 
  106   # copy uploaded file
  107   copy($fh, $filePath); # the file name is a secret!
  108 
  109   return bless {
  110     uuid => $uuid,
  111     dir  => $options{dir},
  112     hash => $hash,
  113     realFileName => $realFileName,
  114   }, ref($invocant) || $invocant;
  115 }
  116 
  117 =item id
  118 
  119 Return the upload's unique ID, or an undefiend value if the upload is not valid.
  120 
  121 =cut
  122 
  123 sub id {
  124   my ($self) = @_;
  125   my $uuid = $self->{uuid};
  126   my $dir = $self->{dir};
  127 
  128   my $infoName = "$uuid.info";
  129   my $infoPath = "$dir/$infoName";
  130 
  131   # make sure info file still exists (i.e. the file hasn't been disposed of)
  132   return unless -e $infoPath;
  133 
  134   return $uuid;
  135 }
  136 
  137 =item hash
  138 
  139 Return the upload's hash, or an undefiend value if the upload is not valid.
  140 
  141 =cut
  142 
  143 sub hash {
  144   my ($self) = @_;
  145   my $uuid = $self->{uuid};
  146   my $dir = $self->{dir};
  147   my $hash = $self->{hash};
  148 
  149   my $infoName = "$uuid.info";
  150   my $infoPath = "$dir/$infoName";
  151 
  152   # make sure info file still exists (i.e. the file hasn't been disposed of)
  153   return unless -e $infoPath;
  154 
  155   return $hash;
  156 }
  157 
  158 =back
  159 
  160 =head1 RETRIEVING UPLOADS
  161 
  162 An upload stored in the upload cache can be retrieved by supplying its ID and
  163 hash (accessible from the above C<id> and C<hash> methods, respectivly. The file
  164 can then be accessed by name or file handle, moved, and disposed of.
  165 
  166 =head2 CONSTRUCTOR
  167 
  168 =over
  169 
  170 =item retrieve($id, $hash, %options)
  171 
  172 Retrieves the Apache::Upload referenced by C<$id> and C<$hash>. The following
  173 keys must be defined in %options:
  174 
  175  dir => the directory in which to store the uploaded file
  176 
  177 =cut
  178 
  179 sub retrieve {
  180   my ($invocant, $uuid, $hash, %options) = @_;
  181 
  182   croak "no upload ID specified" unless $uuid;
  183   croak "no upload hash specified" unless $hash;
  184 
  185   my $infoName = "$uuid.info";
  186   my $infoPath = "$options{dir}/$infoName";
  187 
  188   my $fileName = "$uuid.file";
  189   my $filePath = "$options{dir}/$fileName";
  190 
  191   croak "no upload matches the ID specified" unless -e $infoPath;
  192 
  193   # get real file name and secret from info file
  194   open my $infoFH, "<", $infoPath
  195     or die "failed to read upload info file $infoPath: $!";
  196   my ($realFileName, $secret) = <$infoFH>;
  197   close $infoFH;
  198 
  199   # jesus christ
  200   chomp $realFileName;
  201   chomp $secret;
  202 
  203   # generate correct hash from $uuid and $secret
  204   my $correctHash = md5_hex($uuid, $secret);
  205 
  206   #warn __PACKAGE__, ": secret is $secret\n";
  207   #warn __PACKAGE__, ": correctHash is $correctHash\n";
  208 
  209   croak "upload hash incorrect!" unless $hash eq $correctHash;
  210 
  211   # -- you passed the test... --
  212 
  213   return bless {
  214     uuid => $uuid,
  215     dir => $options{dir},
  216     hash => $hash,
  217     realFileName => $realFileName,
  218   }, ref($invocant) || $invocant;
  219 }
  220 
  221 =back
  222 
  223 =head2 METHODS
  224 
  225 =over
  226 
  227 =item filename
  228 
  229 Returns the original name of the uploaded file.
  230 
  231 =cut
  232 
  233 sub filename {
  234   my ($self) = @_;
  235   my $uuid = $self->{uuid};
  236   my $dir = $self->{dir};
  237   my $realFileName = $self->{realFileName};
  238 
  239   my $infoName = "$uuid.info";
  240   my $infoPath = "$dir/$infoName";
  241 
  242   my $fileName = "$uuid.file";
  243   my $filePath = "$dir/$fileName";
  244 
  245   # make sure info file still exists (i.e. the file hasn't been disposed of)
  246   return unless -e $infoPath;
  247 
  248   return $realFileName;
  249 }
  250 
  251 =item fileHandle
  252 
  253 Return a file handle pointing to the uploaded file, or an undefiend value if the
  254 upload is not valid. Suitable for reading.
  255 
  256 =cut
  257 
  258 sub fileHandle {
  259   my ($self) = @_;
  260   my $uuid = $self->{uuid};
  261   my $dir = $self->{dir};
  262 
  263   my $infoName = "$uuid.info";
  264   my $infoPath = "$dir/$infoName";
  265 
  266   my $fileName = "$uuid.file";
  267   my $filePath = "$dir/$fileName";
  268 
  269   # make sure info file still exists (i.e. the file hasn't been disposed of)
  270   return unless -e $infoPath;
  271 
  272   open my $fh, "<", $filePath
  273     or die "failed to open upload $filePath for reading: $!";
  274   return $fh;
  275 }
  276 
  277 =item filePath
  278 
  279 Return the path to the uploaded file, or an undefiend value if the upload is not
  280 valid.
  281 
  282 If you use this, bear in mind that you must not dispose of the upload (either by
  283 moving or deleting the uploaded file or calling the C<dispose> method). If you
  284 wish to move the file, use the C<disposeTo> method instead.
  285 
  286 =cut
  287 
  288 sub filePath {
  289   my ($self) = @_;
  290   my $uuid = $self->{uuid};
  291   my $dir = $self->{dir};
  292 
  293   my $infoName = "$uuid.info";
  294   my $infoPath = "$dir/$infoName";
  295 
  296   my $fileName = "$uuid.file";
  297   my $filePath = "$dir/$fileName";
  298 
  299   # make sure info file still exists (i.e. the file hasn't been disposed of)
  300   return unless -e $infoPath;
  301 
  302   return $filePath;
  303 }
  304 
  305 =item dispose
  306 
  307 Remove the file from the upload cache. Returns true if the upload was
  308 successfully destroyed, or an undefiend value if the upload is not valid.
  309 
  310 =cut
  311 
  312 sub dispose {
  313   my ($self) = @_;
  314   my $uuid = $self->{uuid};
  315   my $dir = $self->{dir};
  316 
  317   my $infoName = "$uuid.info";
  318   my $infoPath = "$dir/$infoName";
  319 
  320   my $fileName = "$uuid.file";
  321   my $filePath = "$dir/$fileName";
  322 
  323   # make sure info file still exists (i.e. the file hasn't been disposed of)
  324   return unless -e $infoPath;
  325 
  326   unlink $infoPath;
  327   unlink $filePath;
  328 
  329   return 1;
  330 }
  331 
  332 =item disposeTo($path)
  333 
  334 Remove the file from the upload cache, and move it to C<$path>. Returns true if
  335 the upload was successfully moved, or an undefiend value if the upload is not
  336 valid.
  337 
  338 =cut
  339 
  340 sub disposeTo {
  341   my ($self, $newPath) = @_;
  342   my $uuid = $self->{uuid};
  343   my $dir = $self->{dir};
  344 
  345   croak "no path specified" unless $newPath;
  346 
  347   my $infoName = "$uuid.info";
  348   my $infoPath = "$dir/$infoName";
  349 
  350   my $fileName = "$uuid.file";
  351   my $filePath = "$dir/$fileName";
  352 
  353   # make sure info file still exists (i.e. the file hasn't been disposed of)
  354   return unless -e $infoPath;
  355 
  356   unlink $infoPath;
  357   move($filePath, $newPath);
  358 }
  359 
  360 =back
  361 
  362 =head1 AUTHOR
  363 
  364 Written by Sam Hathaway, sh002i at math.rochester.edu. Based on the original
  365 WeBWorK::Upload module by Dennis Lambe, Jr., malsyned at math.rochester.edu.
  366 
  367 =cut
  368 
  369 1;
  370 
  371 __END__
  372 
  373 use Data::UUID;
  374 use MD5 qw();
  375 use WeBWorK::Constants qw(SECRET);
  376 
  377 # This package allows a file to be reliably cached on disk and replaced with
  378 # a string that universally and unforgeably represents that file, and then
  379 # retrieved later given that string.
  380 
  381 sub store_upload {}
  382 
  383 sub retrieve_upload {}
  384 
  385 sub new {
  386   unless (SECRET) {
  387     die "A secret has not been set in WeBWorK::Constants for this server.";
  388   }
  389   my $invocant = shift;
  390   my $class = ref($invocant) || invocant;
  391   my $self = {
  392     basedir => shift; # The directory into which to place the spool file
  393     uploadObject => shift;  # The Apache::Upload object to deal with
  394   };
  395 
  396   my $ug = Data::UUID->new;   # UUID Generator/Handler
  397   my $uuid = $ug->generate; # Unique identifier for this file, guaranteed to be unique until 3400AD
  398   my $uuid_as_string = $ug->to_string($uuid);
  399   my $mac = MD5->hexhash(SECRET . MD5->hexhash(SECRET . $uuid_as_string); # Message Authentication Check. As long as SECRET stays secret,
  400 
  401 
  402   $self->{
  403   $self->{MAC} = MD5->hexhash(SECRET . MD4->hexhash(SECRET . $self{UUID}->  # Message Authentication Check - An unforgeable checksum
  404   };
  405 }
  406 
  407 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9