On Fri, 17 Mar 2006 14:07:35 -0800
Ask Bjørn Hansen <[EMAIL PROTECTED]> wrote:
> I'd like to see a patch that makes a better API for reading the
> command and the command parameters. The default can still be having
> it space separated and provided in @_, but the hacks in mail() and
> rcpt() to read the rest aren't very nice so we should have an API to
> make it neater.
Ok, here we go.
The Qpsmtpd::Command module provides a parse() which does 3 things:
- by default returning the line split()ed by space, as before.
- on MAIL FROM:/RCPT TO: it parses the line by RFC 1869 (SMTP Service
Extensions) rules
- a plugin can do the parsing on it's own (...read: for all other
plugins/the core) by supplying a parsing routine when returning from
a _parse hook, see EXAMPLE in perldoc lib/Qpsmtpd/Command.pm
The rcpt and mail hooks now get a %param where all parameters from the
client are listed in lower case, i.e.
sub hook_mail {
my ($self, $transaction, $sender, %params) = @_;
if ($params{'body'} && uc($params{'body'}) eq 'BINARYMIME') {
...
> To add to the bikeshedding, I don't think it should be configurable
> -- it'd be fun to require them and see how it goes. If it doesn't
> work well, then just make them optional again and a rcpt_pre plugin
> can make them required for those who want that. Options in the core
> are bad, generally speaking.
This [accepting addresses without <>] is now a plugin
("dont_require_angelbrackets") which is called in the new "rcpt_pre"
hook.
Comments, whishes, ...? :)
Hanno
diff -Nurx .svn ../0.3x/config.sample/plugins ./config.sample/plugins
--- ../0.3x/config.sample/plugins 2006-01-13 07:28:49.000000000 +0100
+++ ./config.sample/plugins 2006-04-02 09:55:06.000000000 +0200
@@ -12,6 +12,13 @@
# from one IP!
hosts_allow
+# enable to accept MAIL FROM:/RCPT TO: addresses without surrounding <>
+# dont_require_angelbrackets
+
+# enable to reject MAIL FROM:/RCPT TO: parameters if client helo was HELO
+# (strict RFC 821)... this is not used in EHLO ...
+# parse_addr_withhelo
+
quit_fortune
check_earlytalker
diff -Nurx .svn ../0.3x/lib/Apache/Qpsmtpd.pm ./lib/Apache/Qpsmtpd.pm
--- ../0.3x/lib/Apache/Qpsmtpd.pm 2005-11-20 10:52:17.000000000 +0100
+++ ./lib/Apache/Qpsmtpd.pm 2006-04-02 09:55:06.000000000 +0200
@@ -131,7 +131,7 @@
while (defined(my $data = $self->getline)) {
$data =~ s/\r?\n$//s; # advanced chomp
$self->log(LOGDEBUG, "dispatching $data");
- defined $self->dispatch(split / +/, $data)
+ defined $self->dispatch(split / +/, $data, 2)
or $self->respond(502, "command unrecognized: '$data'");
last if $self->{_quitting};
}
diff -Nurx .svn ../0.3x/lib/Qpsmtpd/Command.pm ./lib/Qpsmtpd/Command.pm
--- ../0.3x/lib/Qpsmtpd/Command.pm 1970-01-01 01:00:00.000000000 +0100
+++ ./lib/Qpsmtpd/Command.pm 2006-04-02 09:55:06.000000000 +0200
@@ -0,0 +1,169 @@
+package Qpsmtpd::Command;
+
+=head1 NAME
+
+Qpsmtpd::Command - parse arguments to SMTP commands
+
+=head1 DESCRIPTION
+
+B<Qpsmtpd::Command> provides just one public sub routine: B<parse()>.
+
+This sub expects two or three arguments. The first is the name of the
+SMTP command (such as I<HELO>, I<MAIL>, ...). The second must be the remaining
+of the line the client sent.
+
+If no third argument is given (or it's not a reference to a CODE) it parses
+the line according to RFC 1869 (SMTP Service Extensions) for the I<MAIL> and
+I<RCPT> commands and splitting by spaces (" ") for all other.
+
+Any module can supply it's own parsing routine by returning a sub routine
+reference from a hook_*_parse. This sub will be called with I<$self>, I<$cmd>
+and I<$line>.
+
+On successfull parsing it MUST return B<OK> (the constant from
+I<Qpsmtpd::Constants>) success as first argument and a list of
+values, which will be the arguments to the hook for this command.
+
+If parsing failed, the second returned value (if any) will be returned to the
+client as error message.
+
+=head1 EXAMPLE
+
+Inside a plugin
+
+ sub hook_unrecognized_command_parse {
+ my ($self, $transaction, $cmd) = @_;
+ return (OK, \&bdat_parser) if ($cmd eq 'bdat');
+ }
+
+ sub bdat_parser {
+ my ($self,$cmd,$line) = @_;
+ # .. do something with $line...
+ return (DENY, "Invalid arguments")
+ if $some_reason_why_there_is_a_syntax_error;
+ return (OK, @args);
+ }
+
+ sub hook_unrecognized_command {
+ my ($self, $transaction, $cmd, @args) = @_;
+ return (DECLINED) if ($self->qp->connection->hello eq 'helo');
+ return (DECLINED) unless ($cmd eq 'bdat');
+ ....
+ }
+
+=cut
+
+use Qpsmtpd::Constants;
+use vars qw(@ISA);
[EMAIL PROTECTED] = qw(Qpsmtpd::SMTP);
+use strict;
+
+sub parse {
+ my ($me,$cmd,$line,$sub) = @_;
+ my $self = {};
+ bless $self, $me;
+ $cmd = lc $1;
+ if ($sub and (ref($sub) eq 'CODE')) {
+ my @ret = eval { $sub->($self, $cmd, $line); };
+ if ($@) {
+ $self->log(LOGERROR, "Failed to parse command [$cmd]: $@");
+ return (DENY, $line, ());
+ }
+ ## my @log = @ret;
+ ## for (@log) {
+ ## $_ ||= "";
+ ## }
+ ## $self->log(LOGDEBUG, "parse($cmd) => [".join("], [", @log)."]");
+ return @ret;
+ }
+ my $parse = "parse_$cmd";
+ if ($self->can($parse)) {
+ # print "CMD=$cmd,line=$line\n";
+ my @out = eval { $self->$parse($cmd, $line); };
+ if ($@) {
+ $self->log(LOGERROR, "$parse($cmd,$line) failed: $@");
+ return(DENY, "Failed to parse line");
+ }
+ return @out;
+ }
+ return(OK, split(/ +/, $line)); # default :)
+}
+
+sub parse_rcpt {
+ my ($self,$cmd,$line) = @_;
+ return (DENY, "Syntax error in command") unless $line =~ s/^to:\s*//i;
+ return &_get_mail_params($cmd, $line);
+}
+
+sub parse_mail {
+ my ($self,$cmd,$line) = @_;
+ return (DENY, "Syntax error in command") unless $line =~ s/^from:\s*//i;
+ return &_get_mail_params($cmd, $line);
+}
+### RFC 1869:
+## 6. MAIL FROM and RCPT TO Parameters
+## [...]
+##
+## esmtp-cmd ::= inner-esmtp-cmd [SP esmtp-parameters] CR LF
+## esmtp-parameters ::= esmtp-parameter *(SP esmtp-parameter)
+## esmtp-parameter ::= esmtp-keyword ["=" esmtp-value]
+## esmtp-keyword ::= (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
+##
+## ; syntax and values depend on esmtp-keyword
+## esmtp-value ::= 1*<any CHAR excluding "=", SP, and all
+## control characters (US ASCII 0-31
+## inclusive)>
+##
+## ; The following commands are extended to
+## ; accept extended parameters.
+## inner-esmtp-cmd ::= ("MAIL FROM:" reverse-path) /
+## ("RCPT TO:" forward-path)
+sub _get_mail_params {
+ my ($cmd,$line) = @_;
+ my @params = ();
+ $line =~ s/\s*$//;
+
+ while ($line =~ s/\s+([A-Za-z0-9][A-Za-z0-9\-]*(=[^= \x00-\x1f]+)?)$//) {
+ push @params, $1;
+ }
+ @params = reverse @params;
+
+ # the above will "fail" (i.e. all of the line in @params) on
+ # some addresses without <> like
+ # MAIL FROM: [EMAIL PROTECTED]
+ # or RCPT TO: postmaster
+
+ # let's see if $line contains nothing and use the first value as address:
+ if ($line) {
+ # parameter syntax error, i.e. not all of the arguments were
+ # stripped by the while() loop:
+ return (DENY, "Syntax error in parameters")
+ if ($line =~ /[EMAIL PROTECTED]/);
+ return (OK, $line, @params);
+ }
+
+ $line = shift @params;
+ if ($cmd eq "mail") {
+ return (OK, "<>") unless $line; # 'MAIL FROM:' --> 'MAIL FROM:<>'
+ return (DENY, "Syntax error in parameters")
+ if ($line =~ /[EMAIL PROTECTED]/); # parameter syntax error
+ }
+ else {
+ if ($line =~ /\@/) {
+ return (DENY, "Syntax error in parameters")
+ if ($line =~ /[EMAIL PROTECTED]/);
+ }
+ else {
+ # XXX: what about 'abuse' in Qpsmtpd::Address?
+ return (DENY, "Syntax error in parameters") if $line =~ /\s/;
+ return (DENY, "Syntax error in address")
+ unless ($line =~ /^(postmaster|abuse)$/i);
+ }
+ }
+ ## XXX: No: let this do a plugin, so it's not up to us to decide
+ ## if we require <> around an address :-)
+ ## unless ($line =~ /^<.*>$/) { $line = "<".$line.">"; }
+ return (OK, $line, @params);
+}
+
+1;
diff -Nurx .svn ../0.3x/lib/Qpsmtpd/Plugin.pm ./lib/Qpsmtpd/Plugin.pm
--- ../0.3x/lib/Qpsmtpd/Plugin.pm 2006-03-04 08:36:21.000000000 +0100
+++ ./lib/Qpsmtpd/Plugin.pm 2006-04-02 09:55:06.000000000 +0200
@@ -4,11 +4,12 @@
# more or less in the order they will fire
our @hooks = qw(
- logging config pre-connection connect ehlo helo
- auth auth-plain auth-login auth-cram-md5
- rcpt mail data data_post queue_pre queue queue_post
+ logging config 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_post queue_pre queue queue_post
quit reset_transaction disconnect post-connection
- unrecognized_command deny ok
+ unrecognized_command_parse unrecognized_command deny ok
);
our %hooks = map { $_ => 1 } @hooks;
diff -Nurx .svn ../0.3x/lib/Qpsmtpd/SMTP.pm ./lib/Qpsmtpd/SMTP.pm
--- ../0.3x/lib/Qpsmtpd/SMTP.pm 2006-03-22 11:45:39.000000000 +0100
+++ ./lib/Qpsmtpd/SMTP.pm 2006-04-02 09:55:06.000000000 +0200
@@ -12,6 +12,7 @@
use Qpsmtpd::Constants;
use Qpsmtpd::Auth;
use Qpsmtpd::Address ();
+use Qpsmtpd::Command;
use Mail::Header ();
#use Data::Dumper;
@@ -47,11 +48,18 @@
sub dispatch {
my $self = shift;
my ($cmd) = lc shift;
+ my $line = shift;
$self->{_counter}++;
+ my ($ok, @params);
if ($cmd !~ /^(\w{1,12})$/ or !exists $self->{_commands}->{$1}) {
- my ($rc, @msg) = $self->run_hooks("unrecognized_command", $cmd, @_);
+ my ($rc, @msg) = $self->run_hooks("unrecognized_command_parse", $cmd);
+ ($ok, $cmd, @params) = Qpsmtpd::Command->parse($cmd, $line, $msg[0]);
+ if ($rc != OK) {
+ return $self->respond(501, $cmd || "Syntax error in command");
+ }
+ ($rc, @msg) = $self->run_hooks("unrecognized_command", $cmd, $line);
@msg = map { split /\n/ } @msg;
if ($rc == DENY_DISCONNECT) {
$self->respond(521, @msg);
@@ -71,7 +79,7 @@
$cmd = $1;
if (1 or $self->{_commands}->{$cmd} and $self->can($cmd)) {
- my ($result) = eval { $self->$cmd(@_) };
+ my ($result) = eval { $self->$cmd($line) };
$self->log(LOGERROR, "XX: $@") if $@;
return $result if defined $result;
return $self->fault("command '$cmd' failed unexpectedly");
@@ -143,13 +151,16 @@
sub helo {
- my ($self, $hello_host, @stuff) = @_;
+ my ($self, $line) = @_;
+ my ($rc, @msg) = $self->run_hooks('helo_parse');
+ my ($ok, $hello_host, @stuff) = Qpsmtpd::Command->parse('helo', $line,
$msg[0]);
+
return $self->respond (501,
"helo requires domain/address - see RFC-2821 4.1.1.1") unless $hello_host;
my $conn = $self->connection;
return $self->respond (503, "but you already said HELO ...") if $conn->hello;
- my ($rc, @msg) = $self->run_hooks("helo", $hello_host, @stuff);
+ ($rc, @msg) = $self->run_hooks("helo", $hello_host, @stuff);
if ($rc == DONE) {
# do nothing
} elsif ($rc == DENY) {
@@ -171,13 +182,15 @@
}
sub ehlo {
- my ($self, $hello_host, @stuff) = @_;
+ my ($self, $line) = @_;
+ my ($rc, @msg) = $self->run_hooks('ehlo_parse');
+ my ($ok, $hello_host, @stuff) = Qpsmtpd::Command->parse('ehlo', $line,
$msg[0]);
return $self->respond (501,
"ehlo requires domain/address - see RFC-2821 4.1.1.1") unless $hello_host;
my $conn = $self->connection;
return $self->respond (503, "but you already said HELO ...") if $conn->hello;
- my ($rc, @msg) = $self->run_hooks("ehlo", $hello_host, @stuff);
+ ($rc, @msg) = $self->run_hooks("ehlo", $hello_host, @stuff);
if ($rc == DONE) {
# do nothing
} elsif ($rc == DENY) {
@@ -229,7 +242,12 @@
}
sub auth {
- my ( $self, $arg, @stuff ) = @_;
+ my ($self, $line) = @_;
+ my ($rc, $sub) = $self->run_hooks('auth_parse');
+ my ($ok, $arg, @stuff) = Qpsmtpd::Command->parse('auth', $line, $sub);
+ return $self->respond(501, $arg || "Syntax error in command")
+ unless ($ok == OK);
+
#they AUTH'd once already
return $self->respond( 503, "but you already said AUTH ..." )
@@ -242,9 +260,7 @@
}
sub mail {
- my $self = shift;
- return $self->respond(501, "syntax error in parameters") if !$_[0] or $_[0]
!~ m/^from:/i;
-
+ my ($self, $line) = @_;
# -> from RFC2821
# The MAIL command (or the obsolete SEND, SOML, or SAML commands)
# begins a mail transaction. Once started, a mail transaction
@@ -269,16 +285,29 @@
return $self->respond(503, "please say hello first ...");
}
else {
- my $from_parameter = join " ", @_;
- $self->log(LOGINFO, "full from_parameter: $from_parameter");
-
- my ($from) = ($from_parameter =~ m/^from:\s*(<[^>]*>)/i)[0];
-
- # support addresses without <> ... maybe we shouldn't?
- ($from) = "<" . ($from_parameter =~ m/^from:\s*(\S+)/i)[0] . ">"
- unless $from;
+ $self->log(LOGINFO, "full from_parameter: $line");
+ my ($rc, @msg) = $self->run_hooks("mail_parse");
+ my ($ok, $from, @params) = Qpsmtpd::Command->parse('mail', $line, $msg[0]);
+ return $self->respond(501, $from || "Syntax error in command")
+ unless ($ok == OK);
+ my %param;
+ foreach (@params) {
+ my ($k,$v) = split /=/, $_, 2;
+ $param{lc $k} = $v;
+ }
+ # to support addresses without <> we now require a plugin
+ # hooking "mail_pre" to
+ # return (OK, "<$from>");
+ # (...or anything else parseable by Qpsmtpd::Address ;-))
+ # see also comment in sub rcpt()
+ ($rc, @msg) = $self->run_hooks("mail_pre", $from);
+ if ($rc == OK) {
+ $from = shift @msg;
+ }
$self->log(LOGALERT, "from email address : [$from]");
+ return $self->respond(501, "could not parse your mail from command")
+ unless $from =~ /^<.*>$/;
if ($from eq "<>" or $from =~ m/\[undefined\]/ or $from eq "<[EMAIL
PROTECTED]>") {
$from = Qpsmtpd::Address->new("<>");
@@ -288,7 +317,7 @@
}
return $self->respond(501, "could not parse your mail from command")
unless $from;
- my ($rc, @msg) = $self->run_hooks("mail", $from);
+ ($rc, @msg) = $self->run_hooks("mail", $from, %param);
if ($rc == DONE) {
return 1;
}
@@ -323,18 +352,39 @@
}
sub rcpt {
- my $self = shift;
- return $self->respond(501, "syntax error in parameters") unless $_[0] and
$_[0] =~ m/^to:/i;
+ my ($self, $line) = @_;
+ my ($rc, @msg) = $self->run_hooks("rcpt_parse");
+ my ($ok, $rcpt, @param) = Qpsmtpd::Command->parse("rcpt", $line, $msg[0]);
+ return $self->respond(501, $rcpt || "Syntax error in command")
+ unless ($ok == OK);
return $self->respond(503, "Use MAIL before RCPT") unless
$self->transaction->sender;
-
- my ($rcpt) = ($_[0] =~ m/to:(.*)/i)[0];
- $rcpt = $_[1] unless $rcpt;
+
+ my %param;
+ foreach (@param) {
+ my ($k,$v) = split /=/, $_, 2;
+ $param{lc $k} = $v;
+ }
+ # to support addresses without <> we now require a plugin
+ # hooking "rcpt_pre" to
+ # return (OK, "<$rcpt>");
+ # (... or anything else parseable by Qpsmtpd::Address ;-))
+ # this means, a plugin can decide to (pre-)accept
+ # addresses like <[EMAIL PROTECTED]> or <[EMAIL PROTECTED] >
+ # by removing the trailing "."/" " from this example...
+ ($rc, @msg) = $self->run_hooks("rcpt_pre", $rcpt);
+ if ($rc == OK) {
+ $rcpt = shift @msg;
+ }
$self->log(LOGALERT, "to email address : [$rcpt]");
+ return $self->respond(501, "could not parse recipient")
+ unless $rcpt =~ /^<.*>$/;
+
$rcpt = (Qpsmtpd::Address->parse($rcpt))[0];
- return $self->respond(501, "could not parse recipient") unless $rcpt;
+ return $self->respond(501, "could not parse recipient")
+ if (!$rcpt or ($rcpt->format eq '<>'));
- my ($rc, @msg) = $self->run_hooks("rcpt", $rcpt);
+ ($rc, @msg) = $self->run_hooks("rcpt", $rcpt, %param);
if ($rc == DONE) {
return 1;
}
diff -Nurx .svn ../0.3x/lib/Qpsmtpd/SelectServer.pm
./lib/Qpsmtpd/SelectServer.pm
--- ../0.3x/lib/Qpsmtpd/SelectServer.pm 2005-11-20 10:52:18.000000000 +0100
+++ ./lib/Qpsmtpd/SelectServer.pm 2006-04-02 09:55:06.000000000 +0200
@@ -121,7 +121,7 @@
}
else {
$qp->log(LOGINFO, "dispatching $req");
- defined $qp->dispatch(split / +/, $req)
+ defined $qp->dispatch(split / +/, $req, 2)
or $qp->respond(502, "command unrecognized: '$req'");
}
}
diff -Nurx .svn ../0.3x/lib/Qpsmtpd/TcpServer.pm ./lib/Qpsmtpd/TcpServer.pm
--- ../0.3x/lib/Qpsmtpd/TcpServer.pm 2006-01-13 07:28:50.000000000 +0100
+++ ./lib/Qpsmtpd/TcpServer.pm 2006-04-02 09:55:06.000000000 +0200
@@ -63,7 +63,7 @@
$_ =~ s/\r?\n$//s; # advanced chomp
$self->log(LOGDEBUG, "dispatching $_");
$self->connection->notes('original_string', $_);
- defined $self->dispatch(split / +/, $_)
+ defined $self->dispatch(split / +/, $_, 2)
or $self->respond(502, "command unrecognized: '$_'");
alarm $timeout;
}
diff -Nurx .svn ../0.3x/plugins/check_badmailfrom ./plugins/check_badmailfrom
--- ../0.3x/plugins/check_badmailfrom 2005-11-20 10:52:17.000000000 +0100
+++ ./plugins/check_badmailfrom 2006-04-02 09:57:28.000000000 +0200
@@ -21,7 +21,7 @@
=cut
sub hook_mail {
- my ($self, $transaction, $sender) = @_;
+ my ($self, $transaction, $sender, %param) = @_;
my @badmailfrom = $self->qp->config("badmailfrom")
or return (DECLINED);
@@ -44,7 +44,7 @@
}
sub hook_rcpt {
- my ($self, $transaction, $rcpt) = @_;
+ my ($self, $transaction, $rcpt, %param) = @_;
my $note = $transaction->notes('badmailfrom');
if ($note) {
$self->log(LOGINFO, $note);
diff -Nurx .svn ../0.3x/plugins/check_badmailfromto
./plugins/check_badmailfromto
--- ../0.3x/plugins/check_badmailfromto 2005-11-20 10:52:17.000000000 +0100
+++ ./plugins/check_badmailfromto 2006-04-02 09:57:49.000000000 +0200
@@ -17,7 +17,7 @@
=cut
sub hook_mail {
- my ($self, $transaction, $sender) = @_;
+ my ($self, $transaction, $sender, %param) = @_;
my @badmailfromto = $self->qp->config("badmailfromto")
or return (DECLINED);
@@ -41,7 +41,7 @@
}
sub hook_rcpt {
- my ($self, $transaction, $rcpt) = @_;
+ my ($self, $transaction, $rcpt, %param) = @_;
my $recipient = lc($rcpt->user) . '@' . lc($rcpt->host);
my $sender = $transaction->notes('badmailfromto');
if ($sender) {
diff -Nurx .svn ../0.3x/plugins/check_badrcptto ./plugins/check_badrcptto
--- ../0.3x/plugins/check_badrcptto 2006-01-25 09:00:35.000000000 +0100
+++ ./plugins/check_badrcptto 2006-04-02 09:58:00.000000000 +0200
@@ -2,7 +2,7 @@
use Qpsmtpd::DSN;
sub hook_rcpt {
- my ($self, $transaction, $recipient) = @_;
+ my ($self, $transaction, $recipient, %param) = @_;
my @badrcptto = $self->qp->config("badrcptto") or return (DECLINED);
return (DECLINED) unless $recipient->host && $recipient->user;
my $host = lc $recipient->host;
diff -Nurx .svn ../0.3x/plugins/dns_whitelist_soft ./plugins/dns_whitelist_soft
--- ../0.3x/plugins/dns_whitelist_soft 2005-11-20 10:52:17.000000000 +0100
+++ ./plugins/dns_whitelist_soft 2006-04-02 09:58:25.000000000 +0200
@@ -139,7 +139,7 @@
}
sub hook_rcpt {
- my ($self, $transaction, $rcpt) = @_;
+ my ($self, $transaction, $rcpt, %param) = @_;
my $ip = $self->qp->connection->remote_ip || return (DECLINED);
my $note = $self->process_sockets;
if ( $note ) {
diff -Nurx .svn ../0.3x/plugins/dnsbl ./plugins/dnsbl
--- ../0.3x/plugins/dnsbl 2006-03-10 07:22:58.000000000 +0100
+++ ./plugins/dnsbl 2006-04-02 09:59:11.000000000 +0200
@@ -167,7 +167,7 @@
}
sub hook_rcpt {
- my ($self, $transaction, $rcpt) = @_;
+ my ($self, $transaction, $rcpt, %param) = @_;
my $connection = $self->qp->connection;
# RBLSMTPD being non-empty means it contains the failure message to return
diff -Nurx .svn ../0.3x/plugins/dont_require_angelbrackets
./plugins/dont_require_angelbrackets
--- ../0.3x/plugins/dont_require_angelbrackets 1970-01-01 01:00:00.000000000
+0100
+++ ./plugins/dont_require_angelbrackets 2006-04-02 09:55:06.000000000
+0200
@@ -0,0 +1,19 @@
+#
+# dont_require_angelbrackets - accept addresses in MAIL FROM:/RCPT TO:
+# commands without surrounding <>
+#
+sub hook_mail_pre {
+ my ($self,$transaction, $addr) = @_;
+ unless ($addr =~ /^<.*>$/) {
+ $addr = "<".$addr.">";
+ }
+ return (OK, $addr);
+}
+
+sub hook_rcpt_pre {
+ my ($self,$transaction, $addr) = @_;
+ unless ($addr =~ /^<.*>$/) {
+ $addr = "<".$addr.">";
+ }
+ return (OK, $addr);
+}
diff -Nurx .svn ../0.3x/plugins/milter ./plugins/milter
--- ../0.3x/plugins/milter 2005-11-20 10:52:17.000000000 +0100
+++ ./plugins/milter 2006-04-02 09:59:41.000000000 +0200
@@ -135,7 +135,7 @@
}
sub hook_mail {
- my ($self, $transaction, $address) = @_;
+ my ($self, $transaction, $address, %param) = @_;
my $milter = $self->qp->connection->notes('milter');
@@ -148,7 +148,7 @@
}
sub hook_rcpt {
- my ($self, $transaction, $address) = @_;
+ my ($self, $transaction, $address, %param) = @_;
my $milter = $self->qp->connection->notes('milter');
diff -Nurx .svn ../0.3x/plugins/parse_addr_withhelo
./plugins/parse_addr_withhelo
--- ../0.3x/plugins/parse_addr_withhelo 1970-01-01 01:00:00.000000000 +0100
+++ ./plugins/parse_addr_withhelo 2006-04-02 09:55:06.000000000 +0200
@@ -0,0 +1,60 @@
+# parse_addr_withhelo
+#
+# strict RFC 821 forbids parameters after the
+# MAIL FROM:<[EMAIL PROTECTED]>
+# and
+# RCPT TO:<[EMAIL PROTECTED]>
+#
+# load this plugin to enforce, else the default EHLO parsing with
+# parameters is done.
+#
+
+sub hook_mail_parse {
+ my $self = shift;
+ return (OK, \&_parse) if ($self->qp->connection->hello eq 'helo');
+ return (DECLINED);
+}
+
+sub hook_rcpt_parse {
+ my $self = shift;
+ return (OK, \&_parse) if ($self->qp->connection->hello eq 'helo');
+ return (DECLINED);
+}
+
+sub _parse {
+ my ($self,$cmd,$line) = @_;
+ $self->log(LOGDEBUG, "_parse() cmd=[$cmd], line=[$line]");
+ if ($cmd eq 'mail') {
+ return(DENY, "Syntax error in command")
+ unless ($line =~ s/^from:\s*//i);
+ }
+ else { # cmd eq 'rcpt'
+ return(DENY, "Syntax error in command")
+ unless ($line =~ s/^to:\s*//i);
+ }
+
+ if ($line =~ s/^(<.*>)\s*//) {
+ my $addr = $1;
+ return (DENY, "No parameters allowed in ".uc($cmd))
+ if ($line =~ /^\S/);
+ return (OK, $addr, ());
+ }
+
+ ## now, no <> are given
+ $line =~ s/\s*$//;
+ if ($line =~ /\@/) {
+ return (DENY, "No parameters allowed in ".uc($cmd))
+ if ($line =~ /[EMAIL PROTECTED]/);
+ return (OK, $line, ());
+ }
+
+ if ($cmd eq "mail") {
+ return (OK, "<>") unless $line; # 'MAIL FROM:' -> 'MAIL FROM:<>'
+ return (DENY, "Could not parse your MAIL FROM command");
+ }
+ else {
+ return (DENY, "Could not parse your RCPT TO command")
+ unless $line =~ /^(postmaster|abuse)$/i;
+ }
+}
+
diff -Nurx .svn ../0.3x/plugins/rcpt_ok ./plugins/rcpt_ok
--- ../0.3x/plugins/rcpt_ok 2006-01-25 09:00:35.000000000 +0100
+++ ./plugins/rcpt_ok 2006-04-02 09:59:53.000000000 +0200
@@ -5,7 +5,7 @@
use Qpsmtpd::DSN;
sub hook_rcpt {
- my ($self, $transaction, $recipient) = @_;
+ my ($self, $transaction, $recipient, %param) = @_;
my $host = lc $recipient->host;
my @rcpt_hosts = ($self->qp->config("me"), $self->qp->config("rcpthosts"));
diff -Nurx .svn ../0.3x/plugins/require_resolvable_fromhost
./plugins/require_resolvable_fromhost
--- ../0.3x/plugins/require_resolvable_fromhost 2006-01-25 09:00:35.000000000
+0100
+++ ./plugins/require_resolvable_fromhost 2006-04-02 10:00:19.000000000
+0200
@@ -5,7 +5,7 @@
my %invalid = ();
sub hook_mail {
- my ($self, $transaction, $sender) = @_;
+ my ($self, $transaction, $sender, %param) = @_;
return DECLINED
if ($self->qp->connection->notes('whitelistclient'));
diff -Nurx .svn ../0.3x/plugins/rhsbl ./plugins/rhsbl
--- ../0.3x/plugins/rhsbl 2006-03-10 07:22:58.000000000 +0100
+++ ./plugins/rhsbl 2006-04-02 10:00:29.000000000 +0200
@@ -1,7 +1,7 @@
sub hook_mail {
- my ($self, $transaction, $sender) = @_;
+ my ($self, $transaction, $sender, %param) = @_;
my $res = new Net::DNS::Resolver;
my $sel = IO::Select->new();
diff -Nurx .svn ../0.3x/plugins/sender_permitted_from
./plugins/sender_permitted_from
--- ../0.3x/plugins/sender_permitted_from 2005-11-20 10:52:17.000000000
+0100
+++ ./plugins/sender_permitted_from 2006-04-02 10:01:04.000000000 +0200
@@ -34,7 +34,7 @@
}
sub hook_mail {
- my ($self, $transaction, $sender) = @_;
+ my ($self, $transaction, $sender, %param) = @_;
return (DECLINED) unless ($sender->format ne "<>"
and $sender->host && $sender->user);
@@ -71,7 +71,7 @@
}
sub hook_rcpt {
- my ($self, $transaction, $rcpt) = @_;
+ my ($self, $transaction, $rcpt, %param) = @_;
# special addresses don't get SPF-tested.
return DECLINED if $rcpt and $rcpt->user and $rcpt->user =~
/^(?:postmaster|abuse|mailer-daemon|root)$/i;
diff -Nurx .svn ../0.3x/t/addresses.t ./t/addresses.t
--- ../0.3x/t/addresses.t 2005-11-20 10:52:17.000000000 +0100
+++ ./t/addresses.t 2006-04-02 09:55:06.000000000 +0200
@@ -27,4 +27,11 @@
is(($smtpd->command($command))[0], 250, $command);
is($smtpd->transaction->sender->format, '<[EMAIL PROTECTED]>', 'got the right
sender');
+$command = 'MAIL FROM:<[EMAIL PROTECTED]> SIZE=1230 CORRECT-WITHOUT-ARG';
+is(($smtpd->command($command))[0], 250, $command);
+
+$command = 'MAIL FROM:';
+is(($smtpd->command($command))[0], 250, $command);
+is($smtpd->transaction->sender->format, '<>', 'got the right sender');
+