Jouke has asked for the wisdom of the Perl Monks concerning the following question:
I have the strangest problem I've seen so far in Perl. Please let someone explain what is going wrong here:
The case is simple. I have a file to upload via HTTP (you know: upload button, use CGI and get the file). I've rewritten the original script to this:
#!/usr/bin/perl -w
use strict;
use CGI;
$|=1;
my $buffer;
print "Content-type: text/html\n\n";
print "<html><body>";
my $req = CGI->new;
my $filename = $req->param("filename");
$filename =~ m/(.*?$CGI::SL)*(.*)(?=$)/;
my $localfilename = "$2";
open(FILE, ">$localfilename") || print "Could not open $localfilename
+for output: $!<br>\n";
while (my $bytesread = read($filename,$buffer,1024))
{
print FILE $buffer;
}
close(FILE);
print "File saved as $localfilename (original filename=$filename)<br>"
+;
print "</body></html>";
the HTML file looks like this (most simple form of course)
<html>
<body>
<form method=POST encoding="multipart/form-data" action="http://blur/n
+ewtest.cgi">
<table border=1>
<tr><td>Filename: </td><td><input type=file name="filename"></td></tr>
<tr><td colspan=2><input type=submit></td></tr>
</table>
</form>
</body>
</html>
When I run this code and select any file, the moment the 'open' is called, the value of $localfilename is erased. Originally I used FileHandle but to make sure the error wasn't in that module, I replaced it with the plain open statement.
Now the strange part: when I leave out the use strict; everything works fine and the value of $localfilename is preserved.
I'm at a new job here, and everyone here tells me 'use strict' is no good, and I'm trying to convince them of the opposite, 'cause I always use it, and really believe anyone should use it, but this is making my statement a bit hard to defend....
Jouke Visser, Perl 'Adept'
Re: Bug in 'strict'? - CGI::SL, upload, CGI::Carp and stuff
by bjelli (Pilgrim) on Apr 05, 2001 at 19:27 UTC
|
I played around with your Code with Netscape on Windows 2000
as the client, Apache on Debian. I ran into different problems:
1) CGI::SL
My client supplies the Filename "C:\WINNT\Media\Chimes.wav",
while CGI::SL is /. Your pattern-matching/substitution
thing did not work out, localfile got set to the whole filename.
I'd recommend 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.
2) upload and strict refs
trying to read from $filehandle I got the error:
Can't use string ("C:\WINNT\Media\chimes.wav") as a symbol ref while "strict
refs"...
In the manpage for CGI I found:
However, there are problems with the dual nature of the
upload fields. If you use strict, then Perl will complain
when you try to use a string as a filehandle. You can get
around this by placing the file reading code in a block
containing the no strict pragma.
So a quick way to fix your code turned out to be
{
no strict;
while (my $bytesread = read($filename,$buffer,1024))
{
print FILE $buffer;
}
}
But Beware! the CGI man page
goes on to talk about security problems with this
whole filename/filehandle-duality.
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.
Debugging-Output from CGI
When debugging CGI don't count on the fact
that your script "didn't even get there" if
you can't see a certain line of debugging output.
In my experience webservers tend to chew off
some of the output (just when you least
expect it, see also Murphys Law :-).
Look into the server logfiles, or
use CGI::Carp qw(fatalsToBrowser);
That gives you all the error-messages
right in the Browser.
--
Brigitte 'I never met a chocolate I didnt like' Jellinek
http://www.horus.com/~bjelli/ http://perlwelt.horus.at | [reply] [d/l] [select] |
|
> or use CGI::Carp qw(fatalsToBrowser);
> That gives you all the error-messages right in the Browser
This too, is a misleading statement. If you have compile-time errors, these may or may not appear in the browser, depending upon whether the 'use CGI::Carp' has been evaluated before the compile-time error.
I've unfortunately had way too many situations where I was forced to look in the server error log (difficult, at best, if you're using a hosting company) anyway, even with use CGI::Carp qw(fatalsToBrowser);.
But then, this is a little off-topic, is it not?
(we now return you to your regularly-scheduled debate)
| [reply] [d/l] [select] |
Re: Bug in 'strict'??
by OeufMayo (Curate) on Apr 05, 2001 at 18:04 UTC
|
Jouke,
Beware of using $CGI::LS as it can bite you pretty badly. I was trying to reproduce your problem on my machine (Win2K), but it failed miserabily on compilation because of a missing parens in the regex. After checking the regex, I realized that $CGI::LS was equal to '\' in Win32 systems, and thus the regex was interpolated like that m/(.*?\)*(.*)(?=$)/, which is obviously a Bad Thing™.
It doesn't have much to do with your use strict problem, but I thought you'd like to know!
<kbd>--
my $OeufMayo = new PerlMonger::Paris({http => 'paris.mongueurs.net'});</kbd>
Edit 2001-03-05 by tye to close <code>
| [reply] [d/l] [select] |
|
| [reply] |
Re: Bug in 'strict'??
by merlyn (Sage) on Apr 05, 2001 at 16:51 UTC
|
You're using $2 without verifying the previous match truly matched. That's a "reject from code review" mistake in my book. Go back and ensure that the regex is matching.
-- Randal L. Schwartz, Perl hacker | [reply] |
|
You're right merlyn but that's not the case here. I just reduced it to the minimum, and this DOES work without 'strict' and DOESN'T with 'strict'. I surely want to know why (and btw: it matches...)
Jouke Visser, Perl 'Adept'
| [reply] |
|
| [reply] |
|
|
Re: Bug in 'strict'??
by arturo (Vicar) on Apr 05, 2001 at 17:16 UTC
|
I don't know what's going on, but let me give you a response to your cow orkers: let's suppose, worst-case scenario (and highly unlikely) that this is a bug in strict -- the claim that strict is "no good" is still silly (lots of good and useful software tools have bugs). It makes you *think* about your variables and how they're going to be scoped, rather than just splashing them anywhere, and possibly making tyops. I maintain a bunch of scripts that were written without strict in mind, and when things go wrong, tracking down the error is very very hard, because you have no idea where the variable was defined. IF, of course, one is so disciplined as to make the things that should be globals globals, and keep the things that should have local scope local, you won't run into problems. But why should you have to manage those issues yourself when there's a tool that manages that for you? It won't solve all your problems, but it will give you a huge head start on solving them over someone who *doesn't* use strict vars.
And note, that's just strict 'vars' ... you could go on about strict 'refs' and strict 'subs' too =)
Philosophy can be made out of anything. Or less -- Jerry A. Fodor
| [reply] [d/l] [select] |
Re: Bug in 'strict'??
by TheoPetersen (Priest) on Apr 05, 2001 at 17:53 UTC
|
I've tried this on two platforms (SCO and RH Linux) with two versions of Perl
(5.005_03 and 5.6 respectively) and can't produce the bug; I get the
same behavior in either case. I'm using the original code
as posted (though I cut everything after the open) and ensured that
my parameter matched the regex.
What are your platform's vital statistics, out of curiousity?
I'm curious if varying the regex or doing without it will affect the problem.
Have you tried doing the same thing via another means? A split on a literal
delimiter or some such?
Update: after a flurry of /msgs we established that the script
works correctly from the command line but not when run from VN Server.
It does display the correct version of Perl in the latter case.
I'm stumped as to what the server could do to induce this error.
..Theo | [reply] |
|
We're running on Debian Linux with VN Server as webserver (don't ask me why they do here), but no Apache...perl is 5.005_03.
Jouke Visser, Perl 'Adept'
| [reply] |
Re: Bug in 'strict'??
by modred (Pilgrim) on Apr 05, 2001 at 16:54 UTC
|
I don't know if it is a bug in strict or not but you need to check
to make sure that the match works before assigning $2 to anything. If
the match fails then $localfilename will be empty and the
open will fail.
Something like
my $localfilename;
if($filename =~ m/(.*?$CGI::SL)*(.*)(?=$)/ )
{
$localfilename = $2;
}
else
{
# handle the error however you need to
}
| [reply] [d/l] |
|
You're right modred (I could have predicted these replies of course), but it does match. That's not even the case. $localfilename gets a value from $2. Take that as true, but just by calling open() the value disappears.
Jouke Visser, Perl 'Adept'
| [reply] |
Re: Bug in 'strict'??
by Jouke (Curate) on Apr 05, 2001 at 17:44 UTC
|
OK, so here's for the people who think there's a problem with the regex (there isn't): this is a version which checks if the thing matches. If it doesn't, it gets a silly value...
After the assignment of the $2 to $localfilename, I print out the value of $localfilename, and yet it prints out the errormessage with an empty string for $localfilename...
#!/usr/bin/perl -w
use strict;
use CGI;
$|=1; # Don't buffer the output
my $buffer;
#print out the correct content-type
print "Content-type: text/html\n\n";
# start with nice html-tags
print "<html><body>";
# new CGI object
my $req = CGI->new;
#retrieve the value of the selected filename
my $filename = $req->param("filename");
# get only the filename (not the entire path) and if it doesn't match,
+ give $localfilename a silly value
my $localfilename = $filename =~ m/(.*?$CGI::SL)*(.*)(?=$)/ ? $2 : "AS
+illyValue";
# print out $localfilename to see if it has the correct value
print $localfilename;
# open $localfilename for output (and here it fails, with the $localfi
+lename as an empty string)
open(FILE, ">$localfilename") || print "Could not open $localfilename
+for output: $!<br>\n";
# it doesn't even get here...so ignore please...
while (my $bytesread = read($filename,$buffer,1024))
{
print FILE $buffer;
}
close(FILE);
print "File saved as $localfilename (original filename=$filename)<br>"
+;
print "</body></html>";
Jouke Visser, Perl 'Adept'
| [reply] [d/l] |
|
I'll go w a y out on a limb and blame this on a
bug in the regex engine.
m/(.*?$CGI::SL)*(.*)(?=$)/ is pretty, um, well,
isn't pretty. ;) m/([^$CGI::SL]+)$/ is going
to give the regex engine much fewer fits. See if that fixes
things (that requires that you use $1 instead of $2 or just
use my( $localfilename )= m/... instead).
-
tye
(but my friends call me "Tye")
| [reply] [d/l] [select] |
Re: Bug in 'strict'??
by perigeeV (Hermit) on Apr 05, 2001 at 17:53 UTC
|
Forgive my paranoia, but:
if( ($you == $new_employee) && (defined $strict_argument)) {
haze($new_employee);
}
Sorry, but I've been there. You might get a clean CGI.pm
and diff it against what you're using.
As I said, probably just paranoid. | [reply] [d/l] |
|
HAHAHA ....
anyways ... i've run into this problem before ...
what you have to do is change the variable name of $localfilename to $ZardonsBlackendCrownOfThorns and that way you won't be overwriting somebody else's
globally defined variable ... but at the same time you get to praise our dark master Zardon every time you recieve an upload.
in all my DarkUpload scripts I add the line
if $ZardosEternalRagingHunger++ > rand() {
unlink FILEHANDLE;
print "It was the whim of the dark lord to devour your file\n";
}
PS. i'm looking for work, if anyone needs a good perl programmer with CGI experience ;) | [reply] [d/l] [select] |
|
| [reply] |
Re: Bug in 'strict'??
by grinder (Bishop) on Apr 05, 2001 at 17:03 UTC
|
my $filename = $req->param("filename");
my ($localfilename) =
($filename =~ m/(?:.*?$CGI::SL)*(.*)(?=$)/);
Otherwise, no, I don't see it...
-- g r i n d e r
| [reply] [d/l] |
Works great for me!
by osorronophris (Novice) on Apr 05, 2001 at 20:27 UTC
|
I stuck this script in my cgi-bin on my own machine and it
worked like a charm! The only change I made was changing
open(FILE, ">$localfilename") to open(FILE, ">/tmp/$localfilename").
Here are my system stats:
OpenBSD 2.8 (i386)
perl v5.6.0 built for i386-openbsd
Apache 1.3
And here's the output of your script:
File saved as test (original filename=/home/mosfet/test)
so....
All idiomatic problems aside (like that whole "if" around
the regex thing) your script is FINE. There are no relevent
bugs in "use strict", which using, by the way, is about the best
thing anyone can do in a production environment. If your coworkers
give you that whole *@#$! about use strict being only for
beginners or what-not, I'm sorry, they're just wrong. use
strict is a GOOD IDEA. Unwillingness to use strict can lead to
large problems that wouldn't happen in other languages. That's
why it's there.
The one reason I can find that your script may not be working
is the use of $CGI::SL. On my system it's "/", which is correct
for *nix. Since this is determined by the server, it may not
change properly for Windows. I can't test this because I don't
have access to Windows. But if that's the case, that could be
your problem.
PS: Just for kicks I tried it with "use diagnostics" and no
errors were produced...
| [reply] |
|
|