Book HomeCGI Programming with PerlSearch this book

9.4. sendmail

Without sendmail , Internet email might not exist. Although other mail transport agents (MTAs) do exist, the vast majority of mail servers on the Internet all run sendmail. It was originally written by Eric Allman starting around 1980 and, as we mentioned earlier, the Internet was very different then. sendmail tackled the formidable task of transferring mail between very different networks. Thus, it has never been a simple program, and it has grown. It has become one of the most complicated applications to ever fully understand; the number of command-line flags and configuration parameters it now accepts is truly mind-boggling. Fortunately, we only need to learn a few things in order to have it send messages for us. If you want to learn more about sendmail, see sendmail by Bryan Costales with Eric Allman (O'Reilly & Associates, Inc.).

sendmail generally comes preinstalled on Unix machines and has recently been ported to Windows NT. On Unix, it is often installed in /usr/lib/sendmail, but /usr/sbin/sendmail and /usr/ucb/lib/sendmail are also possible locations. These examples will use /usr/lib/sendmail as the location of the sendmail program. If your copy is installed elsewhere, simply replace this with the path to your copy of sendmail.

9.4.1. Command-Line Options

You generally want to call sendmail with at least a couple of command-line options. When sending a message, sendmail assumes it is being run interactively by a user, so it sets the sender to be that of the user, and it allows the user to enter a period on its own line to signal the end of the message. You can override these settings and will probably want to. In addition, if you are sending multiple email messages, you may wish to queue them so that sendmail can deliver them asynchronously without pausing to deliver each one.

Table 9-1 lists the important options you should know.

Table 9-1. Common sendmail Options

Option

Description

-t

Read To, Cc, and Bcc from the message headers.

-f "email address"

Make the message appear to be From the specified email address.

-F "full name"

Make the message appear to be From the specified name.

-i

Ignore periods on lines by themselves.

-odq

Queue messages to be sent later instead of processing them one at a time.

Example 9-1 is a short CGI script in Perl that uses many of these options.

Example 9-1. feedback_sendmail.cgi

#!/usr/bin/perl -wT

use strict;
use CGI;

# Clean up environment for taint mode before calling sendmail
BEGIN {
    $ENV{PATH} = "/bin:/usr/bin";
    delete @ENV{ qw( IFS CDPATH ENV BASH_ENV ) };
}

my $q       = new CGI;
my $email   = validate_email_address( $q->param( "email" ) );
my $message = $q->param( "message" );

unless ( $email ) {
    print $q->header( "text/html" ),
          $q->start_html( "Invalid Email Address" ),
          $q->h1( "Invalid Email Address" ),
          $q->p( "The email address you entered is invalid. " .
                 "Please use your browser's Back button to " .
                 "return to the form and try again." );
          $q->end_html;
    exit;
}

send_feedback( $email, $message );
send_receipt( $email );

print $q->redirect( "/feedback/thanks.html" );

sub send_feedback {
    my( $email, $message ) = @_;
    
    open MAIL, "| /usr/lib/sendmail -t -i"
        or die "Could not open sendmail: $!";
    
    print MAIL <<END_OF_MESSAGE;
To: webmaster\@scripted.com
Reply-To: $email
Subject: Web Site Feedback

Feedback from a user:

$message
END_OF_MESSAGE
    close MAIL or die "Error closing sendmail: $!";
}

sub send_receipt {
    my $email = shift;
    
    open MAIL, "| /usr/lib/sendmail -t -F'$from_name' -f'$from_email'"
        or die "Could not open sendmail: $!";
    print MAIL <<END_OF_MESSAGE;
To: $email
Subject: Your feedback

Your message has been sent and someone should be responding to you 
shortly. Thanks for taking the time to provide us with your feedback!
END_OF_MESSAGE
    close MAIL or die "Error closing sendmail: $!";
}

We collect two pieces of information from the user: an email address and a message to send to customer service. We validate the email address according to the subroutine earlier in this chapter, but we don't include the code for that subroutine here. The script then composes two messages and forwards users to a static page to thank them.

The first message goes to customer service. It uses the -t option as well as the -i option. The -i option is a good idea if the message includes any dynamic information. It prevents a single dot from prematurely ending the email message.

The -t option is the most important of these options. It tells sendmail to read the routing information for the recipient from the message itself. Otherwise you have to provide the recipient's email address on the command line. Generally, you call sendmail like this:

/usr/lib/sendmail mary@somewhere.com

sendmail then reads the message including the headers and body from its STDIN and sends the message on to Mary, even if the To, Cc, or Bcc fields say it should go elsewhere! This can get confusing.

You should always use the -t flag. First, it makes your life easier, since it automatically handles the To, Cc, and Bcc fields. Second, it lets you avoid that awful security risk of passing user data through the shell. Many times you will be sending email to an address that was entered into an HTML form, so being able to simply include the email address in the body of the message instead is another big win.

Once this message has been sent, the script sends a confirmation to the user. It also uses the -t option here, and here we see the security benefit. The email address comes from the user, but we don't have to worry about passing it through the shell.

In this second email, we also use two other fields to override the sender's routing information. sendmail will not automatically read the sender's email address from the headers as it does for the -t option. This must be specified with the -f and -F options. There are two options in order to support the extended address notation including a name and an email address in this form:

The Webmaster <webmaster@scripted.com>

It is important to override the sender's routing information because if the message to Mary bounces, it will come back to the original sender, and if the user that the web server runs as has a standard account with a mail box, bounced messages will collect there. If it has no mail account, then they'll bounce back and forth either until they time out or some system administrator gets annoyed at the increased network traffic and steps in. Ideally, your system should be configured so that any mail addressed to nobody (the user your web server runs as) is automatically forwarded to the webmaster. If this hasn't been done, or you aren't sure, then it's a good idea to set the -f option to a real email address that someone monitors or that is processed automatically. We'll see how to set up a process to handle mail like this at the end of this chapter.

Note that if you do override the sender's email address with the -f option, sendmail will add an extra header to the email message unless you are a trusted user. This extra header typically looks like the following:

X-Authentication-Warning: scripted.com: sguelich set sender to nobody@scripted.com using -f

By default, the users who have permission to use the -f option without generating this warning are root, daemon, and uucp. Most mail agents do not actually pay attention to this header, so it is rare that recipients will see it. However, you can avoid sending it by adding nobody to the trusted users section in /etc/sendmail.cf.

9.4.2. Mail Queue

The remaining option we haven't discussed is the -odq option. It is useful if you are sending out many messages at the same time. For example, you may run a web site that connects job hunters with available positions. You have the job hunters record keywords describing the types of positions they are looking for in a database along with their email addresses. Then, when the new positions available today have been entered, you start a CGI script which matches the job hunters' keywords against the positions. The script generates and sends out customized messages to the job hunters notifying them if there are any matches. In this example, you would want to use the -odq option. It takes sendmail time to find remote servers and deliver messages, so your script runs much, much faster if you simply add them to the queue to be processed separately and don't wait for sendmail to try to deliver each message.

You do need to make sure that sendmail is configured on your system to process the queue or the messages may just sit around indefinitely. If you aren't sure, ask your system administrator.

Also note that queuing messages this way is only a good idea if each message you are sending out is unique. If you are sending the same message to multiple people, don't queue a separate message addressed to each person, use the Bcc field instead.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.