169 lines
4.4 KiB
Perl
169 lines
4.4 KiB
Perl
#!perl -w
|
|
|
|
=head1 NAME
|
|
|
|
smtp-forward
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This plugin forwards the mail via SMTP to a specified server, rather than
|
|
delivering the email locally.
|
|
|
|
It also supports the Postfix XCLIENT extension.
|
|
|
|
=head1 CONFIG
|
|
|
|
It takes one required parameter, the IP address or hostname to forward to.
|
|
|
|
queue/smtp-forward 10.2.2.2
|
|
|
|
Optionally you can also add a port:
|
|
|
|
queue/smtp-forward 10.2.2.2 9025
|
|
|
|
And a flag:
|
|
|
|
queue/smtp-forward 10.2.2.2 9025 xclient
|
|
|
|
=cut
|
|
|
|
use Net::SMTP;
|
|
use Net::Cmd qw//;
|
|
|
|
sub init {
|
|
my ($self, $qp, @args) = @_;
|
|
|
|
if (@args <= 0) {
|
|
die "No SMTP server specified in smtp-forward config";
|
|
};
|
|
|
|
if ($args[0] =~ /^([\.\w_-]+)$/) {
|
|
$self->{_smtp_server} = $1;
|
|
}
|
|
else {
|
|
die "Bad data in smtp server: $args[0]";
|
|
}
|
|
|
|
$self->{_smtp_port} = 25;
|
|
if (@args > 1 and $args[1] =~ /^(\d+)$/) {
|
|
$self->{_smtp_port} = $1;
|
|
}
|
|
|
|
for (my $i = 2; $i < @args; $i++) {
|
|
if ($args[$i] !~ /^(\w+)$/) {
|
|
$self->log(LOGWARN, "WARNING: Rejecting invalid flag");
|
|
next;
|
|
}
|
|
my $flag = lc($1);
|
|
$self->log(LOGWARN, "WARNING: Unknown flag $flag") unless $flag eq 'xclient';
|
|
$self->{_flags}{$flag} = 1;
|
|
}
|
|
}
|
|
|
|
sub hook_queue {
|
|
my ($self, $transaction) = @_;
|
|
|
|
$self->log(LOGINFO,
|
|
"forwarding to $self->{_smtp_server}:$self->{_smtp_port}");
|
|
my $smtp = Net::SMTP->new(
|
|
$self->{_smtp_server},
|
|
Port => $self->{_smtp_port},
|
|
Timeout => 60,
|
|
Hello => $self->qp->config("me"),
|
|
) || die $!;
|
|
|
|
|
|
my $xcret = $self->xclient($smtp);
|
|
return(DECLINED, $xcret) if defined $xcret;
|
|
|
|
$smtp->mail($transaction->sender->address || "")
|
|
or return DECLINED, "Unable to queue message ($!)";
|
|
for ($transaction->recipients) {
|
|
$smtp->to($_->address)
|
|
or return DECLINED, "Unable to queue message ($!)";
|
|
}
|
|
$smtp->data() or return DECLINED, "Unable to queue message ($!)";
|
|
$smtp->datasend($transaction->header->as_string)
|
|
or return DECLINED, "Unable to queue message ($!)";
|
|
$transaction->body_resetpos;
|
|
while (my $line = $transaction->body_getline) {
|
|
$smtp->datasend($line)
|
|
or return DECLINED, "Unable to queue message ($!)";
|
|
}
|
|
$smtp->dataend() or return DECLINED, "Unable to queue message ($!)";
|
|
my $qid = $smtp->message();
|
|
my @list = split(' ', $qid);
|
|
$qid = pop(@list);
|
|
|
|
$smtp->quit() or return DECLINED, "Unable to queue message ($!)";
|
|
$self->log(LOGINFO, "finished queueing");
|
|
return OK, "queued as $qid";
|
|
}
|
|
|
|
sub xclient {
|
|
my ($self, $smtp) = @_;
|
|
|
|
return unless $self->{_flags}{xclient};
|
|
|
|
my $parts = $smtp->supports('XCLIENT');
|
|
if (!defined($parts)) { # what parts do they want?
|
|
return "Unable to queue message (Server does not advertise XCLIENT support)";
|
|
};
|
|
|
|
my %haveparts;
|
|
for my $part (split(/\s+/, $parts)) {
|
|
next unless $part =~ /^(\w+)$/;
|
|
$haveparts{uc($part)} = 1;
|
|
}
|
|
|
|
my $conn = $self->qp->connection;
|
|
my @rparts;
|
|
|
|
if ($haveparts{NAME}) {
|
|
my $name = $conn->remote_host || '[UNAVAILABLE]';
|
|
$name = '[UNAVAILABLE]' if ($name eq 'Unknown');
|
|
push(@rparts, "NAME=$name");
|
|
}
|
|
|
|
if ($haveparts{ADDR}) {
|
|
my $ip = $conn->remote_ip;
|
|
push(@rparts, "ADDR=$ip");
|
|
}
|
|
|
|
if ($haveparts{PORT}) {
|
|
my $port = $conn->remote_port;
|
|
push(@rparts, "PORT=$port");
|
|
}
|
|
|
|
my $hello_name = $self->connection->hello_host;
|
|
$hello_name ||= '[UNAVAILABLE]';
|
|
if ($haveparts{HELO}) {
|
|
push(@rparts, "HELO=$hello_name");
|
|
}
|
|
|
|
my $hello = $conn->hello;
|
|
if ($haveparts{PROTO} && defined($hello)) {
|
|
my $proto = (uc($hello) eq 'EHLO') ? 'ESMTP' : 'SMTP';
|
|
push(@rparts, "PROTO=$proto");
|
|
}
|
|
|
|
while (scalar(@rparts)) {
|
|
my @items;
|
|
my $cursz = 0;
|
|
while (defined(my $item = $rparts[0])) {
|
|
my $len = length($item);
|
|
last if ($cursz + $len > 500);
|
|
$cursz += $len;
|
|
push(@items, shift @rparts);
|
|
}
|
|
|
|
last unless @items;
|
|
if ($smtp->command('XCLIENT', @items)->response() != Net::Cmd::CMD_OK) {
|
|
return "Unable to queue message (XCLIENT failed)";
|
|
}
|
|
}
|
|
|
|
$smtp->hello($hello_name) or return "Unable to queue message (HELLO after XCLIENT failed)";
|
|
return;
|
|
}
|