Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Can I do a file upload without using any modules?

by frinky (Novice)
on Apr 22, 2002 at 00:49 UTC ( [id://160961]=perlquestion: print w/replies, xml ) Need Help??

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

Hi there, Me has a problem: I made a script which was able to upload a file, read 'post'-data, and query-string-data, without using a single module. (Except perl itself off course).
Now I changed the script a bit to fit in with the rest of my pages, and 'taadaah', it's not working anymore.

It seems as if the script can't parse the vars anymore, since when I call "if ($name) {...}" doesn't even react, even though I'm sure to have a var which is called 'name'.

Here is part of the form:
<form action="main.cgi?afd=upload" method="post" enctype="multipart/fo +rm-data" name="form" id="form" target="_top"> <input type="file" size=25 name="upload_foto" id="upload_foto" value=" +$filename" tabindex=2> <input type="text" size=25 name="naam" id="naam" value="$naam" tabinde +x=1>
Here is the script:
sub Parse_Form { if ($ENV{'REQUEST_METHOD'} eq 'GET') { @pairs = split(/&/, $ENV{'QUERY_STRING'}); } elsif ($ENV{'REQUEST_METHOD'} eq 'POST') { read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); if ($ENV{'QUERY_STRING'}) { @getpairs =split(/&/, $ENV{'QUERY_STRING'}); push(@pairs,@getpairs); } } else { print "Content-type: text/html\n\n"; print "<P>Use Post or Get"; } foreach $pair (@pairs) { ($key, $value) = split (/=/, $pair); $key =~ tr/+/ /; $key =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~s/<!--(.|\n)*-->//g; if ($formdata{$key}) { ## maakt er een Array van als key al vo +orkomt $formdata{$key} .= ", $value"; } else { $formdata{$key} = $value; } } } if ($naam) { print "Content-type: text/html\n\nGelukt om in die kut-sub te komen."; exit; }
And I'm so very sure it used to work...
PLEASE??????? I don't get it...

Replies are listed 'Best First'.
Re: Can I do a file upload without using any modules?
by Ovid (Cardinal) on Apr 22, 2002 at 01:05 UTC

    frinky, the limitation that you have run into is one of the many reasons why people recommend that you use CGI.pm or one of its (few) competitors on the CPAN. Your code cannot handle file uploads and is unlikely to be able to do so at any point in the future. This is because the encoding for 'multipart/form-data' is much different from the standard encoding that you are used to. Lets look at a standard upload form.

    <form action="/cgi-bin/display.cgi" method="post" enctype="multipart/f +orm-data"> <input type="file" name="file" /><br /> <input type="hidden" name="hiddenData" value="nothing to see h +ere, folks" /><br /> <input type="checkbox" name="someCheckbox" value="someValue" check +ed="checked" />Checkbox<br /> <input type="submit"><br /> </form>

    Let's say that I use that form to upload a small file named 'temp.pl'. The entity-body (the part after the headers) that is sent to the browser might resemble the following:

    -----------------------------7d03ca41c0 Content-Disposition: form-data; name="file"; filename="C:\WINDOWS\Desk +top\temp.pl" Content-Type: text/plain #!C:/perl/bin/perl.exe -w use strict; $SIG{INT} = 'IGNORE'; my $count=0; while (){ print $count++; } -----------------------------7d03ca41c0 Content-Disposition: form-data; name="hiddenData" nothing to see here, folks -----------------------------7d03ca41c0 Content-Disposition: form-data; name="someCheckbox" someValue -----------------------------7d03ca41c0--

    Not only is that rather difficult to parse, but different browsers often have subtly different formats for multipart/form-data. Debugging those issues can be a nightmare. That is one of the many reasons people suggest that you use a standard module instead of trying to roll your own.

    Since CGI.pm is part of the standard distribution, you should have no problem using it. Check out Lesson 2 of my CGI course for more information about this topic.

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Can I do a file upload without using any modules?
by dws (Chancellor) on Apr 22, 2002 at 02:11 UTC
    I made a script which was able to upload a file, read 'post'-data, and query-string-data, without using a single module.

    You don't say why you didn't use a single module. If it's an exercise in "can I do this", then pat yourself on the back and move on. For production code, CGI.pm is a more responsible* thing to use, and it's included in all modern Perl distributions, so your chances of not encountering it are effectively nil.

    * There are numerous subtle security problems that crop up if you try to handle CGI requests by hand. CGI.pm handles them, and it's been thoroughly tested.

Re: Can I do a file upload without using any modules?
by clintp (Curate) on Apr 22, 2002 at 01:47 UTC
    I don't *believe* CGI.pm requires any C-compiled object files beyond what Perl provides. People who are afraid of modules for one reason or another (distribution, etc...) might want to look into creating a globular script -- just take CGI.pm (bits and pieces or *all* of it) and include it in the body of your script. Sometimes it takes a bit of work, but it's not overwhelmingly difficult.
      Of course, people that do that will have to license their script under the same license as CGI.pm if they distribute it.

      -sam

Re: Can I do a file upload without using any modules?
by samtregar (Abbot) on Apr 22, 2002 at 04:48 UTC
    Everyone else has already told you to use CGI.pm and I agree. However, you want to know why your script doesn't print "Gelukt om in die kut-sub te komen," right? That's simple - you don't initialize $naam anywhere in your code. Do a search and you'll see that the if() is the first mention of the variable. The code above puts the form variables in the hash %formdata. Maybe you meant if($formdata{naam})?

    -sam

Re: Can I do a file upload without using any modules?
by beebware (Pilgrim) on Apr 22, 2002 at 14:38 UTC
    Yes you can and here's a very messy way to do it. The reason I had to 'roll-my-own' is because a browser plugin we had to use for a project didn't quite send the data in a proper GET/POST request, therefore CGI.pm couldn't handle the data. For "commercial" reasons, I've had to extract that specific part of the file:
    sub library_read_form { # Reads the incoming data from the web browser my ($buffer,$pair,$datasent,$name,$value,$mydata,$filename,$contentty +pe)=("","","","","","","",""); if (length($ENV{'CONTENT_LENGTH'})>0) { if ($ENV{'CONTENT_TYPE'}=~/^multipart\/form-data/i) { binmode(STDIN); # cope with Windows platforms - but only if we have + to... } read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); if (length($ENV{'QUERY_STRING'}>0)) { $buffer.="&".$ENV{'QUERY_STRIN +G'};} } else { $buffer=$ENV{'QUERY_STRING'}; } foreach $var (sort(keys(%ENV))) { $val = $ENV{$var};$val =~ s|\n|\\n|g;$val =~ s|"|\\"|g; } if (($buffer=~/^\<doc /) && ($buffer=~/\<\/doc\>\n?$/)) { ### section removed..... ### don't ask what this did or why, I'm not allowed to say :( } else { if (!($ENV{'CONTENT_TYPE'}=~/^multipart\/form-data/i)) { @pairs = split(/(&|;)/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $datasent.=";$name=$value"; if (length($form{$name})>0) { $value=$form{$name}."\0".$value; } $form{lc($name)} = $value; } } else { $ENV{'CONTENT_TYPE'}=~/boundary=([^"]+)/i; $boundary="--".$1;$section=0; @lines=split(/(\n|\r)/,$buffer); MULTIPART: foreach $line (@lines) { if ($section==3) { if ($line=~/content-type:\s?(.*)/i) { $contenttype=$1; } elsif (!($line=~/^(\n|\r|\n\r|\r\n)?$/)) { $section=4; } } elsif ($section==2) { if ($line=~/^content-type:\s?(.*)/i) { $contenttype=$1; } elsif ($line=~/^(\n|\r|\n\r|\r\n)?$/) { $section=3; # data possibly starts next line } } elsif ($section==1) { if ($line=~/^content-disposition:\s?form-data;\s?name="([^"]+)" +(.*)$/i) { $name=$1;if ($2=~/filename="([^"]+)"/) { $filename=$1; } $section=2; # prepare for data } } if ($line=~/^$boundary(--)?$/) { $section=1;# found boundary if ((length($name)>0) && (length($filename)<1)) { chomp($mydata);chop($mydata); # munch munch $datasent.=";$name=$mydata"; if (length($form{$name})>0) { $mydata=$form{$name}."\0".$mydata; + } $form{lc($name)}=$mydata; } elsif (length($name)>0) { $form_filenames{$name}=$filename;$form_contenttype{$name}=$conte +nttype; $tempfile=$tempdir.(time())."_".int(rand(300000)); $tempfile.=".tmp"; open (DATA,">".$tempfile) || die("Unable to open temp directory +for storage\n"); binmode(DATA);print DATA $mydata;close DATA; $form_data{$name}=$tempfile; } $name="";$mydata="";$filename="";$contenttype=""; } if ($section==4) { $mydata.=$line; } } } } $datasent.=";"; }
    Data is normally stored in the $form hash (for example $form{'lastnode_id'}=160961), but if have an uploaded file (say from a <input type="file" name="tester"> tag, you'll have $form_data{'tester'} (which contains the name of the temporary file), $form_filenames{'tester'} (which contains the 'user provided' upload path), $form_contenttype{'tester'} (which contains the user provided MIME type).
    Ok, ok, the code is neither neat, (probably not) secure or 'strict-safe' but it should give you an idea how to do what you are trying to achieve (and it's been happily running on a Windows2000 Server box with Apache for nearly 2 years now). Please consider using CGI.pm unless you have a REALLY good reason not to!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://160961]
Approved by rob_au
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (6)
As of 2024-04-24 09:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found