> so inside QP, the qpsmtpd::address object would have a known parameter
> that brings up the per-address persistence hash, which would be a flat
> hash. Something like
..

See in my mind, per-recipient config and persistent data storage are more
separate.  Maybe part of the reason I look at it this way is that in my
own implementation, I never really write "config" for a recipient, I only
read it (from my persistent storage, the db, of course).  I don't see it
necessary to be able to say $rcpt->config( spam_threshold => 10 ) from QP,
I'd do it from the UI).  "Stored" things are always written from QP
(logging) and sometimes read (auto whitelist ).  A hook_user_config plugin
would even be likely to make use of the persistent storage itself, but I
still see the concepts as split, and you could implement and benefit from
either one without the other.  When I went to write a reference plugin for
hook_user_config, I just thought of one where an admin can just drop
config for users into directories on the fs in the event that he wants to
override what's already set on the global level; I think the hook
structure even fell through to $qp->config() so it could really just begin
with an extension of /etc/qpsmtpd and go from there, just like
$qp->config() does.

That said, for persistent storage I would like to see a more
straight-forward API and skeleton API; something like:  $txn->get,
$txn->store, $rcpt->get, $rcpt->store, and a corresponding hook_get and
hook_store that are passed ( $self, $txn, [ $rcpt ] ) so it knows what to
key on, though it can even ignore it if it wants.  Extra points if QP
provides a
safe-but-reusing-connections-appropriately-depending-on-the-forking-model
DBI handle via $self->qp->dbh, and maybe a $self->qp->cache() for a
'persistent' Cache::FastMmap or Memcached cache, but the plugin could be
required to establish the actual data store for itself.  Then the plugin
goes to town.  It can store everything in a generic way, key => value or
whatever, or it could map the key names to your own business logic.  The
reference persistent storage plugin could still implement the type of
store you're talking about, and that would work out of the box for people
who just want to have greylisting out of the box or etc; but if I'm
reading it correctly, even if it's really awesome it's likely a lot of
developers would just have to scrap it in favor of what they're already
doing, at which point the more flexibility hook_store plugins have the
better.

# in stock upstream plugins/greylist
    $rcpt->store( greylist => $self->qp->connection->remote_ip );

# in our internal storage plugin that overrides the generic one
# I don't think anyone would actually want this in particular :)
sub init { shift->{dbh} = $dbi->connect(...) }
sub dbh { shift->{dbh} }
sub hook_store {
    my ( $self, $txn, $rcpt, $conf, $value ) = @_;
    return DECLINED unless $conf eq 'greylist';
    return OK unless $rcpt->notes('user_uid');
    my $sth = $self->dbh->prepare_cached(
        "INSERT INTO bobsgreylist (ip,helo,rcpt) VALUES (?,?,?)" );
    $sth->execute( $value,
                   $self->qp->connection->hello_host,
                   $rcpt->notes('user_uid') );
    return OK;
}

Unlike per-recip config, though we don't have any API etc. written
in-house to support generic persistent storage writing, for now we just
stick our DBI directly in our plugins, making them even more forked; so
this is all purely theoretical and David has the advantage of speaking
from some experience :)

-Jared

Reply via email to