Mike's Web

Lesson 3

This lesson assumes that you already know how to create HTML forms. If not, please read the Mosaic Forms Tutorial first.

Processing FORM data

For any CGI that must process a form using the POST method, you'll have to read the input from the form, and translate special characters back into what the person actually typed in. For example, any blank spaces in a form field are passed along the input string as a plus sign ("+").

You may have used something like cgi-lib.pl in the past to handle unparsing your forms, but all you ever need is this bite of code right here: (special thanks to Reuven M. Lerner, from whose form-mail.pl script this was taken)

form parse code

read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);

foreach $pair (@pairs)
{
    ($name, $value) = split(/=/, $pair);

    # Un-Webify plus signs and %-encoding
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

    # Stop people from using subshells to execute commands
    # Not a big deal when using sendmail, but very important
    # when using UCB mail (aka mailx).
    $value =~ s/~!/ ~!/g;

    $FORM{$name} = $value;
}
Now, after your cgi gets through the above block of code, you'll have an associative array called %FORM that contains all the values that were entered into the form. The keys of the %FORM array are the variable names themselves. So, for example, if you have three text fields in the form - called name, email-address, and age, you could refer to them in your script by using $FORM{'name'}, $FORM{'email-address'}, and $FORM{'age'}.

Now, let's write a generic form-processor script, using the above code, plus a bit more. Create a new file called form1.cgi, and enter this code. This is the above form-handler code, plus a loop that prints out each variable.

#!/usr/bin/perl

print "Content-type:text/html\n\n";

read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@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;
    $value =~ s/~!/ ~!/g;
    $FORM{$name} = $value;
}

print "<html><head><title>Form Output</title></head><body>";
print "<h2>Results from FORM post</h2>\n";
foreach $key (keys(%FORM)) {
   print "$key = $FORM{$key}<br>";
}
print "</body></html>";

This code can be used to handle ANY form. Whatever variables you have in your form, this cgi will print them out, along with the data the person has entered.

Let's test it. Enter some stuff into the fields below, and press "send" when finished. The output will be the variable names of these text boxes plus the actual data you type into each field. (view the source for this form)


       Your Name: 
   Email Address: 
             Age: 
  Favorite Color: 


Tip: If you've ever built a form, and had trouble getting the boxes to align, try putting <pre> tags around the input fields. Then you can line them up with your text editor, and the result is a much neater looking form. The reason for this is that most web browsers use a fixed-width font for preformatted text, so aligning forms and other data is much easier in a preformatted text block than in regular HTML.

There are a few special cases to note here with forms. First, if you have a textarea field (such as for comments), you'll need to add this line right after the form parse code:

$FORM{'comments'} =~ s/\r/\n/;
All this does is replace raw linefeeds with a regular carriage return.

Also, in the event of things like checkboxes and radio buttons, you'll want to use significant variable names in your HTML form itself, so that the data will be meaningful to you.

Let's take this one step further. Most people using forms want the data emailed back to them, so, let's write a form-to-mail cgi. First you'll need to figure out where the sendmail program lives on the system you're on. (For IO, it's in /usr/sbin/sendmail. If you're not sure where yours is, try doing which sendmail or whereis sendmail; usually one of these two commands will yield the location of the sendmail program.)

Copy your form1.cgi to a new file named form2.cgi. Now the only change will be to the foreach loop. Instead of printing to standard output (the HTML page the person sees after clicking submit), we want to print the values of the variables to a mail message. So, first, we must open input to the sendmail program. You also need to specify the recipient of the email.

Note: perl will complain if you use an "@" sign inside a regular string (a string delimited by "double quotes") or a print <<EndHTML block. You can safely put an @-sign inside a single-quote string, like 'kira@metronet.com', or you can escape the @-sign in other strings by using a backslash. For example, "kira\@metronet.com".

#!/usr/bin/perl

print "Content-type:text/html\n\n";

read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@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;
    $value =~ s/~!/ ~!/g;
    $FORM{$name} = $value;
}

$mailprog = '/usr/sbin/sendmail';

# change the email address to your own (so you get the mail and not me) :)
$recipient = 'kira@metronet.com';

# this opens an output stream and pipes it directly to the sendmail
# program.
open (MAIL, "|$mailprog $recipient") || die "Can't open $mailprog!\n";

# here we're printing out the header info for the mail message.  The
# reply-to can be set to the email address of the sender, assuming you
# have actually defined a field in your form called 'email'.
print MAIL "Reply-to: $FORM{'email'} ($FORM{'name'})\n";

# print out a subject line so you know it's from your form cgi.
# The two \n\n's end the header section of the message.  anything
# you print after this point will be part of the body of the mail.
print MAIL "Subject: Form Data\n\n";

# here you're just printing out all the variables and values, just like
# before in the previous script, only the output is to the mail message
# rather than the followup HTML page.
foreach $key (keys(%FORM)) {
  print MAIL "$key = $FORM{$key}\n";
}

# when you finish writing to the mail message, be sure to close the
# input stream so it actually gets mailed.
close(MAIL);

# now print something to the HTML page, usually thanking the person
# for filling out the form, and giving them a link back to your homepage
print <<EndHTML;
<h2>Thank You</h2>
Thank you for writing.  Your mail has been delivered.<p>
Return to the <a href="http://slsq1b/cgi_course/lession3.html">CGI Class</a><p>
</body></html>
EndHTML

You don't need to include the comments in the above code; they are just there to show you what's happening.

Now let's test it again. Here's the form again, only the action this time points to form2.cgi.


       Your Name: 
   Email Address: 
             Age: 
  Favorite_Color: 


And that's all there is to it. If you have problems with the script, check to be sure you're using the correct path for sendmail. Also remember the trick of running the script from the Unix shell - this may point out errors you might have missed before.

Lesson 4 will cover some more advanced form processing topics.


Copyright © 1997 by Jacqueline D. Hamilton All Rights Reserved.


Mike's Home | Lesson 1 | Lesson 2 | Lesson 3 | Lesson 4 | Lesson 5 | Lesson 6 | Generic Lesson