All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jsupload.public.jsupload.cgi.pl Maven / Gradle / Ivy

Go to download

JsUpload library: JavaScript ready to use in non gwt web apps and cgi-bin server script.

There is a newer version: 1.0.3
Show newest version
#!/usr/bin/perl -w
#
# Copyright 2010 Manolo Carrasco Moñino. (manolo at apache/org)
# http://code.google.com/p/gwtupload
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#

############################################################################################
## * * The provided server script has been written in perl for the GWTUpload/JSUpload library.
## *
## * * This script requires perl, CGI and Digest::MD5, which are installed by default in
## * most linux/unix distributions.
## *
## * * You have to put this file in a script/cgi-bin enabled folder of your web server, and
## * you have to set the rigth execution permissions.
## *
## * # *Operation*:
## *    * When it receives a POST request, it updates periodically a status file with the progress, 
## *      and stores all received form elements in a temporary folder for the user session.
## *    * When it receives GET requests, returns a xml response with the progess information.
## *    * When the GET request, has the parameter show=xxxx, it returns the content of the form
## *      element whose name is xxxx. If the element is a file it sends a response with the adequate
## *      content-type, otherwise it returns a xml response with the element's value.
## * # *Configuration*: At the top of the script you have some customizable variables.
## *   * $idname: is the name for the sessionid cookie name, you could redefine it to match
## *              the name of the cookie set by your application. By default the name is CGISESSID.
## *   * $tmp_dir: is the prefix used in the path location to store the data received. By default
## *              it is set to "/tmp/uploader", normally you have to change it to match the path
## *              configured in your application. It is strongly recommended that this directory 
## *              should be created apart from "/tmp", in a non-httpd accessible path.
## *   * $max_size: is the maximum size in bytes allowed for the entire request size, note that it is not
## *              the real file size because the request includes the headers and could have more
## *              files or parameters. Normally only one file is transfered, and the request size
## *              is very closed to the file size.
## *   * $mkpath: if true jsupload will create the folder to store the received data, otherwise
## *              it is the application the responsible of creating it. Note that setting it to false 
## *              your system is protected to be used as a surreptitious storage area. The files 
## *              in this folder will be created with the default perl permissions (0666 or the user umask), 
## *              so in the case you leave the application to create the folder be aware to set the
## *              appropriate permissions.
## *   * $slow:   if it has a value greater than zero, after each chunk received, jsupload will sleep
## *              for this value in seconds (it may be fractional). It is used to see the progress bar 
## *              in fast networks when you are testing or developing your app.
## * # *Integration*:
## *   * The files are stored in the folder $tmp_dir/xxxx where xxxx is the session id (cookie CGISESSID).
## *   * For each form element received, it stores a file /tmp/uploader/xxxx/yyyy.info 
## *     where yyyy is the name of the element.
## *   * In the case of file elements, it puts the content of the file in the file
## *     /tmp/uploader/xxxx/yyyy.bin.
## *   * The application must create, handle, and clean $tmp_dir files.
############################################################################################
# this script version.
my $version = "0.6.4";

use CGI;
use Digest::MD5;
use File::Path;
use strict;
use warnings;
use Data::Dumper;

my $idname   = "CGISESSID";
my $tmp_dir  = "/tmp/uploader";
my $max_size = 2000000;
# Note that by default we set mkpath=1 to avoid problems to newbies. 
# You should set it to 0 and make the server create the folder when the user is validated
# and before uploading any file. 
my $mkpath = 1;
my $slow = 0;

# Get the sessionId or create a new one
# do not use CGI here, because we need to handle STDIN in order to update the progress status.
my $sid = new Digest::MD5()->add( $$, time(), rand(time) )->hexdigest();
my $set_cookie = "";
if ( $ENV{'HTTP_COOKIE'} && $ENV{'HTTP_COOKIE'} =~ /$idname="*([^";]+)/ ) {
    $sid = $1;
} elsif ($mkpath) {
    $set_cookie = "Set-Cookie: CGISESSID=$sid; path=/\n"
} else {
    writeResponse("JSUPLOAD: Unable to find $idname");
}
my $user_dir = "$tmp_dir/$sid/";
my $data_file = "$user_dir/data.$$";
my $cancel_file = "$user_dir/cancel";
my $error_file = "$user_dir/error";
my $progress_file = "$user_dir/progress";

# Controller:
#   POST is used for uploading.
#   GET is used to get the upload progress or get the content of the uploaded item.
my $method = $ENV{'REQUEST_METHOD'} || 'GET';
my $cgi;
if ( $method =~ /POST/i ) {
    doPost();
} else {
    $cgi = new CGI;
    if ( $cgi->param('show') ) {
        writeItemContent( $cgi->param('show') );
    } elsif ( $cgi->param('new_session') ) {
        writeResponse("ok");
    } elsif ( $cgi->param('remove') ) {
        removeItem( $cgi->param('remove') );
    } elsif ( $cgi->param('cancel') ) {
        cancelProcess();
    } else {
        getProgress();
    }
}
exit;

## This method receives the form content and stores each item in a temporary folder.
sub doPost {
    ## flush after any write operation.
    $| = 1;

    ## Validate permissions.
    if ( $mkpath && !-d "$user_dir" ) {
        mkpath( "$user_dir", 0, 0700 )
          || writeResponse("JSUPLOAD: Unable to create: $user_dir $!");
    }

    ## Exit if the folder does not exist.
    writeResponse("JSUPLOAD: The folder: $user_dir should be created by the application before uploading any file.") 
        unless (-w $user_dir);

    ## Validate request size.
    my $len = $ENV{'CONTENT_LENGTH'} || 3000;
    print STDERR "Receiving $len ($max_size)\n";
    if ( $len && $len > $max_size ) {
        unlink($progress_file);
        unlink($data_file);
        my $maxKB = int ($max_size / 1024);
        my $sizeKB = int ($len / 1024);
        exitWithError("The maximum configured upload size ($maxKB KB.) has been exceeded ($sizeKB KB.) ");
    }

    ## Receive the request, and update progress data
    unlink($cancel_file) if (-f $cancel_file);
    unlink($error_file) if (-f $error_file);
    open( D, ">$data_file" ) || exitWithError("Can't open postfile: $user_dir/postdata $!");
    my ( $n, $done, $line ) = ( 0, 0, "" );
    do {
        ## If we have received an ajax request to cancel the upload, we close
        ## the STDIN in order to make the client stop the uploading.
        if (-f $cancel_file) {
           close(D);
           unlink($progress_file);
           unlink($data_file);
           writeResponse("truecanceled", 1);
        }
        $done += $n;
        updateProgress( $done, $len );
        print D $line;
        select( undef, undef, undef, $slow ) if ($slow);
    } while ( ( $n = sysread( STDIN, $line, 4096 ) ) > 0 );
    close(D);

    ## Process received data
    my $msg = "ok\n";
    open( STDIN, "$data_file" );
    $cgi = new CGI();
    foreach my $key ( $cgi->param() ) {
        my $value = $cgi->param($key);
        if ( defined($value) ) {
            my $fh = $cgi->upload($key);
            if ( defined($fh) ) {
                # In this variable you can send any information to the client side.
                my $servermessage = "jsupload version: $version";

                my $type = $cgi->uploadInfo($value)->{'Content-Type'} || 'unknown';
                my $name = saveFile( $key, $value, $type, $fh );
                my $size = -s "$name";
                $msg .= " \n  $key\n  $value"
                  . "\n  $size\n  $type"
                  . "\n  \n\n  \n ";
            } else {
                saveFile($key, $value, "text/plain");
                $msg .= "$key"
                  . "$value";
            }
        }
    }
    close(STDIN);
    unlink($data_file);
    writeResponse($msg, 1);
}

## Save each received file in the user folder.
## It generates two files, one with the content of the uploaded data,
## and the other one with the item information (original name and content-type)
sub saveFile {
    my ( $key, $name, $type, $fd ) = @_;
    my $bin_file = "";
    if ($fd) {
       $bin_file = $user_dir . $key . ".bin";
       open( BIN, ">$bin_file" ) || return;
       binmode(BIN);
       my ($data, $n);
       while ($n = read($fd, $data, 65536)) {
         print BIN $data;
       }
       close(BIN);
    }
    my $info_file = $user_dir . $key . ".info";
    open( INFO, ">$info_file" ) || return;
    print INFO "$name\n$type\n";
    close(INFO);
    return $bin_file;
}

## Write the server response for each request.
## This response is a simple xml response easy to handle in the browser side.
## Sets the cookie if it wasn't found before
## Terminates the reception closing stdin and exits.
sub writeResponse {
    my ($msg, $post) = @_;
    close(STDIN);
    my $xml = ""
            . "\n\n  $msg\n\n";
    if ($post) {
      $xml =~ s//___/g;
      $xml = '%%%INI%%%' . $xml . '%%%END%%%';
    }
    
    print "Content-Type: text/plain\n$set_cookie\n$xml";
    exit;
}

## write the upload progress in the status file
sub updateProgress {
    my ( $done, $total ) = @_;
    open( F, ">$progress_file" );
    print F "$done/$total";
    close(F);
}

sub exitWithError {
    my $msg = shift || "Server Error $!";
    open( F, ">$error_file" );
    print F $msg;
    close(F);
    print STDERR "Upload Error: $msg\n";
    writeResponse("JSUPLOAD: $msg");
}

sub cancelProcess {
    open( F, ">$cancel_file" );
    print F "cancel";
    close(F);
    print STDERR "Cancelled \n";
    writeResponse("true");
}

## read the upload progress from the status file
sub getProgress {
    if ( -f "$error_file" ) {
        my $error=`cat $error_file`;
        writeResponse("JSUPLOAD: $errortrue");
    }
    if ( -f "$cancel_file" ) {
        writeResponse("truecanceled");
    }
    my ( $done, $total, $percent ) = ( 0, 0, 0 );
    if ( open( F, "$user_dir/progress" ) ) {
        my $l = ;
        if ( $l =~ /^(\d+)\/(\d+)/ ) {
            ( $done, $total ) = ( $1, $2 );
            $percent = $total != 0 ? $done * 100 / $total : 0;
        }
        close(F);
    }
    my $ret =
        "$percent"
      . "$done"
      . "$total";
    $ret .= "ok" if ( $percent >= 100 );
    writeResponse($ret);
}

## Generates the response when the client asks for an item
## if it is a form item it returns a xml response with its value
## if it is an uploaded file it returns the content of this file, setting the content-type to the original value.
sub writeItemContent {
    select( undef, undef, undef, 0.4 );
    my $item = shift;
    if ( open( F, "$user_dir/$item.info" ) ) {
        my $value = ;
        $value =~ s/[\r\n]+$//g;
        my $type = ;
        $type =~ s/[\r\n]+$//g;
        close(F);
        if ( open( F, "$user_dir/$item.bin" ) ) {
            print "Content-type: $type\n\n";
            while () {
                print $_;
            }
            close(F);
        } else {
            writeResponse("$value");
        }
    } else {
        writeResponse("item not found");
    }
}

sub removeItem {
    my $item = shift;
    if (-f "$user_dir/$item.info") {
        unlink ("$user_dir/$item.info");
        unlink ("$user_dir/$item.bin");
        writeResponse("true");
    } else {
        writeResponse("item not found");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy