Thursday, December 17, 2009

Create a Custom MDA with Postfix and Perl

Wow, this was a fun one. We have an internal "project manager" that we use at work, instead of my prefered program, RT. The other day, my boss asked me to set up this program so that they could email tasks to it, instead of having to pull up the site to create a new task. The easy part was building the queue into our system. But the fun part was setting up Postfix to receive and parse the emails.

My first thought was to set up Procmail to send the messages to my parsing script. I'd never used it before, and I'd heard horror stories about writing "recipes" in it. What I had not heard was how difficult it could be to get it to play right. The mail server that I was using was not one that I had set up, and it had some weirdness about it that I wasn't familiar with. After fighting with Postfix and Procmail for a while, I managed to learn enough about Postfix configuration to realize that I might as well just skip Procmail, and write my own MDA.

Now, when I say MDA, it's a bit of a misnomer. It receives and parses emails, but rather than filtering and delivering emails to a specific mailbox, it dumps a few fields into a database. To avoid confounding the issue too much, I will try and keep this post to the bare minimum. My setup uses virtual mailboxes, but I won't go into the steps to set that up. I also won't cover the DBI code that I wrote. If I get enough requests, maybe those can go into other posts.

First things first. You need to edit the mail.cf file to set up some transports. There were two specific lines that I needed to add to mine:

virtual_transport = virtual
transport_maps = hash:/etc/postfix/transport

This allows me to only send messages sent to specific email addresses to my MDA. So the next step is to add the addresses to /etc/postfix/transport that you want forwarded to your MDA:

tasks@mytaskmanager.com mymda
projects@mytaskmanager.com mymda

Make sure to hash the file once you've edited it:

postmap /etc/postfix/transport

You've probably guessed that "mymda" is what you're going to call your MDA. This doesn't have to be the name of your script, it's just a pointer to the lines that you're about to add to your master.cf file:

mymda unix - n n - - pipe
flags=R user=vmail argv=/usr/local/bin/mymdascript.pl USER=${user} EXTENSION=${extension}

You can see in here where we actually define the name of your script, in this case "/usr/local/bin/mymdascript.pl". Now that we're done with the Postfix configuration (remember to restart postfix for it to take effect), we can go ahead and set up that script. It's going to look something like this:

#!/usr/bin/perl

use Mail::Internet;

my @rfc2822 = <STDIN>;
my $email = Mail::Internet->new( [ @rfc2822 ] );

my $from = $email->head->get("From");
my $date = $email->head->get("Date");
my $subject = $email->head->get("Subject");
my $body = $email->body();
$body = join( '', @$body );
...snip...

This is a very, very basic script. It will receive the email from Postfix using STDIN, and to save you the trouble of parsing it out manually, I just ran it through Mail::Internet (part of the MailTools package).

Keep in mind that each line that you pull out of the message will have a newline in it, so $from, $date, $subject, etc. may need to be chomped, depending on your needs. Also, the date is hopefully in RFC2822 format, so in my case, I had to run it throught DateTime::Format::Mail to get it ready for MySQL.

Like I said, this isn't a full MDA. But it can be used for accepting things like commands, preformatted data, etc. from email and processing them, without having to deal with the overhead of something like Procmail. And if you want to use it to write a full-featured MDA, by all means feel free. And really, now that you know that the script is going to pull the message from STDIN, you're free to use C, Python, even Bash if you want.

No comments:

Post a Comment

Comments for posts over 14 days are moderated

Note: Only a member of this blog may post a comment.