xref: /freebsd/contrib/dma/local.c (revision fbe95b885f3431b1d8003545b32e8ffa88f2d16b)
1a9e8641dSBaptiste Daroussin /*
2e56bad4aSBaptiste Daroussin  * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>.
3a9e8641dSBaptiste Daroussin  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
4a9e8641dSBaptiste Daroussin  *
5a9e8641dSBaptiste Daroussin  * This code is derived from software contributed to The DragonFly Project
6e56bad4aSBaptiste Daroussin  * by Simon Schubert <2@0x2c.org>.
7a9e8641dSBaptiste Daroussin  *
8a9e8641dSBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
9a9e8641dSBaptiste Daroussin  * modification, are permitted provided that the following conditions
10a9e8641dSBaptiste Daroussin  * are met:
11a9e8641dSBaptiste Daroussin  *
12a9e8641dSBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
13a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
14a9e8641dSBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
15a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in
16a9e8641dSBaptiste Daroussin  *    the documentation and/or other materials provided with the
17a9e8641dSBaptiste Daroussin  *    distribution.
18a9e8641dSBaptiste Daroussin  * 3. Neither the name of The DragonFly Project nor the names of its
19a9e8641dSBaptiste Daroussin  *    contributors may be used to endorse or promote products derived
20a9e8641dSBaptiste Daroussin  *    from this software without specific, prior written permission.
21a9e8641dSBaptiste Daroussin  *
22a9e8641dSBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23a9e8641dSBaptiste Daroussin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24a9e8641dSBaptiste Daroussin  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25a9e8641dSBaptiste Daroussin  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26a9e8641dSBaptiste Daroussin  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27a9e8641dSBaptiste Daroussin  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28a9e8641dSBaptiste Daroussin  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29a9e8641dSBaptiste Daroussin  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30a9e8641dSBaptiste Daroussin  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31a9e8641dSBaptiste Daroussin  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32a9e8641dSBaptiste Daroussin  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33a9e8641dSBaptiste Daroussin  * SUCH DAMAGE.
34a9e8641dSBaptiste Daroussin  */
35a9e8641dSBaptiste Daroussin 
36a9e8641dSBaptiste Daroussin #include <sys/types.h>
37a9e8641dSBaptiste Daroussin #include <sys/wait.h>
38a9e8641dSBaptiste Daroussin 
39a9e8641dSBaptiste Daroussin #include <err.h>
40a9e8641dSBaptiste Daroussin #include <errno.h>
41a9e8641dSBaptiste Daroussin #include <fcntl.h>
42a9e8641dSBaptiste Daroussin #include <limits.h>
43a9e8641dSBaptiste Daroussin #include <paths.h>
44a9e8641dSBaptiste Daroussin #include <signal.h>
45a9e8641dSBaptiste Daroussin #include <stdint.h>
46a9e8641dSBaptiste Daroussin #include <stdio.h>
47*fbe95b88SBaptiste Daroussin #include <strings.h>
48b86d1398SJung-uk Kim #include <string.h>
49a9e8641dSBaptiste Daroussin #include <syslog.h>
50a9e8641dSBaptiste Daroussin #include <unistd.h>
51a9e8641dSBaptiste Daroussin 
52a9e8641dSBaptiste Daroussin #include "dma.h"
53a9e8641dSBaptiste Daroussin 
54a9e8641dSBaptiste Daroussin static int
create_mbox(const char * name)55a9e8641dSBaptiste Daroussin create_mbox(const char *name)
56a9e8641dSBaptiste Daroussin {
57a9e8641dSBaptiste Daroussin 	struct sigaction sa, osa;
58a9e8641dSBaptiste Daroussin 	pid_t child, waitchild;
59a9e8641dSBaptiste Daroussin 	int status;
60a9e8641dSBaptiste Daroussin 	int i;
61a9e8641dSBaptiste Daroussin 	long maxfd;
62a9e8641dSBaptiste Daroussin 	int e;
63a9e8641dSBaptiste Daroussin 	int r = -1;
64a9e8641dSBaptiste Daroussin 
65a9e8641dSBaptiste Daroussin 	/*
66a9e8641dSBaptiste Daroussin 	 * We need to enable SIGCHLD temporarily so that waitpid works.
67a9e8641dSBaptiste Daroussin 	 */
68a9e8641dSBaptiste Daroussin 	bzero(&sa, sizeof(sa));
69a9e8641dSBaptiste Daroussin 	sa.sa_handler = SIG_DFL;
70a9e8641dSBaptiste Daroussin 	sigaction(SIGCHLD, &sa, &osa);
71a9e8641dSBaptiste Daroussin 
72a9e8641dSBaptiste Daroussin 	do_timeout(100, 0);
73a9e8641dSBaptiste Daroussin 
74a9e8641dSBaptiste Daroussin 	child = fork();
75a9e8641dSBaptiste Daroussin 	switch (child) {
76a9e8641dSBaptiste Daroussin 	case 0:
77a9e8641dSBaptiste Daroussin 		/* child */
78a9e8641dSBaptiste Daroussin 		maxfd = sysconf(_SC_OPEN_MAX);
79a9e8641dSBaptiste Daroussin 		if (maxfd == -1)
80a9e8641dSBaptiste Daroussin 			maxfd = 1024;	/* what can we do... */
81a9e8641dSBaptiste Daroussin 
82a9e8641dSBaptiste Daroussin 		for (i = 3; i <= maxfd; ++i)
83a9e8641dSBaptiste Daroussin 			close(i);
84a9e8641dSBaptiste Daroussin 
85*fbe95b88SBaptiste Daroussin 		execl(LIBEXEC_PATH "/dma-mbox-create", "dma-mbox-create", name, (char *)NULL);
86a9e8641dSBaptiste Daroussin 		syslog(LOG_ERR, "cannot execute "LIBEXEC_PATH"/dma-mbox-create: %m");
87e56bad4aSBaptiste Daroussin 		exit(EX_SOFTWARE);
88a9e8641dSBaptiste Daroussin 
89a9e8641dSBaptiste Daroussin 	default:
90a9e8641dSBaptiste Daroussin 		/* parent */
91a9e8641dSBaptiste Daroussin 		waitchild = waitpid(child, &status, 0);
92a9e8641dSBaptiste Daroussin 
93a9e8641dSBaptiste Daroussin 		e = errno;
94a9e8641dSBaptiste Daroussin 
95a9e8641dSBaptiste Daroussin 		do_timeout(0, 0);
96a9e8641dSBaptiste Daroussin 
97a9e8641dSBaptiste Daroussin 		if (waitchild == -1 && e == EINTR) {
98a9e8641dSBaptiste Daroussin 			syslog(LOG_ERR, "hung child while creating mbox `%s': %m", name);
99a9e8641dSBaptiste Daroussin 			break;
100a9e8641dSBaptiste Daroussin 		}
101a9e8641dSBaptiste Daroussin 
102a9e8641dSBaptiste Daroussin 		if (waitchild == -1) {
103a9e8641dSBaptiste Daroussin 			syslog(LOG_ERR, "child disappeared while creating mbox `%s': %m", name);
104a9e8641dSBaptiste Daroussin 			break;
105a9e8641dSBaptiste Daroussin 		}
106a9e8641dSBaptiste Daroussin 
107a9e8641dSBaptiste Daroussin 		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
108a9e8641dSBaptiste Daroussin 			syslog(LOG_ERR, "error creating mbox `%s'", name);
109a9e8641dSBaptiste Daroussin 			break;
110a9e8641dSBaptiste Daroussin 		}
111a9e8641dSBaptiste Daroussin 
112a9e8641dSBaptiste Daroussin 		/* success */
113a9e8641dSBaptiste Daroussin 		r = 0;
114a9e8641dSBaptiste Daroussin 		break;
115a9e8641dSBaptiste Daroussin 
116a9e8641dSBaptiste Daroussin 	case -1:
117a9e8641dSBaptiste Daroussin 		/* error */
118a9e8641dSBaptiste Daroussin 		syslog(LOG_ERR, "error creating mbox");
119a9e8641dSBaptiste Daroussin 		break;
120a9e8641dSBaptiste Daroussin 	}
121a9e8641dSBaptiste Daroussin 
122a9e8641dSBaptiste Daroussin 	sigaction(SIGCHLD, &osa, NULL);
123a9e8641dSBaptiste Daroussin 
124a9e8641dSBaptiste Daroussin 	return (r);
125a9e8641dSBaptiste Daroussin }
126a9e8641dSBaptiste Daroussin 
127a9e8641dSBaptiste Daroussin int
deliver_local(struct qitem * it)128a9e8641dSBaptiste Daroussin deliver_local(struct qitem *it)
129a9e8641dSBaptiste Daroussin {
130a9e8641dSBaptiste Daroussin 	char fn[PATH_MAX+1];
131a9e8641dSBaptiste Daroussin 	char line[1000];
132a9e8641dSBaptiste Daroussin 	const char *sender;
133a9e8641dSBaptiste Daroussin 	const char *newline = "\n";
134a9e8641dSBaptiste Daroussin 	size_t linelen;
135a9e8641dSBaptiste Daroussin 	int tries = 0;
136a9e8641dSBaptiste Daroussin 	int mbox;
137a9e8641dSBaptiste Daroussin 	int error;
138a9e8641dSBaptiste Daroussin 	int hadnl = 0;
139a9e8641dSBaptiste Daroussin 	off_t mboxlen;
140a9e8641dSBaptiste Daroussin 	time_t now = time(NULL);
141a9e8641dSBaptiste Daroussin 
142a9e8641dSBaptiste Daroussin 	error = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, it->addr);
143a9e8641dSBaptiste Daroussin 	if (error < 0 || (size_t)error >= sizeof(fn)) {
144a9e8641dSBaptiste Daroussin 		syslog(LOG_NOTICE, "local delivery deferred: %m");
145a9e8641dSBaptiste Daroussin 		return (1);
146a9e8641dSBaptiste Daroussin 	}
147a9e8641dSBaptiste Daroussin 
148a9e8641dSBaptiste Daroussin retry:
149a9e8641dSBaptiste Daroussin 	/* wait for a maximum of 100s to get the lock to the file */
150a9e8641dSBaptiste Daroussin 	do_timeout(100, 0);
151a9e8641dSBaptiste Daroussin 
152a9e8641dSBaptiste Daroussin 	/* don't use O_CREAT here, because we might be running as the wrong user. */
153a9e8641dSBaptiste Daroussin 	mbox = open_locked(fn, O_WRONLY|O_APPEND);
154a9e8641dSBaptiste Daroussin 	if (mbox < 0) {
155a9e8641dSBaptiste Daroussin 		int e = errno;
156a9e8641dSBaptiste Daroussin 
157a9e8641dSBaptiste Daroussin 		do_timeout(0, 0);
158a9e8641dSBaptiste Daroussin 
159a9e8641dSBaptiste Daroussin 		switch (e) {
160a9e8641dSBaptiste Daroussin 		case EACCES:
161a9e8641dSBaptiste Daroussin 		case ENOENT:
162a9e8641dSBaptiste Daroussin 			/*
163a9e8641dSBaptiste Daroussin 			 * The file does not exist or we can't access it.
164a9e8641dSBaptiste Daroussin 			 * Call dma-mbox-create to create it and fix permissions.
165a9e8641dSBaptiste Daroussin 			 */
166a9e8641dSBaptiste Daroussin 			if (tries > 0 || create_mbox(it->addr) != 0) {
167a9e8641dSBaptiste Daroussin 				syslog(LOG_ERR, "local delivery deferred: can not create `%s'", fn);
168a9e8641dSBaptiste Daroussin 				return (1);
169a9e8641dSBaptiste Daroussin 			}
170a9e8641dSBaptiste Daroussin 			++tries;
171a9e8641dSBaptiste Daroussin 			goto retry;
172a9e8641dSBaptiste Daroussin 
173a9e8641dSBaptiste Daroussin 		case EINTR:
174a9e8641dSBaptiste Daroussin 			syslog(LOG_NOTICE, "local delivery deferred: can not lock `%s'", fn);
175a9e8641dSBaptiste Daroussin 			break;
176a9e8641dSBaptiste Daroussin 
177a9e8641dSBaptiste Daroussin 		default:
178a9e8641dSBaptiste Daroussin 			syslog(LOG_NOTICE, "local delivery deferred: can not open `%s': %m", fn);
179a9e8641dSBaptiste Daroussin 			break;
180a9e8641dSBaptiste Daroussin 		}
181a9e8641dSBaptiste Daroussin 		return (1);
182a9e8641dSBaptiste Daroussin 	}
183a9e8641dSBaptiste Daroussin 	do_timeout(0, 0);
184a9e8641dSBaptiste Daroussin 
185a9e8641dSBaptiste Daroussin 	mboxlen = lseek(mbox, 0, SEEK_END);
186a9e8641dSBaptiste Daroussin 
187a9e8641dSBaptiste Daroussin 	/* New mails start with \nFrom ...., unless we're at the beginning of the mbox */
188a9e8641dSBaptiste Daroussin 	if (mboxlen == 0)
189a9e8641dSBaptiste Daroussin 		newline = "";
190a9e8641dSBaptiste Daroussin 
191a9e8641dSBaptiste Daroussin 	/* If we're bouncing a message, claim it comes from MAILER-DAEMON */
192a9e8641dSBaptiste Daroussin 	sender = it->sender;
193a9e8641dSBaptiste Daroussin 	if (strcmp(sender, "") == 0)
194a9e8641dSBaptiste Daroussin 		sender = "MAILER-DAEMON";
195a9e8641dSBaptiste Daroussin 
196a9e8641dSBaptiste Daroussin 	if (fseek(it->mailf, 0, SEEK_SET) != 0) {
197a9e8641dSBaptiste Daroussin 		syslog(LOG_NOTICE, "local delivery deferred: can not seek: %m");
198a9e8641dSBaptiste Daroussin 		goto out;
199a9e8641dSBaptiste Daroussin 	}
200a9e8641dSBaptiste Daroussin 
2012382c29eSBaptiste Daroussin 	error = snprintf(line, sizeof(line), "%sFrom %s %s", newline, sender, ctime(&now));
202a9e8641dSBaptiste Daroussin 	if (error < 0 || (size_t)error >= sizeof(line)) {
203a9e8641dSBaptiste Daroussin 		syslog(LOG_NOTICE, "local delivery deferred: can not write header: %m");
204a9e8641dSBaptiste Daroussin 		goto out;
205a9e8641dSBaptiste Daroussin 	}
206a9e8641dSBaptiste Daroussin 	if (write(mbox, line, error) != error)
207a9e8641dSBaptiste Daroussin 		goto wrerror;
208a9e8641dSBaptiste Daroussin 
209a9e8641dSBaptiste Daroussin 	while (!feof(it->mailf)) {
210a9e8641dSBaptiste Daroussin 		if (fgets(line, sizeof(line), it->mailf) == NULL)
211a9e8641dSBaptiste Daroussin 			break;
212a9e8641dSBaptiste Daroussin 		linelen = strlen(line);
213a9e8641dSBaptiste Daroussin 		if (linelen == 0 || line[linelen - 1] != '\n') {
214a9e8641dSBaptiste Daroussin 			syslog(LOG_CRIT, "local delivery failed: corrupted queue file");
215a9e8641dSBaptiste Daroussin 			snprintf(errmsg, sizeof(errmsg), "corrupted queue file");
216a9e8641dSBaptiste Daroussin 			error = -1;
217a9e8641dSBaptiste Daroussin 			goto chop;
218a9e8641dSBaptiste Daroussin 		}
219a9e8641dSBaptiste Daroussin 
220a9e8641dSBaptiste Daroussin 		/*
221a9e8641dSBaptiste Daroussin 		 * mboxro processing:
222a9e8641dSBaptiste Daroussin 		 * - escape lines that start with "From " with a > sign.
223*fbe95b88SBaptiste Daroussin 		 * - be reversible by escaping lines that contain an arbitrary
224a9e8641dSBaptiste Daroussin 		 *   number of > signs, followed by "From ", i.e. />*From / in regexp.
225a9e8641dSBaptiste Daroussin 		 * - strict mbox processing only requires escaping after empty lines,
226a9e8641dSBaptiste Daroussin 		 *   yet most MUAs seem to relax this requirement and will treat any
227a9e8641dSBaptiste Daroussin 		 *   line starting with "From " as the beginning of a new mail.
228a9e8641dSBaptiste Daroussin 		 */
229a9e8641dSBaptiste Daroussin 		if ((!MBOX_STRICT || hadnl) &&
230a9e8641dSBaptiste Daroussin 		    strncmp(&line[strspn(line, ">")], "From ", 5) == 0) {
231a9e8641dSBaptiste Daroussin 			const char *gt = ">";
232a9e8641dSBaptiste Daroussin 
233a9e8641dSBaptiste Daroussin 			if (write(mbox, gt, 1) != 1)
234a9e8641dSBaptiste Daroussin 				goto wrerror;
235a9e8641dSBaptiste Daroussin 			hadnl = 0;
236a9e8641dSBaptiste Daroussin 		} else if (strcmp(line, "\n") == 0) {
237a9e8641dSBaptiste Daroussin 			hadnl = 1;
238a9e8641dSBaptiste Daroussin 		} else {
239a9e8641dSBaptiste Daroussin 			hadnl = 0;
240a9e8641dSBaptiste Daroussin 		}
241a9e8641dSBaptiste Daroussin 		if ((size_t)write(mbox, line, linelen) != linelen)
242a9e8641dSBaptiste Daroussin 			goto wrerror;
243a9e8641dSBaptiste Daroussin 	}
244a9e8641dSBaptiste Daroussin 	close(mbox);
245a9e8641dSBaptiste Daroussin 	return (0);
246a9e8641dSBaptiste Daroussin 
247a9e8641dSBaptiste Daroussin wrerror:
248a9e8641dSBaptiste Daroussin 	syslog(LOG_ERR, "local delivery failed: write error: %m");
249a9e8641dSBaptiste Daroussin 	error = 1;
250a9e8641dSBaptiste Daroussin chop:
251a9e8641dSBaptiste Daroussin 	if (ftruncate(mbox, mboxlen) != 0)
252a9e8641dSBaptiste Daroussin 		syslog(LOG_WARNING, "error recovering mbox `%s': %m", fn);
253a9e8641dSBaptiste Daroussin out:
254a9e8641dSBaptiste Daroussin 	close(mbox);
255a9e8641dSBaptiste Daroussin 	return (error);
256a9e8641dSBaptiste Daroussin }
257