in reply to form mail with post, getting "premature ending of script headers"

Others have suggested that you use a prefab solution, and that is no doubt really good advice if your aim does not extend beyond this script. But for didactic reasons, I think it would be good to go over the code you have and make some... adjustments.
#!/bin/perl if ($ENV{'REQUEST_METHOD'} eq "GET") {$buffer = $ENV{'QUERY_STRING'};} elsif ($ENV{'REQUENT_METHOD'} eq "POST") {read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'});} $bufferb = $buffer; @forminputs = split(/&/, $bufferb); foreach $forminput (@forminputs) { ($name, $value) = split(/=/, $forminput); $value =~ tr/+/ /; $value =~ s/%([a-fA-f0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $in{$name} = $value; }

Let's start with that much. First of all, getting form input is something 80% or more of the cgi scripts you ever write will need to do. Rather than inlining it, make it a subroutine. That also improves the legibility of your code, because it's very clear what getforminput() does. Also, you can put your subroutine in a my-cgi-includes.pl file and require it from all your various scripts, so that as you discover problems with it, you can fix it in one place and all your scripts can benefit. My advice is to write it so that it returns a reference to a hash of name/value pairs. Then your code can do this:

require "my-cgi-includes.pl"; my %input = %{getforminput()};

Someone will quickly jump up and say that you should use cgi instead, but I say that the instead part is wrong: using cgi is an implementation detail of your subroutine. The key principle that applies generally is to move that oft-repeated code off by itself so that you can easily fix it up in whatever way (e.g., by deciding to use cgi) when you figure out how or get around to it. For starters, and for learning purposes, let's take what you've got and work with it:

sub getforminput { my %in; my $buffer; # Because this is now a subroutine that can get # called from various places, we must be careful # not to tromp on the script's own variables. if ($ENV{'REQUEST_METHOD'} eq "GET") {$buffer = $ENV{'QUERY_STRING'} +;} elsif ($ENV{'REQUENT_METHOD'} eq "POST") { read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'}); } else { return undef; # If $ENV{REQUEST_METHOD} is not set, maybe there's +no input. # In that case the script will probably print a blan +k form. } # I'm not sure what your assigning from $buffer to $bufferb was # intended to accomplish, but I think it just creates an extra # variable. Skip it, and go straight to the split: my @forminputs = split(/&/, $buffer); foreach $forminput (@forminputs) { my ($name, $value) = split(/=/, $forminput); $value =~ tr/+/ /; $value =~ s/%([a-fA-f0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # Some of that stuff cgi could do for you, possibly better, but # for now we'll move on... $in{$name} = $value; # Good. You did right to put the input in a hash. Not # only is it convenient to access that way, but it is # also all together in one nice container that can be # easily passed around, and there is no risk of tromping # on other variables (as there would be with $$name=$value # and similar hacks). } return \%in; }

I only made minor changes, but now what you have is not only more re-usable, but if you discover a problem with it you can fix it up without much risk to the rest of the script(s).

Let's move on...

print "Content-type: text/html\n\n"; #mail sent to admin open (LMAIL, "|usr/sbin/sendmail -t"); print LMAIL("To: $in{myEmail}\n"); print LMAIL ("From: $in{Email}\n"); print LMAIL ("Subject: $in{Form}\n"); print LMAIL ("--------------------\n\n"); print LMAIL("$in{Message}\n\n"); #mail sent to user open (MAIL, "|/usr/sbin/sendmail -t"); print MAIL<<toEnd;

mmmmkay... First you print out your headers, then you try to send the mail. If it were me, I would be doing it the other way, sending the mail first and then printing the page that tells the user what we did. But that is a minor thing.

More important, as the other poster pointed out, we need to do some error checking before we send the message...

my %input=%{getforminput()}; my $validto = "jonadab\@bright.net,jonadab_homeip_net\@yahoo.co.uk" +; if ($validto !~ $input{myEmail}) { $errors .= "<li>The To: address you selected was not one of the +available choices. Please pick an option from the dropdown list.</li +>\n"; } if ($input{Email} !~ /^[^@]+[@][^@]+$/) { $errors .= "<li>The From: address you entered does not appear to + be valid. Please enter a real email address.</li>\n"; } if (not $input{Subject}) { $errors .= "<li>You left the subject blank. Please fill in a sh +ort description of what your message is about.</li>\n"; } if (not $input{Message}) { $errors .= "<li>You left the message itself blank. Surely you h +ad something to say?</li>\n"; } if ($errors) { print "Content-type: text/html <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"htt +p://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> <html>\n<head>\n<title>Error(s) encountered</title>\n</head> <body>\n<p>One or more errors were encountered when trying to send you +r message. Please hit your browser's back button and correct the following +issues:\n</p> <ul>$errors</ul>\n</body>\n</html>\n"; exit 0; # Update: If there were errors, we don't want to send the +message. }

If you might write multiple scripts that send mail, you should make that validation into a subroutine that you can include. Also, the actual mail sending code can be a subroutine that takes the from, to, subject, and body as four arguments... If it were me I'd be using Net::SMTP, but for now let's take what you have and make it a subroutine...

sub sendmail { my ($to, $from, $subject, $body) = @_; open (LMAIL, "|usr/sbin/sendmail -t"); print LMAIL("To: $to\n"); print LMAIL ("From: $from\n"); print LMAIL ("Subject: $subject\n"); print LMAIL ("--------------------\n\n"); print LMAIL ("$body\n\n"); print LMAIL ("-- \nSent from $ENV{REMOTE_ADDR} using $0 on $ENV{HOST +NAME}\n"); close LMAIL; }

As an added bonus, making that a subroutine means you can send one message to the admin and another to the user by just calling it twice:

# mail sent to admin sendmail($input{myEmail}, $input{Email}, $input{Subject}, $input{Messa +ge}); # mail sent to user sendmail($input{Email}, $input{myEmail}, "Re: ".$input{Subject}, "Than +ks for your email! \n\n");

Later if you move the script to a server that does not have /usr/bin/sendmail, you just rewrite your sendmail() subroutine, and the rest will go unchanged. Now, on to your web page output...

#submit screen print ("<html><head<title>$in{Form}</title></head>"); print ("<body bgcolor=\"ffffff\">"); print ("Thanks for your email."); print ("</body></html");

Move the part that prints the content-type header down here too, to save confusion. This is very simple HTML, but it looks wellformed and mostly gets the point across. You could make it more elaborate later, once you get the thing working.

One little thing, though: NEVER EVER set a background colour without also setting the foreground. If you specify a white background, you need to specify a dark foreground. (Black, dark blue, whatever; as long as it shows up against white.)

Oh, and rather than put the form in a separate HTML file, I generally have the script print the blank form if it doesn't get any input. But that's more a matter of website structure than of Perl.


sub H{$_=shift;while($_){$c=0;while(s/^2//){$c++;}s/^4//;$ v.=(' ','|','_',"\n",'\\','/')[$c]}$v}sub A{$_=shift;while ($_){$d=hex chop;for(1..4){$pl.=($d%2)?4:2;$d>>=1}}$pl}$H= "16f6da116f6db14b4b0906c4f324";print H(A($H)) # -- jonadab