#!/usr/bin/perl
use Fcntl;
use File::Basename;
##
# Resumable file upload:
#
# When a collection of files is about to be uploaded, Rad Upload
# will send a zero byte request but the query string will have a
# parameter named 'cmd' with a value of 0.
#
# The request may or may not include a hash code. If a hash code is
# included, that means we are attempting to resume failed/paused
# transfer of multiple files. Some files may have been completely
# uploaded - some may have be partially uploaded.
#
# When a hash is not included this script will generate one and send
# it back to the client. All future requests in this transfer need to
# include this hash code in the query string.
#
# Before each file is uploaded, another empty request with a 'cmd' of
# 1 has to be sent to the script. The script must then return the
# number of bytes that have been previously written for that file. -1
# will be the response if an error has occured. 0 will be returned if
# this is the first attempt to upload the file in question.
#
# When all the files have been uploaded another zero byte request will
# be made. This time cmd parameter will be set to 2 - meaning that we
# have finished uploading the files. Now the script should send a
# nicely formatted reply and move the files to a permanent location.
#
# Last but not least when the upload was interrupted we have another
# response, in this case cmd=3
##
##
# Copyright Rad Inks (Pvt) Ltd. 2006.
# reproduction prohibited without permission.
##
##
# the top level folder for saving files.
##
$save_path="/dev/shm/uploaded/";
@qstring=split(/&/,$ENV{'QUERY_STRING'});
foreach(@qstring)
{
@pair = split(/=/, $_);
$pair[1] =~ tr/+/ /;
$pair[1] =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$params{$pair[0]} = $pair[1];
}
$fname = $params{'fname'};
$hash = $params{'hash'};
$cmd = $params{'cmd'};
##
# the top level folder on the client computer.
##
$userfile_parent = $params{'userfile_parent'};
if(defined($userfile_parent))
{
#print STDERR "userfile_parent = $userfile_parent";
$fname =~ s/$userfile_parent//;
}
##
# Finds all the files that have been uploaded, moves them to
# a permanent location, prepares server response.
##
sub find_files
{
my ($path,$move) = @_;
if($move==1)
{
$newPath = $path;
$newPath =~ s/$hash\///;
}
#print STDERR "Find Files in - $path \n";
my @files = split("\n",`ls $path`);
foreach(@files)
{
if($_ ne '.' && $_ ne '..')
{
#print STDERR $_;
my $rel_path=$path . $_;
if( -d $rel_path)
{
if($move == 1 && !(-d $newPath . $_))
{
mkdir($newPath . $_);
}
my $bk_newPath = $newPath;
find_files("$rel_path/",$move);
$newPath = $bk_newPath;
}
else
{
print "
| $_ | ";
print (-s ($rel_path));
print " |
";
#print STDERR "file $_ size = " . -s ($rel_path);
if($move == 1)
{
rename($rel_path,$newPath . $_);
print STDERR "Trying to move from $rel_path to $newPath$_\n";
}
}
}
}
if($move == 1)
{
print STDERR "rmdir $path";
rmdir($path);
}
}
if(defined ($cmd))
{
if($cmd == 0)
{
# trim()
print STDERR "content length" . $ENV{'Content-length'};
print "Content-type: text/plain\n\n";
if($hash eq '')
{
#
# The client wants upload a bunch of files. Since the
# client does not have a previously generated hashcode
# we make a new one. To be able to validate the hash we
# prefix it with 0, and suffix it with 1.
#
$hash = "0";
for($i=0 ; $i< 32 ;)
{
$j = chr(int(rand(127)));
if($j =~ /[a-zA-Z0-9]/)
{
$hash .=$j;
$i++;
}
}
$hash .= "1";
}
$hash =~ s/^\s+//;
$hash =~ s/\s+$//;
print "$hash\n";
#print STDERR "Sending HASH = 0($hash)1";
}
elsif($cmd == 1)
{
print "Content-type: text/plain\n\n";
#
# trying to find out if this particular file has been
# uploaded before.
#
if( -e "$save_path$hash}/{$fname}")
{
print -s "{$save_path}{$hash}/{$fname}";
#print STDERR "Sending filesize " . (-s "{$save_path}{$hash}/{$fname}");
}
else
{
print "00\n";
#print STDERR "No resumption needed for" . "{$save_path}{$hash}/{$fname}";
}
}
elsif($cmd == 2)
{
print "Content-type: text/html\n\n";
#
# all the files in this transfer have been uploaded.
#
open (INFILE,"head.txt");
while() {
print $_;
}
close(INFILE);
find_files($save_path . $hash . "/",1);
open (INFILE,"foot.txt");
while() {
print $_;
}
close(INFILE);
}
elsif($cmd == 3)
{
print "Content-type: text/html\n\n";
#
# Some files may not have been uploaded and/or some may have been partially uploaded.
#
open (INFILE,"head.txt");
while() {
print $_;
}
close(INFILE);
print '| File Upload Was Interrupted. |
';
find_files($save_path . $hash . "/",0);
open (INFILE,"foot.txt");
while() {
print $_;
}
close(INFILE);
}
}
else
{
print "Content-type: text/plain\n\n";
if(defined ($fname))
{
$save_path .= "$hash/";
$path = $save_path . dirname($fname);
if($path =~ '/(;)|(\.\.)/')
{
print STDERR "$path rejected";
exit;
}
else
{
if(-e $path)
{
unless(-d $path)
{
#
# a file by that name already exists and it's not a directory.
# You will need to handle this situation according to your
# requirements. You have a range of options including siliently
# ingoring this file or rejecting the upload as a whole.
#
}
}
else
{
`mkdir -p "$path"`;
}
}
$offset = $params{'offset'};
$fname = $save_path . $fname;
#print STDERR "path = $fname - APPEND , Offset = $offset\n";
$size = (-s $fname);
if(defined($offset) && $offset > 0 && $offset <= $size)
{
sysopen(FH, $fname, O_RDWR) or die "can't open file: $!";
seek(FH,$offset,0);
}
else
{
sysopen(FH, $fname, O_RDWR|O_CREAT) or die "can't open file: $!";
}
while (read (STDIN ,$LINE, 4096))
{
print FH $LINE;
}
close(FH);
close(STDIN);
print '1';
}
else
{
$fname = $save_path . "trysavingafile.txt";
#print STDERR $fname;
sysopen(FH, $fname, O_RDWR|O_CREAT) or die print 'The folder you have chosen cannot be written to';
print 'Please open applet.html in your browser to start an upload.';
}
}