The cf_wrapper plugin provides a framework for content filters which may reject or accept a mail for different recipients.

Normally this is not possible as SMTP (unlike LMTP) returns only one result for the DATA command, so it is not possible to return per-recipient accept/reject information. The cf_wrapper plugin get around this restriction by splitting the space of sender/recipient pairs into sets for which it expects the same behaviour. During the RCPT phase only recipients from the same set are accepted, all others are temporarily rejected. So during the DATA phase all sender/recipient pairs are from the same set and the content filter(s) will probably either accept or reject the mail for all recipients. If they don't, the minority of recipients is moved to a new set and the message is temporarily rejected. At the next delivery attempt only a smaller set for which an unanimous result has already been reached will get to the DATA phase, so the chance that the mail will be accepted or permanently rejected is now almost 1. At the worst case, the set must be split until only one recipient per transaction is left.

A content filter plugin which wants to use cf_wrapper must return DECLINED and store its results in the transaction note cf_wrapper_results.

This plugin uses a table in a relational database to store the set information. I am using mysql, but any database supported by DBI should work. SQLite is probably a good choice unless you already run an RDBMS.

As a demo, a simple plugin check_cookie is provided which checks if the mail contains a cookie specified by the recipient.

I have also written a plugin majordomo which checks whether a recipient is a closed majordomo list and if that is the case if the address in the From: header is allowed to post to the list.