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

Hi monks, Please can someone shed some light. A simple CGI script to read in a submitted file and print its contents to the screen.
#! /usr/bin/perl -w use CGI; my $query = new CGI; print STDOUT $query->header(); print STDOUT $query->start_html( -title=> "CGI"); my $file = $query->upload('file'); $file =~ m/^.*(\\|\/)(.*)/; my $file_name = $2; open (FILE, "$name") or die "unable to open file"; $info = do { local $/; <FILE> }; close (FILE); print STDOUT "$info"; print STDOUT $query->end_html;
This script will only print the contents of one file that I submit (I have submitted lots to test). Even if you re-name this one file and submit it, the contents won't print. Does anyone have any idea whats going on?? thanks, much appreciated.

Replies are listed 'Best First'.
Re: CGI file very odd
by hardburn (Abbot) on Jan 30, 2003 at 15:34 UTC

    This looks strange to me:

    $file =~ m/^.*(\\|\/)(.*)/; my $file_name = $2; open (FILE, "$name") or die "unable to open file";

    Where is $name delcared? Oh, and the quotes around "$name" are unnecessary.

    Also, what happens if somebody submits a filename like '>blah.txt'? Oops, you just deleted the contents of the file. As of perl 5.6, you can use a three-argument form of open() to avoid this problem:

    open(FILE, '<', $name) or die "Can't open file: $!\n";

    Better still is to do some further cleansing on the name of the file. I see you at least have some basic matching, but even better is to restrict exactly what files are allowed to be submitted via a hash:

    my %allowed_files = ( file1 => '/path/to/file1', file2 => '/path/to/file2', file3 => '/path/to/file3', ); # $file already processed from CGI params my $file_name = defined($allowed_files{$file}) ? $allowed_files{$file} : die "Bad file name\n";

    Admittedly, the above might become a maintence nightmare, depending on the number of files on your allowed list and if that list changes a lot.

Re: CGI file very odd
by Jaap (Curate) on Jan 30, 2003 at 12:29 UTC
    Tip. Keep it simple. Try this first:
    #! /usr/bin/perl -w use strict; use CGI; my $query = new CGI; print $query->header(); print $query->start_html( -title=> "CGI"); while (my $size = read($cgi->param('file'),my $data,1024)) { print $data; } print $query->end_html;
    STDOUT is the default filehandle, so no need to specify it.
      err thanks Jaap but i copied your code exactly and nothing printed to the screen!! ;-0
        Did you submit a form with an <input type="file" name="file> to it?
Re: CGI file very odd
by derby (Abbot) on Jan 30, 2003 at 13:09 UTC
    What version of CGI are you using? My version(s) of CGI.pm (2.752 and 2.89), upload returns a filehandle. From the docs:

    To be safe, use the upload() function (new in version 2.47). When called with the name of an upload field, upload() returns a filehandle, or undef if the parameter is not a valid filehandle.

    $fh = $query->upload('uploaded_file'); while (<$fh>) { print; }
    This is the recommended idiom.

    -derby

      Sorry not sure of the version. Upload seems to have the same effect as param. The CGI uploads the file data.txt fine, but if you delete data.txt and re-name another file data.txt it prints what was in the original file!
        Check the version of CGI:

        perl -MCGI -e 'print $CGI::VERSION'

        This wouldn't be the definitive answer if you have multiple perls (or multiple CGI.pm) on your system, but it's a start.

        As for the same file, check for errors - your upload may be failing:

        my $query = new CGI; my $error = $query->cgi_error; if ($error) { print $query->header(-status=>$error), $query->start_html('Problems'), $query->h2('Request not processed'), $query->strong($error); exit 0; }

        And I'm just assuming you have a typo in your posting ($file_name vs $name)

        -derby

Re: CGI file very odd
by Coruscate (Sexton) on Jan 30, 2003 at 17:38 UTC

    Yep, first of all, I'll tell you to throw in 'use strict' at the top of the script. This would catch the fact that $name, used on line 17 was not declared anywhere within your script. As well, you are using upload() to get a filehandle, then attempting to extract the filename from it. IIRC, upload() does not return the filename in any way whatsoever, different from the way in which param() works (correct me if I am wrong). The part that confuses me the most is your opening of a file for reading. If you meant to replace $name on line 17 with $file_name, then I see what you were trying to do. With what is returned with param() or upload(), you do not need to open the "file" for reading. CGI already passed you the filehandle, so all you have to do is read from it. Rewriting things to make it work and to pretty it up a bit (untested):

    #!/usr/bin/perl -w use strict; use CGI ':standard'; print header(), start_html('CGI Upload'), my $file = param('file'); $file =~ s#^[^/\\]*[/\\]##; #Sufficient for windows and *nix if ($file =~ /[^\w\.\-]/) { print p( strong('Invalid Filename!') ); exit; } my $fh = upload('file'); my $info = do { local $/; <$fh> }; print p($info), end_html;


          C:\>shutdown -s
          >> Could not shut down computer:
          >> Microsoft is logged in remotely.
        

      WOW!! thanks Coruscate and everyone else who offered their replies, i really really appreciate this. I was getting really quite down over the simplest thing not working but the code posted above is perfect. Many thanks ;-)