#!/usr/bin/env perl use 5.012; use warnings; use constant { DB_URI => 'dbi:SQLite:dbname=/tmp/uploader.db', UPLOAD_DIR => '/tmp/uploader', MAX_UPLOAD_SIZE => 1232896, # 1 MiB }; use DBI; use CGI; use CGI::Carp qw(fatalsToBrowser); use File::Temp qw(tempdir); use File::Spec::Functions qw(catfile); # Initialize database connection and prepare statements. my $db = DBI->connect(DB_URI, { RaiseError => 1, PrintError => 0}); my %statements = ( new_upload => $db->prepare(< $db->prepare(< $db->prepare(<new(\&process_upload, undef, 0) or die "Failed to create CGI context"; # Processor of uploaded data. my $dropdir; my %uploads; sub process_upload { my ($name, $buffer, $nbytes) = @_; warn "$name: $nbytes"; my $target = $uploads{$name}; unless (defined($target)) { unless (defined($dropdir)) { $dropdir = tempdir('upload.XXXXXXXX', DIR => UPLOAD_DIR) or die "Failed to create drop directory: $!"; $statements{new_upload}->execute($dropdir, $ENV{CONTENT_LENGTH}); $statements{new_upload}->finish(); } my $sane = $name; $sane =~ s/^[._]+/_/g; $sane =~ s/[^-._0-9A-Za-z]/_/g; open($target, '>:raw', catfile($dropdir, $sane)) or die "Failed to open upload target for '$name': $!"; $uploads{$name} = $target; } syswrite($target, $buffer, $nbytes) or die "Write error during upload of '$name': $!"; $statements{upload_received}->execute($nbytes, $dropdir); $statements{upload_received}->finish(); } # Finish the uploads and generate a response page. # Note that we cannot determine the value of the "user" parameter earlier, # since the client may actually send it as part of the post data and after # the uploaded files! my $user = $cgi->param('user'); print $cgi->header('text/plain'); say "Uploads for ".($user // 'unknown user')." finished:"; while (my ($name, $target) = each(%uploads)) { close($target) or die "Failed to close upload target for '$name': $!"; say " * $name"; } # Write final status into the database. if (defined($dropdir)) { # In a real application the user id should be validated somehow. $statements{upload_complete}->execute($user, $dropdir); $statements{upload_complete}->finish(); } # Close database connection. $db->disconnect();