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