January 21, 2021
Hot Topics:

Building an effective autoresponder

  • By Jay Link
  • Send Email »
  • More Articles »

Now that we've seen how to get e-mail delivered to a program, it's time to process the arriving e-mail messages. That's where Perl comes in.

Answer me!

At its simplest, the autoresponder will read the incoming e-mail, but won't respond to it. (I realize this technically makes it a government worker, and not an autoresponder.) Here's how such a program might look:


while (<STDIN>) {



Notice that the program receives the e-mail as standard input, just as though it were being fed by a "cat" redirect statement.

From here, we want to pluck out the sender's return address. Let's examine the headers of a typical e-mail message, to see how this might best be done:

From rmcginty@earthweb.com Wed Aug 25 15:49:35 1999
Received: from fox.earthweb.com (fox.earthweb.com [])
        by interlink-bbs.com (8.9.3/8.9.3) with ESMTP id PAA18023
        for ; Wed, 25 Aug 1999
15:49:31 -0500
Received: from Whisker ([])
        by fox.earthweb.com (8.9.1/8.9.1) with SMTP id QAA11159
        for ; Wed, 25 Aug 1999
16:43:57 -0400
From: "Rikki McGinty" <rmcginty@earthweb.com>
To: "Jay Link" <jlink@radish.interlink-bbs.com>
Subject: Autoresponder article
Date: Wed, 25 Aug 1999 16:47:02 -0400
Message-ID: <000f01beef3a$fbb77b00$881a98ce@Whisker.earthweb.com>

Notice that there are two "From" lines; one has a colon, and one does not. It might be tempting to use the coloned "From" line, as it appears to be more straightforward than the other, but it has a problem: it's inconsistent. The "From with a colon" line can actually use one of four different formats, as shown below:

From: rmcginty@earthweb.com
From: <rmcginty@earthweb.com>
From: "Rikki McGinty" <rmcginty@earthweb.com>
From: rmcginty@earthweb.com ("Rikki McGinty")

Therefore, I suggest using the initial, colon-less "From" line. You can grab it with a statement like this:

if (/^From /x) { }

To get rid of the word "From" itself, and the following date, use the "split" command:

($from, $address, $date) = split (/ /, $_, 3);

I like consistency; since e-mail addresses can be upper and lower case, I usually convert them all into lower case. This will be useful later, as you'll see.

$address =~ tr/[A-Z]/[a-z]/;

So now, the program looks like this:

#!/usr/bin/perl while (<STDIN>) { if (/^From /x) { ($from, $address, $date) = split (/ /, $_, 3); $address =~ tr/[A-Z]/[a-z]/; } } exit(0);

Looks good. Unfortunately, we still have a problem.

Smartening code

Some of you may have realized that the body of the incoming e-mail message might contain the word "From " (followed by a space) at the beginning of a line. If this happens, our address extraction routine will get the proper return address at first pass, but will then replace that address with gibberish later on (i.e., with some unwanted portion of the message). Therefore, we just want the first occurrence of the word "From", and we want to ignore all future placements.

I use a variable called $flag to accomplish this. When the program starts, $flag is set to 0. When the first "From" line is encountered, $flag becomes one, and all subsequent "From" lines are ignored. Here's the code:

#!/usr/bin/perl $flag = 0; while (<STDIN>) { if ((/^From /x) && ($flag == 0)) { ($from, $address, $date) = split (/ /, $_, 3); $address =~ tr/[A-Z]/[a-z]/; $flag = 1; } } exit(0);

Now we can rest assured that our $address is correct.

The next step is to return the automated reply to the sender. This is, after all, the whole point of the program. See below:

#!/usr/bin/perl $flag = 0; while (<STDIN>) { if ((/^From /x) && ($flag == 0)) { ($from, $address, $date) = split (/ /, $_, 3); $address =~ tr/[A-Z]/[a-z]/; $flag = 1; } } open SENDMAIL, "| sendmail -bm -oi -f info\@example.com $address"; print SENDMAIL <<EOM; From: "Example Bot" <info\@example.com> To: $address Subject: Your email has been received. Thanks for writing! EOM close SENDMAIL; exit(0);

And that does it! Of course, you can elaborate as much as you'd like. Remember that there needs to be a blank line after the headers.

Let's take a moment to go over the command line options I've used with Sendmail. The -bm flag invokes Sendmail as a mailer program. -oi means that Sendmail won't terminate the outgoing message prematurely, should it encounter a line consisting of only a period ("."). (Normally, most mail programs stop accepting input when they're given a solitary period. My message didn't contain any period-only lines, but I use this as a precaution. Should someone absentmindedly change the outgoing message to include a period-only line, the program won't need to be changed.)

-f means "from", i.e., the e-mail address from which the autoresponse will appear to have been sent. If this isn't present, the message will be sent from user "bin" or whatever/whomever invoked the autoresponder. Naturally, -f must be followed by an address. The last argument is the "to" address, which is $address.

Getting loopy

Sometimes, by quirk of fate or deliberate mischief, an autoresponder might get into a perpetual mail loop. For example, if your autoresponder receives a spam from a non-existent address, then it will blindly send a reply to that same address. This will usually generate a "MAILER-DAEMON" error message from the remote mail server, which will be sent (of course) back to the autoresponder. Receiving the error message, your autoresponder will then send your autoresponse to the remote "MAILER-DAEMON" address, which will then result in yet another error. The remote "MAILER-DAEMON" replies, repeat, repeat, Pete fell out and who was left? This can go on forever, and in extreme cases, it'll bog down your server to such a degree that legitimate mail cannot get through.

My fix is to eliminate all "MAILER-DAEMON" mail. Insert this snippet into the main code:

if ($address =~ /mailer-daemon/) { exit(0); } elsif ($address =~ /postmaster/) { exit(0); }

We know "mailer-daemon" will be lower-case, because we made $address lower-case with our "tr" line. Remember? Anyway, feel free to include as many "elseif" lines as you deem necessary.

Here's another "cheater" method: Make the -f "from" address (the one given on the Sendmail invocation line) a false address. If a human is reading your mail, they'll see the proper "From:" line (From with a colon). If it's a machine, it'll reply to the bogus address. If you feel bad about using a false address, you might consider using an alias for "/dev/null" on your machine. All mail sent to /dev/null is deleted, so you could have a line like this in /etc/aliases :

nobody: /dev/null

Then send your mail from "nobody". Again, a human will just see the proper address. Here's what the relevant headers would look like:

open SENDMAIL, "| sendmail -bm -oi -f nobody\@example.com $address"; print SENDMAIL <<EOM; From: "Example Bot" <info\@example.com>

See what I mean?

Perhaps you'd like a log of everyone who's written to your autoresponder? This can be easily accomplished; customize the following as you see fit:

open (FILE, ">>/etc/autorespond.logfile"); print FILE "$address\n"; close (FILE);

If you wanted the date as well, you could simply include the first "From" line (from the incoming message) without any editing.

Let's say you wanted your outgoing message to be contained in an external file (instead of in the program). Just wipe out the "EOM" segments, and everything in between, and replace them with the following file-reading code:

Delete: print SENDMAIL <<EOM; <message> EOM


open (FILE, "/etc/autorespond.message"); while (<FILE>) { print SENDMAIL; } close (FILE);

That wraps up most of the options that people commonly use with their autoresponders. It would be trivial to send different messages based upon different criteria: time of day, day of week, originating message's domain, and so on. It is my hope that the preceding information will give you a base upon which you can expand, explore, and manipulate to make the perfect autoresponder to fit your needs.ø

Related resources

1. Sendmail home page The official home page of Sendmail, maintained by the Sendmail Consortium.
2. Sendmail, by Bryan Costales and Eric Allman. O'Reilly's classic reference guide.
3. Perl home page Need more information on developing applications with Perl? Here's the place.
4. IETF's list of RFCs RFC822 is of particular interest to e-mail application programmers.

Jay Link is twenty-something and lives in Springfield, IL. He administrates InterLink BBS - an unintentionally nonprofit Internet service provider - in his fleeting spare moments as well as working various odd jobs to pay the rent.

Page 2 of 2

This article was originally published on September 9, 1999

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date