File: qmail-queue

package info (click to toggle)
qpsmtpd 0.94-4
  • links: PTS
  • area: main
  • in suites: bullseye, buster
  • size: 2,284 kB
  • sloc: perl: 17,176; sh: 543; makefile: 186; sql: 100
file content (130 lines) | stat: -rw-r--r-- 3,761 bytes parent folder | download | duplicates (4)
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
    }
}