When I was designing my anti-SPAM filter, I noticed you're just running a series of code after each other. To keep everything nice and tidy I split up the various routines into small pieces of code designed to do one thing and then pass on to the next filter. My project has the codename "redsky". Yes, it's cliché but I couldn't think of anything else.
By itself, redsky is pretty useless. It's only capable of running a series of filters after each other. The real power is in the filters itself. The 3 filters that come shipped with redsky are designed to interface with another piece of software called proxsmtpd (but can be used in other ways as well, they just use STDIN and STDOUT), but there's no reasons you could write your own input/output filters and make redsky a mini-SMTP server.
Here are the packages that are of interest:
* phpemailtoolbox (http://aur.archlinux.org/packages.php?ID=22237)
This is a small set of email related functions and classes. It is required by redsky.
This is the main redsky code. It contains three filters:
* redsky_read: this reads the email in question from STDIN. It is designed for interfacing with proxsmtpd, but can be used in a generic manner as well.
* redsky_discard: when the discard flag is set on an email, this filter will log this event.
* redsky_out: this outputs the email to STDOUT, again designed to interface with proxsmtpd but is suitable for general purpose as well. In the mailinglist
* redsky-filters (http://aur.archlinux.org/packages.php?ID=24393)
This is a set of complementary filters for redsky, it has for example SMTP blacklist checking, whitelisting, autoreplies, etc... They are mainly designed to use in a postfix content scanner context. Only the redsky_sqlconn filter is required for mailinglist manager.
* mlm-filters (http://aur.archlinux.org/packages.php?ID=24399)
This is the most interesting set of filters. Mlm stands for Mailing List Manager. There's a queueing part and a sending part.
For the queueing part:
* mlm_getinfo: Fetch information for a certain list using SQL
* mlm_loopdetect: Check if a message is looping or not
* mlm_bouncehandler: If the message received was sent to a bounce address, store it in an SQL table
* mlm_getsubscribers: Get the subscribers from SQL for the current list
* mlm_access: Determine if a sender has access to the list. It currently supports 4 methods: use an ACL, only allow subscribers, require a passphrase or no limitations
* mlm_subjecttag: Tag the subject if the mailinglist is configured for that
* mlm_addfooter: Add a footer to the emails if the mailinglist is configured for that
* mlm_queuer: Write a spoolfile and put all the recipients into an SQL table
For the sending part:
* mlm_singleinstance: Only allow a single instance of redsky to be run
* mlm_queuebuilder: Build a queue of messages to be sent and their respective recipients
* mlm_queuerunner: Process the earlier built queue using sendmail to send messages
redsky is written to support multiple configurations. In the MLM case, you would have two configuration files (examples are shipped with mlm-filters), one for the queuer and one for the sender. The queuer would be called by the MTA while to sender could be executing with a cronjob. Use -c to supply an alternate config, for example:
redsky -c /etc/mlm.queuer.config.php
You will notice that the redsky-filters and mlm-filters come shipped with example configuration files. You will find them in /usr/share/php/redsky/. Some filters depend on each other, so it is important in which order they are executed. For example, the mlm_access cannot determine if the sender is a subscriber when mlm_getsubscribers hasn't run first, because it uses the information from mlm_getsubscribers for this.
Now maybe the hardest part, integration with an email server. There's a small how-to, README.postfix, in the source tarball of mlm-filters.
To allow the messages to be distributed, just call /usr/bin/redsky -c /etc/redsky.runner.config.php
Ok, all this information was probably hard to swallow. I will try to answer any questions you have regarding the implementation. If you want to ask me a direct question you can find me on IRC, freenode with the nick RedShift. I am in #archlinux.
It's also easier if you start by testing it out directly from the shell instead of immediatly integrating it into your MTA.