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