1f67bedddSMatthias Schmidt /*
237d59876SJohn Marino * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>.
3f67bedddSMatthias Schmidt * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4f67bedddSMatthias Schmidt *
5f67bedddSMatthias Schmidt * This code is derived from software contributed to The DragonFly Project
637d59876SJohn Marino * by Simon Schubert <2@0x2c.org>.
7f67bedddSMatthias Schmidt *
8f67bedddSMatthias Schmidt * Redistribution and use in source and binary forms, with or without
9f67bedddSMatthias Schmidt * modification, are permitted provided that the following conditions
10f67bedddSMatthias Schmidt * are met:
11f67bedddSMatthias Schmidt *
12f67bedddSMatthias Schmidt * 1. Redistributions of source code must retain the above copyright
13f67bedddSMatthias Schmidt * notice, this list of conditions and the following disclaimer.
14f67bedddSMatthias Schmidt * 2. Redistributions in binary form must reproduce the above copyright
15f67bedddSMatthias Schmidt * notice, this list of conditions and the following disclaimer in
16f67bedddSMatthias Schmidt * the documentation and/or other materials provided with the
17f67bedddSMatthias Schmidt * distribution.
18f67bedddSMatthias Schmidt * 3. Neither the name of The DragonFly Project nor the names of its
19f67bedddSMatthias Schmidt * contributors may be used to endorse or promote products derived
20f67bedddSMatthias Schmidt * from this software without specific, prior written permission.
21f67bedddSMatthias Schmidt *
22f67bedddSMatthias Schmidt * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23f67bedddSMatthias Schmidt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24f67bedddSMatthias Schmidt * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25f67bedddSMatthias Schmidt * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26f67bedddSMatthias Schmidt * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27f67bedddSMatthias Schmidt * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28f67bedddSMatthias Schmidt * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29f67bedddSMatthias Schmidt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30f67bedddSMatthias Schmidt * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31f67bedddSMatthias Schmidt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32f67bedddSMatthias Schmidt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33f67bedddSMatthias Schmidt * SUCH DAMAGE.
34f67bedddSMatthias Schmidt */
35f67bedddSMatthias Schmidt
36c8b07ee5SSascha Wildner #include "dfcompat.h"
37c8b07ee5SSascha Wildner
38f67bedddSMatthias Schmidt #include <sys/param.h>
394a6863a4SSimon Schubert #include <sys/types.h>
40f67bedddSMatthias Schmidt #include <sys/queue.h>
41f67bedddSMatthias Schmidt #include <sys/stat.h>
42c8b07ee5SSascha Wildner #include <sys/time.h>
43bc7baf1dSSascha Wildner #include <sys/wait.h>
44f67bedddSMatthias Schmidt
45f67bedddSMatthias Schmidt #include <dirent.h>
46f67bedddSMatthias Schmidt #include <err.h>
47f67bedddSMatthias Schmidt #include <errno.h>
48f67bedddSMatthias Schmidt #include <fcntl.h>
49f67bedddSMatthias Schmidt #include <inttypes.h>
5092fe556dSDaniel Fojt #include <libgen.h>
51f67bedddSMatthias Schmidt #include <paths.h>
52f67bedddSMatthias Schmidt #include <pwd.h>
53f67bedddSMatthias Schmidt #include <signal.h>
54f67bedddSMatthias Schmidt #include <stdarg.h>
55f67bedddSMatthias Schmidt #include <stdio.h>
56f67bedddSMatthias Schmidt #include <stdlib.h>
57f67bedddSMatthias Schmidt #include <string.h>
58f67bedddSMatthias Schmidt #include <syslog.h>
59f67bedddSMatthias Schmidt #include <unistd.h>
60f67bedddSMatthias Schmidt
61f67bedddSMatthias Schmidt #include "dma.h"
62f67bedddSMatthias Schmidt
6352e9aa73SPeter Avalos extern int yyparse(void);
6492fe556dSDaniel Fojt extern FILE *yyin;
6592fe556dSDaniel Fojt
664a23bd3dSMatthias Schmidt static void deliver(struct qitem *);
67f67bedddSMatthias Schmidt
68f67bedddSMatthias Schmidt struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
69f4e61a9fSSimon 'corecode' Schubert struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
70f67bedddSMatthias Schmidt struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
71c8b07ee5SSascha Wildner char username[USERNAME_SIZE];
72c8b07ee5SSascha Wildner uid_t useruid;
731da0a9f2SSimon Schubert const char *logident_base;
74c8b07ee5SSascha Wildner char errmsg[ERRMSG_SIZE];
75f67bedddSMatthias Schmidt
769afa363fSSimon Schubert static int daemonize = 1;
7714dfb991SJoris Giovannangeli static int doqueue = 0;
78f8633e71SSimon Schubert
79ca259d14SSimon Schubert struct config config = {
80ca259d14SSimon Schubert .smarthost = NULL,
81ca259d14SSimon Schubert .port = 25,
82c8b07ee5SSascha Wildner .aliases = "/etc/aliases",
83ca259d14SSimon Schubert .spooldir = "/var/spool/dma",
84ca259d14SSimon Schubert .authpath = NULL,
85ca259d14SSimon Schubert .certfile = NULL,
86ca259d14SSimon Schubert .features = 0,
87ca259d14SSimon Schubert .mailname = NULL,
88c8b07ee5SSascha Wildner .masquerade_host = NULL,
89c8b07ee5SSascha Wildner .masquerade_user = NULL,
9092fe556dSDaniel Fojt .fingerprint = NULL,
91ca259d14SSimon Schubert };
92ca259d14SSimon Schubert
93ca259d14SSimon Schubert
94c8b07ee5SSascha Wildner static void
sighup_handler(int signo)95c8b07ee5SSascha Wildner sighup_handler(int signo)
96c8b07ee5SSascha Wildner {
97c8b07ee5SSascha Wildner (void)signo; /* so that gcc doesn't complain */
98c8b07ee5SSascha Wildner }
99c8b07ee5SSascha Wildner
100f67bedddSMatthias Schmidt static char *
set_from(struct queue * queue,const char * osender)1011c9e6b7bSSimon Schubert set_from(struct queue *queue, const char *osender)
102f67bedddSMatthias Schmidt {
10314dfb991SJoris Giovannangeli const char *addr;
104f67bedddSMatthias Schmidt char *sender;
105f67bedddSMatthias Schmidt
10692fe556dSDaniel Fojt if (config.masquerade_user) {
10792fe556dSDaniel Fojt addr = config.masquerade_user;
10892fe556dSDaniel Fojt } else if (osender) {
10914dfb991SJoris Giovannangeli addr = osender;
110c8b07ee5SSascha Wildner } else if (getenv("EMAIL") != NULL) {
11114dfb991SJoris Giovannangeli addr = getenv("EMAIL");
112f67bedddSMatthias Schmidt } else {
11314dfb991SJoris Giovannangeli addr = username;
11414dfb991SJoris Giovannangeli }
11514dfb991SJoris Giovannangeli
11614dfb991SJoris Giovannangeli if (!strchr(addr, '@')) {
117c8b07ee5SSascha Wildner const char *from_host = hostname();
118c8b07ee5SSascha Wildner
119c8b07ee5SSascha Wildner if (config.masquerade_host)
120c8b07ee5SSascha Wildner from_host = config.masquerade_host;
12114dfb991SJoris Giovannangeli
12214dfb991SJoris Giovannangeli if (asprintf(&sender, "%s@%s", addr, from_host) <= 0)
12314dfb991SJoris Giovannangeli return (NULL);
12414dfb991SJoris Giovannangeli } else {
12514dfb991SJoris Giovannangeli sender = strdup(addr);
12614dfb991SJoris Giovannangeli if (sender == NULL)
127f67bedddSMatthias Schmidt return (NULL);
128f67bedddSMatthias Schmidt }
129f67bedddSMatthias Schmidt
130f67bedddSMatthias Schmidt if (strchr(sender, '\n') != NULL) {
131f67bedddSMatthias Schmidt errno = EINVAL;
132f67bedddSMatthias Schmidt return (NULL);
133f67bedddSMatthias Schmidt }
134f67bedddSMatthias Schmidt
1351c9e6b7bSSimon Schubert queue->sender = sender;
136f67bedddSMatthias Schmidt return (sender);
137f67bedddSMatthias Schmidt }
138f67bedddSMatthias Schmidt
139f67bedddSMatthias Schmidt static int
read_aliases(void)140f67bedddSMatthias Schmidt read_aliases(void)
141f67bedddSMatthias Schmidt {
142ca259d14SSimon Schubert yyin = fopen(config.aliases, "r");
143ca259d14SSimon Schubert if (yyin == NULL) {
144ca259d14SSimon Schubert /*
145ca259d14SSimon Schubert * Non-existing aliases file is not a fatal error
146ca259d14SSimon Schubert */
147ca259d14SSimon Schubert if (errno == ENOENT)
148ca259d14SSimon Schubert return (0);
149ca259d14SSimon Schubert /* Other problems are. */
150ca259d14SSimon Schubert return (-1);
151ca259d14SSimon Schubert }
152f67bedddSMatthias Schmidt if (yyparse())
153f67bedddSMatthias Schmidt return (-1); /* fatal error, probably malloc() */
154f67bedddSMatthias Schmidt fclose(yyin);
155f67bedddSMatthias Schmidt return (0);
156f67bedddSMatthias Schmidt }
157f67bedddSMatthias Schmidt
158c8b07ee5SSascha Wildner static int
do_alias(struct queue * queue,const char * addr)159c8b07ee5SSascha Wildner do_alias(struct queue *queue, const char *addr)
160c8b07ee5SSascha Wildner {
161c8b07ee5SSascha Wildner struct alias *al;
162c8b07ee5SSascha Wildner struct stritem *sit;
163c8b07ee5SSascha Wildner int aliased = 0;
164c8b07ee5SSascha Wildner
165c8b07ee5SSascha Wildner LIST_FOREACH(al, &aliases, next) {
166c8b07ee5SSascha Wildner if (strcmp(al->alias, addr) != 0)
167c8b07ee5SSascha Wildner continue;
168c8b07ee5SSascha Wildner SLIST_FOREACH(sit, &al->dests, next) {
169c8b07ee5SSascha Wildner if (add_recp(queue, sit->str, EXPAND_ADDR) != 0)
170c8b07ee5SSascha Wildner return (-1);
171c8b07ee5SSascha Wildner }
172c8b07ee5SSascha Wildner aliased = 1;
173c8b07ee5SSascha Wildner }
174c8b07ee5SSascha Wildner
175c8b07ee5SSascha Wildner return (aliased);
176c8b07ee5SSascha Wildner }
177c8b07ee5SSascha Wildner
178f4e61a9fSSimon 'corecode' Schubert int
add_recp(struct queue * queue,const char * str,int expand)1791c9e6b7bSSimon Schubert add_recp(struct queue *queue, const char *str, int expand)
180f67bedddSMatthias Schmidt {
181f67bedddSMatthias Schmidt struct qitem *it, *tit;
182f67bedddSMatthias Schmidt struct passwd *pw;
183f67bedddSMatthias Schmidt char *host;
184f67bedddSMatthias Schmidt int aliased = 0;
185f67bedddSMatthias Schmidt
186f67bedddSMatthias Schmidt it = calloc(1, sizeof(*it));
187f67bedddSMatthias Schmidt if (it == NULL)
188f67bedddSMatthias Schmidt return (-1);
189f67bedddSMatthias Schmidt it->addr = strdup(str);
190f67bedddSMatthias Schmidt if (it->addr == NULL)
191f67bedddSMatthias Schmidt return (-1);
192f67bedddSMatthias Schmidt
1931c9e6b7bSSimon Schubert it->sender = queue->sender;
194f67bedddSMatthias Schmidt host = strrchr(it->addr, '@');
195f67bedddSMatthias Schmidt if (host != NULL &&
196f67bedddSMatthias Schmidt (strcmp(host + 1, hostname()) == 0 ||
197f67bedddSMatthias Schmidt strcmp(host + 1, "localhost") == 0)) {
198f67bedddSMatthias Schmidt *host = 0;
199f67bedddSMatthias Schmidt }
200f67bedddSMatthias Schmidt LIST_FOREACH(tit, &queue->queue, next) {
201f67bedddSMatthias Schmidt /* weed out duplicate dests */
202f67bedddSMatthias Schmidt if (strcmp(tit->addr, it->addr) == 0) {
203f67bedddSMatthias Schmidt free(it->addr);
204f67bedddSMatthias Schmidt free(it);
205f67bedddSMatthias Schmidt return (0);
206f67bedddSMatthias Schmidt }
207f67bedddSMatthias Schmidt }
208f67bedddSMatthias Schmidt LIST_INSERT_HEAD(&queue->queue, it, next);
20914dfb991SJoris Giovannangeli
21014dfb991SJoris Giovannangeli /**
21114dfb991SJoris Giovannangeli * Do local delivery if there is no @.
21214dfb991SJoris Giovannangeli * Do not do local delivery when NULLCLIENT is set.
21314dfb991SJoris Giovannangeli */
21414dfb991SJoris Giovannangeli if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) {
2154a23bd3dSMatthias Schmidt it->remote = 0;
216f67bedddSMatthias Schmidt if (expand) {
217c8b07ee5SSascha Wildner aliased = do_alias(queue, it->addr);
218c8b07ee5SSascha Wildner if (!aliased && expand == EXPAND_WILDCARD)
219c8b07ee5SSascha Wildner aliased = do_alias(queue, "*");
220c8b07ee5SSascha Wildner if (aliased < 0)
221f67bedddSMatthias Schmidt return (-1);
222f67bedddSMatthias Schmidt if (aliased) {
223f67bedddSMatthias Schmidt LIST_REMOVE(it, next);
224f67bedddSMatthias Schmidt } else {
2254a23bd3dSMatthias Schmidt /* Local destination, check */
226f67bedddSMatthias Schmidt pw = getpwnam(it->addr);
227f67bedddSMatthias Schmidt if (pw == NULL)
228f67bedddSMatthias Schmidt goto out;
22913e288a1SSimon 'corecode' Schubert /* XXX read .forward */
2304a23bd3dSMatthias Schmidt endpwent();
231f67bedddSMatthias Schmidt }
232f67bedddSMatthias Schmidt }
233f67bedddSMatthias Schmidt } else {
2344a23bd3dSMatthias Schmidt it->remote = 1;
235f67bedddSMatthias Schmidt }
236f67bedddSMatthias Schmidt
237f67bedddSMatthias Schmidt return (0);
238f67bedddSMatthias Schmidt
239f67bedddSMatthias Schmidt out:
240f67bedddSMatthias Schmidt free(it->addr);
241f67bedddSMatthias Schmidt free(it);
242f67bedddSMatthias Schmidt return (-1);
243f67bedddSMatthias Schmidt }
244f67bedddSMatthias Schmidt
2454a23bd3dSMatthias Schmidt static struct qitem *
go_background(struct queue * queue)2464a23bd3dSMatthias Schmidt go_background(struct queue *queue)
247f67bedddSMatthias Schmidt {
248f67bedddSMatthias Schmidt struct sigaction sa;
249f67bedddSMatthias Schmidt struct qitem *it;
250f67bedddSMatthias Schmidt pid_t pid;
251f67bedddSMatthias Schmidt
252f67bedddSMatthias Schmidt if (daemonize && daemon(0, 0) != 0) {
2534a23bd3dSMatthias Schmidt syslog(LOG_ERR, "can not daemonize: %m");
25492fe556dSDaniel Fojt exit(EX_OSERR);
255f67bedddSMatthias Schmidt }
256f67bedddSMatthias Schmidt daemonize = 0;
2574a23bd3dSMatthias Schmidt
258f67bedddSMatthias Schmidt bzero(&sa, sizeof(sa));
259f67bedddSMatthias Schmidt sa.sa_handler = SIG_IGN;
260f67bedddSMatthias Schmidt sigaction(SIGCHLD, &sa, NULL);
261f67bedddSMatthias Schmidt
2624dcaa51bSMatthias Schmidt LIST_FOREACH(it, &queue->queue, next) {
2634a23bd3dSMatthias Schmidt /* No need to fork for the last dest */
2649afa363fSSimon Schubert if (LIST_NEXT(it, next) == NULL)
2659afa363fSSimon Schubert goto retit;
2664a23bd3dSMatthias Schmidt
267f67bedddSMatthias Schmidt pid = fork();
268f67bedddSMatthias Schmidt switch (pid) {
269f67bedddSMatthias Schmidt case -1:
270f67bedddSMatthias Schmidt syslog(LOG_ERR, "can not fork: %m");
27192fe556dSDaniel Fojt exit(EX_OSERR);
272f67bedddSMatthias Schmidt break;
273f67bedddSMatthias Schmidt
274f67bedddSMatthias Schmidt case 0:
275f67bedddSMatthias Schmidt /*
276f67bedddSMatthias Schmidt * Child:
277f67bedddSMatthias Schmidt *
278f67bedddSMatthias Schmidt * return and deliver mail
279f67bedddSMatthias Schmidt */
2809afa363fSSimon Schubert retit:
2819afa363fSSimon Schubert /*
28224c80b2bSSascha Wildner * If necessary, acquire the queue and * mail files.
2839afa363fSSimon Schubert * If this fails, we probably were raced by another
28414dfb991SJoris Giovannangeli * process. It is okay to be raced if we're supposed
28514dfb991SJoris Giovannangeli * to flush the queue.
2869afa363fSSimon Schubert */
287405f48eeSSimon Schubert setlogident("%s", it->queueid);
28814dfb991SJoris Giovannangeli switch (acquirespool(it)) {
28914dfb991SJoris Giovannangeli case 0:
29014dfb991SJoris Giovannangeli break;
29114dfb991SJoris Giovannangeli case 1:
29214dfb991SJoris Giovannangeli if (doqueue)
29392fe556dSDaniel Fojt exit(EX_OK);
29414dfb991SJoris Giovannangeli syslog(LOG_WARNING, "could not lock queue file");
29592fe556dSDaniel Fojt exit(EX_SOFTWARE);
29614dfb991SJoris Giovannangeli default:
29792fe556dSDaniel Fojt exit(EX_SOFTWARE);
29814dfb991SJoris Giovannangeli }
2999afa363fSSimon Schubert dropspool(queue, it);
3004a23bd3dSMatthias Schmidt return (it);
301f67bedddSMatthias Schmidt
302f67bedddSMatthias Schmidt default:
303f67bedddSMatthias Schmidt /*
304f67bedddSMatthias Schmidt * Parent:
305f67bedddSMatthias Schmidt *
306f67bedddSMatthias Schmidt * fork next child
307f67bedddSMatthias Schmidt */
308f67bedddSMatthias Schmidt break;
309f67bedddSMatthias Schmidt }
310f67bedddSMatthias Schmidt }
311f67bedddSMatthias Schmidt
312f67bedddSMatthias Schmidt syslog(LOG_CRIT, "reached dead code");
31392fe556dSDaniel Fojt exit(EX_SOFTWARE);
314f67bedddSMatthias Schmidt }
315f67bedddSMatthias Schmidt
316f67bedddSMatthias Schmidt static void
deliver(struct qitem * it)3174a23bd3dSMatthias Schmidt deliver(struct qitem *it)
318f67bedddSMatthias Schmidt {
319f67bedddSMatthias Schmidt int error;
32014dfb991SJoris Giovannangeli unsigned int backoff = MIN_RETRY, slept;
321f67bedddSMatthias Schmidt struct timeval now;
322f67bedddSMatthias Schmidt struct stat st;
323f67bedddSMatthias Schmidt
324c8b07ee5SSascha Wildner snprintf(errmsg, sizeof(errmsg), "unknown bounce reason");
325c8b07ee5SSascha Wildner
326f67bedddSMatthias Schmidt retry:
32792fe556dSDaniel Fojt syslog(LOG_INFO, "<%s> trying delivery", it->addr);
328f67bedddSMatthias Schmidt
3294a23bd3dSMatthias Schmidt if (it->remote)
330c8b07ee5SSascha Wildner error = deliver_remote(it);
3314a23bd3dSMatthias Schmidt else
332c8b07ee5SSascha Wildner error = deliver_local(it);
333f67bedddSMatthias Schmidt
334f67bedddSMatthias Schmidt switch (error) {
335f67bedddSMatthias Schmidt case 0:
33692fe556dSDaniel Fojt syslog(LOG_INFO, "<%s> delivery successful", it->addr);
3374e8d31bcSDaniel Fojt delqueue(it);
33892fe556dSDaniel Fojt exit(EX_OK);
339f67bedddSMatthias Schmidt
340f67bedddSMatthias Schmidt case 1:
341f67bedddSMatthias Schmidt if (stat(it->queuefn, &st) != 0) {
342405f48eeSSimon Schubert syslog(LOG_ERR, "lost queue file `%s'", it->queuefn);
34392fe556dSDaniel Fojt exit(EX_SOFTWARE);
344f67bedddSMatthias Schmidt }
345f67bedddSMatthias Schmidt if (gettimeofday(&now, NULL) == 0 &&
3462e570fe1SSimon Schubert (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) {
347c8b07ee5SSascha Wildner snprintf(errmsg, sizeof(errmsg),
348f67bedddSMatthias Schmidt "Could not deliver for the last %d seconds. Giving up.",
34939f7c85aSSimon Schubert MAX_TIMEOUT);
350f67bedddSMatthias Schmidt goto bounce;
351f67bedddSMatthias Schmidt }
35214dfb991SJoris Giovannangeli for (slept = 0; slept < backoff;) {
35314dfb991SJoris Giovannangeli slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT);
35414dfb991SJoris Giovannangeli if (flushqueue_since(slept)) {
35514dfb991SJoris Giovannangeli backoff = MIN_RETRY;
35614dfb991SJoris Giovannangeli goto retry;
35714dfb991SJoris Giovannangeli }
35814dfb991SJoris Giovannangeli }
35914dfb991SJoris Giovannangeli if (slept >= backoff) {
360c8b07ee5SSascha Wildner /* pick the next backoff between [1.5, 2.5) times backoff */
361c8b07ee5SSascha Wildner backoff = backoff + backoff / 2 + random() % backoff;
362f67bedddSMatthias Schmidt if (backoff > MAX_RETRY)
363f67bedddSMatthias Schmidt backoff = MAX_RETRY;
364c8b07ee5SSascha Wildner }
365f67bedddSMatthias Schmidt goto retry;
366f67bedddSMatthias Schmidt
367f67bedddSMatthias Schmidt case -1:
368f67bedddSMatthias Schmidt default:
369f67bedddSMatthias Schmidt break;
370f67bedddSMatthias Schmidt }
371f67bedddSMatthias Schmidt
372f67bedddSMatthias Schmidt bounce:
3734a23bd3dSMatthias Schmidt bounce(it, errmsg);
374f67bedddSMatthias Schmidt /* NOTREACHED */
375f67bedddSMatthias Schmidt }
376f67bedddSMatthias Schmidt
37730833a29SSimon Schubert void
run_queue(struct queue * queue)378f67bedddSMatthias Schmidt run_queue(struct queue *queue)
379f67bedddSMatthias Schmidt {
3804a23bd3dSMatthias Schmidt struct qitem *it;
3814a23bd3dSMatthias Schmidt
382f67bedddSMatthias Schmidt if (LIST_EMPTY(&queue->queue))
383f67bedddSMatthias Schmidt return;
384f67bedddSMatthias Schmidt
3854a23bd3dSMatthias Schmidt it = go_background(queue);
3864a23bd3dSMatthias Schmidt deliver(it);
387f67bedddSMatthias Schmidt /* NOTREACHED */
388f67bedddSMatthias Schmidt }
389f67bedddSMatthias Schmidt
390f67bedddSMatthias Schmidt static void
show_queue(struct queue * queue)391f67bedddSMatthias Schmidt show_queue(struct queue *queue)
392f67bedddSMatthias Schmidt {
393f67bedddSMatthias Schmidt struct qitem *it;
3949afa363fSSimon Schubert int locked = 0; /* XXX */
395f67bedddSMatthias Schmidt
396f67bedddSMatthias Schmidt if (LIST_EMPTY(&queue->queue)) {
397f67bedddSMatthias Schmidt printf("Mail queue is empty\n");
398f67bedddSMatthias Schmidt return;
399f67bedddSMatthias Schmidt }
400f67bedddSMatthias Schmidt
401f67bedddSMatthias Schmidt LIST_FOREACH(it, &queue->queue, next) {
4027b5c40d5SSimon Schubert printf("ID\t: %s%s\n"
4037b5c40d5SSimon Schubert "From\t: %s\n"
40471b5e57dSSimon Schubert "To\t: %s\n",
4057b5c40d5SSimon Schubert it->queueid,
4069afa363fSSimon Schubert locked ? "*" : "",
4077b5c40d5SSimon Schubert it->sender, it->addr);
40871b5e57dSSimon Schubert
40971b5e57dSSimon Schubert if (LIST_NEXT(it, next) != NULL)
41071b5e57dSSimon Schubert printf("--\n");
411f67bedddSMatthias Schmidt }
412f67bedddSMatthias Schmidt }
413f67bedddSMatthias Schmidt
414f67bedddSMatthias Schmidt /*
415f67bedddSMatthias Schmidt * TODO:
416f67bedddSMatthias Schmidt *
417f67bedddSMatthias Schmidt * - alias processing
418f67bedddSMatthias Schmidt * - use group permissions
419f67bedddSMatthias Schmidt * - proper sysexit codes
420f67bedddSMatthias Schmidt */
421f67bedddSMatthias Schmidt
4224a23bd3dSMatthias Schmidt int
main(int argc,char ** argv)4234a23bd3dSMatthias Schmidt main(int argc, char **argv)
424f67bedddSMatthias Schmidt {
425c8b07ee5SSascha Wildner struct sigaction act;
426f67bedddSMatthias Schmidt char *sender = NULL;
4274e8d31bcSDaniel Fojt char *own_name = NULL;
428f67bedddSMatthias Schmidt struct queue queue;
429f67bedddSMatthias Schmidt int i, ch;
430*577b958fSDaniel Fojt int nodot = 0, showq = 0, queue_only = 0, newaliases = 0;
431ff48fce6SSimon Schubert int recp_from_header = 0;
432f67bedddSMatthias Schmidt
433c8b07ee5SSascha Wildner set_username();
434c8b07ee5SSascha Wildner
435c8b07ee5SSascha Wildner /*
436c8b07ee5SSascha Wildner * We never run as root. If called by root, drop permissions
437c8b07ee5SSascha Wildner * to the mail user.
438c8b07ee5SSascha Wildner */
439c8b07ee5SSascha Wildner if (geteuid() == 0 || getuid() == 0) {
440c8b07ee5SSascha Wildner struct passwd *pw;
441c8b07ee5SSascha Wildner
44214dfb991SJoris Giovannangeli errno = 0;
443c8b07ee5SSascha Wildner pw = getpwnam(DMA_ROOT_USER);
44414dfb991SJoris Giovannangeli if (pw == NULL) {
44514dfb991SJoris Giovannangeli if (errno == 0)
44692fe556dSDaniel Fojt errx(EX_CONFIG, "user '%s' not found", DMA_ROOT_USER);
44714dfb991SJoris Giovannangeli else
44892fe556dSDaniel Fojt err(EX_OSERR, "cannot drop root privileges");
44914dfb991SJoris Giovannangeli }
450c8b07ee5SSascha Wildner
451c8b07ee5SSascha Wildner if (setuid(pw->pw_uid) != 0)
45292fe556dSDaniel Fojt err(EX_OSERR, "cannot drop root privileges");
453c8b07ee5SSascha Wildner
454c8b07ee5SSascha Wildner if (geteuid() == 0 || getuid() == 0)
45592fe556dSDaniel Fojt errx(EX_OSERR, "cannot drop root privileges");
456c8b07ee5SSascha Wildner }
457c8b07ee5SSascha Wildner
458f67bedddSMatthias Schmidt atexit(deltmp);
459c8b07ee5SSascha Wildner init_random();
460e50076f8SSimon Schubert
461e50076f8SSimon Schubert bzero(&queue, sizeof(queue));
462f67bedddSMatthias Schmidt LIST_INIT(&queue.queue);
463f67bedddSMatthias Schmidt
4644e8d31bcSDaniel Fojt own_name = basename(argv[0]);
4654e8d31bcSDaniel Fojt
4664e8d31bcSDaniel Fojt if (strcmp(own_name, "mailq") == 0) {
467eb870081SSimon Schubert argv++; argc--;
468eb870081SSimon Schubert showq = 1;
46900db07ffSSascha Wildner if (argc != 0 && strcmp(argv[0], "-Ac") != 0)
47092fe556dSDaniel Fojt errx(EX_USAGE, "invalid arguments");
471eb870081SSimon Schubert goto skipopts;
4724e8d31bcSDaniel Fojt } else if (strcmp(own_name, "newaliases") == 0) {
473*577b958fSDaniel Fojt newaliases = 1;
474*577b958fSDaniel Fojt goto skipopts;
4754e8d31bcSDaniel Fojt } else if (strcmp(own_name, "hoststat") == 0 ||
4764e8d31bcSDaniel Fojt strcmp(own_name, "purgestat") == 0) {
47792fe556dSDaniel Fojt exit(EX_OK);
478eb870081SSimon Schubert }
479eb870081SSimon Schubert
480ae3c3bbdSMatthias Schmidt opterr = 0;
481ff48fce6SSimon Schubert while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) {
482f67bedddSMatthias Schmidt switch (ch) {
483f67bedddSMatthias Schmidt case 'A':
484f67bedddSMatthias Schmidt /* -AX is being ignored, except for -A{c,m} */
485f67bedddSMatthias Schmidt if (optarg[0] == 'c' || optarg[0] == 'm') {
486f67bedddSMatthias Schmidt break;
487f67bedddSMatthias Schmidt }
4884e8d31bcSDaniel Fojt /* Else FALLTHROUGH */
489f67bedddSMatthias Schmidt case 'b':
490f67bedddSMatthias Schmidt /* -bX is being ignored, except for -bp */
491f67bedddSMatthias Schmidt if (optarg[0] == 'p') {
492f67bedddSMatthias Schmidt showq = 1;
493f67bedddSMatthias Schmidt break;
494a9337db7SSimon Schubert } else if (optarg[0] == 'q') {
495a9337db7SSimon Schubert queue_only = 1;
496a9337db7SSimon Schubert break;
497f67bedddSMatthias Schmidt }
4984e8d31bcSDaniel Fojt /* Else FALLTHROUGH */
499f67bedddSMatthias Schmidt case 'D':
500f67bedddSMatthias Schmidt daemonize = 0;
501f67bedddSMatthias Schmidt break;
502f67bedddSMatthias Schmidt case 'L':
503405f48eeSSimon Schubert logident_base = optarg;
504f67bedddSMatthias Schmidt break;
505f67bedddSMatthias Schmidt case 'f':
506f67bedddSMatthias Schmidt case 'r':
507f67bedddSMatthias Schmidt sender = optarg;
508f67bedddSMatthias Schmidt break;
509f67bedddSMatthias Schmidt
510ff48fce6SSimon Schubert case 't':
511ff48fce6SSimon Schubert recp_from_header = 1;
512ff48fce6SSimon Schubert break;
513ff48fce6SSimon Schubert
514f67bedddSMatthias Schmidt case 'o':
515f67bedddSMatthias Schmidt /* -oX is being ignored, except for -oi */
516f67bedddSMatthias Schmidt if (optarg[0] != 'i')
517f67bedddSMatthias Schmidt break;
5184e8d31bcSDaniel Fojt /* Else FALLTHROUGH */
519f67bedddSMatthias Schmidt case 'i':
520f67bedddSMatthias Schmidt nodot = 1;
521f67bedddSMatthias Schmidt break;
522f67bedddSMatthias Schmidt
523f67bedddSMatthias Schmidt case 'q':
52414dfb991SJoris Giovannangeli /* Don't let getopt slup up other arguments */
52514dfb991SJoris Giovannangeli if (optarg && *optarg == '-')
52614dfb991SJoris Giovannangeli optind--;
527f67bedddSMatthias Schmidt doqueue = 1;
528f67bedddSMatthias Schmidt break;
529f67bedddSMatthias Schmidt
53098dfc119SSimon Schubert /* Ignored options */
53198dfc119SSimon Schubert case 'B':
53298dfc119SSimon Schubert case 'C':
53398dfc119SSimon Schubert case 'd':
53498dfc119SSimon Schubert case 'F':
53598dfc119SSimon Schubert case 'h':
53698dfc119SSimon Schubert case 'N':
53798dfc119SSimon Schubert case 'n':
538*577b958fSDaniel Fojt case 'O':
53998dfc119SSimon Schubert case 'R':
54098dfc119SSimon Schubert case 'U':
54198dfc119SSimon Schubert case 'V':
54298dfc119SSimon Schubert case 'v':
54398dfc119SSimon Schubert case 'X':
54498dfc119SSimon Schubert break;
54598dfc119SSimon Schubert
546abbe6a2cSSimon Schubert case ':':
547abbe6a2cSSimon Schubert if (optopt == 'q') {
548abbe6a2cSSimon Schubert doqueue = 1;
549abbe6a2cSSimon Schubert break;
550abbe6a2cSSimon Schubert }
5514e8d31bcSDaniel Fojt /* Else FALLTHROUGH */
552abbe6a2cSSimon Schubert
553f67bedddSMatthias Schmidt default:
554abbe6a2cSSimon Schubert fprintf(stderr, "invalid argument: `-%c'\n", optopt);
55592fe556dSDaniel Fojt exit(EX_USAGE);
556f67bedddSMatthias Schmidt }
557f67bedddSMatthias Schmidt }
558f67bedddSMatthias Schmidt argc -= optind;
559f67bedddSMatthias Schmidt argv += optind;
560ae3c3bbdSMatthias Schmidt opterr = 1;
561f67bedddSMatthias Schmidt
56213e288a1SSimon 'corecode' Schubert if (argc != 0 && (showq || doqueue))
56392fe556dSDaniel Fojt errx(EX_USAGE, "sending mail and queue operations are mutually exclusive");
56413e288a1SSimon 'corecode' Schubert
56513e288a1SSimon 'corecode' Schubert if (showq + doqueue > 1)
56692fe556dSDaniel Fojt errx(EX_USAGE, "conflicting queue operations");
56713e288a1SSimon 'corecode' Schubert
568eb870081SSimon Schubert skipopts:
569405f48eeSSimon Schubert if (logident_base == NULL)
570405f48eeSSimon Schubert logident_base = "dma";
571405f48eeSSimon Schubert setlogident(NULL);
572f67bedddSMatthias Schmidt
573c8b07ee5SSascha Wildner act.sa_handler = sighup_handler;
574c8b07ee5SSascha Wildner act.sa_flags = 0;
575c8b07ee5SSascha Wildner sigemptyset(&act.sa_mask);
576c8b07ee5SSascha Wildner if (sigaction(SIGHUP, &act, NULL) != 0)
577c8b07ee5SSascha Wildner syslog(LOG_WARNING, "can not set signal handler: %m");
578569443a9SSimon 'corecode' Schubert
579c8b07ee5SSascha Wildner parse_conf(CONF_PATH "/dma.conf");
580f67bedddSMatthias Schmidt
581ca259d14SSimon Schubert if (config.authpath != NULL)
582ca259d14SSimon Schubert parse_authfile(config.authpath);
583f67bedddSMatthias Schmidt
584f67bedddSMatthias Schmidt if (showq) {
585e50076f8SSimon Schubert if (load_queue(&queue) < 0)
58692fe556dSDaniel Fojt errlog(EX_NOINPUT, "can not load queue");
587e50076f8SSimon Schubert show_queue(&queue);
588f67bedddSMatthias Schmidt return (0);
589f67bedddSMatthias Schmidt }
590f67bedddSMatthias Schmidt
591f67bedddSMatthias Schmidt if (doqueue) {
59214dfb991SJoris Giovannangeli flushqueue_signal();
593e50076f8SSimon Schubert if (load_queue(&queue) < 0)
59492fe556dSDaniel Fojt errlog(EX_NOINPUT, "can not load queue");
595e50076f8SSimon Schubert run_queue(&queue);
596f67bedddSMatthias Schmidt return (0);
597f67bedddSMatthias Schmidt }
598f67bedddSMatthias Schmidt
5994a23bd3dSMatthias Schmidt if (read_aliases() != 0)
60092fe556dSDaniel Fojt errlog(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases);
601f67bedddSMatthias Schmidt
602*577b958fSDaniel Fojt if (newaliases)
603*577b958fSDaniel Fojt return(0);
604*577b958fSDaniel Fojt
6051c9e6b7bSSimon Schubert if ((sender = set_from(&queue, sender)) == NULL)
60692fe556dSDaniel Fojt errlog(EX_SOFTWARE, NULL);
607f67bedddSMatthias Schmidt
6081c9e6b7bSSimon Schubert if (newspoolf(&queue) != 0)
60992fe556dSDaniel Fojt errlog(EX_CANTCREAT, "can not create temp file in `%s'", config.spooldir);
6104a23bd3dSMatthias Schmidt
611405f48eeSSimon Schubert setlogident("%s", queue.id);
612405f48eeSSimon Schubert
613ff48fce6SSimon Schubert for (i = 0; i < argc; i++) {
614c8b07ee5SSascha Wildner if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0)
61592fe556dSDaniel Fojt errlogx(EX_DATAERR, "invalid recipient `%s'", argv[i]);
616ff48fce6SSimon Schubert }
617ff48fce6SSimon Schubert
618ff48fce6SSimon Schubert if (LIST_EMPTY(&queue.queue) && !recp_from_header)
61992fe556dSDaniel Fojt errlogx(EX_NOINPUT, "no recipients");
620ff48fce6SSimon Schubert
621ff48fce6SSimon Schubert if (readmail(&queue, nodot, recp_from_header) != 0)
62292fe556dSDaniel Fojt errlog(EX_NOINPUT, "can not read mail");
623f67bedddSMatthias Schmidt
624ff48fce6SSimon Schubert if (LIST_EMPTY(&queue.queue))
62592fe556dSDaniel Fojt errlogx(EX_NOINPUT, "no recipients");
626ff48fce6SSimon Schubert
6271c9e6b7bSSimon Schubert if (linkspool(&queue) != 0)
62892fe556dSDaniel Fojt errlog(EX_CANTCREAT, "can not create spools");
629f67bedddSMatthias Schmidt
630f67bedddSMatthias Schmidt /* From here on the mail is safe. */
631f67bedddSMatthias Schmidt
632ca259d14SSimon Schubert if (config.features & DEFER || queue_only)
633f67bedddSMatthias Schmidt return (0);
634f67bedddSMatthias Schmidt
63530833a29SSimon Schubert run_queue(&queue);
636f67bedddSMatthias Schmidt
637f67bedddSMatthias Schmidt /* NOTREACHED */
638f67bedddSMatthias Schmidt return (0);
639f67bedddSMatthias Schmidt }
640