On 2006-07-14 11:22:37 -0600, Bryan Scott wrote:
> 2) For any part of the system not already created, I want the design of new 
> pieces to be modular enough that others find them useful

Same here. Unfortunately the qpsmtpd community seems to be quite
affected by the NIH (Not Invented Here) syndrome. Quite a bit of
functionality get reinvented over and over again.


> I have talked about it before on the list, and spurred various discussions, 
> but I don't recall coming to a consensus on the "best way" or official 
> qpsmtpd way to do it.  I would hope we're at a point (or can get to one) 
> where much of the resulting work could be contributed back to the main 
> distribution.
> 
> Ok, so here goes.
> 
> First off is the per-user configuration goal (again).  Some users complain 
> of too much filtering, others complain of not enough.  Site-wide bayesian 
> filters aren't doing the trick, and some people deal with foreign countries 
> more than others do.  So I want to get where users can pick and choose 
> various filter (i.e. plugins) to use, and even per-user config of said 
> plugin (where appropriate).  I know Gavin has this implemented to some 
> degree, but his site's per_user_config is at 0.28, and we're running 0.31 
> (rc2?).

There are two problems involved:

1) Passing per-user config. There are many plugins which need a per-user
   config, and there are many ways to store such a configuration (one or
   many files, LDAP, RDBMS, ...). If there is a common way to pass
   user config data between plugins we need only n+m instead of n*m
   plugins.

2) A message may be sent to more than one recipient in a single
   transaction, per-user configuration may cause the need for such a
   message to be rejected for some users but accepted for others -
   unfortunately SMTP doesn't allow that.

I have developed solutions for both problems. You can find the plugins
in the contrib directory or on my website. I have described them before,
but as they don't fit together very well, I will describe them again and
propose a IMHO better solution for the first problem (which needs
changes in the core, though).

1) Using transaction notes for per-user config:

    The basic idea here is to use one transaction note (recipient_options)
    to store options for the *current* recipient. Typically the first
    plugin with a rcpt hook sets the note (e.g., by looking up the
    per-user config in a file, from LDAP or whatever) and the other
    plugins can use the options in their rcpt hook. For example, I
    needed to enable greylisting on a per-user basis, so my version of
    the greylisting plugin contains the lines in the recipient hook:

        my $ro = $transaction->notes('recipient_options');
        return DECLINED unless ($ro && $ro->{denysoft_greylist});

2) splitting multi-recipient transactions with temporary failures:

    The problem can easily and trivially be solved by accepting only one
    recipient per transaction and returning temporary failures for all
    others. Unfortunately, this is not practical: A message may have
    dozens or even hundreds of recipients and most MTAs wait minutes or
    hours before retrying after a temporary failure: So messages with
    many recipients would be extremely delayed. The situation can be
    improved by assuming that the final status will be the same for all
    recipients (either all will accept or reject it) and generating a
    temporary failure if that turns out not to be the case. When the
    client MTA retries, the result is already known, and temporary
    failures can be returned at the RCPT stage for those recipients
    which don't have the same result as the first.

You can see the problem: The recipient_options transaction note is only
useable in the rcpt hook, while for content filters you need the config
in the data_post hook. It is easy to add a rcpt_hook which just saves
away the relevant option for later use in the data_post hook, but I
believe there is a more elegant solution:

Addresses in Qpsmtpd are not simple strings, they are objects of class
Qpsmtp::Address. We can add a method 'config' or 'notes' (or both) to
this class. So the snippet from denysoft_greylist would become:

    return DECLINED unless $rcpt->config('denysoft_greylist');

and - since the same object is returned by $transaction->recipients -
you can use it the same way in the data_post hook:

    for ($transaction->recipients()) {
        if ($_->config('check_bayesian')) { ... }
    }

There would probably have to be an additional hook (hook_address_config)
for consistency with 'global' config.

Comments?

        hp

Reply via email to