dma: restructure logging
[dragonfly.git] / libexec / dma / dma.c
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/libexec/dma/dma.c,v 1.5 2008/09/30 17:47:21 swildner Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/queue.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42
43 #include <dirent.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <inttypes.h>
48 #include <netdb.h>
49 #include <paths.h>
50 #include <pwd.h>
51 #include <signal.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <syslog.h>
57 #include <unistd.h>
58
59 #include "dma.h"
60
61
62
63 static void deliver(struct qitem *);
64
65 struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
66 struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
67 struct virtusers virtusers = LIST_HEAD_INITIALIZER(virtusers);
68 struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
69 static int daemonize = 1;
70 struct config *config;
71 static const char *username;
72 static uid_t uid;
73 static const char *logident_base;
74
75
76 const char *
77 hostname(void)
78 {
79         static char name[MAXHOSTNAMELEN+1];
80         int initialized = 0;
81         FILE *fp;
82         size_t len;
83
84         if (initialized)
85                 return (name);
86
87         if (config->mailname != NULL && config->mailname[0] != '\0') {
88                 snprintf(name, sizeof(name), "%s", config->mailname);
89                 initialized = 1;
90                 return (name);
91         }
92         if (config->mailnamefile != NULL && config->mailnamefile[0] != '\0') {
93                 fp = fopen(config->mailnamefile, "r");
94                 if (fp != NULL) {
95                         if (fgets(name, sizeof(name), fp) != NULL) {
96                                 len = strlen(name);
97                                 while (len > 0 &&
98                                     (name[len - 1] == '\r' ||
99                                      name[len - 1] == '\n'))
100                                         name[--len] = '\0';
101                                 if (name[0] != '\0') {
102                                         initialized = 1;
103                                         return (name);
104                                 }
105                         }
106                         fclose(fp);
107                 }
108         }
109         if (gethostname(name, sizeof(name)) != 0)
110                 strcpy(name, "(unknown hostname)");
111         initialized = 1;
112         return name;
113 }
114
115 static void
116 setlogident(const char *fmt, ...)
117 {
118         char *tag = NULL;
119
120         if (fmt != NULL) {
121                 va_list ap;
122                 char *sufx;
123
124                 va_start(ap, fmt);
125                 vasprintf(&sufx, fmt, ap);
126                 if (sufx != NULL) {
127                         asprintf(&tag, "%s[%s]", logident_base, sufx);
128                         free(sufx);
129                 }
130                 va_end(ap);
131         }
132         closelog();
133         openlog(tag != NULL ? tag : logident_base, 0, LOG_MAIL);
134 }
135
136 static const char *
137 check_username(const char *name, uid_t ckuid)
138 {
139         struct passwd *pwd;
140
141         if (name == NULL)
142                 return (NULL);
143         pwd = getpwnam(name);
144         if (pwd == NULL || pwd->pw_uid != ckuid)
145                 return (NULL);
146         return (name);
147 }
148
149 static void
150 set_username(void)
151 {
152         struct passwd *pwd;
153         char *u = NULL;
154
155         uid = getuid();
156         username = check_username(getlogin(), uid);
157         if (username != NULL)
158                 return;
159         username = check_username(getenv("LOGNAME"), uid);
160         if (username != NULL)
161                 return;
162         username = check_username(getenv("USER"), uid);
163         if (username != NULL)
164                 return;
165         pwd = getpwuid(uid);
166         if (pwd != NULL && pwd->pw_name != NULL && pwd->pw_name[0] != '\0' &&
167             (u = strdup(pwd->pw_name)) != NULL) {
168                 username = check_username(u, uid);
169                 if (username != NULL)
170                         return;
171                 else
172                         free(u);
173         }
174         asprintf(__DECONST(void *, &username), "%ld", (long)uid);
175         if (username != NULL)
176                 return;
177         username = "unknown-or-invalid-username";
178 }
179
180 static char *
181 set_from(const char *osender)
182 {
183         struct virtuser *v;
184         char *sender;
185
186         if ((config->features & VIRTUAL) != 0) {
187                 SLIST_FOREACH(v, &virtusers, next) {
188                         if (strcmp(v->login, username) == 0) {
189                                 sender = strdup(v->address);
190                                 if (sender == NULL)
191                                         return(NULL);
192                                 goto out;
193                         }
194                 }
195         }
196
197         if (osender) {
198                 sender = strdup(osender);
199                 if (sender == NULL)
200                         return (NULL);
201         } else {
202                 if (asprintf(&sender, "%s@%s", username, hostname()) <= 0)
203                         return (NULL);
204         }
205
206         if (strchr(sender, '\n') != NULL) {
207                 errno = EINVAL;
208                 return (NULL);
209         }
210
211 out:
212         return (sender);
213 }
214
215 static int
216 read_aliases(void)
217 {
218         yyin = fopen(config->aliases, "r");
219         if (yyin == NULL)
220                 return (0);     /* not fatal */
221         if (yyparse())
222                 return (-1);    /* fatal error, probably malloc() */
223         fclose(yyin);
224         return (0);
225 }
226
227 int
228 add_recp(struct queue *queue, const char *str, const char *sender, int expand)
229 {
230         struct qitem *it, *tit;
231         struct stritem *sit;
232         struct alias *al;
233         struct passwd *pw;
234         char *host;
235         int aliased = 0;
236
237         it = calloc(1, sizeof(*it));
238         if (it == NULL)
239                 return (-1);
240         it->addr = strdup(str);
241         if (it->addr == NULL)
242                 return (-1);
243
244         it->sender = sender;
245         host = strrchr(it->addr, '@');
246         if (host != NULL &&
247             (strcmp(host + 1, hostname()) == 0 ||
248              strcmp(host + 1, "localhost") == 0)) {
249                 *host = 0;
250         }
251         LIST_FOREACH(tit, &queue->queue, next) {
252                 /* weed out duplicate dests */
253                 if (strcmp(tit->addr, it->addr) == 0) {
254                         free(it->addr);
255                         free(it);
256                         return (0);
257                 }
258         }
259         LIST_INSERT_HEAD(&queue->queue, it, next);
260         if (strrchr(it->addr, '@') == NULL) {
261                 it->remote = 0;
262                 if (expand) {
263                         LIST_FOREACH(al, &aliases, next) {
264                                 if (strcmp(al->alias, it->addr) != 0)
265                                         continue;
266                                 SLIST_FOREACH(sit, &al->dests, next) {
267                                         if (add_recp(queue, sit->str, sender, 1) != 0)
268                                                 return (-1);
269                                 }
270                                 aliased = 1;
271                         }
272                         if (aliased) {
273                                 LIST_REMOVE(it, next);
274                         } else {
275                                 /* Local destination, check */
276                                 pw = getpwnam(it->addr);
277                                 if (pw == NULL)
278                                         goto out;
279                                 /* XXX read .forward */
280                                 endpwent();
281                         }
282                 }
283         } else {
284                 it->remote = 1;
285         }
286
287         return (0);
288
289 out:
290         free(it->addr);
291         free(it);
292         return (-1);
293 }
294
295 static void
296 deltmp(void)
297 {
298         struct stritem *t;
299
300         SLIST_FOREACH(t, &tmpfs, next) {
301                 unlink(t->str);
302         }
303 }
304
305 int
306 open_locked(const char *fname, int flags, ...)
307 {
308         int mode = 0;
309
310         if (flags & O_CREAT) {
311                 va_list ap;
312                 va_start(ap, flags);
313                 mode = va_arg(ap, int);
314                 va_end(ap);
315         }
316
317 #ifndef O_EXLOCK
318         int fd, save_errno;
319
320         fd = open(fname, flags, mode);
321         if (fd < 0)
322                 return(fd);
323         if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
324                 save_errno = errno;
325                 close(fd);
326                 errno = save_errno;
327                 return(-1);
328         }
329         return(fd);
330 #else
331         return(open(fname, flags|O_EXLOCK, mode));
332 #endif
333 }
334
335 static char *
336 rfc822date(void)
337 {
338         static char str[50];
339         size_t error;
340         time_t now;
341
342         now = time(NULL);
343         error = strftime(str, sizeof(str), "%a, %d %b %Y %T %z",
344                        localtime(&now));
345         if (error == 0)
346                 strcpy(str, "(date fail)");
347         return (str);
348 }
349
350 static int
351 strprefixcmp(const char *str, const char *prefix)
352 {
353         return (strncasecmp(str, prefix, strlen(prefix)));
354 }
355
356 static int
357 readmail(struct queue *queue, const char *sender, int nodot)
358 {
359         char line[1000];        /* by RFC2822 */
360         size_t linelen;
361         int error;
362         int had_headers = 0;
363         int had_from = 0;
364         int had_messagid = 0;
365         int had_date = 0;
366
367         error = snprintf(line, sizeof(line),
368                 "Received: from %s (uid %d)\n"
369                 "\t(envelope-from %s)\n"
370                 "\tid %s\n"
371                 "\tby %s (%s)\n"
372                 "\t%s\n",
373                 username, uid,
374                 sender,
375                 queue->id,
376                 hostname(), VERSION,
377                 rfc822date());
378         if (error < 0 || (size_t)error >= sizeof(line))
379                 return (-1);
380         if (write(queue->mailfd, line, error) != error)
381                 return (-1);
382
383         while (!feof(stdin)) {
384                 if (fgets(line, sizeof(line), stdin) == NULL)
385                         break;
386                 linelen = strlen(line);
387                 if (linelen == 0 || line[linelen - 1] != '\n') {
388                         errno = EINVAL;         /* XXX mark permanent errors */
389                         return (-1);
390                 }
391                 if (!had_headers) {
392                         if (strprefixcmp(line, "Date:") == 0)
393                                 had_date = 1;
394                         else if (strprefixcmp(line, "Message-Id:") == 0)
395                                 had_messagid = 1;
396                         else if (strprefixcmp(line, "From:") == 0)
397                                 had_from = 1;
398                 }
399                 if (strcmp(line, "\n") == 0 && !had_headers) {
400                         had_headers = 1;
401                         while (!had_date || !had_messagid || !had_from) {
402                                 if (!had_date) {
403                                         had_date = 1;
404                                         snprintf(line, sizeof(line), "Date: %s\n", rfc822date());
405                                 } else if (!had_messagid) {
406                                         /* XXX better msgid, assign earlier and log? */
407                                         had_messagid = 1;
408                                         snprintf(line, sizeof(line), "Message-Id: <%s@%s>\n",
409                                                  queue->id, hostname());
410                                 } else if (!had_from) {
411                                         had_from = 1;
412                                         snprintf(line, sizeof(line), "From: <%s>\n", sender);
413                                 }
414                                 if ((size_t)write(queue->mailfd, line, strlen(line)) != strlen(line))
415                                         return (-1);
416                         }
417                         strcpy(line, "\n");
418                 }
419                 if (!nodot && linelen == 2 && line[0] == '.')
420                         break;
421                 if ((size_t)write(queue->mailfd, line, linelen) != linelen)
422                         return (-1);
423         }
424         if (fsync(queue->mailfd) != 0)
425                 return (-1);
426
427         syslog(LOG_INFO, "new mail from user=%s uid=%d envelope_from=<%s>",
428                username, uid, sender);
429
430         return (0);
431 }
432
433 static struct qitem *
434 go_background(struct queue *queue)
435 {
436         struct sigaction sa;
437         struct qitem *it;
438         pid_t pid;
439
440         if (daemonize && daemon(0, 0) != 0) {
441                 syslog(LOG_ERR, "can not daemonize: %m");
442                 exit(1);
443         }
444         daemonize = 0;
445
446         bzero(&sa, sizeof(sa));
447         sa.sa_flags = SA_NOCLDWAIT;
448         sa.sa_handler = SIG_IGN;
449         sigaction(SIGCHLD, &sa, NULL);
450
451         LIST_FOREACH(it, &queue->queue, next) {
452                 /* No need to fork for the last dest */
453                 if (LIST_NEXT(it, next) == NULL) {
454                         setlogident("%s", it->queueid);
455                         return (it);
456                 }
457
458                 pid = fork();
459                 switch (pid) {
460                 case -1:
461                         syslog(LOG_ERR, "can not fork: %m");
462                         exit(1);
463                         break;
464
465                 case 0:
466                         /*
467                          * Child:
468                          *
469                          * return and deliver mail
470                          */
471                         setlogident("%s", it->queueid);
472                         return (it);
473
474                 default:
475                         /*
476                          * Parent:
477                          *
478                          * fork next child
479                          */
480                         break;
481                 }
482         }
483
484         syslog(LOG_CRIT, "reached dead code");
485         exit(1);
486 }
487
488 static void
489 bounce(struct qitem *it, const char *reason)
490 {
491         struct queue bounceq;
492         struct qitem *bit;
493         char line[1000];
494         size_t pos;
495         int error;
496
497         /* Don't bounce bounced mails */
498         if (it->sender[0] == 0) {
499                 syslog(LOG_INFO, "can not bounce a bounce message, discarding");
500                 exit(1);
501         }
502
503         LIST_INIT(&bounceq.queue);
504         if (add_recp(&bounceq, it->sender, "", 1) != 0)
505                 goto fail;
506         if (newspoolf(&bounceq, "") != 0)
507                 goto fail;
508
509         syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id);
510         setlogident("%s", bounceq.id);
511
512         bit = LIST_FIRST(&bounceq.queue);
513         error = fprintf(bit->mailf,
514                 "Received: from MAILER-DAEMON\n"
515                 "\tid %s\n"
516                 "\tby %s (%s)\n"
517                 "\t%s\n"
518                 "X-Original-To: <%s>\n"
519                 "From: MAILER-DAEMON <>\n"
520                 "To: %s\n"
521                 "Subject: Mail delivery failed\n"
522                 "Message-Id: <%s@%s>\n"
523                 "Date: %s\n"
524                 "\n"
525                 "This is the %s at %s.\n"
526                 "\n"
527                 "There was an error delivering your mail to <%s>.\n"
528                 "\n"
529                 "%s\n"
530                 "\n"
531                 "%s\n"
532                 "\n",
533                 bounceq.id,
534                 hostname(), VERSION,
535                 rfc822date(),
536                 it->addr,
537                 it->sender,
538                 bounceq.id, hostname(),
539                 rfc822date(),
540                 VERSION, hostname(),
541                 it->addr,
542                 reason,
543                 config->features & FULLBOUNCE ?
544                     "Original message follows." :
545                     "Message headers follow.");
546         if (error < 0)
547                 goto fail;
548         if (fflush(bit->mailf) != 0)
549                 goto fail;
550
551         if (fseek(it->mailf, it->hdrlen, SEEK_SET) != 0)
552                 goto fail;
553         if (config->features & FULLBOUNCE) {
554                 while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
555                         if ((size_t)write(bounceq.mailfd, line, pos) != pos)
556                                 goto fail;
557                 }
558         } else {
559                 while (!feof(it->mailf)) {
560                         if (fgets(line, sizeof(line), it->mailf) == NULL)
561                                 break;
562                         if (line[0] == '\n')
563                                 break;
564                         if ((size_t)write(bounceq.mailfd, line, strlen(line)) != strlen(line))
565                                 goto fail;
566                 }
567         }
568         if (fsync(bounceq.mailfd) != 0)
569                 goto fail;
570         if (linkspool(&bounceq) != 0)
571                 goto fail;
572         /* bounce is safe */
573
574         delqueue(it);
575
576         bit = go_background(&bounceq);
577         deliver(bit);
578         /* NOTREACHED */
579
580 fail:
581         syslog(LOG_CRIT, "error creating bounce: %m");
582         delqueue(it);
583         exit(1);
584 }
585
586 static void
587 deliver(struct qitem *it)
588 {
589         int error;
590         unsigned int backoff = MIN_RETRY;
591         const char *errmsg = "unknown bounce reason";
592         struct timeval now;
593         struct stat st;
594
595 retry:
596         syslog(LOG_INFO, "trying delivery");
597
598         if (it->remote)
599                 error = deliver_remote(it, &errmsg);
600         else
601                 error = deliver_local(it, &errmsg);
602
603         switch (error) {
604         case 0:
605                 delqueue(it);
606                 syslog(LOG_INFO, "delivery successful");
607                 exit(0);
608
609         case 1:
610                 if (stat(it->queuefn, &st) != 0) {
611                         syslog(LOG_ERR, "lost queue file `%s'", it->queuefn);
612                         exit(1);
613                 }
614                 if (gettimeofday(&now, NULL) == 0 &&
615                     (now.tv_sec - st.st_mtimespec.tv_sec > MAX_TIMEOUT)) {
616                         asprintf(__DECONST(void *, &errmsg),
617                                  "Could not deliver for the last %d seconds. Giving up.",
618                                  MAX_TIMEOUT);
619                         goto bounce;
620                 }
621                 sleep(backoff);
622                 backoff *= 2;
623                 if (backoff > MAX_RETRY)
624                         backoff = MAX_RETRY;
625                 goto retry;
626
627         case -1:
628         default:
629                 break;
630         }
631
632 bounce:
633         bounce(it, errmsg);
634         /* NOTREACHED */
635 }
636
637 static void
638 run_queue(struct queue *queue)
639 {
640         struct qitem *it;
641
642         if (LIST_EMPTY(&queue->queue))
643                 return;
644
645         it = go_background(queue);
646         deliver(it);
647         /* NOTREACHED */
648 }
649
650 static void
651 show_queue(struct queue *queue)
652 {
653         struct qitem *it;
654
655         if (LIST_EMPTY(&queue->queue)) {
656                 printf("Mail queue is empty\n");
657                 return;
658         }
659
660         LIST_FOREACH(it, &queue->queue, next) {
661                 printf("ID\t: %s%s\n"
662                        "From\t: %s\n"
663                        "To\t: %s\n"
664                        "--\n",
665                        it->queueid,
666                        it->locked ? "*" : "",
667                        it->sender, it->addr);
668         }
669 }
670
671 /*
672  * TODO:
673  *
674  * - alias processing
675  * - use group permissions
676  * - proper sysexit codes
677  */
678
679 int
680 main(int argc, char **argv)
681 {
682         char *sender = NULL;
683         struct qitem *it;
684         struct queue queue;
685         struct queue lqueue;
686         int i, ch;
687         int nodot = 0, doqueue = 0, showq = 0;
688
689         atexit(deltmp);
690         LIST_INIT(&queue.queue);
691
692         if (strcmp(argv[0], "mailq") == 0) {
693                 argv++; argc--;
694                 showq = 1;
695                 if (argc != 0)
696                         errx(1, "invalid arguments");
697                 goto skipopts;
698         }
699
700         opterr = 0;
701         while ((ch = getopt(argc, argv, "A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:UV:vX:")) != -1) {
702                 switch (ch) {
703                 case 'A':
704                         /* -AX is being ignored, except for -A{c,m} */
705                         if (optarg[0] == 'c' || optarg[0] == 'm') {
706                                 break;
707                         }
708                         /* else FALLTRHOUGH */
709                 case 'b':
710                         /* -bX is being ignored, except for -bp */
711                         if (optarg[0] == 'p') {
712                                 showq = 1;
713                                 break;
714                         }
715                         /* else FALLTRHOUGH */
716                 case 'D':
717                         daemonize = 0;
718                         break;
719                 case 'L':
720                         logident_base = optarg;
721                         break;
722                 case 'f':
723                 case 'r':
724                         sender = optarg;
725                         break;
726
727                 case 'o':
728                         /* -oX is being ignored, except for -oi */
729                         if (optarg[0] != 'i')
730                                 break;
731                         /* else FALLTRHOUGH */
732                 case 'O':
733                         break;
734                 case 'i':
735                         nodot = 1;
736                         break;
737
738                 case 'q':
739                         doqueue = 1;
740                         break;
741
742                 /* Ignored options */
743                 case 'B':
744                 case 'C':
745                 case 'd':
746                 case 'F':
747                 case 'h':
748                 case 'N':
749                 case 'n':
750                 case 'R':
751                 case 'U':
752                 case 'V':
753                 case 'v':
754                 case 'X':
755                         break;
756
757                 default:
758                         exit(1);
759                 }
760         }
761         argc -= optind;
762         argv += optind;
763         opterr = 1;
764
765         if (argc != 0 && (showq || doqueue))
766                 errx(1, "sending mail and queue operations are mutually exclusive");
767
768         if (showq + doqueue > 1)
769                 errx(1, "conflicting queue operations");
770
771 skipopts:
772         if (logident_base == NULL)
773                 logident_base = "dma";
774         setlogident(NULL);
775         set_username();
776
777         /* XXX fork root here */
778
779         config = calloc(1, sizeof(*config));
780         if (config == NULL)
781                 err(1, NULL);
782
783         if (parse_conf(CONF_PATH) < 0) {
784                 free(config);
785                 err(1, "can not read config file");
786         }
787
788         if (config->features & VIRTUAL)
789                 if (parse_virtuser(config->virtualpath) < 0)
790                         err(1, "can not read virtual user file `%s'",
791                                 config->virtualpath);
792
793         if (parse_authfile(config->authpath) < 0)
794                 err(1, "can not read SMTP authentication file");
795
796         if (showq) {
797                 load_queue(&lqueue, 1);
798                 show_queue(&lqueue);
799                 return (0);
800         }
801
802         if (doqueue) {
803                 load_queue(&lqueue, 0);
804                 run_queue(&lqueue);
805                 return (0);
806         }
807
808         if (read_aliases() != 0)
809                 err(1, "can not read aliases file `%s'", config->aliases);
810
811         if ((sender = set_from(sender)) == NULL)
812                 err(1, NULL);
813
814         for (i = 0; i < argc; i++) {
815                 if (add_recp(&queue, argv[i], sender, 1) != 0)
816                         errx(1, "invalid recipient `%s'", argv[i]);
817         }
818
819         if (LIST_EMPTY(&queue.queue))
820                 errx(1, "no recipients");
821
822         if (newspoolf(&queue, sender) != 0)
823                 err(1, "can not create temp file");
824
825         setlogident("%s", queue.id);
826
827         if (readmail(&queue, sender, nodot) != 0)
828                 err(1, "can not read mail");
829
830         if (linkspool(&queue) != 0)
831                 err(1, "can not create spools");
832
833         /* From here on the mail is safe. */
834
835         if (config->features & DEFER)
836                 return (0);
837
838         it = go_background(&queue);
839         deliver(it);
840
841         /* NOTREACHED */
842         return (0);
843 }