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