At the Forge - Integrating E-mail

Keep users coming back to your Web site with e-mail reminders about news or discussions that interest them.

Like many of you, I am plagued by a torrent of spam on a daily basis. SpamAssassin, the open-source tool for analyzing and categorizing incoming e-mail, has proven to be an excellent ally in my fight against spam. And if you ever have run an e-mail list, you undoubtedly have discovered that e-mail worms don't discriminate; they post to lists as easily as they send mail to individuals.

Although system administration and SMTP servers are a bit off-topic for this column, I feel compelled to sing the praises of qpsmtpd, the open-source SMTP server developed by Ask Bjoern Hansen. qpsmtpd originally was designed for use with qmail, but now it apparently is able to work with other MTAs, including Sendmail and Postfix.

Why would you want to insert qpsmtpd instead of the default qmail-smtpd? If you're a Perl hacker, the reason to switch is qpsmtpd is written in Perl. But if you're less of a language bigot, you still can find a lot to love. That's because qpsmtpd divides SMTP's mail-sending routines into a number of stages and allows you to add your own hooks and functionality to each of these stages.

I downloaded qpsmtpd from its home at (yes, that's two O characters in a row), followed the installation instructions and was up and running in about 20 minutes. Remember that qpsmtpd is a full-fledged SMTP server, meaning it refuses to run if you have another SMTP server listening on port 25. If you have been using daemontools to ensure the SMTP server starts at boot time and stays up following that, you should double-check that no link exists from /service to the old SMTP server. Otherwise, you might end up with two competing SMTP dæmons when your machine is next restarted.

The key to qpsmtpd is its plugins, which live in the plugins subdirectory. You can add or remove plugins by modifying the config/plugins file, with one plugin listed per line. For example, a portion of my config/plugins file looks like this:

# quit_fortune

count_unrecognized_commands 4


In other words, I commented out the quit_fortune plugin but have activated the check_earlytalker, count_unrecognized_commands and require_resolvable_fromhost plugins. count_unrecognized_commands takes a single numeric argument, which we have provided here.

To see these plugins or to add your own, go into the plugins directory itself. Each plugin consists of a register subroutine that attaches the plugin to one of qpsmtpd's various hooks and another subroutine that is invoked by the hook. For example, the require_resolvable_fromhost plugin begins with the following:

use Net::DNS qw(mx);

sub register {
  my ($self, $qp) = @_;
  $self->register_hook("mail", "mail_handler");

In other words, the register subroutine tells qpsmtpd that whenever the SMTP client invokes the mail command, the mail_handler subroutine also should be invoked. That subroutine does the following:

sub mail_handler {
  my ($self, $transaction, $sender) = @_;

  $sender->format ne "<>"
    and $self->qp->config
    and !check_dns($sender->host)
    and return (DENYSOFT,
        ? "Could not resolve ". $sender->host
        : "FQDN required in the envelope sender"));
   return DECLINED;

If you have done any Web development in mod_perl, this should look somewhat familiar to you. mail_handler can return DECLINED, which indicates that everything is fine and the mail can go through. Or, it can return DENYSOFT, which allows the sender to try again later. This happens because we don't want to start rejecting mail when a DNS server goes down; we're interested in punishing only spammers and others who shouldn't be sending mail directly. We also can return DENY, which rejects the mail outright.

I was able to write a new, working plugin within a few hours of downloading qpsmtpd, despite the lack of good documentation, and I'm sure that many other readers will have similar experiences. The fact that qpsmtpd is written in Perl means you have fast, easy access to everything that a usual Perl program would, as well as any CPAN modules that could make development easier.

You can attach plugins to a number of different hooks, including helo, ehlo, connect and even rcpt—each of which can perform tests of various sorts. There's even a SpamAssassin plugin for qpsmtpd, which invokes the famous spam-checking software before the message arrives in your mailbox.

I have been using qpsmtpd for about a month, and the amount of spam in my mailbox has declined rather impressively, even from the low amount that SpamAssassin was letting through. If you run your own machine, I strongly encourage you to look at qpsmtpd. It is an excellent example of how to write software to take arbitrary plugins, and as a bonus, you will receive only the mail that you should receive.