1 /*        $NetBSD: pickup.c,v 1.5 2025/02/25 19:15:47 christos Exp $  */
2 
3 /*++
4 /* NAME
5 /*        pickup 8
6 /* SUMMARY
7 /*        Postfix local mail pickup
8 /* SYNOPSIS
9 /*        \fBpickup\fR [generic Postfix daemon options]
10 /* DESCRIPTION
11 /*        The \fBpickup\fR(8) daemon waits for hints that new mail has been
12 /*        dropped into the \fBmaildrop\fR directory, and feeds it into the
13 /*        \fBcleanup\fR(8) daemon.
14 /*        Ill-formatted files are deleted without notifying the originator.
15 /*        This program expects to be run from the \fBmaster\fR(8) process
16 /*        manager.
17 /* STANDARDS
18 /* .ad
19 /* .fi
20 /*        None. The \fBpickup\fR(8) daemon does not interact with
21 /*        the outside world.
22 /* SECURITY
23 /* .ad
24 /* .fi
25 /*        The \fBpickup\fR(8) daemon is moderately security sensitive. It runs
26 /*        with fixed low privilege and can run in a chrooted environment.
27 /*        However, the program reads files from potentially hostile users.
28 /*        The \fBpickup\fR(8) daemon opens no files for writing, is careful about
29 /*        what files it opens for reading, and does not actually touch any data
30 /*        that is sent to its public service endpoint.
31 /* DIAGNOSTICS
32 /*        Problems and transactions are logged to \fBsyslogd\fR(8)
33 /*        or \fBpostlogd\fR(8).
34 /* BUGS
35 /*        The \fBpickup\fR(8) daemon copies mail from file to the \fBcleanup\fR(8)
36 /*        daemon.  It could avoid message copying overhead by sending a file
37 /*        descriptor instead of file data, but then the already complex
38 /*        \fBcleanup\fR(8) daemon would have to deal with unfiltered user data.
39 /* CONFIGURATION PARAMETERS
40 /* .ad
41 /* .fi
42 /*        As the \fBpickup\fR(8) daemon is a relatively long-running process, up
43 /*        to an hour may pass before a \fBmain.cf\fR change takes effect.
44 /*        Use the command "\fBpostfix reload\fR" command to speed up a change.
45 /*
46 /*        The text below provides only a parameter summary. See
47 /*        \fBpostconf\fR(5) for more details including examples.
48 /* CONTENT INSPECTION CONTROLS
49 /* .ad
50 /* .fi
51 /* .IP "\fBcontent_filter (empty)\fR"
52 /*        After the message is queued, send the entire message to the
53 /*        specified \fItransport:destination\fR.
54 /* .IP "\fBreceive_override_options (empty)\fR"
55 /*        Enable or disable recipient validation, built-in content
56 /*        filtering, or address mapping.
57 /* MISCELLANEOUS CONTROLS
58 /* .ad
59 /* .fi
60 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
61 /*        The default location of the Postfix main.cf and master.cf
62 /*        configuration files.
63 /* .IP "\fBipc_timeout (3600s)\fR"
64 /*        The time limit for sending or receiving information over an internal
65 /*        communication channel.
66 /* .IP "\fBline_length_limit (2048)\fR"
67 /*        Upon input, long lines are chopped up into pieces of at most
68 /*        this length; upon delivery, long lines are reconstructed.
69 /* .IP "\fBmax_idle (100s)\fR"
70 /*        The maximum amount of time that an idle Postfix daemon process waits
71 /*        for an incoming connection before terminating voluntarily.
72 /* .IP "\fBmax_use (100)\fR"
73 /*        The maximal number of incoming connections that a Postfix daemon
74 /*        process will service before terminating voluntarily.
75 /* .IP "\fBprocess_id (read-only)\fR"
76 /*        The process ID of a Postfix command or daemon process.
77 /* .IP "\fBprocess_name (read-only)\fR"
78 /*        The process name of a Postfix command or daemon process.
79 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
80 /*        The location of the Postfix top-level queue directory.
81 /* .IP "\fBsyslog_facility (mail)\fR"
82 /*        The syslog facility of Postfix logging.
83 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
84 /*        A prefix that is prepended to the process name in syslog
85 /*        records, so that, for example, "smtpd" becomes "prefix/smtpd".
86 /* .PP
87 /*        Available in Postfix 3.3 and later:
88 /* .IP "\fBservice_name (read-only)\fR"
89 /*        The master.cf service name of a Postfix daemon process.
90 /* .PP
91 /*        Available in Postfix 3.5 and later:
92 /* .IP "\fBinfo_log_address_format (external)\fR"
93 /*        The email address form that will be used in non-debug logging
94 /*        (info, warning, etc.).
95 /* SEE ALSO
96 /*        cleanup(8), message canonicalization
97 /*        sendmail(1), Sendmail-compatible interface
98 /*        postdrop(1), mail posting agent
99 /*        postconf(5), configuration parameters
100 /*        master(5), generic daemon options
101 /*        master(8), process manager
102 /*        postlogd(8), Postfix logging
103 /*        syslogd(8), system logging
104 /* LICENSE
105 /* .ad
106 /* .fi
107 /*        The Secure Mailer license must be distributed with this software.
108 /* AUTHOR(S)
109 /*        Wietse Venema
110 /*        IBM T.J. Watson Research
111 /*        P.O. Box 704
112 /*        Yorktown Heights, NY 10598, USA
113 /*
114 /*        Wietse Venema
115 /*        Google, Inc.
116 /*        111 8th Avenue
117 /*        New York, NY 10011, USA
118 /*--*/
119 
120 /* System library. */
121 
122 #include <sys_defs.h>
123 #include <sys/stat.h>
124 #include <dirent.h>
125 #include <unistd.h>
126 #include <stdlib.h>
127 #include <time.h>
128 #include <string.h>
129 #include <fcntl.h>
130 #include <errno.h>
131 #include <ctype.h>
132 
133 /* Utility library. */
134 
135 #include <msg.h>
136 #include <scan_dir.h>
137 #include <vstring.h>
138 #include <vstream.h>
139 #include <set_ugid.h>
140 #include <safe_open.h>
141 #include <watchdog.h>
142 #include <stringops.h>
143 
144 /* Global library. */
145 
146 #include <mail_queue.h>
147 #include <mail_open_ok.h>
148 #include <mymalloc.h>
149 #include <mail_proto.h>
150 #include <cleanup_user.h>
151 #include <mail_date.h>
152 #include <mail_params.h>
153 #include <mail_conf.h>
154 #include <record.h>
155 #include <rec_type.h>
156 #include <lex_822.h>
157 #include <input_transp.h>
158 #include <rec_attr_map.h>
159 #include <mail_version.h>
160 #include <smtputf8.h>
161 #include <info_log_addr_form.h>
162 
163 /* Single-threaded server skeleton. */
164 
165 #include <mail_server.h>
166 
167 /* Application-specific. */
168 
169 char   *var_filter_xport;
170 char   *var_input_transp;
171 
172  /*
173   * Structure to bundle a bunch of information about a queue file.
174   */
175 typedef struct {
176     char   *id;                                   /* queue file basename */
177     struct stat st;                     /* queue file status */
178     char   *path;                       /* name for open/remove */
179     char   *sender;                     /* sender address */
180 } PICKUP_INFO;
181 
182  /*
183   * What action should be taken after attempting to deliver a message: remove
184   * the file from the maildrop, or leave it alone. The latter is also used
185   * for files that are still being written to.
186   */
187 #define REMOVE_MESSAGE_FILE   1
188 #define KEEP_MESSAGE_FILE     2
189 
190  /*
191   * Transparency: before mail is queued, do we allow address mapping,
192   * automatic bcc, header/body checks?
193   */
194 int     pickup_input_transp_mask;
195 
196 /* file_read_error - handle error while reading queue file */
197 
file_read_error(PICKUP_INFO * info,int type)198 static int file_read_error(PICKUP_INFO *info, int type)
199 {
200     msg_warn("uid=%ld: unexpected or malformed record type %d",
201                (long) info->st.st_uid, type);
202     return (REMOVE_MESSAGE_FILE);
203 }
204 
205 /* cleanup_service_error_reason - handle error writing to cleanup service. */
206 
cleanup_service_error_reason(PICKUP_INFO * info,int status,const char * reason)207 static int cleanup_service_error_reason(PICKUP_INFO *info, int status,
208                                                           const char *reason)
209 {
210 
211     /*
212      * XXX If the cleanup server gave a reason, then it was already logged.
213      * Don't bother logging it another time.
214      *
215      * XXX Discard a message without recipient. This can happen with "postsuper
216      * -r" when a message is already delivered (or bounced). The Postfix
217      * sendmail command rejects submissions without recipients.
218      */
219     if (reason == 0 || *reason == 0)
220           msg_warn("%s: error writing %s: %s",
221                      info->path, info->id, cleanup_strerror(status));
222     return ((status & (CLEANUP_STAT_BAD | CLEANUP_STAT_RCPT)) ?
223               REMOVE_MESSAGE_FILE : KEEP_MESSAGE_FILE);
224 }
225 
226 #define cleanup_service_error(info, status) \
227           cleanup_service_error_reason((info), (status), (char *) 0)
228 
229 /* copy_segment - copy a record group */
230 
copy_segment(VSTREAM * qfile,VSTREAM * cleanup,PICKUP_INFO * info,VSTRING * buf,char * expected)231 static int copy_segment(VSTREAM *qfile, VSTREAM *cleanup, PICKUP_INFO *info,
232                                       VSTRING *buf, char *expected)
233 {
234     int     type;
235     int     check_first = (*expected == REC_TYPE_CONTENT[0]);
236     int     time_seen = 0;
237     char   *attr_name;
238     char   *attr_value;
239     char   *saved_attr;
240     int     skip_attr;
241 
242     /*
243      * Limit the input record size. All front-end programs should protect the
244      * mail system against unreasonable inputs. This also requires that we
245      * limit the size of envelope records written by the local posting agent.
246      *
247      * Records with named attributes are filtered by postdrop(1).
248      *
249      * We must allow PTR records here because of "postsuper -r".
250      */
251     for (;;) {
252           if ((type = rec_get(qfile, buf, var_line_limit)) < 0
253               || strchr(expected, type) == 0)
254               return (file_read_error(info, type));
255           if (msg_verbose)
256               msg_info("%s: read %c %s", info->id, type, vstring_str(buf));
257           if (type == *expected)
258               break;
259           if (type == REC_TYPE_FROM) {
260               if (info->sender == 0)
261                     info->sender = mystrdup(vstring_str(buf));
262               /* Compatibility with Postfix < 2.3. */
263               if (time_seen == 0)
264                     rec_fprintf(cleanup, REC_TYPE_TIME, "%ld",
265                                   (long) info->st.st_mtime);
266           }
267           if (type == REC_TYPE_TIME)
268               time_seen = 1;
269 
270           /*
271            * XXX Workaround: REC_TYPE_FILT (used in envelopes) == REC_TYPE_CONT
272            * (used in message content).
273            *
274            * As documented in postsuper(1), ignore content filter record.
275            */
276           if (*expected != REC_TYPE_CONTENT[0]) {
277               if (type == REC_TYPE_FILT)
278                     /* Discard FILTER record after "postsuper -r". */
279                     continue;
280               if (type == REC_TYPE_RDR)
281                     /* Discard REDIRECT record after "postsuper -r". */
282                     continue;
283           }
284           if (*expected == REC_TYPE_EXTRACT[0]) {
285               if (type == REC_TYPE_RRTO)
286                     /* Discard return-receipt record after "postsuper -r". */
287                     continue;
288               if (type == REC_TYPE_ERTO)
289                     /* Discard errors-to record after "postsuper -r". */
290                     continue;
291               if (type == REC_TYPE_ATTR) {
292                     saved_attr = mystrdup(vstring_str(buf));
293                     skip_attr = (split_nameval(saved_attr,
294                                                      &attr_name, &attr_value) == 0
295                                    && rec_attr_map(attr_name) == 0);
296                     myfree(saved_attr);
297                     /* Discard other/header/body action after "postsuper -r". */
298                     if (skip_attr)
299                         continue;
300               }
301           }
302 
303           /*
304            * XXX Force an empty record when the queue file content begins with
305            * whitespace, so that it won't be considered as being part of our
306            * own Received: header. What an ugly Kluge.
307            */
308           if (check_first
309               && (type == REC_TYPE_NORM || type == REC_TYPE_CONT)) {
310               check_first = 0;
311               if (VSTRING_LEN(buf) > 0 && IS_SPACE_TAB(vstring_str(buf)[0]))
312                     rec_put(cleanup, REC_TYPE_NORM, "", 0);
313           }
314           if ((REC_PUT_BUF(cleanup, type, buf)) < 0)
315               return (cleanup_service_error(info, CLEANUP_STAT_WRITE));
316     }
317     return (0);
318 }
319 
320 /* pickup_copy - copy message to cleanup service */
321 
pickup_copy(VSTREAM * qfile,VSTREAM * cleanup,PICKUP_INFO * info,VSTRING * buf)322 static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup,
323                                    PICKUP_INFO *info, VSTRING *buf)
324 {
325     time_t  now = time((time_t *) 0);
326     int     status;
327     char   *name;
328 
329     /*
330      * Protect against time-warped time stamps. Warn about mail that has been
331      * queued for an excessive amount of time. Allow for some time drift with
332      * network clients that mount the maildrop remotely - especially clients
333      * that can't get their daylight savings offsets right.
334      */
335 #define DAY_SECONDS 86400
336 #define HOUR_SECONDS 3600
337 
338     if (info->st.st_mtime > now + 2 * HOUR_SECONDS) {
339           msg_warn("%s: message %s dated %ld seconds into the future",
340                      info->id, info->path, (long) (info->st.st_mtime - now));
341           info->st.st_mtime = now;
342     } else if (info->st.st_mtime < now - DAY_SECONDS) {
343           msg_warn("%s: message %s has been queued for %d days",
344                      info->id, info->path,
345                      (int) ((now - info->st.st_mtime) / DAY_SECONDS));
346     }
347 
348     /*
349      * Add content inspection transport. See also postsuper(1).
350      */
351     if (*var_filter_xport)
352           rec_fprintf(cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
353 
354     /*
355      * Copy the message envelope segment. Allow only those records that we
356      * expect to see in the envelope section. The envelope segment must
357      * contain an envelope sender address.
358      */
359     if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_ENVELOPE)) != 0)
360           return (status);
361     if (info->sender == 0) {
362           msg_warn("%s: uid=%ld: no envelope sender",
363                      info->id, (long) info->st.st_uid);
364           return (REMOVE_MESSAGE_FILE);
365     }
366 
367     /*
368      * For messages belonging to $mail_owner also log the maildrop queue id.
369      * This supports message tracking for mail requeued via "postsuper -r".
370      */
371 #define MAIL_IS_REQUEUED(info) \
372     ((info)->st.st_uid == var_owner_uid && ((info)->st.st_mode & S_IROTH) == 0)
373 
374     if (MAIL_IS_REQUEUED(info)) {
375           msg_info("%s: uid=%d from=<%s> orig_id=%s", info->id,
376                (int) info->st.st_uid, info_log_addr_form_sender(info->sender),
377                      ((name = strrchr(info->path, '/')) != 0 ?
378                       name + 1 : info->path));
379     } else {
380           msg_info("%s: uid=%d from=<%s>", info->id,
381               (int) info->st.st_uid, info_log_addr_form_sender(info->sender));
382     }
383 
384     /*
385      * Message content segment. Send a dummy message length. Prepend a
386      * Received: header to the message contents. For tracing purposes,
387      * include the message file ownership, without revealing the login name.
388      */
389     rec_fputs(cleanup, REC_TYPE_MESG, "");
390     rec_fprintf(cleanup, REC_TYPE_NORM, "Received: by %s (%s, from userid %ld)",
391                     var_myhostname, var_mail_name, (long) info->st.st_uid);
392     rec_fprintf(cleanup, REC_TYPE_NORM, "\tid %s; %s", info->id,
393                     mail_date(info->st.st_mtime));
394 
395     /*
396      * Copy the message content segment. Allow only those records that we
397      * expect to see in the message content section.
398      */
399     if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_CONTENT)) != 0)
400           return (status);
401 
402     /*
403      * Send the segment with information extracted from message headers.
404      * Permit a non-empty extracted segment, so that list manager software
405      * can to output recipients after the message, and so that sysadmins can
406      * re-inject messages after a change of configuration.
407      */
408     rec_fputs(cleanup, REC_TYPE_XTRA, "");
409     if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_EXTRACT)) != 0)
410           return (status);
411 
412     /*
413      * There are no errors. Send the end-of-data marker, and get the cleanup
414      * service completion status. XXX Since the pickup service is unable to
415      * bounce, the cleanup service can report only soft errors here.
416      */
417     rec_fputs(cleanup, REC_TYPE_END, "");
418     if (attr_scan(cleanup, ATTR_FLAG_MISSING,
419                       RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
420                       RECV_ATTR_STR(MAIL_ATTR_WHY, buf),
421                       ATTR_TYPE_END) != 2)
422           return (cleanup_service_error(info, CLEANUP_STAT_WRITE));
423 
424     /*
425      * Depending on the cleanup service completion status, delete the message
426      * file, or try again later. Bounces are dealt with by the cleanup
427      * service itself. The master process wakes up the cleanup service every
428      * now and then.
429      */
430     if (status) {
431           return (cleanup_service_error_reason(info, status, vstring_str(buf)));
432     } else {
433           return (REMOVE_MESSAGE_FILE);
434     }
435 }
436 
437 /* pickup_file - initialize for file copy and cleanup */
438 
pickup_file(PICKUP_INFO * info)439 static int pickup_file(PICKUP_INFO *info)
440 {
441     VSTRING *buf = vstring_alloc(100);
442     int     status;
443     VSTREAM *qfile;
444     VSTREAM *cleanup;
445     int     cleanup_flags;
446 
447     /*
448      * Open the submitted file. If we cannot open it, and we're not having a
449      * file descriptor leak problem, delete the submitted file, so that we
450      * won't keep complaining about the same file again and again. XXX
451      * Perhaps we should save "bad" files elsewhere for further inspection.
452      * XXX How can we delete a file when open() fails with ENOENT?
453      */
454     qfile = safe_open(info->path, O_RDONLY | O_NONBLOCK, 0,
455                           (struct stat *) 0, -1, -1, buf);
456     if (qfile == 0) {
457           if (errno != ENOENT)
458               msg_warn("open input file %s: %s", info->path, vstring_str(buf));
459           vstring_free(buf);
460           if (errno == EACCES)
461               msg_warn("if this file was created by Postfix < 1.1, then you may have to chmod a+r %s/%s",
462                          var_queue_dir, info->path);
463           return (errno == EACCES ? KEEP_MESSAGE_FILE : REMOVE_MESSAGE_FILE);
464     }
465 
466     /*
467      * Contact the cleanup service and read the queue ID that it has
468      * allocated. In case of trouble, request that the cleanup service
469      * bounces its copy of the message. because the original input file is
470      * not readable by the bounce service.
471      *
472      * If mail is re-injected with "postsuper -r", disable Milter applications.
473      * If they were run before the mail was queued then there is no need to
474      * run them again. Moreover, the queue file does not contain enough
475      * information to reproduce the exact same SMTP events and Sendmail
476      * macros that Milters received when the mail originally arrived in
477      * Postfix.
478      *
479      * The actual message copying code is in a separate routine, so that it is
480      * easier to implement the many possible error exits without forgetting
481      * to close files, or to release memory.
482      */
483     cleanup_flags =
484           input_transp_cleanup(CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_EXTERNAL,
485                                    pickup_input_transp_mask);
486     /* As documented in postsuper(1). */
487     if (MAIL_IS_REQUEUED(info))
488           cleanup_flags &= ~CLEANUP_FLAG_MILTER;
489     else
490           cleanup_flags |= smtputf8_autodetect(MAIL_SRC_MASK_SENDMAIL);
491     /* TODO(wietse) REQUIRETLS? */
492 
493     cleanup = mail_connect_wait(MAIL_CLASS_PUBLIC, var_cleanup_service);
494     if (attr_scan(cleanup, ATTR_FLAG_STRICT,
495                       RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_CLEANUP),
496                       RECV_ATTR_STR(MAIL_ATTR_QUEUEID, buf),
497                       ATTR_TYPE_END) != 1
498           || attr_print(cleanup, ATTR_FLAG_NONE,
499                           SEND_ATTR_INT(MAIL_ATTR_FLAGS, cleanup_flags),
500                           ATTR_TYPE_END) != 0) {
501           status = KEEP_MESSAGE_FILE;
502     } else {
503           info->id = mystrdup(vstring_str(buf));
504           status = pickup_copy(qfile, cleanup, info, buf);
505     }
506     vstream_fclose(qfile);
507     vstream_fclose(cleanup);
508     vstring_free(buf);
509     return (status);
510 }
511 
512 /* pickup_init - init info structure */
513 
pickup_init(PICKUP_INFO * info)514 static void pickup_init(PICKUP_INFO *info)
515 {
516     info->id = 0;
517     info->path = 0;
518     info->sender = 0;
519 }
520 
521 /* pickup_free - wipe info structure */
522 
pickup_free(PICKUP_INFO * info)523 static void pickup_free(PICKUP_INFO *info)
524 {
525 #define SAFE_FREE(x) { if (x) myfree(x); }
526 
527     SAFE_FREE(info->id);
528     SAFE_FREE(info->path);
529     SAFE_FREE(info->sender);
530 }
531 
532 /* pickup_service - service client */
533 
pickup_service(char * unused_buf,ssize_t unused_len,char * unused_service,char ** argv)534 static void pickup_service(char *unused_buf, ssize_t unused_len,
535                                          char *unused_service, char **argv)
536 {
537     SCAN_DIR *scan;
538     char   *queue_name;
539     PICKUP_INFO info;
540     const char *path;
541     char   *id;
542     int     file_count;
543 
544     /*
545      * Sanity check. This service takes no command-line arguments.
546      */
547     if (argv[0])
548           msg_fatal("unexpected command-line argument: %s", argv[0]);
549 
550     /*
551      * Skip over things that we don't want to open, such as files that are
552      * still being written, or garbage. Leave it up to the sysadmin to remove
553      * garbage. Keep scanning the queue directory until we stop removing
554      * files from it.
555      *
556      * When we find a file, stroke the watchdog so that it will not bark while
557      * some application is keeping us busy by injecting lots of mail into the
558      * maildrop directory.
559      */
560     queue_name = MAIL_QUEUE_MAILDROP;             /* XXX should be a list */
561     do {
562           file_count = 0;
563           scan = scan_dir_open(queue_name);
564           while ((id = scan_dir_next(scan)) != 0) {
565               if (mail_open_ok(queue_name, id, &info.st, &path) == MAIL_OPEN_YES) {
566                     pickup_init(&info);
567                     info.path = mystrdup(path);
568                     watchdog_pat();
569                     if (pickup_file(&info) == REMOVE_MESSAGE_FILE) {
570                         if (REMOVE(info.path))
571                               msg_warn("remove %s: %m", info.path);
572                         else
573                               file_count++;
574                     }
575                     pickup_free(&info);
576               }
577           }
578           scan_dir_close(scan);
579     } while (file_count);
580 }
581 
582 /* post_jail_init - drop privileges */
583 
post_jail_init(char * unused_name,char ** unused_argv)584 static void post_jail_init(char *unused_name, char **unused_argv)
585 {
586 
587     /*
588      * In case master.cf was not updated for unprivileged service.
589      */
590     if (getuid() != var_owner_uid)
591           set_ugid(var_owner_uid, var_owner_gid);
592 
593     /*
594      * Initialize the receive transparency options: do we want unknown
595      * recipient checks, do we want address mapping.
596      */
597     pickup_input_transp_mask =
598           input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
599 }
600 
601 MAIL_VERSION_STAMP_DECLARE;
602 
603 /* main - pass control to the multi-threaded server skeleton */
604 
main(int argc,char ** argv)605 int     main(int argc, char **argv)
606 {
607     static const CONFIG_STR_TABLE str_table[] = {
608           VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
609           VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
610           0,
611     };
612 
613     /*
614      * Fingerprint executables and core dumps.
615      */
616     MAIL_VERSION_STAMP_ALLOCATE;
617 
618     /*
619      * Use the multi-threaded skeleton, because no-one else should be
620      * monitoring our service socket while this process runs.
621      *
622      * XXX The default watchdog timeout for trigger servers is 1000s, while the
623      * cleanup server watchdog timeout is $daemon_timeout (i.e. several
624      * hours). We override the default 1000s timeout to avoid problems with
625      * slow mail submission. The real problem is of course that the
626      * single-threaded pickup server is not a good solution for mail
627      * submissions.
628      */
629     trigger_server_main(argc, argv, pickup_service,
630                               CA_MAIL_SERVER_STR_TABLE(str_table),
631                               CA_MAIL_SERVER_POST_INIT(post_jail_init),
632                               CA_MAIL_SERVER_SOLITARY,
633                               CA_MAIL_SERVER_WATCHDOG(&var_daemon_timeout),
634                               0);
635 }
636