2004-11-06

Filter Your Mailing Lists With qmail + maildrop

maildrop makes it easy to filter your incoming mail. I've already told you about how to filter your mail according to the envelope recipient. What this means is that rather than subscribing to a dozen mailing lists with one address and filtering every message by the "From:", "To:", and "Cc:" headers, you subscribe to a dozen mailing lists with a dozen different addresses and use dot-qmail files to write them to their appropriate Maildirs.

You are using Maildirs, aren't you?

qmail handles Maildirs and user-controlled address extensions like a champ. If you own alice@local.server and you wish to subscribe to the mailing list "Lemons@some.mail.server.somewhere", you may configure a new mail address simply by typing:

    $ echo "./Maildir/" > ~/.qmail-12345-lemons

Now you may give the Lemons list your new address: "alice-12345-lemons@local.server". qmail will deliver new messages directly to "./Maildir/". Why is this useful? Couple-a reasons:

  1. "12345" isn't just a combination that an idiot would use on his luggage. It's a cookie that will make it harder for spammers to guess your address. "12345" isn't very strong, but what if you replaced it with "e93b85ce8efbdc547592225101f9e060"? Yeah, I thought so.
  2. You don't need to deliver all mail to "./Maildir/". You can create "./Maildir/.Lemons-list/" and put that in your dot-qmail file instead. This requires zero additional filtering steps. You've made a new address and you've made a new Maildir. Your filtering is done.

For a time, I ran my mail this way, and all was good. Until, of course, the time came that I needed to filter my mailing lists, too. Now, there's a good reason why I haven't wiped maildrop off my server, and this is why. maildrop can use one of multiple different filter files you present to it. By default, maildrop looks for "/etc/maildroprc" and "~/.mailfilter", but you can pass it any filter filename you wish inside a dot-qmail file. More on that later on.

The reason I want to filter some of my mailing lists is because of spam of the most vile nature: clueless users. I can single out a couple of people who are so clueless about darn near everything they touch that reading their posts, and the followups to their posts, and their followups to their followups, makes me wish for the day when computers can stab people in the face over TCP/IP.

So let's say I'm subscribed to the Lemons list as "12345-lemons". With the old way, my ~/.qmail-12335-lemons file contained the line "./Maildir/.Lemons-list/". That's insufficient for filtering the content of mailing list messages because qmail will simply write the new message to the Maildir and call it a day. I need to make qmail pass the message to maildrop, so that maildrop can look it over, deem it safe or unsafe, and either deliver it or trash it. Delivering and trashing are two actions that differ simply by whether the message is written to "/dev/null" to "./Maildir/.Lemons-List/".

So I first must make a new mail filter. I'll name it after my nemesis and call it "~/.mailfilter-no-bob". It looks like this:

    # log all activity to a file
    logfile "./.droplog"
    
    # Gotta love 'im. And by love I mean hate.
    if (/Bob/:b)
     to /dev/null
    
    if (/^Delivered-To:.*12345-lemons@mailinglists\.my\.server/:h)
     to "./Maildir/.Lemons-list/"
    if (/^Delivered-To:.*54321-limes@mailinglists\.my\.server/:h)
     to "./Maildir/.Limes-list/"
    
    # default: everything else goes to the inbox
    to "./Maildir/"

This filter logs to the file ".droplog", scans the body of every mail message for the literal string "Bob", and writes the message to /dev/null if it finds anything. Otherwise, it assumes the message is clean and tries to figure out where to write the message. I could write a separate mailfilter for each list: ~/.mailfilter-no-bob-lemons and ~/.mailfilter-no-bob-limes and so on. Instead, I'd rather narrow it down to one place that looks for messages from Bob and then a series of branches to direct the safe messages to the appropriate Maildir. For this I like to use the contents of the "Delivered-To:" field. Be aware that a message can contain more than one "Delivered-To" field. Some MTAs don't even use the "Delivered-To:" header at all!

Our new filter next needs a default store, someplace to deliver any mail that may slip through holes we might have inadvertently left in it. We provide that with an extra, unconditional 'to "./Maildir/"' line. maildrop demands filter files be non world-readable, so run "chmod 0600 ./.mailfilter-no-bob" and you'll be ready to finally put your filter into production.

Ordinarily, the contents of your dot-qmail file would be a Maildir location. You may now remove that line and replace it with a call to your new maildrop mailfilter:

./Maildir/Lemons-list

becomes

|preline maildrop ./.mailfilter-no-bob

The dot-qmail file for the Limes list can now look exactly the same as the dot-qmail file for the Lemons lists. Since maildrop is in charge of delivery now, qmail doesn't need to know anything more than to pass it through preline and hand it to maildrop with the given filter file. The rest is history: I haven't been getting any junk messages from people who annoy me, and the extra effort of penning a short little filter is completely worth it.

No comments: