diff --git a/docs/hooks.pod b/docs/hooks.pod index 3dd7b5a..3c7b6f6 100644 --- a/docs/hooks.pod +++ b/docs/hooks.pod @@ -732,6 +732,35 @@ is called. It's probably best not to try acessing it. Example plugin is F from the qpsmtpd distribution. +=head2 hook_user_config + +Called when a per-user configuration directive is requested, for example +if someone calls Cconfig($cfg_name);>. +Allowed return codes are + +=over 4 + +=item DECLINED + +plugin didn't find the requested value + +=item OK + +requested values as C<@list>, example: + + return (OK, @{$config{$value}}) + if exists $config{$value}; + return (DECLINED); + +=back + +Arguments: + + my ($self,$transaction,$user,$value) = @_; + # $value: the requested config item(s) + +Example plugin is F from the qpsmtpd distribution. + =head2 hook_unrecognized_command This is called if the client sent a command unknown to the core of qpsmtpd. diff --git a/lib/Qpsmtpd.pm b/lib/Qpsmtpd.pm index 9ef1056..2e8c675 100644 --- a/lib/Qpsmtpd.pm +++ b/lib/Qpsmtpd.pm @@ -148,14 +148,17 @@ sub config { # first try the cache # XXX - is this always the right thing to do? what if a config hook # can return different values on subsequent calls? - if ($_config_cache->{$c}) { + my $is_address = (ref $type and $type->can('address')); + if ($_config_cache->{$c} and ! $is_address) { $self->log(LOGDEBUG, "config($c) returning (@{$_config_cache->{$c}}) from cache"); return wantarray ? @{$_config_cache->{$c}} : $_config_cache->{$c}->[0]; } # then run the hooks - my ($rc, @config) = $self->run_hooks_no_respond("config", $c); + my @args = $is_address ? ('user_config',$type,$c) : ('config',$c); + my ($rc, @config) = $self->run_hooks_no_respond(@args); + return wantarray ? @config : $config[0] if $is_address; $self->log(LOGDEBUG, "config($c): hook returned ($rc, @config) "); if ($rc == OK) { $self->log(LOGDEBUG, diff --git a/lib/Qpsmtpd/Address.pm b/lib/Qpsmtpd/Address.pm index a0f6b50..5e25813 100644 --- a/lib/Qpsmtpd/Address.pm +++ b/lib/Qpsmtpd/Address.pm @@ -338,6 +338,19 @@ sub notes { return $self->{_notes}->{$key} = shift; } +=head2 config($value) + +Looks up a configuration directive based on this recipient, using any plugins that utilize +hook_rcpt_config + +=cut + +sub config { + my ($self,$key) = @_; + my $qp = $self->notes('qp_obj') or return; + return $qp->config($key,$self); +} + sub _addr_cmp { require UNIVERSAL; my ($left, $right, $swap) = @_; diff --git a/lib/Qpsmtpd/Plugin.pm b/lib/Qpsmtpd/Plugin.pm index 5dde02c..7f1cc80 100644 --- a/lib/Qpsmtpd/Plugin.pm +++ b/lib/Qpsmtpd/Plugin.pm @@ -9,7 +9,7 @@ use Qpsmtpd::Constants; # more or less in the order they will fire our @hooks = qw( - logging config post-fork pre-connection connect ehlo_parse ehlo + logging config user_config post-fork pre-connection connect ehlo_parse ehlo helo_parse helo auth_parse auth auth-plain auth-login auth-cram-md5 rcpt_parse rcpt_pre rcpt mail_parse mail mail_pre data data_headers_end data_post queue_pre queue queue_post vrfy noop diff --git a/lib/Qpsmtpd/SMTP.pm b/lib/Qpsmtpd/SMTP.pm index c6db8a5..a53abec 100644 --- a/lib/Qpsmtpd/SMTP.pm +++ b/lib/Qpsmtpd/SMTP.pm @@ -396,6 +396,7 @@ sub mail_pre_respond { } return $self->respond(501, "could not parse your mail from command") unless $from; + $from->notes('qp_obj',$self); $self->run_hooks("mail", $from, %$param); } @@ -485,6 +486,7 @@ sub rcpt_pre_respond { return $self->respond(501, "could not parse recipient") if (!$rcpt or ($rcpt->format eq '<>')); + $rcpt->notes('qp_obj',$self); $self->run_hooks("rcpt", $rcpt, %$param); }