k2OS has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I am trying to pass a unique db-id from a hidden field in my form to the upload_hook-sub in my file upload-script (to use sanitized filenames), but seem to have run into something that was only half-baked.

According to the manual page for CGI, $data can be used to pass extra data to the upload_hook (ie. a database ID), _but_ according to the manual (and the tests I've done) I cannot perform the necessary param()-call on the submitted data to actually get the DB-id needed before declaring my CGI-object ($q = CGI->new(\&hook, $data) - a true hen&egg-situation as I see it.

I've searched the site over and over and over again, but couldn't find one single example code that actually uses $data for anything. I did fine one that uses CGI and CGI::Ajax to make an upload progress bar, but it uses tainted filenames to track the status of the upload, which, I believe, is stronly discouraged.

I have been poking at CGI.pm to see if I might be able to pass my unique ID to the hook in some other way (along with $filename and $bytes_read that already is passed by default), and I did manage to find the spot where data is passed to the hook, but my perl-foo ends there, unfortunately.

Can anyone provide working example of using upload_hooks with $data, or should I post a bug-report, asking the CGI-developers to kindly re-iterate the implementation for the upload_hook?

I thank thee kindly in advance for any reply

  • Comment on Passing database ID to \&hook through $data (CGI)

Replies are listed 'Best First'.
Re: Passing database ID to \&hook through $data (CGI)
by stonecolddevin (Parson) on Sep 20, 2008 at 06:42 UTC

    I commend you for your coherent post but honestly I can't help you (nor can many of the monks minus the talented visualizers out there) with out seeing some if not all of your code.

    Post your work and I'm sure someone (if not myself) can offer you assistance.

    meh.
Re: Passing database ID to \&hook through $data (CGI)
by Anonymous Monk on Sep 20, 2008 at 07:21 UTC
    The $data field is optional; it lets you pass configuration information (e.g. a database handle) to your hook callback.

    What you're trying to do is not what this was intended for, but you may be able to work around with QUERY_STRING

    my $q = CGI->new($ENV{QUERY_STRING}); $data = $q->param('chicken'); $data = $1 if $data =~ //;# untaint chicken $q = CGI->new(\&hook, $data);
    as long as action is /foo/bar.cgi?chicken=ididid QUERY_STRING will always contain your chicken
      What you're trying to do is not what this was intended for, but you may be able to work around with QUERY_STRING

      Then I do wonder what it was intended for then, if not for tracking the state of uploads.

      I made up a small example, using bits of your code:

      The upload-form: (form.html)

      <!-- using GET will make the QUERY_STRING as mentioned in the previous + post, but.. that's not how it's supposed to be? --> <form action="minitest.cgi" enctype="multipart/form-data" method="GET" +> <input type="file" name="file"> <input type="submit" value="send"> <input type="hidden" name="pregeneratedID" value="123456"> </form>

      the upload-script: (minitest.cgi)

      #!/usr/bin/perl -w use strict; use CGI qw/:standard/; use CGI::Carp qw(fatalsToBrowser); my $query = CGI->new($ENV{QUERY_STRING}); # I will initially save my file with this name and copy it to the corr +ect name later (ID+original suffix) # ID has to be passed to 'hook', to keep track of the upload-status in + a DB (I will just use a plain file here for testing) # as only $filename (the originale name on the client-disk is passed t +o the hook, there is no relation between the # sanitized filename I will use when saving the uploaded file, and the + filename used in the hook, thus practically rendering # the hook useless my $data = $query->param('pregeneratedID'); if (!$data) { print $query->header(); print "no data?"; exit; } $data = $1 if $data =~ //;# untaint generatedID - for some reason this + sets the contents of $data to .. nothing if (!$data) { print $query->header(); print "no data?"; exit; } $query = CGI->new(\&hook, $data); my $fh = $query->upload('file'); # I want to save the file with the same filename as the ID (so it's ea +sier to keep track of during download) # for testing purposes, I will not handle renaming and moving the file + in this example my $name = $data; open ( UPLOADFILE, ">uploads/$name" ) or die ("$!"); binmode UPLOADFILE; while ( <$fh> ) { print UPLOADFILE; } close UPLOADFILE; print $query->header(); print "look in uploads/test.log and see if $data was passed to the hoo +k<br>\n"; print "If the file is not there, it means hook was never called, provi +ng the manual right (blasted!))<br>\n"; print "filetype: ".$query->uploadInfo($fh)->{'Content-Type'}; # this b +arfs, as nothing survives the sanitizing.. #### sub hook { my ($filename,$buffer,$bytes_received,$data) = @_; open (LOG, ">>uploads/test.log"); print LOG $filename." ".$bytes_received. " - ".$data."\n"; close (LOG); }

      This leaves me with an empty file in uploads/ named '123456' and no log-file, meaning the hook wasn't called.

      I can only conclude, that the hook-feature makes it possible to either keep track of a specific upload by using un-sanitized filenames, giving me the option of actually using the progress-information logged by the hook, or using fully sanitized filenames in the hook as well, (by generating the ID within the upload-script, before declaring the cgi-obj), but then not having any posibilities of keeping track of the upload while it is in progress.. unless CGI.pm is modified so it will pass more information from the upload-handler. As I see it, the $data-option is nearly useless

      Of course, if there is any other way of doing this in a nice way, don't keep it back :)

        Then I do wonder what it was intended for then, if not for tracking the state of uploads.
        Nowhere is it suggested you can get your chicken before upload hook. It says The $data field is optional; it lets you pass configuration information (e.g. a database handle) to your hook callback.

        <!-- using GET will make the QUERY_STRING as mentioned in the previous post, but.. that's not how it's supposed to be? --> Who suggested you use get? I said as long as action is /foo/bar.cgi?chicken=ididid Example

        #!/usr/bin/perl -- use strict; use warnings; use CGI(); $|=1; $ENV{QUERY_STRING}=""; { my $q = CGI->new("".$ENV{QUERY_STRING}); $q->param(qw' chicken ididid '); print join "\n", $q->start_html, $q->start_form( -method=>'POST', -action=> $q->self_url, -enctype=> &CGI::MULTIPART ), $q->filefield('file'), $q->hidden(pregeneratedID => 123456 ), $q->end_form, $q->end_html; } __END__ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-U +S"> <head> <title>Untitled Document</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1 +" /> </head> <body> <form method="post" action="http://localhost?chicken=ididid" enctype=" +multipart/form-data"> <input type="file" name="file" /> <input type="hidden" name="pregeneratedID" value="123456" /> </form> </body> </html>