Deliver Maildir

A C program to reliably deliver mail to a Maildir directory with commit and abort support.

Summary

This program will not deliver your mail if used in the most obvious fashion. There is a good reason for this, so please read read the section titled 'Instructions for use' carefully!

I wrote this program when I wanted to write a mail filtering program that could deliver to a Maildir directory. The mail filtering program might deliver to one of several different Maildir directories, and I wanted it to be able to delay the final delivery until the program was almost finished, and the mail mostly written to disk. So, I added a signalling channel using POSIX signals to allow you to signal the program whether or not you really wanted to deliver the mail or not.

This program attempts to adhere very strictly to the Maildir protocol.

Instructions for use

You start the program with an argument of which maildir you want to deliver to. Then, feed in the mail message on stdin. After that, wait for it to print 'initialized' on stdout (which should've happened while you were dumping in your message). And after that, you send it SIGUSR1 and close the pipe to stdin. Make sure you don't close the pipe until you've sent SIGUSR1. The SIGUSR1 signal acts as the 'commit' operation.

Here is what the program prints out as usage information if you don't give it exactly one command line argument:

Usage: delivermaildir <maildir>

Notes: This program will write out newline terminated line with the
word 'initialized' on stdout when it has set up signal handling.
After this happens, something should send a SIGUSR1 signal, then wait
for a newline terminated line saying 'delivery comfirmed' to be sent
to stdout before stdin of this program is closed.  If this doesn't
happen, this program will assume that the maildir delivery should be
aborted.

If you want, you can wait for the program to print 'delivery confirmed' on stdout after sending SIGUSR1. And, you can also send SIGUSR2 to abort delivery after sending SIGUSR1 to commit. All signals must be sent before the pipe is closed, and after sending each signal, you should wait for a 'delivery confirmed' or 'delivery aborted' message on stdout before sending another.

Why do it that way?

Here is the set of design constraints that I had to solve for. This is why the program must operate in this fashion:

  1. A Maildir delivery program needs to change its current working directory to the Maildir directory to be fully compliant with the spec.
  2. If a program might need to deliver to one of several different Maildirs, this means it must have more than one current working directory, which is an absurtity.
  3. So, the Maildir delivery program should be an external program that accepts the mail to be delivered on stdin.
  4. If it relies on EOF on stdin to decide when the mail is complete, it will deliver the mail, even though the original program may have unexpectedly quit, reporting an error status back to the MTA. This will cause delivermaildir to deliver the mail, and the MTA to deliver it again because it thought the original delivery failed, resulting in double delivery of the same mail.
  5. So, it must have a different signalling mechanism that a program can use to tell it that the email message can be delivered.
  6. Unix processes standardly only have one input file descriptor, and it's a huge pain to try to get various process launching mechanisms to let you have additional input file descriptors.
  7. So, the signalling mechanism cannot be a separate file descriptor.
  8. Signals are the only other sane option.

So, in order to convince this program to deliver your mail, you must send it SIGUSR1 before you close the pipe. But, you must be sure the program has set up signal handlers before you send it the signal, so you should wait for the string "initialized\n" to appear on stdout before sending it the signal. And, in order to be completely correct, you must send SIGUSR1 after you've written (and flushed) the last byte of the email message to the pipe, but before you've closed it.

Where's the code?

The current released version is 0.8. I will consider it to be 1.0 when it has a Makefile and has been autoconfiscated. There are example wrappers for bash and Python. I would like it if someone would write wrappers for perl and PHP.

Here's the 0.8 tarball: delivermaildir-0.8.tar.bz2

Here's a link to the main branch in a Mercurial repository: https://hg.omnifarious.org/~hopper/delivermaildir


Eric Hopper eric-www@omnifarious.org My other projects