1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
#!perl -w
=head1 NAME
qmail-queue
=head1 DESCRIPTION
This is the most common plugin used to queue incoming mails. A
variation of this plugin would maybe forward the mail via smtp.
=head1 CONFIG
It takes one optional parameter, the location of qmail-queue. This
makes it easy to use a qmail-queue replacement.
queue/qmail-queue /var/qmail/bin/another-qmail-queue
If set the environment variable QMAILQUEUE overrides this setting.
=cut
use strict;
use warnings;
use Qpsmtpd::Constants;
use POSIX ();
sub register {
my ($self, $qp, @args) = @_;
if (@args > 0) {
$self->{_queue_exec} = $args[0];
$self->log(LOGWARN, "WARNING: Ignoring additional arguments.")
if @args > 1;
}
$self->{_queue_exec} ||= ($ENV{QMAIL} || '/var/qmail') . "/bin/qmail-queue";
$self->{_queue_exec} = $ENV{QMAILQUEUE} if $ENV{QMAILQUEUE};
}
sub hook_queue {
my ($self, $transaction) = @_;
# these bits inspired by Peter Samuels "qmail-queue wrapper"
pipe(MESSAGE_READER, MESSAGE_WRITER) or die "Could not create message pipe";
pipe(ENVELOPE_READER, ENVELOPE_WRITER)
or die "Could not create envelope pipe";
local $SIG{PIPE} = sub { die 'SIGPIPE' };
my $child = fork();
!defined $child and die "Could not fork";
if ($child) {
# Parent
my $oldfh = select MESSAGE_WRITER;
$| = 1;
select ENVELOPE_WRITER;
$| = 1;
select $oldfh;
close MESSAGE_READER or die "close msg reader fault";
close ENVELOPE_READER or die "close envelope reader fault";
$transaction->header->print(\*MESSAGE_WRITER);
$transaction->body_resetpos;
while (my $line = $transaction->body_getline) {
print MESSAGE_WRITER $line;
}
close MESSAGE_WRITER;
my @rcpt = map { "T" . $_->address } $transaction->recipients;
my $from = "F" . ($transaction->sender->address || "");
print ENVELOPE_WRITER "$from\0", join("\0", @rcpt), "\0\0"
or return (DECLINED, "Could not print addresses to queue");
close ENVELOPE_WRITER;
waitpid($child, 0);
my $exit_code = $? >> 8;
$exit_code
and return (DECLINED, "Unable to queue message ($exit_code)");
my $msg_id = $transaction->header->get('Message-Id') || '';
$msg_id =~ s/[\r\n].*//s; # don't allow newlines in the Message-Id here
$msg_id = "<$msg_id>" unless $msg_id =~ /^<.*>$/; # surround in <>'s
return (OK, "Queued! " . time . " qp $child $msg_id");
}
elsif (defined $child) {
# Child
close MESSAGE_WRITER or exit 1;
close ENVELOPE_WRITER or exit 2;
# Untaint $self->{_queue_exec}
my $queue_exec = $self->{_queue_exec};
if ($queue_exec =~ /^(\/[\/\-\_\.a-z0-9A-Z]*)$/) {
$queue_exec = $1;
}
else {
$self->log(LOGERROR,
"FATAL ERROR: Unexpected characters in qmail-queue plugin argument"
);
# This exit is ok as we're exiting a forked child process.
exit 3;
}
# save the original STDIN and STDOUT in case exec() fails below
open(SAVE_STDIN, "<&STDIN");
open(SAVE_STDOUT, ">&STDOUT");
POSIX::dup2(fileno(MESSAGE_READER), 0)
or die "Unable to dup MESSAGE_READER: $!";
POSIX::dup2(fileno(ENVELOPE_READER), 1)
or die "Unable to dup ENVELOPE_READER: $!";
my $ppid = getppid();
$self->log(LOGNOTICE, "(for $ppid) Queuing to $queue_exec");
my $rc = exec $queue_exec;
# close the pipe
close(MESSAGE_READER);
close(MESSAGE_WRITER);
exit 6; # we'll only get here if the exec fails
}
}
|