Basic approach ============== Every message that comes into, or goes out of, mailman will go via this fixup system. Once on the way in, and once on the way out. On input, the fixup system will squirrel away a copy of the message. On output, the fixup system will replace the message with the saved copy. The fixup system identifies the messages by a unique ID which it puts into the message header on input. Plumbing ======== The fixup is designed to be plubmed into Exim. There will be two routers to divert incoming and outgoing messages. On input: we modify MAILMAN_WRAP in the exim4 configuration, to .../path/to/outflank-mailman/input /var/lib/mailman/mail/mailman so that exim runs our script as a wrapper. On output: mailman likes to send mail by SMTP to localhost. We change mailman to connect to a dedicated localhost IP address. All messages that * Have an Outflank-Mailman-Id header * Were received over SMTP at that localhost IP address are diverted to a pipe transport to .../path/to/outflank-mailman/output -f To avoid any funny business, all messages that * Have an Outflank-Mailman-Id header * Were *not* received over SMTP at the special localhost IP address are made undeliverable. This ensures that all Outflank-Mailman-Id headers in the system are precisely those generated by the input script and passed through mailman. Operation ========= outflank-mailman/input Generates a new id value for Outflank-Mailman-Id Reads the input message Saves the input message in the database under the id Calls the real mailman and feeds it A new Outflank-Mailman-Id header A new Received header The input message (eg using tmpfile) outflank-mailman/output Reads the input message, consults the database, and calls exim (as sendmail) again to actually deliver the output message. In call cases, Outflank-Mailman-Id is deleted from the input message, and an additional Received header is added to the output (which mentions relevant details including the id). If there is no List-Post header: simply copies the input to the output. If there is a List-Post header but the id mentioned in the Outflank-Mailman-Id is not found in the database, copies the input to the output but also adds a warning in "Outflank-Mailman-Warning:" Otherwise, combine the input with the message data from the database, as follows: * Received headers: use from input (adding our own) * Precedence, Errors-To: use from input unless found in database copy, in which case use database copy * List-*, Delivered-To, X-BeenThere, first, those from input, then any from db * Other headers: use from database copy * Body: from database In added Received header, put a comment saying the message was restored to the version received by mailman. Save the input in a debugging table in the database. Database and logging ==================== sqlite3. Two tables: id => timestamp, incoming message data id =>* sub-id, timestamp, outgoing message data outflank-mailman/output runs exim with one of -oMt outflank-mailman//not-post -oMt outflank-mailman//not-found -oMt outflank-mailman// and this will then appear in the exim4 mainlog. Old entries (>28 days?) in the db are deleted periodically by a cron job. cron job should run every 1/10th-1/30th the expiry period. db has no index on timestamps.