Sending Mail via the Web
Last month, we looked at a simple CGI program (read-mail.pl) that allows us to view our e-mail from within a web browser. This program takes advantage of the fact that most e-mail today is delivered to a “post office” server, from which it is downloaded by a user's mail-reading program. Mail clients use the POP3 protocol to retrieve messages from a post office server, which means our program can retrieve a user's mail by connecting to a server and retrieving one or more messages.
read-mail.pl is good enough for basic purposes, in that it makes it possible to retrieve mail from any web browser in the world. However, while it makes it possible to read e-mail messages, it does not provide any mail-sending capabilities. True, many web browsers include such a capability—but many, such as Netscape Navigator, do not.
This month, then, we will take a look at how to send mail via the web. Between read-mail.pl (from last month) and send-mail.pl (in this article), we will have a simple, integrated mail system that allows users to perform all rudimentary tasks from any web browser.
Sending mail based on the contents of an HTML form was one of the first uses to which CGI programs were put, back in 1993 when CGI and HTML forms first arrived on the scene. As we will see, sending e-mail is not particularly difficult from within a CGI program. However, we will look at issues related to security as well as what we would need to do to turn these simple programs into a full-fledged Hotmail competitor.
Sending e-mail from within a program is normally quite straightforward, particularly if you are using Perl. You open a pipe to a mail-sending program, and send it the headers and data for the message you want to send. For instance, here is a simple program that sends a short “hello, world” message to my address, email@example.com:
#!/usr/bin/perl -w use strict; use diagnostics; my $mailprog = '/usr/lib/sendmail'; my $recipient = 'firstname.lastname@example.org'; open (MAIL, "|$mailprog $recipient") die "Cannot open $mailprog: $! "; print MAIL "From: nobody\n"; print MAIL "To: $recipient\n"; print "\n"; print MAIL "Hello there!\n"; close MAIL;
There are several things to notice about this program. First of all, we set $mailprog to “/usr/lib/sendmail”, the default name and location of the mail transfer agent (MTA) on Linux systems. If your copy of sendmail is in another location or if you are using a program other than sendmail, you will need to change the value of $mailprog.
Similarly, mail is sent to a single address, what is defined in $recipient. We will discuss the issue of recipients later, when we look at the issue of program security. Keep in mind that restricting the number of recipients to which the program will send e-mail reduces the possibility that your program will be turned into a mail gateway by spammers or others interested in sending anonymous mail.
We open a connection to $mailprog using Perl's open function, which allows us to write to a program's standard input (STDIN) by treating the program name as a file name, and by prefacing the program's name with a | character. Anything we print or write to that file handle will be treated as if it were sent to the program's STDIN. Any output from the program is ignored.
Finally, notice how we insert a new line character between the final header and the message body. As with HTTP, SMTP (the “simple mail transfer protocol” used for most mail transfer on the Internet) requires a blank line between the header and any data. This allows the receiving program to identify which lines are headers and which are in the body.
Those of us who have been sending mail from within Perl programs were delighted when the Mail::Sendmail module, written by Milivoj Ivkovic, was released to CPAN (the Comprehensive Perl Archive Network, based at http://www.cpan.org/). This module provides a portable method for sending mail from within a Perl program, but also provides a layer of abstraction between your program and the underlying mail system.
It is important to understand how the mail-sending mechanism works on your computer, particularly when it comes time to debug problems with sending or receiving e-mail. However, being able to send mail with three or four lines of Perl from a package maintained and updated independently of your program makes it possible to write shorter, more reliable programs. I have begun using Mail::Sendmail in all of my programs that send e-mail, and I suggest you do so as well, unless you have good reason to stick with the old system described above. One possible reason not to use Mail::Sendmail is if your program will be installed on a system without this package and on which you could not expect it to be installed. Given the ease with which packages can be downloaded and installed from CPAN, however, this should not deter you in a serious way.
The Mail::Sendmail module, like all other modules, must be imported at the top of any program that uses it with a use statement:
If you have not installed the module, or if it is not installed in one of the directories named in @INC (the path through which Perl searches when importing modules), Perl will fail with a fatal error.
After Mail::Sendmail is imported, sending a message becomes a two-step process. In the first stage, define a hash in which the keys are the headers and contents of the message. Specify the message body with the Message (or Body or Text) key. For example:
my %mail = (To => $recipient, From => $sender, Message => "Hello, there!");
You can then send the mail using the statement:
sendmail %mail;The sendmail function is imported into the current name space automatically with the use Mail::Sendmail instruction. Mail::Sendmail defines many other functions as well, but none of these are imported into the default name space unless you explicitly request it.
As you can see, the above code is significantly shorter and easier to understand than what we did first. The fact that it is more portable and easier to maintain are nice additional benefits.