~ubuntu-branches/ubuntu/trusty/postfix/trusty-proposed

« back to all changes in this revision

Viewing changes to src/pipe/pipe.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2005-02-27 09:33:07 UTC
  • Revision ID: james.westby@ubuntu.com-20050227093307-cn789t27ibnlh6tf
Tags: upstream-2.1.5
ImportĀ upstreamĀ versionĀ 2.1.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      pipe 8
 
4
/* SUMMARY
 
5
/*      Postfix delivery to external command
 
6
/* SYNOPSIS
 
7
/*      \fBpipe\fR [generic Postfix daemon options] command_attributes...
 
8
/* DESCRIPTION
 
9
/*      The \fBpipe\fR daemon processes requests from the Postfix queue
 
10
/*      manager to deliver messages to external commands.
 
11
/*      This program expects to be run from the \fBmaster\fR(8) process
 
12
/*      manager.
 
13
/*
 
14
/*      Message attributes such as sender address, recipient address and
 
15
/*      next-hop host name can be specified as command-line macros that are
 
16
/*      expanded before the external command is executed.
 
17
/*
 
18
/*      The \fBpipe\fR daemon updates queue files and marks recipients
 
19
/*      as finished, or it informs the queue manager that delivery should
 
20
/*      be tried again at a later time. Delivery status reports are sent
 
21
/*      to the \fBbounce\fR(8), \fBdefer\fR(8) or \fBtrace\fR(8) daemon as
 
22
/*      appropriate.
 
23
/* SINGLE-RECIPIENT DELIVERY
 
24
/* .ad
 
25
/* .fi
 
26
/*      Some external commands cannot handle more than one recipient
 
27
/*      per delivery request. Examples of such transports are pagers,
 
28
/*      fax machines, and so on.
 
29
/*
 
30
/*      To prevent Postfix from sending multiple recipients per delivery
 
31
/*      request, specify
 
32
/*
 
33
/* .ti +4
 
34
/*      \fItransport\fB_destination_recipient_limit = 1\fR
 
35
/*
 
36
/*      in the Postfix \fBmain.cf\fR file, where \fItransport\fR
 
37
/*      is the name in the first column of the Postfix \fBmaster.cf\fR
 
38
/*      entry for the pipe-based delivery transport.
 
39
/* COMMAND ATTRIBUTE SYNTAX
 
40
/* .ad
 
41
/* .fi
 
42
/*      The external command attributes are given in the \fBmaster.cf\fR
 
43
/*      file at the end of a service definition.  The syntax is as follows:
 
44
/* .IP "\fBflags=BDFORhqu.>\fR (optional)"
 
45
/*      Optional message processing flags. By default, a message is
 
46
/*      copied unchanged.
 
47
/* .RS
 
48
/* .IP \fBB\fR
 
49
/*      Append a blank line at the end of each message. This is required
 
50
/*      by some mail user agents that recognize "\fBFrom \fR" lines only
 
51
/*      when preceded by a blank line.
 
52
/* .IP \fBD\fR
 
53
/*      Prepend a "\fBDelivered-To: \fIrecipient\fR" message header with the
 
54
/*      envelope recipient address. Note: for this to work, the
 
55
/*      \fItransport\fB_destination_recipient_limit\fR must be 1.
 
56
/* .IP \fBF\fR
 
57
/*      Prepend a "\fBFrom \fIsender time_stamp\fR" envelope header to
 
58
/*      the message content.
 
59
/*      This is expected by, for example, \fBUUCP\fR software.
 
60
/* .IP \fBO\fR
 
61
/*      Prepend an "\fBX-Original-To: \fIrecipient\fR" message header
 
62
/*      with the recipient address as given to Postfix. Note: for this to
 
63
/*      work, the \fItransport\fB_destination_recipient_limit\fR must be 1.
 
64
/* .IP \fBR\fR
 
65
/*      Prepend a \fBReturn-Path:\fR message header with the envelope sender
 
66
/*      address.
 
67
/* .IP \fBh\fR
 
68
/*      Fold the command-line \fB$recipient\fR domain name and \fB$nexthop\fR
 
69
/*      host name to lower case.
 
70
/*      This is recommended for delivery via \fBUUCP\fR.
 
71
/* .IP \fBq\fR
 
72
/*      Quote white space and other special characters in the command-line
 
73
/*      \fB$sender\fR and \fB$recipient\fR address localparts (text to the
 
74
/*      left of the right-most \fB@\fR character), according to an 8-bit
 
75
/*      transparent version of RFC 822.
 
76
/*      This is recommended for delivery via \fBUUCP\fR or \fBBSMTP\fR.
 
77
/* .sp
 
78
/*      The result is compatible with the address parsing of command-line
 
79
/*      recipients by the Postfix \fBsendmail\fR mail submission command.
 
80
/* .sp
 
81
/*      The \fBq\fR flag affects only entire addresses, not the partial
 
82
/*      address information from the \fB$user\fR, \fB$extension\fR or
 
83
/*      \fB$mailbox\fR command-line macros.
 
84
/* .IP \fBu\fR
 
85
/*      Fold the command-line \fB$recipient\fR address localpart (text to
 
86
/*      the left of the right-most \fB@\fR character) to lower case.
 
87
/*      This is recommended for delivery via \fBUUCP\fR.
 
88
/* .IP \fB.\fR
 
89
/*      Prepend \fB.\fR to lines starting with "\fB.\fR". This is needed
 
90
/*      by, for example, \fBBSMTP\fR software.
 
91
/* .IP \fB>\fR
 
92
/*      Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
 
93
/*      by, for example, \fBUUCP\fR software.
 
94
/* .RE
 
95
/* .IP "\fBuser\fR=\fIusername\fR (required)"
 
96
/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
 
97
/*      The external command is executed with the rights of the
 
98
/*      specified \fIusername\fR.  The software refuses to execute
 
99
/*      commands with root privileges, or with the privileges of the
 
100
/*      mail system owner. If \fIgroupname\fR is specified, the
 
101
/*      corresponding group ID is used instead of the group ID of
 
102
/*      \fIusername\fR.
 
103
/* .IP "\fBeol=string\fR (optional, default: \fB\en\fR)"
 
104
/*      The output record delimiter. Typically one would use either
 
105
/*      \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
 
106
/*      sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
 
107
/*      \e\fIoctal\fR and \fB\e\e\fR.
 
108
/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
 
109
/*      Messages greater in size than this limit (in bytes) will be bounced
 
110
/*      back to the sender.
 
111
/* .IP "\fBargv\fR=\fIcommand\fR... (required)"
 
112
/*      The command to be executed. This must be specified as the
 
113
/*      last command attribute.
 
114
/*      The command is executed directly, i.e. without interpretation of
 
115
/*      shell meta characters by a shell command interpreter.
 
116
/* .sp
 
117
/*      In the command argument vector, the following macros are recognized
 
118
/*      and replaced with corresponding information from the Postfix queue
 
119
/*      manager delivery request:
 
120
/* .RS
 
121
/* .IP \fB${\fBextension\fR}\fR
 
122
/*      This macro expands to the extension part of a recipient address.
 
123
/*      For example, with an address \fIuser+foo@domain\fR the extension is
 
124
/*      \fIfoo\fR.
 
125
/* .sp
 
126
/*      A command-line argument that contains \fB${\fBextension\fR}\fR expands
 
127
/*      into as many command-line arguments as there are recipients.
 
128
/* .sp
 
129
/*      This information is modified by the \fBu\fR flag for case folding.
 
130
/* .IP \fB${\fBmailbox\fR}\fR
 
131
/*      This macro expands to the complete local part of a recipient address.
 
132
/*      For example, with an address \fIuser+foo@domain\fR the mailbox is
 
133
/*      \fIuser+foo\fR.
 
134
/* .sp
 
135
/*      A command-line argument that contains \fB${\fBmailbox\fR}\fR
 
136
/*      expands into as many command-line arguments as there are recipients.
 
137
/* .sp
 
138
/*      This information is modified by the \fBu\fR flag for case folding.
 
139
/* .IP \fB${\fBnexthop\fR}\fR
 
140
/*      This macro expands to the next-hop hostname.
 
141
/* .sp
 
142
/*      This information is modified by the \fBh\fR flag for case folding.
 
143
/* .IP \fB${\fBrecipient\fR}\fR
 
144
/*      This macro expands to the complete recipient address.
 
145
/* .sp
 
146
/*      A command-line argument that contains \fB${\fBrecipient\fR}\fR
 
147
/*      expands into as many command-line arguments as there are recipients.
 
148
/* .sp
 
149
/*      This information is modified by the \fBhqu\fR flags for quoting
 
150
/*      and case folding.
 
151
/* .IP \fB${\fBsender\fR}\fR
 
152
/*      This macro expands to the envelope sender address.
 
153
/* .sp
 
154
/*      This information is modified by the \fBq\fR flag for quoting.
 
155
/* .IP \fB${\fBsize\fR}\fR
 
156
/*      This macro expands to Postfix's idea of the message size, which
 
157
/*      is an approximation of the size of the message as delivered.
 
158
/* .IP \fB${\fBuser\fR}\fR
 
159
/*      This macro expands to the username part of a recipient address.
 
160
/*      For example, with an address \fIuser+foo@domain\fR the username
 
161
/*      part is \fIuser\fR.
 
162
/* .sp
 
163
/*      A command-line argument that contains \fB${\fBuser\fR}\fR expands
 
164
/*      into as many command-line arguments as there are recipients.
 
165
/* .sp
 
166
/*      This information is modified by the \fBu\fR flag for case folding.
 
167
/* .RE
 
168
/* .PP
 
169
/*      In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
 
170
/*      $(\fIname\fR) are also recognized.  Specify \fB$$\fR where a single
 
171
/*      \fB$\fR is wanted.
 
172
/* DIAGNOSTICS
 
173
/*      Command exit status codes are expected to
 
174
/*      follow the conventions defined in <\fBsysexits.h\fR>.
 
175
/*
 
176
/*      Problems and transactions are logged to \fBsyslogd\fR(8).
 
177
/*      Corrupted message files are marked so that the queue manager
 
178
/*      can move them to the \fBcorrupt\fR queue for further inspection.
 
179
/* SECURITY
 
180
/* .fi
 
181
/* .ad
 
182
/*      This program needs a dual personality 1) to access the private
 
183
/*      Postfix queue and IPC mechanisms, and 2) to execute external
 
184
/*      commands as the specified user. It is therefore security sensitive.
 
185
/* CONFIGURATION PARAMETERS
 
186
/* .ad
 
187
/* .fi
 
188
/*      Changes to \fBmain.cf\fR are picked up automatically as pipe(8)
 
189
/*      processes run for only a limited amount of time. Use the command
 
190
/*      "\fBpostfix reload\fR" to speed up a change.
 
191
/*
 
192
/*      The text below provides only a parameter summary. See
 
193
/*      postconf(5) for more details including examples.
 
194
/* RESOURCE AND RATE CONTROLS
 
195
/* .ad
 
196
/* .fi
 
197
/*      In the text below, \fItransport\fR is the first field in a
 
198
/*      \fBmaster.cf\fR entry.
 
199
/* .IP "\fItransport\fB_destination_concurrency_limit ($default_destination_concurrency_limit)\fR"
 
200
/*      Limit the number of parallel deliveries to the same destination,
 
201
/*      for delivery via the named \fItransport\fR.
 
202
/*      The limit is enforced by the Postfix queue manager.
 
203
/* .IP "\fItransport\fB_destination_recipient_limit ($default_destination_recipient_limit)\fR"
 
204
/*      Limit the number of recipients per message delivery, for delivery
 
205
/*      via the named \fItransport\fR.
 
206
/*      The limit is enforced by the Postfix queue manager.
 
207
/* .IP "\fItransport\fB_time_limit ($command_time_limit)\fR"
 
208
/*      Limit the time for delivery to external command, for delivery via
 
209
/*      the named \fItransport\fR.
 
210
/*      The limit is enforced by the pipe delivery agent.
 
211
/* MISCELLANEOUS CONTROLS
 
212
/* .ad
 
213
/* .fi
 
214
/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
 
215
/*      The default location of the Postfix main.cf and master.cf
 
216
/*      configuration files.
 
217
/* .IP "\fBdaemon_timeout (18000s)\fR"
 
218
/*      How much time a Postfix daemon process may take to handle a
 
219
/*      request before it is terminated by a built-in watchdog timer.
 
220
/* .IP "\fBexport_environment (see 'postconf -d' output)\fR"
 
221
/*      The list of environment variables that a Postfix process will export
 
222
/*      to non-Postfix processes.
 
223
/* .IP "\fBipc_timeout (3600s)\fR"
 
224
/*      The time limit for sending or receiving information over an internal
 
225
/*      communication channel.
 
226
/* .IP "\fBmail_owner (postfix)\fR"
 
227
/*      The UNIX system account that owns the Postfix queue and most Postfix
 
228
/*      daemon processes.
 
229
/* .IP "\fBmax_idle (100s)\fR"
 
230
/*      The maximum amount of time that an idle Postfix daemon process
 
231
/*      waits for the next service request before exiting.
 
232
/* .IP "\fBmax_use (100)\fR"
 
233
/*      The maximal number of connection requests before a Postfix daemon
 
234
/*      process terminates.
 
235
/* .IP "\fBprocess_id (read-only)\fR"
 
236
/*      The process ID of a Postfix command or daemon process.
 
237
/* .IP "\fBprocess_name (read-only)\fR"
 
238
/*      The process name of a Postfix command or daemon process.
 
239
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
 
240
/*      The location of the Postfix top-level queue directory.
 
241
/* .IP "\fBrecipient_delimiter (empty)\fR"
 
242
/*      The separator between user names and address extensions (user+foo).
 
243
/* .IP "\fBsyslog_facility (mail)\fR"
 
244
/*      The syslog facility of Postfix logging.
 
245
/* .IP "\fBsyslog_name (postfix)\fR"
 
246
/*      The mail system name that is prepended to the process name in syslog
 
247
/*      records, so that "smtpd" becomes, for example, "postfix/smtpd".
 
248
/* SEE ALSO
 
249
/*      qmgr(8), queue manager
 
250
/*      bounce(8), delivery status reports
 
251
/*      postconf(5), configuration parameters
 
252
/*      master(8), process manager
 
253
/*      syslogd(8), system logging
 
254
/* LICENSE
 
255
/* .ad
 
256
/* .fi
 
257
/*      The Secure Mailer license must be distributed with this software.
 
258
/* AUTHOR(S)
 
259
/*      Wietse Venema
 
260
/*      IBM T.J. Watson Research
 
261
/*      P.O. Box 704
 
262
/*      Yorktown Heights, NY 10598, USA
 
263
/*--*/
 
264
 
 
265
/* System library. */
 
266
 
 
267
#include <sys_defs.h>
 
268
#include <unistd.h>
 
269
#include <stdlib.h>
 
270
#include <string.h>
 
271
#include <pwd.h>
 
272
#include <grp.h>
 
273
#include <fcntl.h>
 
274
#include <ctype.h>
 
275
 
 
276
#ifdef STRCASECMP_IN_STRINGS_H
 
277
#include <strings.h>
 
278
#endif
 
279
 
 
280
/* Utility library. */
 
281
 
 
282
#include <msg.h>
 
283
#include <vstream.h>
 
284
#include <vstring.h>
 
285
#include <argv.h>
 
286
#include <htable.h>
 
287
#include <dict.h>
 
288
#include <iostuff.h>
 
289
#include <mymalloc.h>
 
290
#include <mac_parse.h>
 
291
#include <set_eugid.h>
 
292
#include <split_at.h>
 
293
#include <stringops.h>
 
294
 
 
295
/* Global library. */
 
296
 
 
297
#include <recipient_list.h>
 
298
#include <deliver_request.h>
 
299
#include <mail_params.h>
 
300
#include <mail_conf.h>
 
301
#include <bounce.h>
 
302
#include <defer.h>
 
303
#include <deliver_completed.h>
 
304
#include <sent.h>
 
305
#include <pipe_command.h>
 
306
#include <mail_copy.h>
 
307
#include <mail_addr.h>
 
308
#include <canon_addr.h>
 
309
#include <split_addr.h>
 
310
#include <off_cvt.h>
 
311
#include <quote_822_local.h>
 
312
#include <flush_clnt.h>
 
313
 
 
314
/* Single server skeleton. */
 
315
 
 
316
#include <mail_server.h>
 
317
 
 
318
/* Application-specific. */
 
319
 
 
320
 /*
 
321
  * The mini symbol table name and keys used for expanding macros in
 
322
  * command-line arguments.
 
323
  * 
 
324
  * XXX Update  the parse_callback() routine when something gets added here,
 
325
  * even when the macro is not recipient dependent.
 
326
  */
 
327
#define PIPE_DICT_TABLE         "pipe_command"  /* table name */
 
328
#define PIPE_DICT_NEXTHOP       "nexthop"       /* key */
 
329
#define PIPE_DICT_RCPT          "recipient"     /* key */
 
330
#define PIPE_DICT_SENDER        "sender"/* key */
 
331
#define PIPE_DICT_USER          "user"  /* key */
 
332
#define PIPE_DICT_EXTENSION     "extension"     /* key */
 
333
#define PIPE_DICT_MAILBOX       "mailbox"       /* key */
 
334
#define PIPE_DICT_SIZE          "size"  /* key */
 
335
 
 
336
 /*
 
337
  * Flags used to pass back the type of special parameter found by
 
338
  * parse_callback.
 
339
  */
 
340
#define PIPE_FLAG_RCPT          (1<<0)
 
341
#define PIPE_FLAG_USER          (1<<1)
 
342
#define PIPE_FLAG_EXTENSION     (1<<2)
 
343
#define PIPE_FLAG_MAILBOX       (1<<3)
 
344
 
 
345
 /*
 
346
  * Additional flags. These are colocated with mail_copy() flags. Allow some
 
347
  * space for extension of the mail_copy() interface.
 
348
  */
 
349
#define PIPE_OPT_FOLD_USER      (1<<16)
 
350
#define PIPE_OPT_FOLD_HOST      (1<<17)
 
351
#define PIPE_OPT_QUOTE_LOCAL    (1<<18)
 
352
 
 
353
#define PIPE_OPT_FOLD_FLAGS     (PIPE_OPT_FOLD_USER | PIPE_OPT_FOLD_HOST)
 
354
 
 
355
 /*
 
356
  * Tunable parameters. Values are taken from the config file, after
 
357
  * prepending the service name to _name, and so on.
 
358
  */
 
359
int     var_command_maxtime;            /* system-wide */
 
360
 
 
361
 /*
 
362
  * For convenience. Instead of passing around lists of parameters, bundle
 
363
  * them up in convenient structures.
 
364
  */
 
365
 
 
366
 /*
 
367
  * Structure for service-specific configuration parameters.
 
368
  */
 
369
typedef struct {
 
370
    int     time_limit;                 /* per-service time limit */
 
371
} PIPE_PARAMS;
 
372
 
 
373
 /*
 
374
  * Structure for command-line parameters.
 
375
  */
 
376
typedef struct {
 
377
    char  **command;                    /* argument vector */
 
378
    uid_t   uid;                        /* command privileges */
 
379
    gid_t   gid;                        /* command privileges */
 
380
    int     flags;                      /* mail_copy() flags */
 
381
    VSTRING *eol;                       /* output record delimiter */
 
382
    off_t   size_limit;                 /* max size in bytes we will accept */
 
383
} PIPE_ATTR;
 
384
 
 
385
 /*
 
386
  * Structure for command-line parameter macro expansion.
 
387
  */
 
388
typedef struct {
 
389
    const char *service;                /* for warnings */
 
390
    int     expand_flag;                /* callback result */
 
391
} PIPE_STATE;
 
392
 
 
393
 /*
 
394
  * Silly little macros.
 
395
  */
 
396
#define STR     vstring_str
 
397
 
 
398
/* parse_callback - callback for mac_parse() */
 
399
 
 
400
static int parse_callback(int type, VSTRING *buf, char *context)
 
401
{
 
402
    PIPE_STATE *state = (PIPE_STATE *) context;
 
403
    struct cmd_flags {
 
404
        const char *name;
 
405
        int     flags;
 
406
    };
 
407
    static struct cmd_flags cmd_flags[] = {
 
408
        PIPE_DICT_NEXTHOP, 0,
 
409
        PIPE_DICT_RCPT, PIPE_FLAG_RCPT,
 
410
        PIPE_DICT_SENDER, 0,
 
411
        PIPE_DICT_USER, PIPE_FLAG_USER,
 
412
        PIPE_DICT_EXTENSION, PIPE_FLAG_EXTENSION,
 
413
        PIPE_DICT_MAILBOX, PIPE_FLAG_MAILBOX,
 
414
        PIPE_DICT_SIZE, 0,
 
415
        0, 0,
 
416
    };
 
417
    struct cmd_flags *p;
 
418
 
 
419
    /*
 
420
     * See if this command-line argument references a special macro.
 
421
     */
 
422
    if (type == MAC_PARSE_VARNAME) {
 
423
        for (p = cmd_flags; /* see below */ ; p++) {
 
424
            if (p->name == 0) {
 
425
                msg_warn("file %s/%s: service %s: unknown macro name: \"%s\"",
 
426
                         var_config_dir, MASTER_CONF_FILE,
 
427
                         state->service, vstring_str(buf));
 
428
                return (MAC_PARSE_ERROR);
 
429
            } else if (strcmp(vstring_str(buf), p->name) == 0) {
 
430
                state->expand_flag |= p->flags;
 
431
                return (0);
 
432
            }
 
433
        }
 
434
    }
 
435
    return (0);
 
436
}
 
437
 
 
438
/* morph_recipient - morph a recipient address */
 
439
 
 
440
static void morph_recipient(VSTRING *buf, const char *address, int flags)
 
441
{
 
442
    char   *cp;
 
443
 
 
444
    /*
 
445
     * Quote the recipient address as appropriate.
 
446
     */
 
447
    if (flags & PIPE_OPT_QUOTE_LOCAL)
 
448
        quote_822_local(buf, address);
 
449
    else
 
450
        vstring_strcpy(buf, address);
 
451
 
 
452
    /*
 
453
     * Fold the recipient address as appropriate.
 
454
     */
 
455
    switch (flags & PIPE_OPT_FOLD_FLAGS) {
 
456
    case PIPE_OPT_FOLD_HOST:
 
457
        if ((cp = strrchr(STR(buf), '@')) != 0)
 
458
            lowercase(cp + 1);
 
459
        break;
 
460
    case PIPE_OPT_FOLD_USER:
 
461
        if ((cp = strrchr(STR(buf), '@')) != 0) {
 
462
            *cp = 0;
 
463
            lowercase(STR(buf));
 
464
            *cp = '@';
 
465
            break;
 
466
        }
 
467
    case PIPE_OPT_FOLD_USER | PIPE_OPT_FOLD_HOST:
 
468
        lowercase(STR(buf));
 
469
        break;
 
470
    }
 
471
}
 
472
 
 
473
/* expand_argv - expand macros in the argument vector */
 
474
 
 
475
static ARGV *expand_argv(const char *service, char **argv,
 
476
                                 RECIPIENT_LIST *rcpt_list, int flags)
 
477
{
 
478
    VSTRING *buf = vstring_alloc(100);
 
479
    ARGV   *result;
 
480
    char  **cpp;
 
481
    PIPE_STATE state;
 
482
    int     i;
 
483
    char   *ext;
 
484
 
 
485
    /*
 
486
     * This appears to be simple operation (replace $name by its expansion).
 
487
     * However, it becomes complex because a command-line argument that
 
488
     * references $recipient must expand to as many command-line arguments as
 
489
     * there are recipients (that's wat programs called by sendmail expect).
 
490
     * So we parse each command-line argument, and depending on what we find,
 
491
     * we either expand the argument just once, or we expand it once for each
 
492
     * recipient. In either case we end up parsing the command-line argument
 
493
     * twice. The amount of CPU time wasted will be negligible.
 
494
     * 
 
495
     * Note: we can't use recursive macro expansion here, because recursion
 
496
     * would screw up mail addresses that contain $ characters.
 
497
     */
 
498
#define NO      0
 
499
#define EARLY_RETURN(x) { argv_free(result); vstring_free(buf); return (x); }
 
500
 
 
501
    result = argv_alloc(1);
 
502
    for (cpp = argv; *cpp; cpp++) {
 
503
        state.service = service;
 
504
        state.expand_flag = 0;
 
505
        if (mac_parse(*cpp, parse_callback, (char *) &state) & MAC_PARSE_ERROR)
 
506
            EARLY_RETURN(0);
 
507
        if (state.expand_flag == 0) {           /* no $recipient etc. */
 
508
            argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
 
509
        } else {                                /* contains $recipient etc. */
 
510
            for (i = 0; i < rcpt_list->len; i++) {
 
511
 
 
512
                /*
 
513
                 * This argument contains $recipient.
 
514
                 */
 
515
                if (state.expand_flag & PIPE_FLAG_RCPT) {
 
516
                    morph_recipient(buf, rcpt_list->info[i].address, flags);
 
517
                    dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, STR(buf));
 
518
                }
 
519
 
 
520
                /*
 
521
                 * This argument contains $user. Extract the plain user name.
 
522
                 * Either anything to the left of the extension delimiter or,
 
523
                 * in absence of the latter, anything to the left of the
 
524
                 * rightmost @.
 
525
                 * 
 
526
                 * Beware: if the user name is blank (e.g. +user@host), the
 
527
                 * argument is suppressed. This is necessary to allow for
 
528
                 * cyrus bulletin-board (global mailbox) delivery. XXX But,
 
529
                 * skipping empty user parts will also prevent other
 
530
                 * expansions of this specific command-line argument.
 
531
                 */
 
532
                if (state.expand_flag & PIPE_FLAG_USER) {
 
533
                    morph_recipient(buf, rcpt_list->info[i].address,
 
534
                                    flags & PIPE_OPT_FOLD_FLAGS);
 
535
                    if (split_at_right(STR(buf), '@') == 0)
 
536
                        msg_warn("no @ in recipient address: %s",
 
537
                                 rcpt_list->info[i].address);
 
538
                    if (*var_rcpt_delim)
 
539
                        split_addr(STR(buf), *var_rcpt_delim);
 
540
                    if (*STR(buf) == 0)
 
541
                        continue;
 
542
                    dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
 
543
                }
 
544
 
 
545
                /*
 
546
                 * This argument contains $extension. Extract the recipient
 
547
                 * extension: anything between the leftmost extension
 
548
                 * delimiter and the rightmost @. The extension may be blank.
 
549
                 */
 
550
                if (state.expand_flag & PIPE_FLAG_EXTENSION) {
 
551
                    morph_recipient(buf, rcpt_list->info[i].address,
 
552
                                    flags & PIPE_OPT_FOLD_FLAGS);
 
553
                    if (split_at_right(STR(buf), '@') == 0)
 
554
                        msg_warn("no @ in recipient address: %s",
 
555
                                 rcpt_list->info[i].address);
 
556
                    if (*var_rcpt_delim == 0
 
557
                      || (ext = split_addr(STR(buf), *var_rcpt_delim)) == 0)
 
558
                        ext = "";               /* insert null arg */
 
559
                    dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext);
 
560
                }
 
561
 
 
562
                /*
 
563
                 * This argument contains $mailbox. Extract the mailbox name:
 
564
                 * anything to the left of the rightmost @.
 
565
                 */
 
566
                if (state.expand_flag & PIPE_FLAG_MAILBOX) {
 
567
                    morph_recipient(buf, rcpt_list->info[i].address,
 
568
                                    flags & PIPE_OPT_FOLD_FLAGS);
 
569
                    if (split_at_right(STR(buf), '@') == 0)
 
570
                        msg_warn("no @ in recipient address: %s",
 
571
                                 rcpt_list->info[i].address);
 
572
                    dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
 
573
                }
 
574
 
 
575
                /*
 
576
                 * Done.
 
577
                 */
 
578
                argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
 
579
            }
 
580
        }
 
581
    }
 
582
    argv_terminate(result);
 
583
    vstring_free(buf);
 
584
    return (result);
 
585
}
 
586
 
 
587
/* get_service_params - get service-name dependent config information */
 
588
 
 
589
static void get_service_params(PIPE_PARAMS *config, char *service)
 
590
{
 
591
    char   *myname = "get_service_params";
 
592
 
 
593
    /*
 
594
     * Figure out the command time limit for this transport.
 
595
     */
 
596
    config->time_limit =
 
597
        get_mail_conf_int2(service, "_time_limit", var_command_maxtime, 1, 0);
 
598
 
 
599
    /*
 
600
     * Give the poor tester a clue of what is going on.
 
601
     */
 
602
    if (msg_verbose)
 
603
        msg_info("%s: time_limit %d", myname, config->time_limit);
 
604
}
 
605
 
 
606
/* get_service_attr - get command-line attributes */
 
607
 
 
608
static void get_service_attr(PIPE_ATTR *attr, char **argv)
 
609
{
 
610
    char   *myname = "get_service_attr";
 
611
    struct passwd *pwd;
 
612
    struct group *grp;
 
613
    char   *user;                       /* user name */
 
614
    char   *group;                      /* group name */
 
615
    char   *size;                       /* max message size */
 
616
    char   *cp;
 
617
 
 
618
    /*
 
619
     * Initialize.
 
620
     */
 
621
    user = 0;
 
622
    group = 0;
 
623
    attr->command = 0;
 
624
    attr->flags = 0;
 
625
    attr->eol = vstring_strcpy(vstring_alloc(1), "\n");
 
626
    attr->size_limit = 0;
 
627
 
 
628
    /*
 
629
     * Iterate over the command-line attribute list.
 
630
     */
 
631
    for ( /* void */ ; *argv != 0; argv++) {
 
632
 
 
633
        /*
 
634
         * flags=stuff
 
635
         */
 
636
        if (strncasecmp("flags=", *argv, sizeof("flags=") - 1) == 0) {
 
637
            for (cp = *argv + sizeof("flags=") - 1; *cp; cp++) {
 
638
                switch (*cp) {
 
639
                case 'B':
 
640
                    attr->flags |= MAIL_COPY_BLANK;
 
641
                    break;
 
642
                case 'D':
 
643
                    attr->flags |= MAIL_COPY_DELIVERED;
 
644
                    break;
 
645
                case 'F':
 
646
                    attr->flags |= MAIL_COPY_FROM;
 
647
                    break;
 
648
                case 'O':
 
649
                    attr->flags |= MAIL_COPY_ORIG_RCPT;
 
650
                    break;
 
651
                case 'R':
 
652
                    attr->flags |= MAIL_COPY_RETURN_PATH;
 
653
                    break;
 
654
                case '.':
 
655
                    attr->flags |= MAIL_COPY_DOT;
 
656
                    break;
 
657
                case '>':
 
658
                    attr->flags |= MAIL_COPY_QUOTE;
 
659
                    break;
 
660
                case 'h':
 
661
                    attr->flags |= PIPE_OPT_FOLD_HOST;
 
662
                    break;
 
663
                case 'q':
 
664
                    attr->flags |= PIPE_OPT_QUOTE_LOCAL;
 
665
                    break;
 
666
                case 'u':
 
667
                    attr->flags |= PIPE_OPT_FOLD_USER;
 
668
                    break;
 
669
                default:
 
670
                    msg_fatal("unknown flag: %c (ignored)", *cp);
 
671
                    break;
 
672
                }
 
673
            }
 
674
        }
 
675
 
 
676
        /*
 
677
         * user=username[:groupname]
 
678
         */
 
679
        else if (strncasecmp("user=", *argv, sizeof("user=") - 1) == 0) {
 
680
            user = *argv + sizeof("user=") - 1;
 
681
            if ((group = split_at(user, ':')) != 0)     /* XXX clobbers argv */
 
682
                if (*group == 0)
 
683
                    group = 0;
 
684
            if ((pwd = getpwnam(user)) == 0)
 
685
                msg_fatal("%s: unknown username: %s", myname, user);
 
686
            attr->uid = pwd->pw_uid;
 
687
            if (group != 0) {
 
688
                if ((grp = getgrnam(group)) == 0)
 
689
                    msg_fatal("%s: unknown group: %s", myname, group);
 
690
                attr->gid = grp->gr_gid;
 
691
            } else {
 
692
                attr->gid = pwd->pw_gid;
 
693
            }
 
694
        }
 
695
 
 
696
        /*
 
697
         * eol=string
 
698
         */
 
699
        else if (strncasecmp("eol=", *argv, sizeof("eol=") - 1) == 0) {
 
700
            unescape(attr->eol, *argv + sizeof("eol=") - 1);
 
701
        }
 
702
 
 
703
        /*
 
704
         * size=max_message_size (in bytes)
 
705
         */
 
706
        else if (strncasecmp("size=", *argv, sizeof("size=") - 1) == 0) {
 
707
            size = *argv + sizeof("size=") - 1;
 
708
            if ((attr->size_limit = off_cvt_string(size)) < 0)
 
709
                msg_fatal("%s: bad size= value: %s", myname, size);
 
710
        }
 
711
 
 
712
        /*
 
713
         * argv=command...
 
714
         */
 
715
        else if (strncasecmp("argv=", *argv, sizeof("argv=") - 1) == 0) {
 
716
            *argv += sizeof("argv=") - 1;       /* XXX clobbers argv */
 
717
            attr->command = argv;
 
718
            break;
 
719
        }
 
720
 
 
721
        /*
 
722
         * Bad.
 
723
         */
 
724
        else
 
725
            msg_fatal("unknown attribute name: %s", *argv);
 
726
    }
 
727
 
 
728
    /*
 
729
     * Sanity checks. Verify that every member has an acceptable value.
 
730
     */
 
731
    if (user == 0)
 
732
        msg_fatal("missing user= command-line attribute");
 
733
    if (attr->command == 0)
 
734
        msg_fatal("missing argv= command-line attribute");
 
735
    if (attr->uid == 0)
 
736
        msg_fatal("user= command-line attribute specifies root privileges");
 
737
    if (attr->uid == var_owner_uid)
 
738
        msg_fatal("user= command-line attribute specifies mail system owner %s",
 
739
                  var_mail_owner);
 
740
    if (attr->gid == 0)
 
741
        msg_fatal("user= command-line attribute specifies privileged group id 0");
 
742
    if (attr->gid == var_owner_gid)
 
743
        msg_fatal("user= command-line attribute specifies mail system owner %s group id %ld",
 
744
                  var_mail_owner, (long) attr->gid);
 
745
 
 
746
    /*
 
747
     * Give the poor tester a clue of what is going on.
 
748
     */
 
749
    if (msg_verbose)
 
750
        msg_info("%s: uid %ld, gid %ld, flags %d, size %ld",
 
751
                 myname, (long) attr->uid, (long) attr->gid,
 
752
                 attr->flags, (long) attr->size_limit);
 
753
}
 
754
 
 
755
/* eval_command_status - do something with command completion status */
 
756
 
 
757
static int eval_command_status(int command_status, char *service,
 
758
                                     DELIVER_REQUEST *request, VSTREAM *src,
 
759
                                       char *why)
 
760
{
 
761
    RECIPIENT *rcpt;
 
762
    int     status;
 
763
    int     result = 0;
 
764
    int     n;
 
765
 
 
766
    /*
 
767
     * Depending on the result, bounce or defer the message, and mark the
 
768
     * recipient as done where appropriate.
 
769
     */
 
770
    switch (command_status) {
 
771
    case PIPE_STAT_OK:
 
772
        for (n = 0; n < request->rcpt_list.len; n++) {
 
773
            rcpt = request->rcpt_list.info + n;
 
774
            status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
 
775
                          request->queue_id, rcpt->orig_addr,
 
776
                          rcpt->address, rcpt->offset, service,
 
777
                          request->arrival_time, "%s", request->nexthop);
 
778
            if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
 
779
                deliver_completed(src, rcpt->offset);
 
780
            result |= status;
 
781
        }
 
782
        break;
 
783
    case PIPE_STAT_BOUNCE:
 
784
        for (n = 0; n < request->rcpt_list.len; n++) {
 
785
            rcpt = request->rcpt_list.info + n;
 
786
            status = bounce_append(DEL_REQ_TRACE_FLAGS(request->flags),
 
787
                                   request->queue_id, rcpt->orig_addr,
 
788
                                   rcpt->address, rcpt->offset, service,
 
789
                                   request->arrival_time, "%s", why);
 
790
            if (status == 0)
 
791
                deliver_completed(src, rcpt->offset);
 
792
            result |= status;
 
793
        }
 
794
        break;
 
795
    case PIPE_STAT_DEFER:
 
796
        for (n = 0; n < request->rcpt_list.len; n++) {
 
797
            rcpt = request->rcpt_list.info + n;
 
798
            result |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
 
799
                                   request->queue_id, rcpt->orig_addr,
 
800
                                   rcpt->address, rcpt->offset, service,
 
801
                                   request->arrival_time, "%s", why);
 
802
        }
 
803
        break;
 
804
    case PIPE_STAT_CORRUPT:
 
805
        result |= DEL_STAT_DEFER;
 
806
        break;
 
807
    default:
 
808
        msg_panic("eval_command_status: bad status %d", command_status);
 
809
        /* NOTREACHED */
 
810
    }
 
811
    return (result);
 
812
}
 
813
 
 
814
/* deliver_message - deliver message with extreme prejudice */
 
815
 
 
816
static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
 
817
{
 
818
    char   *myname = "deliver_message";
 
819
    static PIPE_PARAMS conf;
 
820
    static PIPE_ATTR attr;
 
821
    RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
 
822
    VSTRING *why = vstring_alloc(100);
 
823
    VSTRING *buf;
 
824
    ARGV   *expanded_argv = 0;
 
825
    int     deliver_status;
 
826
    int     command_status;
 
827
    ARGV   *export_env;
 
828
 
 
829
#define DELIVER_MSG_CLEANUP() { \
 
830
        vstring_free(why); \
 
831
        if (expanded_argv) argv_free(expanded_argv); \
 
832
    }
 
833
 
 
834
    if (msg_verbose)
 
835
        msg_info("%s: from <%s>", myname, request->sender);
 
836
 
 
837
    /*
 
838
     * First of all, replace an empty sender address by the mailer daemon
 
839
     * address. The resolver already fixes empty recipient addresses.
 
840
     * 
 
841
     * XXX Should sender and recipient be transformed into external (i.e.
 
842
     * quoted) form? Problem is that the quoting rules are transport
 
843
     * specific. Such information must evidently not be hard coded into
 
844
     * Postfix, but would have to be provided in the form of lookup tables.
 
845
     */
 
846
    if (request->sender[0] == 0) {
 
847
        buf = vstring_alloc(100);
 
848
        canon_addr_internal(buf, MAIL_ADDR_MAIL_DAEMON);
 
849
        myfree(request->sender);
 
850
        request->sender = vstring_export(buf);
 
851
    }
 
852
 
 
853
    /*
 
854
     * Sanity checks. The get_service_params() and get_service_attr()
 
855
     * routines also do some sanity checks. Look up service attributes and
 
856
     * config information only once. This is safe since the information comes
 
857
     * from a trusted source, not from the delivery request.
 
858
     */
 
859
    if (request->nexthop[0] == 0)
 
860
        msg_fatal("empty nexthop hostname");
 
861
    if (rcpt_list->len <= 0)
 
862
        msg_fatal("recipient count: %d", rcpt_list->len);
 
863
    if (attr.command == 0) {
 
864
        get_service_params(&conf, service);
 
865
        get_service_attr(&attr, argv);
 
866
    }
 
867
 
 
868
    /*
 
869
     * The D flag cannot be specified for multi-recipient deliveries.
 
870
     */
 
871
    if ((attr.flags & MAIL_COPY_DELIVERED) && (rcpt_list->len > 1)) {
 
872
        deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
 
873
                                             request, request->fp,
 
874
                                             "mailer configuration error");
 
875
        msg_warn("pipe flag `D' requires %s_destination_recipient_limit = 1",
 
876
                 service);
 
877
        DELIVER_MSG_CLEANUP();
 
878
        return (deliver_status);
 
879
    }
 
880
 
 
881
    /*
 
882
     * The O flag cannot be specified for multi-recipient deliveries.
 
883
     */
 
884
    if ((attr.flags & MAIL_COPY_ORIG_RCPT) && (rcpt_list->len > 1)) {
 
885
        deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
 
886
                                             request, request->fp,
 
887
                                             "mailer configuration error");
 
888
        msg_warn("pipe flag `O' requires %s_destination_recipient_limit = 1",
 
889
                 service);
 
890
        DELIVER_MSG_CLEANUP();
 
891
        return (deliver_status);
 
892
    }
 
893
 
 
894
    /*
 
895
     * Check that this agent accepts messages this large.
 
896
     */
 
897
    if (attr.size_limit != 0 && request->data_size > attr.size_limit) {
 
898
        if (msg_verbose)
 
899
            msg_info("%s: too big: size_limit = %ld, request->data_size = %ld",
 
900
                     myname, (long) attr.size_limit, request->data_size);
 
901
 
 
902
        deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
 
903
                                 request, request->fp, "message too large");
 
904
        DELIVER_MSG_CLEANUP();
 
905
        return (deliver_status);
 
906
    }
 
907
 
 
908
    /*
 
909
     * Don't deliver a trace-only request.
 
910
     */
 
911
    if (DEL_REQ_TRACE_ONLY(request->flags)) {
 
912
        RECIPIENT *rcpt;
 
913
        int     status;
 
914
        int     n;
 
915
 
 
916
        deliver_status = 0;
 
917
        for (n = 0; n < request->rcpt_list.len; n++) {
 
918
            rcpt = request->rcpt_list.info + n;
 
919
            status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
 
920
                          request->queue_id, rcpt->orig_addr,
 
921
                          rcpt->address, rcpt->offset, service,
 
922
                          request->arrival_time,
 
923
                          "delivers to command: %s", attr.command[0]);
 
924
            if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
 
925
                deliver_completed(request->fp, rcpt->offset);
 
926
            deliver_status |= status;
 
927
        }
 
928
        DELIVER_MSG_CLEANUP();
 
929
        return (deliver_status);
 
930
    }
 
931
 
 
932
    /*
 
933
     * Deliver. Set the nexthop and sender variables, and expand the command
 
934
     * argument vector. Recipients will be expanded on the fly. XXX Rewrite
 
935
     * envelope and header addresses according to transport-specific
 
936
     * rewriting rules.
 
937
     */
 
938
    if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0)
 
939
        msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp));
 
940
 
 
941
    buf = vstring_alloc(10);
 
942
    if (attr.flags & PIPE_OPT_QUOTE_LOCAL) {
 
943
        quote_822_local(buf, request->sender);
 
944
        dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf));
 
945
    } else
 
946
        dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender);
 
947
    if (attr.flags & PIPE_OPT_FOLD_HOST) {
 
948
        vstring_strcpy(buf, request->nexthop);
 
949
        lowercase(STR(buf));
 
950
        dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, STR(buf));
 
951
    } else
 
952
        dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
 
953
    vstring_sprintf(buf, "%ld", (long) request->data_size);
 
954
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
 
955
    vstring_free(buf);
 
956
 
 
957
    if ((expanded_argv = expand_argv(service, attr.command,
 
958
                                     rcpt_list, attr.flags)) == 0) {
 
959
        deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
 
960
                                             request, request->fp,
 
961
                                             "mailer configuration error");
 
962
        DELIVER_MSG_CLEANUP();
 
963
        return (deliver_status);
 
964
    }
 
965
    export_env = argv_split(var_export_environ, ", \t\r\n");
 
966
 
 
967
    command_status = pipe_command(request->fp, why,
 
968
                                  PIPE_CMD_UID, attr.uid,
 
969
                                  PIPE_CMD_GID, attr.gid,
 
970
                                  PIPE_CMD_SENDER, request->sender,
 
971
                                  PIPE_CMD_COPY_FLAGS, attr.flags,
 
972
                                  PIPE_CMD_ARGV, expanded_argv->argv,
 
973
                                  PIPE_CMD_TIME_LIMIT, conf.time_limit,
 
974
                                  PIPE_CMD_EOL, STR(attr.eol),
 
975
                                  PIPE_CMD_EXPORT, export_env->argv,
 
976
                           PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr,
 
977
                             PIPE_CMD_DELIVERED, rcpt_list->info[0].address,
 
978
                                  PIPE_CMD_END);
 
979
    argv_free(export_env);
 
980
 
 
981
    deliver_status = eval_command_status(command_status, service, request,
 
982
                                         request->fp, vstring_str(why));
 
983
 
 
984
    /*
 
985
     * Clean up.
 
986
     */
 
987
    DELIVER_MSG_CLEANUP();
 
988
 
 
989
    return (deliver_status);
 
990
}
 
991
 
 
992
/* pipe_service - perform service for client */
 
993
 
 
994
static void pipe_service(VSTREAM *client_stream, char *service, char **argv)
 
995
{
 
996
    DELIVER_REQUEST *request;
 
997
    int     status;
 
998
 
 
999
    /*
 
1000
     * This routine runs whenever a client connects to the UNIX-domain socket
 
1001
     * dedicated to delivery via external command. What we see below is a
 
1002
     * little protocol to (1) tell the queue manager that we are ready, (2)
 
1003
     * read a request from the queue manager, and (3) report the completion
 
1004
     * status of that request. All connection-management stuff is handled by
 
1005
     * the common code in single_server.c.
 
1006
     */
 
1007
    if ((request = deliver_request_read(client_stream)) != 0) {
 
1008
        status = deliver_message(request, service, argv);
 
1009
        deliver_request_done(client_stream, request, status);
 
1010
    }
 
1011
}
 
1012
 
 
1013
/* pre_accept - see if tables have changed */
 
1014
 
 
1015
static void pre_accept(char *unused_name, char **unused_argv)
 
1016
{
 
1017
    const char *table;
 
1018
 
 
1019
    if ((table = dict_changed_name()) != 0) {
 
1020
        msg_info("table %s has changed -- restarting", table);
 
1021
        exit(0);
 
1022
    }
 
1023
}
 
1024
 
 
1025
/* drop_privileges - drop privileges most of the time */
 
1026
 
 
1027
static void drop_privileges(char *unused_name, char **unused_argv)
 
1028
{
 
1029
    set_eugid(var_owner_uid, var_owner_gid);
 
1030
}
 
1031
 
 
1032
/* pre_init - initialize */
 
1033
 
 
1034
static void pre_init(char *unused_name, char **unused_argv)
 
1035
{
 
1036
    flush_init();
 
1037
}
 
1038
 
 
1039
/* main - pass control to the single-threaded skeleton */
 
1040
 
 
1041
int     main(int argc, char **argv)
 
1042
{
 
1043
    static CONFIG_TIME_TABLE time_table[] = {
 
1044
        VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
 
1045
        0,
 
1046
    };
 
1047
 
 
1048
    single_server_main(argc, argv, pipe_service,
 
1049
                       MAIL_SERVER_TIME_TABLE, time_table,
 
1050
                       MAIL_SERVER_PRE_INIT, pre_init,
 
1051
                       MAIL_SERVER_POST_INIT, drop_privileges,
 
1052
                       MAIL_SERVER_PRE_ACCEPT, pre_accept,
 
1053
                       0);
 
1054
}