3889821d16
Exim's BSMTP interface will indicate the SMTP response to the exchange; actually use it rather than assuming all errors are 400-class soft ones. Tolerate $transaction->header returning undef (since it evidently can under some conditions). Convert a few errant tabs to spaces. Fix vi modeline. Signed-off-by: Robert <rspier@pobox.com>
152 lines
4.9 KiB
Plaintext
152 lines
4.9 KiB
Plaintext
=head1 NAME
|
|
|
|
exim-bsmtp
|
|
|
|
$Id$
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This plugin enqueues mail from qpsmtpd into Exim via BSMTP
|
|
|
|
=head1 INSTALLATION
|
|
|
|
The qpsmtpd user B<must> be configured in the I<trusted_users> setting
|
|
in your Exim configuration. If it is not, queueing will still work,
|
|
but sender addresses will not be honored by exim, which will make all
|
|
mail appear to originate from the smtpd user itself.
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
The plugin accepts configuration settings in space-delimited name/value
|
|
pairs. For example:
|
|
|
|
queue/exim-bsmtp exim_path /usr/sbin/exim4
|
|
|
|
=over 4
|
|
|
|
=item exim_path I<path>
|
|
|
|
The path to use to execute the Exim BSMTP receiver; by default this is
|
|
I</usr/sbin/rsmtp>. The commandline switch '-bS' will be added (this is
|
|
actually redundant with rsmtp, but harmless).
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
=head1 LICENSE
|
|
|
|
Copyright (c) 2004 by Devin Carraway <qpsmtpd@devin.com>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use IO::File;
|
|
use Sys::Hostname qw(hostname);
|
|
use File::Temp qw(tempfile);
|
|
|
|
sub register {
|
|
my ($self, $qp, %args) = @_;
|
|
|
|
$self->{_exim_path} = $args{exim_path} || '/usr/sbin/rsmtp';
|
|
$self->{_exim_path} = $1 if $self->{_exim_path} =~ /(.*)/;
|
|
unless (-x $self->{_exim_path}) {
|
|
$self->log(LOGERROR, "Could not find exim at $self->{_exim_path};".
|
|
" please set exim_path in config/plugins");
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
sub hook_queue {
|
|
my ($self, $transaction) = @_;
|
|
|
|
unless ($transaction->header) {
|
|
$self->log(LOGERROR, "No header parsed for transaction; can't enqueue");
|
|
return (DENY, 'Mail unqueuable');
|
|
}
|
|
my $tmp_dir = $self->qp->config('spool_dir') || '/tmp';
|
|
$tmp_dir = $1 if ($tmp_dir =~ /(.*)/);
|
|
my ($tmp, $tmpfn) = tempfile("exim-bsmtp.$$.XXXXXX", DIR => $tmp_dir);
|
|
unless ($tmp && $tmpfn) {
|
|
$self->log(LOGERROR, "Couldn't create tempfile: $!");
|
|
return (DECLINED, 'Internal error enqueueing mail');
|
|
}
|
|
|
|
print $tmp "HELO ", hostname(), "\n",
|
|
"MAIL FROM:<", ($transaction->sender->address || ''), ">\n";
|
|
print $tmp "RCPT TO:<", ($_->address || ''), ">\n"
|
|
for $transaction->recipients;
|
|
print $tmp "DATA\n", $transaction->header->as_string;
|
|
$transaction->body_resetpos;
|
|
while (my $line = $transaction->body_getline) {
|
|
$line =~ s/^\./../;
|
|
print $tmp $line;
|
|
}
|
|
print $tmp ".\nQUIT\n";
|
|
close $tmp;
|
|
|
|
my $cmd = "$self->{_exim_path} -bS < $tmpfn";
|
|
$self->log(LOGDEBUG, "executing cmd $cmd");
|
|
my $exim = new IO::File "$cmd|";
|
|
unless ($exim) {
|
|
$self->log(LOGERROR, "Could not execute $self->{_exim_path}: $!");
|
|
unlink $tmpfn or $self->log(LOGERROR, "unlink: $tmpfn: $!");
|
|
return (DECLINED, "Internal error enqueuing mail");
|
|
}
|
|
# Normally exim produces no output in BSMTP mode; anything that
|
|
# does come out is an error worth logging.
|
|
my $start = time;
|
|
my ($bsmtp_error, $bsmtp_msg);
|
|
while (<$exim>) {
|
|
chomp;
|
|
$self->log(LOGERROR, "exim: $_");
|
|
if (/(\d\d\d)\s(\S.*)/) {
|
|
($bsmtp_error, $bsmtp_msg) = ($1, $2);
|
|
}
|
|
}
|
|
$self->log(LOGDEBUG, "BSMTP finished (".(time - $start)." sec)");
|
|
$exim->close;
|
|
my $exit = $?;
|
|
unlink $tmpfn or $self->log(LOGERROR, "unlink: $tmpfn: $!");
|
|
|
|
$self->log(LOGDEBUG, "Exitcode from exim: $exit");
|
|
if ($bsmtp_error && $bsmtp_error >= 400 && $bsmtp_error < 600) {
|
|
$self->log(LOGERROR, "BSMTP enqueue failed; response $bsmtp_error".
|
|
" ($bsmtp_msg)");
|
|
return ($bsmtp_error < 400 ? DECLINED : DENY, $bsmtp_msg);
|
|
}
|
|
elsif (($exit >> 8) != 0 || $bsmtp_error) {
|
|
$self->log(LOGERROR, 'BSMTP enqueue failed; exitcode '.($exit >> 8).
|
|
" from $self->{_exim_path} -bS");
|
|
return (DECLINED, 'Internal error enqueuing mail');
|
|
}
|
|
|
|
$self->log(LOGINFO, "Enqueued to exim via BSMTP");
|
|
return (OK, "Queued!");
|
|
}
|
|
|
|
|
|
1;
|
|
|
|
# vi: ts=4 sw=4 expandtab syn=perl:
|