xref: /dragonfly/libexec/dma/dma.c (revision 299d9671)
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 #ifdef HAVE_CRYPTO
44 #include <openssl/ssl.h>
45 #endif /* HAVE_CRYPTO */
46 
47 #include <dirent.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <inttypes.h>
52 #include <netdb.h>
53 #include <paths.h>
54 #include <pwd.h>
55 #include <signal.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <syslog.h>
61 #include <unistd.h>
62 
63 #include "dma.h"
64 
65 
66 
67 static void deliver(struct qitem *);
68 static int add_recp(struct queue *, const char *, const char *, int);
69 
70 struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
71 static struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
72 struct virtusers virtusers = LIST_HEAD_INITIALIZER(virtusers);
73 struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
74 static int daemonize = 1;
75 struct config *config;
76 
77 char *
78 hostname(void)
79 {
80 	static char name[MAXHOSTNAMELEN+1];
81 
82 	if (gethostname(name, sizeof(name)) != 0)
83 		strcpy(name, "(unknown hostname)");
84 
85 	return name;
86 }
87 
88 static char *
89 set_from(const char *osender)
90 {
91 	struct virtuser *v;
92 	char *sender;
93 
94 	if ((config->features & VIRTUAL) != 0) {
95 		SLIST_FOREACH(v, &virtusers, next) {
96 			if (strcmp(v->login, getlogin()) == 0) {
97 				sender = strdup(v->address);
98 				if (sender == NULL)
99 					return(NULL);
100 				goto out;
101 			}
102 		}
103 	}
104 
105 	if (osender) {
106 		sender = strdup(osender);
107 		if (sender == NULL)
108 			return (NULL);
109 	} else {
110 		if (asprintf(&sender, "%s@%s", getlogin(), hostname()) <= 0)
111 			return (NULL);
112 	}
113 
114 	if (strchr(sender, '\n') != NULL) {
115 		errno = EINVAL;
116 		return (NULL);
117 	}
118 
119 out:
120 	return (sender);
121 }
122 
123 static int
124 read_aliases(void)
125 {
126 	yyin = fopen(config->aliases, "r");
127 	if (yyin == NULL)
128 		return (0);	/* not fatal */
129 	if (yyparse())
130 		return (-1);	/* fatal error, probably malloc() */
131 	fclose(yyin);
132 	return (0);
133 }
134 
135 static int
136 add_recp(struct queue *queue, const char *str, const char *sender, int expand)
137 {
138 	struct qitem *it, *tit;
139 	struct stritem *sit;
140 	struct alias *al;
141 	struct passwd *pw;
142 	char *host;
143 	int aliased = 0;
144 
145 	it = calloc(1, sizeof(*it));
146 	if (it == NULL)
147 		return (-1);
148 	it->addr = strdup(str);
149 	if (it->addr == NULL)
150 		return (-1);
151 
152 	it->sender = sender;
153 	host = strrchr(it->addr, '@');
154 	if (host != NULL &&
155 	    (strcmp(host + 1, hostname()) == 0 ||
156 	     strcmp(host + 1, "localhost") == 0)) {
157 		*host = 0;
158 	}
159 	LIST_FOREACH(tit, &queue->queue, next) {
160 		/* weed out duplicate dests */
161 		if (strcmp(tit->addr, it->addr) == 0) {
162 			free(it->addr);
163 			free(it);
164 			return (0);
165 		}
166 	}
167 	LIST_INSERT_HEAD(&queue->queue, it, next);
168 	if (strrchr(it->addr, '@') == NULL) {
169 		it->remote = 0;
170 		if (expand) {
171 			LIST_FOREACH(al, &aliases, next) {
172 				if (strcmp(al->alias, it->addr) != 0)
173 					continue;
174 				SLIST_FOREACH(sit, &al->dests, next) {
175 					if (add_recp(queue, sit->str, sender, 1) != 0)
176 						return (-1);
177 				}
178 				aliased = 1;
179 			}
180 			if (aliased) {
181 				LIST_REMOVE(it, next);
182 			} else {
183 				/* Local destination, check */
184 				pw = getpwnam(it->addr);
185 				if (pw == NULL)
186 					goto out;
187 				endpwent();
188 			}
189 		}
190 	} else {
191 		it->remote = 1;
192 	}
193 
194 	return (0);
195 
196 out:
197 	free(it->addr);
198 	free(it);
199 	return (-1);
200 }
201 
202 static void
203 deltmp(void)
204 {
205 	struct stritem *t;
206 
207 	SLIST_FOREACH(t, &tmpfs, next) {
208 		unlink(t->str);
209 	}
210 }
211 
212 static int
213 gentempf(struct queue *queue)
214 {
215 	char fn[PATH_MAX+1];
216 	struct stritem *t;
217 	int fd;
218 
219 	if (snprintf(fn, sizeof(fn), "%s/%s", config->spooldir, "tmp_XXXXXXXXXX") <= 0)
220 		return (-1);
221 	fd = mkstemp(fn);
222 	if (fd < 0)
223 		return (-1);
224 	queue->mailfd = fd;
225 	queue->tmpf = strdup(fn);
226 	if (queue->tmpf == NULL) {
227 		unlink(fn);
228 		return (-1);
229 	}
230 	t = malloc(sizeof(*t));
231 	if (t != NULL) {
232 		t->str = queue->tmpf;
233 		SLIST_INSERT_HEAD(&tmpfs, t, next);
234 	}
235 	return (0);
236 }
237 
238 /*
239  * spool file format:
240  *
241  * envelope-from
242  * queue-id1 envelope-to1
243  * queue-id2 envelope-to2
244  * ...
245  * <empty line>
246  * mail data
247  *
248  * queue ids are unique, formed from the inode of the spool file
249  * and a unique identifier.
250  */
251 static int
252 preparespool(struct queue *queue, const char *sender)
253 {
254 	char line[1000];	/* by RFC2822 */
255 	struct stat st;
256 	int error;
257 	struct qitem *it;
258 	FILE *queuef;
259 	off_t hdrlen;
260 
261 	error = snprintf(line, sizeof(line), "%s\n", sender);
262 	if (error < 0 || (size_t)error >= sizeof(line)) {
263 		errno = E2BIG;
264 		return (-1);
265 	}
266 	if (write(queue->mailfd, line, error) != error)
267 		return (-1);
268 
269 	queuef = fdopen(queue->mailfd, "r+");
270 	if (queuef == NULL)
271 		return (-1);
272 
273 	/*
274 	 * Assign queue id to each dest.
275 	 */
276 	if (fstat(queue->mailfd, &st) != 0)
277 		return (-1);
278 	queue->id = st.st_ino;
279 	LIST_FOREACH(it, &queue->queue, next) {
280 		if (asprintf(&it->queueid, "%"PRIxMAX".%"PRIxPTR,
281 			     queue->id, (uintptr_t)it) <= 0)
282 			return (-1);
283 		if (asprintf(&it->queuefn, "%s/%s",
284 			     config->spooldir, it->queueid) <= 0)
285 			return (-1);
286 		/* File may not exist yet */
287 		if (stat(it->queuefn, &st) == 0)
288 			return (-1);
289 		it->queuef = queuef;
290 		error = snprintf(line, sizeof(line), "%s %s\n",
291 			       it->queueid, it->addr);
292 		if (error < 0 || (size_t)error >= sizeof(line))
293 			return (-1);
294 		if (write(queue->mailfd, line, error) != error)
295 			return (-1);
296 	}
297 	line[0] = '\n';
298 	if (write(queue->mailfd, line, 1) != 1)
299 		return (-1);
300 
301 	hdrlen = lseek(queue->mailfd, 0, SEEK_CUR);
302 	LIST_FOREACH(it, &queue->queue, next) {
303 		it->hdrlen = hdrlen;
304 	}
305 	return (0);
306 }
307 
308 static char *
309 rfc822date(void)
310 {
311 	static char str[50];
312 	size_t error;
313 	time_t now;
314 
315 	now = time(NULL);
316 	error = strftime(str, sizeof(str), "%a, %d %b %Y %T %z",
317 		       localtime(&now));
318 	if (error == 0)
319 		strcpy(str, "(date fail)");
320 	return (str);
321 }
322 
323 static int
324 readmail(struct queue *queue, const char *sender, int nodot)
325 {
326 	char line[1000];	/* by RFC2822 */
327 	size_t linelen;
328 	int error;
329 
330 	error = snprintf(line, sizeof(line), "\
331 Received: from %s (uid %d)\n\
332 \t(envelope-from %s)\n\
333 \tid %"PRIxMAX"\n\
334 \tby %s (%s)\n\
335 \t%s\n",
336 		getlogin(), getuid(),
337 		sender,
338 		queue->id,
339 		hostname(), VERSION,
340 		rfc822date());
341 	if (error < 0 || (size_t)error >= sizeof(line))
342 		return (-1);
343 	if (write(queue->mailfd, line, error) != error)
344 		return (-1);
345 
346 	while (!feof(stdin)) {
347 		if (fgets(line, sizeof(line), stdin) == NULL)
348 			break;
349 		linelen = strlen(line);
350 		if (linelen == 0 || line[linelen - 1] != '\n') {
351 			errno = EINVAL;		/* XXX mark permanent errors */
352 			return (-1);
353 		}
354 		if (!nodot && linelen == 2 && line[0] == '.')
355 			break;
356 		if ((size_t)write(queue->mailfd, line, linelen) != linelen)
357 			return (-1);
358 	}
359 	if (fsync(queue->mailfd) != 0)
360 		return (-1);
361 	return (0);
362 }
363 
364 static int
365 linkspool(struct queue *queue)
366 {
367 	struct qitem *it;
368 
369 	LIST_FOREACH(it, &queue->queue, next) {
370 		if (link(queue->tmpf, it->queuefn) != 0)
371 			goto delfiles;
372 	}
373 	unlink(queue->tmpf);
374 	return (0);
375 
376 delfiles:
377 	LIST_FOREACH(it, &queue->queue, next) {
378 		unlink(it->queuefn);
379 	}
380 	return (-1);
381 }
382 
383 static struct qitem *
384 go_background(struct queue *queue)
385 {
386 	struct sigaction sa;
387 	struct qitem *it;
388 	pid_t pid;
389 
390 	if (daemonize && daemon(0, 0) != 0) {
391 		syslog(LOG_ERR, "can not daemonize: %m");
392 		exit(1);
393 	}
394 	daemonize = 0;
395 
396 	bzero(&sa, sizeof(sa));
397 	sa.sa_flags = SA_NOCLDWAIT;
398 	sa.sa_handler = SIG_IGN;
399 	sigaction(SIGCHLD, &sa, NULL);
400 
401 	LIST_FOREACH(it, &queue->queue, next) {
402 		/* No need to fork for the last dest */
403 		if (LIST_NEXT(it, next) == NULL)
404 			return (it);
405 
406 		pid = fork();
407 		switch (pid) {
408 		case -1:
409 			syslog(LOG_ERR, "can not fork: %m");
410 			exit(1);
411 			break;
412 
413 		case 0:
414 			/*
415 			 * Child:
416 			 *
417 			 * return and deliver mail
418 			 */
419 			return (it);
420 
421 		default:
422 			/*
423 			 * Parent:
424 			 *
425 			 * fork next child
426 			 */
427 			break;
428 		}
429 	}
430 
431 	syslog(LOG_CRIT, "reached dead code");
432 	exit(1);
433 }
434 
435 static void
436 bounce(struct qitem *it, const char *reason)
437 {
438 	struct queue bounceq;
439 	struct qitem *bit;
440 	char line[1000];
441 	int error;
442 
443 	/* Don't bounce bounced mails */
444 	if (it->sender[0] == 0) {
445 		syslog(LOG_CRIT, "%s: delivery panic: can't bounce a bounce",
446 		       it->queueid);
447 		exit(1);
448 	}
449 
450 	syslog(LOG_ERR, "%s: delivery failed, bouncing",
451 	       it->queueid);
452 
453 	LIST_INIT(&bounceq.queue);
454 	if (add_recp(&bounceq, it->sender, "", 1) != 0)
455 		goto fail;
456 	if (gentempf(&bounceq) != 0)
457 		goto fail;
458 	if (preparespool(&bounceq, "") != 0)
459 		goto fail;
460 
461 	bit = LIST_FIRST(&bounceq.queue);
462 	error = fprintf(bit->queuef, "\
463 Received: from MAILER-DAEMON\n\
464 \tid %"PRIxMAX"\n\
465 \tby %s (%s)\n\
466 \t%s\n\
467 X-Original-To: <%s>\n\
468 From: MAILER-DAEMON <>\n\
469 To: %s\n\
470 Subject: Mail delivery failed\n\
471 Message-Id: <%"PRIxMAX"@%s>\n\
472 Date: %s\n\
473 \n\
474 This is the %s at %s.\n\
475 \n\
476 There was an error delivering your mail to <%s>.\n\
477 \n\
478 %s\n\
479 \n\
480 Message headers follow.\n\
481 \n\
482 ",
483 		bounceq.id,
484 		hostname(), VERSION,
485 		rfc822date(),
486 		it->addr,
487 		it->sender,
488 		bounceq.id, hostname(),
489 		rfc822date(),
490 		VERSION, hostname(),
491 		it->addr,
492 		reason);
493 	if (error < 0)
494 		goto fail;
495 	if (fflush(bit->queuef) != 0)
496 		goto fail;
497 
498 	if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0)
499 		goto fail;
500 	while (!feof(it->queuef)) {
501 		if (fgets(line, sizeof(line), it->queuef) == NULL)
502 			break;
503 		if (line[0] == '\n')
504 			break;
505 		write(bounceq.mailfd, line, strlen(line));
506 	}
507 	if (fsync(bounceq.mailfd) != 0)
508 		goto fail;
509 	if (linkspool(&bounceq) != 0)
510 		goto fail;
511 	/* bounce is safe */
512 
513 	unlink(it->queuefn);
514 	fclose(it->queuef);
515 
516 	bit = go_background(&bounceq);
517 	deliver(bit);
518 	/* NOTREACHED */
519 
520 fail:
521 	syslog(LOG_CRIT, "%s: error creating bounce: %m", it->queueid);
522 	unlink(it->queuefn);
523 	exit(1);
524 }
525 
526 static int
527 deliver_local(struct qitem *it, const char **errmsg)
528 {
529 	char fn[PATH_MAX+1];
530 	char line[1000];
531 	size_t linelen;
532 	int mbox;
533 	int error;
534 	off_t mboxlen;
535 	time_t now = time(NULL);
536 
537 	error = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, it->addr);
538 	if (error < 0 || (size_t)error >= sizeof(fn)) {
539 		syslog(LOG_ERR, "%s: local delivery deferred: %m",
540 		       it->queueid);
541 		return (1);
542 	}
543 
544 	/* mailx removes users mailspool file if empty, so open with O_CREAT */
545 	mbox = open(fn, O_WRONLY | O_EXLOCK | O_APPEND | O_CREAT);
546 	if (mbox < 0) {
547 		syslog(LOG_ERR, "%s: local delivery deferred: can not open `%s': %m",
548 		       it->queueid, fn);
549 		return (1);
550 	}
551 	mboxlen = lseek(mbox, 0, SEEK_CUR);
552 
553 	if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0) {
554 		syslog(LOG_ERR, "%s: local delivery deferred: can not seek: %m",
555 		       it->queueid);
556 		return (1);
557 	}
558 
559 	error = snprintf(line, sizeof(line), "From %s\t%s", it->sender, ctime(&now));
560 	if (error < 0 || (size_t)error >= sizeof(line)) {
561 		syslog(LOG_ERR, "%s: local delivery deferred: can not write header: %m",
562 		       it->queueid);
563 		return (1);
564 	}
565 	if (write(mbox, line, error) != error)
566 		goto wrerror;
567 
568 	while (!feof(it->queuef)) {
569 		if (fgets(line, sizeof(line), it->queuef) == NULL)
570 			break;
571 		linelen = strlen(line);
572 		if (linelen == 0 || line[linelen - 1] != '\n') {
573 			syslog(LOG_CRIT, "%s: local delivery failed: corrupted queue file",
574 			       it->queueid);
575 			*errmsg = "corrupted queue file";
576 			error = -1;
577 			goto chop;
578 		}
579 
580 		if (strncmp(line, "From ", 5) == 0) {
581 			const char *gt = ">";
582 
583 			if (write(mbox, gt, 1) != 1)
584 				goto wrerror;
585 		}
586 		if ((size_t)write(mbox, line, linelen) != linelen)
587 			goto wrerror;
588 	}
589 	line[0] = '\n';
590 	if (write(mbox, line, 1) != 1)
591 		goto wrerror;
592 	close(mbox);
593 	return (0);
594 
595 wrerror:
596 	syslog(LOG_ERR, "%s: local delivery failed: write error: %m",
597 	       it->queueid);
598 	error = 1;
599 chop:
600 	if (ftruncate(mbox, mboxlen) != 0)
601 		syslog(LOG_WARNING, "%s: error recovering mbox `%s': %m",
602 		       it->queueid, fn);
603 	close(mbox);
604 	return (error);
605 }
606 
607 static void
608 deliver(struct qitem *it)
609 {
610 	int error;
611 	unsigned int backoff = MIN_RETRY;
612 	const char *errmsg = "unknown bounce reason";
613 	struct timeval now;
614 	struct stat st;
615 
616 	syslog(LOG_INFO, "%s: mail from=<%s> to=<%s>",
617 	       it->queueid, it->sender, it->addr);
618 
619 retry:
620 	syslog(LOG_INFO, "%s: trying delivery",
621 	       it->queueid);
622 
623 	if (it->remote)
624 		error = deliver_remote(it, &errmsg);
625 	else
626 		error = deliver_local(it, &errmsg);
627 
628 	switch (error) {
629 	case 0:
630 		unlink(it->queuefn);
631 		syslog(LOG_INFO, "%s: delivery successful",
632 		       it->queueid);
633 		exit(0);
634 
635 	case 1:
636 		if (stat(it->queuefn, &st) != 0) {
637 			syslog(LOG_ERR, "%s: lost queue file `%s'",
638 			       it->queueid, it->queuefn);
639 			exit(1);
640 		}
641 		if (gettimeofday(&now, NULL) == 0 &&
642 		    (now.tv_sec - st.st_mtimespec.tv_sec > MAX_TIMEOUT)) {
643 			char *msg;
644 
645 			if (asprintf(&msg,
646 				 "Could not deliver for the last %d seconds. Giving up.",
647 				 MAX_TIMEOUT) > 0)
648 				errmsg = msg;
649 			goto bounce;
650 		}
651 		sleep(backoff);
652 		backoff *= 2;
653 		if (backoff > MAX_RETRY)
654 			backoff = MAX_RETRY;
655 		goto retry;
656 
657 	case -1:
658 	default:
659 		break;
660 	}
661 
662 bounce:
663 	bounce(it, errmsg);
664 	/* NOTREACHED */
665 }
666 
667 static void
668 load_queue(struct queue *queue)
669 {
670 	struct stat st;
671 	struct qitem *it;
672 	//struct queue queue, itmqueue;
673 	struct queue itmqueue;
674 	DIR *spooldir;
675 	struct dirent *de;
676 	char line[1000];
677 	char *fn;
678 	FILE *queuef;
679 	char *sender;
680 	char *addr;
681 	char *queueid;
682 	char *queuefn;
683 	off_t hdrlen;
684 	int fd;
685 
686 	LIST_INIT(&queue->queue);
687 
688 	spooldir = opendir(config->spooldir);
689 	if (spooldir == NULL)
690 		err(1, "reading queue");
691 
692 	while ((de = readdir(spooldir)) != NULL) {
693 		sender = NULL;
694 		queuef = NULL;
695 		queueid = NULL;
696 		queuefn = NULL;
697 		fn = NULL;
698 		LIST_INIT(&itmqueue.queue);
699 
700 		/* ignore temp files */
701 		if (strncmp(de->d_name, "tmp_", 4) == 0 ||
702 		    de->d_type != DT_REG)
703 			continue;
704 		if (asprintf(&queuefn, "%s/%s", config->spooldir, de->d_name) < 0)
705 			goto fail;
706 		fd = open(queuefn, O_RDONLY|O_EXLOCK|O_NONBLOCK);
707 		if (fd < 0) {
708 			/* Ignore locked files */
709 			if (errno == EWOULDBLOCK)
710 				continue;
711 			goto skip_item;
712 		}
713 
714 		queuef = fdopen(fd, "r");
715 		if (queuef == NULL)
716 			goto skip_item;
717 		if (fgets(line, sizeof(line), queuef) == NULL ||
718 		    line[0] == 0)
719 			goto skip_item;
720 		line[strlen(line) - 1] = 0;	/* chop newline */
721 		sender = strdup(line);
722 		if (sender == NULL)
723 			goto skip_item;
724 
725 		for (;;) {
726 			if (fgets(line, sizeof(line), queuef) == NULL ||
727 			    line[0] == 0)
728 				goto skip_item;
729 			if (line[0] == '\n')
730 				break;
731 			line[strlen(line) - 1] = 0;
732 			queueid = strdup(line);
733 			if (queueid == NULL)
734 				goto skip_item;
735 			addr = strchr(queueid, ' ');
736 			if (addr == NULL)
737 				goto skip_item;
738 			*addr++ = 0;
739 			if (fn != NULL)
740 				free(fn);
741 			if (asprintf(&fn, "%s/%s", config->spooldir, queueid) < 0)
742 				goto skip_item;
743 			/* Item has already been delivered? */
744 			if (stat(fn, &st) != 0)
745 				continue;
746 			if (add_recp(&itmqueue, addr, sender, 0) != 0)
747 				goto skip_item;
748 			it = LIST_FIRST(&itmqueue.queue);
749 			it->queuef = queuef;
750 			it->queueid = queueid;
751 			it->queuefn = fn;
752 			fn = NULL;
753 		}
754 		if (LIST_EMPTY(&itmqueue.queue)) {
755 			warnx("queue file without items: `%s'", queuefn);
756 			goto skip_item2;
757 		}
758 		hdrlen = ftell(queuef);
759 		while ((it = LIST_FIRST(&itmqueue.queue)) != NULL) {
760 			it->hdrlen = hdrlen;
761 			LIST_REMOVE(it, next);
762 			LIST_INSERT_HEAD(&queue->queue, it, next);
763 		}
764 		continue;
765 
766 skip_item:
767 		warn("reading queue: `%s'", queuefn);
768 skip_item2:
769 		if (sender != NULL)
770 			free(sender);
771 		if (queuefn != NULL)
772 			free(queuefn);
773 		if (fn != NULL)
774 			free(fn);
775 		if (queueid != NULL)
776 			free(queueid);
777 		close(fd);
778 	}
779 	closedir(spooldir);
780 	return;
781 
782 fail:
783 	err(1, "reading queue");
784 }
785 
786 static void
787 run_queue(struct queue *queue)
788 {
789 	struct qitem *it;
790 
791 	if (LIST_EMPTY(&queue->queue))
792 		return;
793 
794 	it = go_background(queue);
795 	deliver(it);
796 	/* NOTREACHED */
797 }
798 
799 static void
800 show_queue(struct queue *queue)
801 {
802 	struct qitem *it;
803 
804 	if (LIST_EMPTY(&queue->queue)) {
805 		printf("Mail queue is empty\n");
806 		return;
807 	}
808 
809 	LIST_FOREACH(it, &queue->queue, next) {
810 		printf("\
811 ID\t: %s\n\
812 From\t: %s\n\
813 To\t: %s\n--\n", it->queueid, it->sender, it->addr);
814 	}
815 }
816 
817 /*
818  * TODO:
819  *
820  * - alias processing
821  * - use group permissions
822  * - proper sysexit codes
823  */
824 
825 int
826 main(int argc, char **argv)
827 {
828 	char *sender = NULL;
829 	char tag[255];
830 	struct qitem *it;
831 	struct queue queue;
832 	struct queue lqueue;
833 	int i, ch;
834 	int nodot = 0, doqueue = 0, showq = 0;
835 
836 	atexit(deltmp);
837 	LIST_INIT(&queue.queue);
838 	snprintf(tag, 254, "dma");
839 
840 	opterr = 0;
841 	while ((ch = getopt(argc, argv, "A:b:Df:iL:o:O:q:r:")) != -1) {
842 		switch (ch) {
843 		case 'A':
844 			/* -AX is being ignored, except for -A{c,m} */
845 			if (optarg[0] == 'c' || optarg[0] == 'm') {
846 				break;
847 			}
848 			/* else FALLTRHOUGH */
849 		case 'b':
850 			/* -bX is being ignored, except for -bp */
851 			if (optarg[0] == 'p') {
852 				showq = 1;
853 				break;
854 			}
855 			/* else FALLTRHOUGH */
856 		case 'D':
857 			daemonize = 0;
858 			break;
859 		case 'L':
860 			if (optarg != NULL)
861 				snprintf(tag, 254, "%s", optarg);
862 			break;
863 		case 'f':
864 		case 'r':
865 			sender = optarg;
866 			break;
867 
868 		case 'o':
869 			/* -oX is being ignored, except for -oi */
870 			if (optarg[0] != 'i')
871 				break;
872 			/* else FALLTRHOUGH */
873 		case 'O':
874 			break;
875 		case 'i':
876 			nodot = 1;
877 			break;
878 
879 		case 'q':
880 			doqueue = 1;
881 			break;
882 
883 		default:
884 			exit(1);
885 		}
886 	}
887 	argc -= optind;
888 	argv += optind;
889 	opterr = 1;
890 
891 	openlog(tag, LOG_PID | LOG_PERROR, LOG_MAIL);
892 
893 	config = malloc(sizeof(struct config));
894 	if (config == NULL)
895 		errx(1, "Cannot allocate enough memory");
896 
897 	memset(config, 0, sizeof(struct config));
898 	if (parse_conf(CONF_PATH, config) < 0) {
899 		free(config);
900 		errx(1, "reading config file");
901 	}
902 
903 	if (config->features & VIRTUAL)
904 		if (parse_virtuser(config->virtualpath) < 0)
905 			errx(1, "error reading virtual user file: %s",
906 				config->virtualpath);
907 
908 	if (parse_authfile(config->authpath) < 0)
909 		err(1, "reading SMTP authentication file");
910 
911 	if (showq) {
912 		if (argc != 0)
913 			errx(1, "sending mail and displaying queue is"
914 				" mutually exclusive");
915 		load_queue(&lqueue);
916 		show_queue(&lqueue);
917 		return (0);
918 	}
919 
920 	if (doqueue) {
921 		if (argc != 0)
922 			errx(1, "sending mail and queue pickup is mutually exclusive");
923 		load_queue(&lqueue);
924 		run_queue(&lqueue);
925 		return (0);
926 	}
927 
928 	if (read_aliases() != 0)
929 		err(1, "reading aliases");
930 
931 	if ((sender = set_from(sender)) == NULL)
932 		err(1, "setting from address");
933 
934 	for (i = 0; i < argc; i++) {
935 		if (add_recp(&queue, argv[i], sender, 1) != 0)
936 			errx(1, "invalid recipient `%s'\n", argv[i]);
937 	}
938 
939 	if (LIST_EMPTY(&queue.queue))
940 		errx(1, "no recipients");
941 
942 	if (gentempf(&queue) != 0)
943 		err(1, "create temp file");
944 
945 	if (preparespool(&queue, sender) != 0)
946 		err(1, "creating spools (1)");
947 
948 	if (readmail(&queue, sender, nodot) != 0)
949 		err(1, "reading mail");
950 
951 	if (linkspool(&queue) != 0)
952 		err(1, "creating spools (2)");
953 
954 	/* From here on the mail is safe. */
955 
956 	if (config->features & DEFER)
957 		return (0);
958 
959 	it = go_background(&queue);
960 	deliver(it);
961 
962 	/* NOTREACHED */
963 	return (0);
964 }
965