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