(slightly edited mail to include the relevant references from
previous messages)

Date: Wed, 09 Oct 2002 18:42:07 +0200
From: Martijn Grendelman <martijn@grendelman.net>
To: Mark Martinec <Mark.Martinec@ijs.si>
Message-id: <053601c26fb2$ce9135c0$270a010a@ilsead.net>

From: Martijn Grendelman
> The problem occurs when I send a single message to different addresses that
> are handled by the same server. For example, martijn@grendelman.net is my
> own, valid address and gekkehenkie@grendelman.net is a non-existent address
> in the same domain.
>   When amavis tries to deliver the message to the second instance of Exim
> (at port 10025), the mail _as a whole_ gets rejected, because Exim will not
> accept _one of the recipients_. Therefor, the firs Exim (at 25) rejects the
> message for all recipients as well. Here are some logs to illustrate:

From: Mark Martinec
| ... There is the third option. I don't know how this is with Exim,
| but in the Postfix setup this comes by default and is the reason
| why there are not more complaints like yours: the first MTA instance
| should be able to check the validity of recipient address.

From: Martijn Grendelman
> I _think_ there is a way to do this with Exim too, but it requires
> a little more hacking in the config file. ... My idea is to duplicate
> each router, add an incoming port condition and change it to send
> the message to amavisd instead of the original destination if the
> condition is met. ... This way, messages will be sent to amavisd
> only after a router is matched, and this is what we want.

Well, here it is... I decided to be verbose, I hope you don't mind. Feel
free to use this in any README's as you see fit.  It's not perfect yet, I
will explain below. First let me tell you what I did. The filosophy is, that
I only want those routers that perform an actual delivery to pass the
message to amavisd. Routers that redirect (like system_aliases and
userforward) will eventually route the message through a delivering router
later, so scanning will take place when that happens.

The only problem (the imperfection) is with pipes and appending to files,
because in those cases, no other router will be used. This means if you use
a pipe or try to append to a file from the aliases file or from a .forward
file (i.e. procmail!), no scanning will take place. At this time, I don't
have a solution for this yet, and by reading around the Exim docs a bit, I
know there can't be an easy one.

I think I should mention that I use Exim for "real" as well as virtual
mailboxes, using a MySQL backend.

The first router, at the top of the list is:

amavis:
  driver = manualroute
  domains = ! +local_domains
  condition = "${if or {{eq {$interface_port}{10025}} \
                        {eq {$received_protocol}{spam-scanned}} \
                       }{0}{1}}"
  route_list = "* localhost byname"
  transport = amavis
  self = send

This one takes care of all non-local domains, i.e. mail that is routed to a
remote server later. Since Exim will always accept and queue messages with
remote destinations, the problem of amavisd rejecting valid addresses does
not occur.

Note that "+local_domains" means the use of a variable of type "domainlist",
defined earlier like this:

LOCAL_DOMAINS = localhost : mydomain.com : myotherdomain.com
VIRTUAL_DOMAINS = mysql;MYSQL_DOMAIN
domainlist local_domains = LOCAL_DOMAINS : VIRTUAL_DOMAINS

MYSQL_DOMAIN is, in its turn, a variable containing an SQL query for
retrieving domain names from the database, but let's not go there ;)

The second new router is inserted just before the "localuser" router, that
matches for local (unix) mailboxes:

localuser_amavis:
  driver = manualroute
  domains = LOCAL_DOMAINS
  condition = "${if or {{eq {$interface_port}{10025}} \
                        {eq {$received_protocol}{spam-scanned}} \
                       }{0}{1}}"
  route_list = "* localhost byname"
  check_local_user
  transport = amavis
  self = send

This one matches only the real (non-virtual) local domains, and it checks
the local part of the recipient with the "check_local_user" directive. The
message only gets passed to amavisd, if the local user exists.

The third (and last) new router, is the one that matches virtual local
users. It is inserted just before the router that handles the actual
delivery to those users (included here just to illustrate, since it's not
standard Exim configuration; most people won't need this):

virtual_localuser_amavis:
  driver = manualroute
  domains = ${lookup mysql {MYSQL_USER}{$value}}
  condition = "${if or {{eq {$interface_port}{10025}} \
                        {eq {$received_protocol}{spam-scanned}} \
                       }{0}{1}}"
  route_list = "* localhost byname"
  retry_use_local_part
  transport = amavis
  self = send

virtual_local_user:
  driver = accept
  domains = ${lookup mysql {MYSQL_USER}{$value}}
  retry_use_local_part
  transport = virtual_delivery

The only difference with the earlier routers is the way the local part gets
checked. In this case, a MySQL query is used to determine the matching
domains, and the actual query makes sure that no domains match if the local
part is invalid.

I hope this is helpful in some way.

Best regards,
Martijn.
