This text is excerpted from CGI Programming 101 (2nd edition). Click here to order the book.

Text copyright © 2004 by Jacqueline Hamilton. All rights reserved. You may print one copy of this page for your personal use. You may not copy this material onto another website or into any other medium.

Chapter 4: Processing Forms and Sending Mail

Most forms you create will send their data using the POST method. POST is more secure than GET, since the data isn't sent as part of the URL, and you can send more data with POST. Also, your browser, web server, or proxy server may cache GET queries, but posted data is resent each time.

Your web browser, when sending form data, encodes the data being sent. Alphanumeric characters are sent as themselves; spaces are converted to plus signs (+); other characters — like tabs, quotes, etc. — are converted to "%HH" — a percent sign and two hexadecimal digits representing the ASCII code of the character. This is called URL encoding.

In order to do anything useful with the data, your program must decode these. Fortunately the CGI.pm module does this work for you. You access the decoded form values the same way you did with GET:

So you already know how to process forms! You can try it now by changing your getform.html form to method="POST" (rather than method="GET"). You'll see that it works identically whether you use GET or POST. Even though the data is sent differently, CGI.pm handles it for you automatically.

The Old Way of Decoding Form Data

Before CGI.pm was bundled with Perl, CGI programmers had to write their own form-parsing code. If you read some older CGI books (including the first edition of this book), or if you're debugging old code, you'll probably encounter the old way of decoding form data. Here's what it looks like:

This code block reads the posted form data from standard input, loops through the fieldname=value fields in the form, and uses the pack function to do URL-decoding. Then it stores each fieldname/value pair in a hash called %FORM.

This code is deprecated and should be avoided; use CGI.pm instead. If you want to upgrade an old program that uses the above code block, you can replace it with this:

Or you could use the Vars function:

Either method will replace the old form-parsing code, although keep in mind that this will not work if your form has multiple fields with the same name. We'll look at how to handle those in the next chapter.

Guestbook Form

One of the first CGI programs you're likely to want to add to your website is a guestbook program, so let's start writing one. First create your HTML form. The actual fields can be up to you, but a bare minimum might look like this:

Source code: http://www.cgi101.com/book/ch4/guestbook1.html

(Stylistically it's better NOT to include a "reset" button on forms like this. It's unlikely the visitor will want to erase what they've typed, and more likely they'll accidentally hit "reset" instead of "send", which can be an aggravating experience. They may not bother to re-fill the form in such cases.)

Now you need to create post.cgi. This is nearly identical to the get.cgi from last chapter, so you may just want to copy that program and make changes:

Program 4-1: post.cgi - Form Processing Program Using POST


Source code: http://www.cgi101.com/book/ch4/post-cgi.html
Working example: http://www.cgi101.com/book/ch4/form.html

Test your program by entering some data into the fields, and pressing "send" when finished. Notice that the data is not sent in the URL this time, as it was with the GET example.

Of course, this form doesn't actually DO anything with the data, which doesn't make it much of a guestbook. Let's see how to send the data in e-mail.

Sending Mail

There are several ways to send mail. We'll be using the sendmail program for these examples. If you're using a non-Unix system (or a Unix without sendmail installed), there are a number of third-party Perl modules that you can use to achieve the same effect. See http://search.cpan.org/ (search for "sendmail") for a list of platform-independent mailers, and Chapter 14 for examples of how to install third-party modules. If you're using ActivePerl on Windows, visit http://www.cgi101.com/book/ch4/ for a link to more information about sending mail from Windows.

Before you can write your form-to-mail CGI program, you'll need to figure out where the sendmail program is installed on your webserver. (For cgi101.com, it's in /usr/sbin/sendmail. If you're not sure where it is, try doing which sendmail or whereis sendmail; usually one of these two commands will yield the correct location.)

Since we're using the -T flag for taint checking, the first thing you need to do before connecting to sendmail is set the PATH environment variable:

The path should be the directory where sendmail is located; if sendmail is in /usr/sbin/sendmail, then $ENV{PATH} should be "/usr/sbin". If it's in /var/lib/sendmail, then $ENV{PATH} should be "/var/lib".

Next you open a pipe to the sendmail program:

The pipe (which is indicated by the | character) causes all of the output printed to that filehandle (MAIL) to be fed directly to the /usr/sbin/sendmail program as if it were standard input to that program. Several flags are also passed to sendmail:

The -t flag tells sendmail to look at the message headers to determine who the mail is being sent to. You'll have to print all of the message headers yourself:

Remember that you can safely put an @-sign inside a single-quoted string, like 'recipient@cgi101.com', or you can escape the @-sign in double-quoted strings by using a backslash ("sender\@cgi101.com").

The message headers are complete when you print a single blank line following the header lines. We've accomplished this by printing two newlines at the end of the subject header:

After that, you can print the body of your message.

Let's try it. Start a new file named guestbook.cgi, and edit it as follows. You don't need to include the comments in the following code; they are just there to show you what's happening.

Program 4-2: guestbook.cgi - Guestbook Program


Source code: http://www.cgi101.com/book/ch4/guestbook-cgi.html

Save and chmod the file, then modify your guestbook.html form so that the action points to guestbook.cgi:

Try testing the form. If the program runs successfully, you'll get e-mail in a few moments with the results of your post. (Remember to change $recipient to your email address!)

Subroutines

In the guestbook program we used a new structure: a subroutine called "dienice." A subroutine is a user-defined function. You've already used functions like param and start_html from the CGI.pm module, and built-in functions like shift and pop. You can also define your own custom functions.

In the mail program, the dienice subroutine is only called if the program can't open the pipe to sendmail. Rather than aborting and giving you a server error (or worse, NO error), you want your program to give you some useful data about what went wrong; dienice does that, by printing the error message and closing HTML tags, and exiting the program. We'll be using the dienice subroutine throughout the rest of the book, as a generic catch-all error-handler.

Subroutines are useful for isolating blocks of code that are reused frequently in your program. The structure of a subroutine is as follows:

The subroutine block starts with the word sub, followed by the name of the subroutine. The code for the subroutine is then enclosed in curly braces { }.

Subroutines can be placed anywhere in your program, though for readability it's usually best to put them at the end, after the main program code.

To invoke a subroutine, enter the subroutine name and an optional list of arguments:

You may prefix the subroutine name with an &-sign:

The &-sign is optional. However, we'll be using this syntax throughout the book to differentiate calls to subroutines we've written ourselves. Calls to built-in functions or functions provided by external modules will not have this sign.

Here is an example of a call to a subroutine named "mysub" with three arguments:

The arguments are passed to the subroutine in the special Perl array @_. You can then assign the elements of that array to special temporary variables, like so:

In this example, the my function limits the scope of $arg1, $arg2 and $arg3 to the mysub subroutine. This keeps your temporary variables visible only to the subroutine itself (where they're actually needed and used), rather than to the entire program (where they're not needed). This also means if you change one of the variables inside your subroutine, the value of the original variable won't change (unless it's a reference, which we'll look at next).

Passing Arrays and Hashes to Subroutines

When passing an array (or a hash) to a subroutine, the array is expanded into a list of its values. This might be okay if the array is the only argument:

However if you have multiple arguments, you're going to run into problems:

In this example, all of the arguments (including $item2 and $item3) are stored in @ary, and $arg2 and $arg3 are undefined. In order to pass the array or hash properly to the subroutine, you need to pass it as a reference, by prefixing the @ (or %) by a backslash:

Now $arrayref is a reference to @array1, $arg2 is whatever the value of $item2 is, and $hashref is a reference to %hash1. To access individual elements of an array reference, instead of using $arrayref[1], you use $arrayref->[1]. Similarly with a hash reference you use $hashref->{key} instead of $hashref{key}.

A reference is a pointer to the original variable. If you change the value of an element of an array reference, you're changing the original array's values.

Optionally you could dereference the array inside your subroutine by doing:

A hash is dereferenced like so:

A dereferenced array (or hash) is localized to your subroutine, so you can change the values of @newarray or %newhash without altering the original variables.

You can find out a lot more about references by reading perldoc perlref and perldoc perlreftut (the Perl reference tutorial).

Subroutine Return Values

Subroutines can return a value:

If you omit the return statement, then the value returned by the subroutine is the value of the last expression executed in that routine.

If you want to save the return value, be sure to assign it to a variable:

Subroutines can also return a list:

Which can then be assigned to a list of variables:

Or an array:

Return vs. Exit

You'll notice that our dienice subroutine does not return a value at all, but rather calls the exit function. exit causes the entire program to terminate immediately.

Sendmail Subroutine

Here is an example of the mail-sending code in a compact subroutine:

Sending Mail to More Than One Recipient

If you want to send mail to more than one email address, just add the desired addresses to the $recipient line, separated by commas:

Defending Against Spammers

When building form-to-mail programs, you need to take precautions to prevent spammers from hijacking your programs to send unwanted e-mail to other recipients. They can do this by writing their own form (or program) to send data to your CGI. If your program prints any of the form fields as mail headers without checking them first, the spammer can insert their own mail headers (and even their own message). The end result: your program becomes a relay for spammers.

The primary defense against this is to not allow the form to specify ANY of the mail headers (such as the From, To, or Subject headers). Note that in our guestbook program, the From, To and Subject headers were all hardcoded in the program.

Of course, it would be nice to have the "From" header show the poster's e-mail address. You could allow this if you validate it first, verifying that it's really an e-mail address and doesn't contain any extra headers. You can validate e-mail addresses by using a regular expression pattern match, which we'll cover in Chapter 13, or by using the Email::Valid module, which we'll look at in Chapter 14.

Resources

Visit http://www.cgi101.com/book/ch4/ for source code and links from this chapter.

Previous Contents Next

 


Table of Contents | Order the Book | CGI101 Home