Tue, 22 May 2007

Archiving Mail with Exim

I was recently asked to configure Exim to archive all mail sent and received by certain customers. Users authenticate to send mail using their email address so I used a domainlist to specify which domains' users should have their mail archived.

domainlist archive_domains = example.com

I created two routers and a transport for handling the mail sent by authenticated users. The first is a redirect router which rewrites the recipient address to a special address containing the sender's address, e.g. _!%#archive#%!_-user@example.com. This router has the unseen option set so the message is routed to the original recipient as usual. This doubles the number of recipients, but Exim discards duplicates so the final recipients are the original recipients plus the sender's archive copy. The second router strips the _!%#archive#%!_- prefix and delivers to the message to the sender's archive mailbox using a special transport.

These routers should probably be the first two since you don't want another router to accept delivery of the message first.

archive_by_sender_rewrite:
  driver = redirect
  condition = ${if and { {def:authenticated_id}{match_domain{${domain:$authenticated_id}}{+archive_domains}} }{yes}{no}}
  data = _!%#archive#%!_-$authenticated_id
  unseen
  no_repeat_use
  no_verify

archive_by_sender:
  local_part_prefix = _!%#archive#%!_-
  driver = accept
  no_verify
  transport=archive_by_sender

Because $authenticated_id is used to get the sender's address, you should have server_set_id = $1 in your authenticators so the variable gets set.

The router to archive recieved mail is pretty simple. It uses the unseen option again to create a copy of the message, and like archive_by_sender uses a separate transport to archive the message. This router should be placed before any routers that accept mail for the +archive_domains. If you use routers to discard or quarantine spam, this one should be before those if you want to archive the spam received.

archive_by_recipient:
  driver = accept
  domains = +archive_domains
  unseen
  no_verify
  transport=archive_by_recipient

Here are the transports. The messages are written to maildir directories. Any missing directories will be created if Exim has permission to create them.

archive_by_sender:
  driver = appendfile
  maildir_format
  mode = 0600
  mode_fail_narrower = false
  envelope_to_add = true
  return_path_add = true
  create_directory
  directory = /path/to/archive/$domain/$local_part/sent

archive_by_recipient:
  driver = appendfile
  maildir_format
  mode = 0600
  mode_fail_narrower = false
  envelope_to_add = true
  return_path_add = true
  create_directory
  directory = /path/to/archive/$domain/$local_part/received

If you don't have the default rule in your rcpt acl to reject local parts contains %, !, etc., you should make sure you don't accept mail for the special archive user address. Safeguarding against malicious users with shell access is left as an exercise for the reader. (Hint: I would probably look at $received_protocol.)

tech » mail | Permanent Link

The state is that great fiction by which everyone tries to live at the expense of everyone else. - Frederic Bastiat