Re: [exim] How to rewrite ${filter ...} expressions for olde…

Top Page
Delete this message
Reply to this message
Author: Ben Allen
Date:  
To: exim-users
Subject: Re: [exim] How to rewrite ${filter ...} expressions for older exims?
On Monday 13 December 2010 21:58:10 Michael Lampe wrote:
> The whole thing is this:
>
> -----
>
> # LDAP Router
>
> LDAP_USER="uid=mail,ou=System-User,dc=localhost"
> LDAP_PASS=xxxxxxxxxxxxxxxxxxxx
> LDAP_URL=ldapi://${quote_ldap_dn:/var/run/ldapi}
> LDAP_LOCALPART_ATT=mailLocalAddress
> LDAP_MAIL_GROUP=E-Mail
> LDAP_UID_QUERY=${lookup ldapm {user=LDAP_USER pass=LDAP_PASS \ 
> LDAP_URL/ou=People,dc=localhost?uid?sub?(LDAP_LOCALPART_ATT=${quote_ldap_dn
> :$local_part})}} 
> LDAP_GROUP_QUERY=${lookup ldapm {user=LDAP_USER
>  pass=LDAP_PASS \
>          LDAP_URL/cn=LDAP_MAIL_GROUP,ou=Groups,dc=localhost?cn?base?\
>                  (uniqueMember=uid=$item,ou=People,dc=localhost)}}

>
> ldap_user:
>    debug_print = "R: ldap_user for $local_part@$domain"
>    driver = accept
>    domains = +local_domains
>    local_parts = ! root
>    local_part_suffix = +*
>    local_part_suffix_optional
>    address_data = ${filter {<\n
> LDAP_UID_QUERY}{eq{LDAP_GROUP_QUERY}{LDAP_MAIL_GROUP}}}
>    condition = ${if >{${strlen:${filter {<\n
> LDAP_UID_QUERY}{eq{LDAP_GROUP_QUERY}{LDAP_MAIL_GROUP}}}} }{0}}
>    transport = pipe_delivery_cyrus
>    cannot_route_message = ldap_user: mail address $local_part does not
>  exist


Right then (apologies if you know this already):
- Based on what you've shown above LDAP_UID_QUERY looks up the uid(s) of
user(s) who's addresses match the current $local_part.
-The filter then basically checks if these uids are members of the "E-mail"
group.

There's a couple if strange things (someone correct me if I'm wrong about
anything):
- LDAP_GROUP_QUERY uses ldapm (multiple matches separated by newlines).
However, if there are in fact multiple matches (a uid is a member of more than
one group), then the filter's eq{}{} will fail to match and membership of "E-
mail" won't get the mail delivered. That leads me to think that there's only
one possible group in the ldap database, the E-mail group, making it (at least
partly) redundant (it could still be used to 'enable' or 'disable' a user's
address.

- An email address can map to more than one uid. I suppose it's one way to
do multiple recipients for role accounts etc. However, you would usually do
that sort of thing in a rewrite router, not directly in the delivery router.
It's easier (IMHO) to track issues that way.

- Since this is an accept router, and the transport is called
"pipe_delivery_cyrus" I'm guessing the transport uses the cyrus 'deliver'
command through a pipe. I'd like to take a look at it, since my guess is that
it uses $address_data to choose mailbox(es) to deliver to. The way the old
admin has written the command should show whether he/she thought it'd be
handling multiple deliveries for an individual message. Also check if
"batch_max" is set. If not (default is 1, i.e. individual deliveries) then
it's one user per mailbox and address, so the $filter isn't needed (or it's a
bug)

So, your options (in no particular order):
- If no users have the same email address you can get rid of ${filter}
completely. Switch to ldap (from ldapm) if you want and then change the
condition so that essentially LDAP_UID_QUERY replaces $item in
LDAP_GROUP_QUERY.

- If there IS the possibility of different uids having the same $local_part,
you should be able to get filter's behaviour by a bit of regex magic and a
"dynamic" LDAP_GROUP_QUERY. Given a newline separated list of uids from
LDAP_UID_QUERY, the following should (might) work as a drop in replacement for
LDAP_GROUP_QUERY:

LDAP_GROUP_QUERY=${lookup ldapm {user=LDAP_USER pass=LDAP_PASS \
    LDAP_URL/cn=LDAP_MAIL_GROUP,ou=Groups,dc=localhost?cn?base?\
    (|\
      ${sg{\
          ${tr{LDAP_UID_QUERY}{\n}{ }}}\
        {\N(\S+)\N}\
        {(uniqueMember=uid=\$1,ou=People,dc=localhost)}\
      }\
    )


Basically, it takes LDAP_UID_QUERY and strips out newlines, replacing with
spaces. The $sg then takes all the non whitespace character groups and wraps
them with some stuff to make an ldap query "OR" string. Thinking about it,
this may actually be faster than using $filter for a long list of uids, since
no matter how long the list, only one lookup is ever done. Is there a maximum
string length in exim? Doubt it'd be a problem in this case, but who knows.
Note that some of the above might need ${quote_ldap:}ing and stuff. I've not
tested it in an actual lookup, but the expansion works.

- Use a perl function/external program/something else to do the splitting
for you.

That should give you some ideas. I would advise however that (as Bill said),
you need to test this sort of thing very thoroughly if it's a production
environment. Also, give us a gander at the transport.

Finally, don't assume that the way the configuration is at the moment is the
correct way to do things (it may appear to be working, but just give it a
while, I guarantee that bugs will appear). If you can give us the details that
Bill asked about, I'm sure we'll have some ideas.

Hope that helps.

Ben