> 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