]>
Commit | Line | Data |
---|---|---|
28be0b96 SS |
1 | /* |
2 | * Copyright (c) 2008 The DragonFly Project. All rights reserved. | |
3 | * | |
4 | * This code is derived from software contributed to The DragonFly Project | |
5 | * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * 3. Neither the name of The DragonFly Project nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific, prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <errno.h> | |
36 | #include <syslog.h> | |
37 | #include <unistd.h> | |
38 | ||
39 | #include "dma.h" | |
40 | ||
41 | void | |
42 | bounce(struct qitem *it, const char *reason) | |
43 | { | |
44 | struct queue bounceq; | |
45 | char line[1000]; | |
46 | size_t pos; | |
47 | int error; | |
48 | ||
49 | /* Don't bounce bounced mails */ | |
50 | if (it->sender[0] == 0) { | |
51 | syslog(LOG_INFO, "can not bounce a bounce message, discarding"); | |
52 | exit(1); | |
53 | } | |
54 | ||
6f5efe4f | 55 | bzero(&bounceq, sizeof(bounceq)); |
28be0b96 | 56 | LIST_INIT(&bounceq.queue); |
057afad5 SS |
57 | bounceq.sender = ""; |
58 | if (add_recp(&bounceq, it->sender, 1) != 0) | |
28be0b96 SS |
59 | goto fail; |
60 | ||
057afad5 | 61 | if (newspoolf(&bounceq) != 0) |
28be0b96 SS |
62 | goto fail; |
63 | ||
64 | syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id); | |
65 | setlogident("%s", bounceq.id); | |
66 | ||
67 | error = fprintf(bounceq.mailf, | |
68 | "Received: from MAILER-DAEMON\n" | |
69 | "\tid %s\n" | |
70 | "\tby %s (%s)\n" | |
71 | "\t%s\n" | |
72 | "X-Original-To: <%s>\n" | |
73 | "From: MAILER-DAEMON <>\n" | |
74 | "To: %s\n" | |
75 | "Subject: Mail delivery failed\n" | |
76 | "Message-Id: <%s@%s>\n" | |
77 | "Date: %s\n" | |
78 | "\n" | |
79 | "This is the %s at %s.\n" | |
80 | "\n" | |
81 | "There was an error delivering your mail to <%s>.\n" | |
82 | "\n" | |
83 | "%s\n" | |
84 | "\n" | |
85 | "%s\n" | |
86 | "\n", | |
87 | bounceq.id, | |
88 | hostname(), VERSION, | |
89 | rfc822date(), | |
90 | it->addr, | |
91 | it->sender, | |
92 | bounceq.id, hostname(), | |
93 | rfc822date(), | |
94 | VERSION, hostname(), | |
95 | it->addr, | |
96 | reason, | |
a0c4afa6 | 97 | config.features & FULLBOUNCE ? |
28be0b96 SS |
98 | "Original message follows." : |
99 | "Message headers follow."); | |
100 | if (error < 0) | |
101 | goto fail; | |
102 | ||
6ddd63e1 | 103 | if (fseek(it->mailf, 0, SEEK_SET) != 0) |
28be0b96 | 104 | goto fail; |
a0c4afa6 | 105 | if (config.features & FULLBOUNCE) { |
28be0b96 SS |
106 | while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) { |
107 | if (fwrite(line, 1, pos, bounceq.mailf) != pos) | |
108 | goto fail; | |
109 | } | |
110 | } else { | |
111 | while (!feof(it->mailf)) { | |
112 | if (fgets(line, sizeof(line), it->mailf) == NULL) | |
113 | break; | |
114 | if (line[0] == '\n') | |
115 | break; | |
116 | if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1) | |
117 | goto fail; | |
118 | } | |
119 | } | |
120 | ||
057afad5 | 121 | if (linkspool(&bounceq) != 0) |
28be0b96 SS |
122 | goto fail; |
123 | /* bounce is safe */ | |
124 | ||
125 | delqueue(it); | |
126 | ||
127 | run_queue(&bounceq); | |
128 | /* NOTREACHED */ | |
129 | ||
130 | fail: | |
131 | syslog(LOG_CRIT, "error creating bounce: %m"); | |
132 | delqueue(it); | |
133 | exit(1); | |
134 | } | |
135 | ||
f734c0ae SS |
136 | struct parse_state { |
137 | char addr[1000]; | |
138 | int pos; | |
139 | ||
140 | enum { | |
141 | NONE = 0, | |
142 | START, | |
143 | MAIN, | |
144 | EOL, | |
145 | QUIT | |
146 | } state; | |
147 | int comment; | |
148 | int quote; | |
149 | int brackets; | |
150 | int esc; | |
151 | }; | |
152 | ||
153 | /* | |
154 | * Simplified RFC2822 header/address parsing. | |
155 | * We copy escapes and quoted strings directly, since | |
156 | * we have to pass them like this to the mail server anyways. | |
157 | * XXX local addresses will need treatment | |
158 | */ | |
159 | static int | |
160 | parse_addrs(struct parse_state *ps, char *s, struct queue *queue) | |
161 | { | |
162 | char *addr; | |
163 | ||
164 | again: | |
165 | switch (ps->state) { | |
166 | case NONE: | |
167 | return (-1); | |
168 | ||
169 | case START: | |
170 | /* init our data */ | |
171 | bzero(ps, sizeof(*ps)); | |
172 | ||
173 | /* skip over header name */ | |
174 | while (*s != ':') | |
175 | s++; | |
176 | s++; | |
177 | ps->state = MAIN; | |
178 | break; | |
179 | ||
180 | case MAIN: | |
181 | /* all fine */ | |
182 | break; | |
183 | ||
184 | case EOL: | |
185 | switch (*s) { | |
186 | case ' ': | |
187 | case '\t': | |
188 | s++; | |
189 | /* continue */ | |
190 | break; | |
191 | ||
192 | default: | |
193 | ps->state = QUIT; | |
194 | if (ps->pos != 0) | |
195 | goto newaddr; | |
196 | return (0); | |
197 | } | |
198 | ||
199 | case QUIT: | |
200 | return (0); | |
201 | } | |
202 | ||
203 | for (; *s != 0; s++) { | |
204 | if (ps->esc) { | |
205 | ps->esc = 0; | |
206 | ||
207 | switch (*s) { | |
208 | case '\r': | |
209 | case '\n': | |
210 | goto err; | |
211 | ||
212 | default: | |
213 | goto copy; | |
214 | } | |
215 | } | |
216 | ||
217 | if (ps->quote) { | |
218 | switch (*s) { | |
219 | case '"': | |
220 | ps->quote = 0; | |
221 | goto copy; | |
222 | ||
223 | case '\\': | |
224 | ps->esc = 1; | |
225 | goto copy; | |
226 | ||
227 | case '\r': | |
228 | case '\n': | |
229 | goto eol; | |
230 | ||
231 | default: | |
232 | goto copy; | |
233 | } | |
234 | } | |
235 | ||
236 | switch (*s) { | |
237 | case '(': | |
238 | ps->comment++; | |
239 | break; | |
240 | ||
241 | case ')': | |
242 | if (ps->comment) | |
243 | ps->comment--; | |
244 | else | |
245 | goto err; | |
246 | goto skip; | |
247 | ||
248 | case '"': | |
249 | ps->quote = 1; | |
250 | goto copy; | |
251 | ||
252 | case '\\': | |
253 | ps->esc = 1; | |
254 | goto copy; | |
255 | ||
256 | case '\r': | |
257 | case '\n': | |
258 | goto eol; | |
259 | } | |
260 | ||
261 | if (ps->comment) | |
262 | goto skip; | |
263 | ||
264 | switch (*s) { | |
265 | case ' ': | |
266 | case '\t': | |
267 | /* ignore whitespace */ | |
268 | goto skip; | |
269 | ||
270 | case '<': | |
1aa8a6d7 | 271 | /* this is the real address now */ |
f734c0ae | 272 | ps->brackets = 1; |
1aa8a6d7 | 273 | ps->pos = 0; |
f734c0ae SS |
274 | goto skip; |
275 | ||
276 | case '>': | |
277 | if (!ps->brackets) | |
278 | goto err; | |
279 | ps->brackets = 0; | |
280 | ||
281 | s++; | |
282 | goto newaddr; | |
283 | ||
284 | case ':': | |
285 | /* group - ignore */ | |
286 | ps->pos = 0; | |
287 | goto skip; | |
288 | ||
289 | case ',': | |
290 | case ';': | |
291 | s++; | |
292 | goto newaddr; | |
293 | ||
294 | default: | |
295 | goto copy; | |
296 | } | |
297 | ||
298 | copy: | |
299 | if (ps->comment) | |
300 | goto skip; | |
301 | ||
302 | if (ps->pos + 1 == sizeof(ps->addr)) | |
303 | goto err; | |
304 | ps->addr[ps->pos++] = *s; | |
305 | ||
306 | skip: | |
307 | ; | |
308 | } | |
309 | ||
310 | eol: | |
311 | ps->state = EOL; | |
312 | return (0); | |
313 | ||
314 | err: | |
315 | ps->state = QUIT; | |
316 | return (-1); | |
317 | ||
318 | newaddr: | |
319 | ps->addr[ps->pos] = 0; | |
320 | ps->pos = 0; | |
321 | addr = strdup(ps->addr); | |
322 | if (addr == NULL) | |
323 | errlog(1, NULL); | |
324 | ||
325 | add_recp(queue, addr, 1); | |
f734c0ae SS |
326 | goto again; |
327 | } | |
328 | ||
28be0b96 | 329 | int |
f734c0ae | 330 | readmail(struct queue *queue, int nodot, int recp_from_header) |
28be0b96 | 331 | { |
f734c0ae | 332 | struct parse_state parse_state; |
28be0b96 SS |
333 | char line[1000]; /* by RFC2822 */ |
334 | size_t linelen; | |
335 | size_t error; | |
336 | int had_headers = 0; | |
337 | int had_from = 0; | |
338 | int had_messagid = 0; | |
339 | int had_date = 0; | |
f734c0ae SS |
340 | int nocopy = 0; |
341 | ||
342 | parse_state.state = NONE; | |
28be0b96 SS |
343 | |
344 | error = fprintf(queue->mailf, | |
345 | "Received: from %s (uid %d)\n" | |
346 | "\t(envelope-from %s)\n" | |
347 | "\tid %s\n" | |
348 | "\tby %s (%s)\n" | |
349 | "\t%s\n", | |
350 | username, getuid(), | |
057afad5 | 351 | queue->sender, |
28be0b96 SS |
352 | queue->id, |
353 | hostname(), VERSION, | |
354 | rfc822date()); | |
355 | if ((ssize_t)error < 0) | |
356 | return (-1); | |
357 | ||
358 | while (!feof(stdin)) { | |
359 | if (fgets(line, sizeof(line), stdin) == NULL) | |
360 | break; | |
361 | linelen = strlen(line); | |
362 | if (linelen == 0 || line[linelen - 1] != '\n') { | |
363 | errno = EINVAL; /* XXX mark permanent errors */ | |
364 | return (-1); | |
365 | } | |
366 | if (!had_headers) { | |
f734c0ae SS |
367 | /* |
368 | * Unless this is a continuation, switch of | |
369 | * the Bcc: nocopy flag. | |
370 | */ | |
371 | if (!(line[0] == ' ' || line[0] == '\t')) | |
372 | nocopy = 0; | |
373 | ||
28be0b96 SS |
374 | if (strprefixcmp(line, "Date:") == 0) |
375 | had_date = 1; | |
376 | else if (strprefixcmp(line, "Message-Id:") == 0) | |
377 | had_messagid = 1; | |
378 | else if (strprefixcmp(line, "From:") == 0) | |
379 | had_from = 1; | |
f734c0ae SS |
380 | else if (strprefixcmp(line, "Bcc:") == 0) |
381 | nocopy = 1; | |
382 | ||
383 | if (parse_state.state != NONE) { | |
384 | if (parse_addrs(&parse_state, line, queue) < 0) { | |
385 | errlogx(1, "invalid address in header\n"); | |
386 | /* NOTREACHED */ | |
387 | } | |
388 | } | |
389 | ||
390 | if (recp_from_header && ( | |
391 | strprefixcmp(line, "To:") == 0 || | |
392 | strprefixcmp(line, "Cc:") == 0 || | |
393 | strprefixcmp(line, "Bcc:") == 0)) { | |
394 | parse_state.state = START; | |
395 | if (parse_addrs(&parse_state, line, queue) < 0) { | |
396 | errlogx(1, "invalid address in header\n"); | |
397 | /* NOTREACHED */ | |
398 | } | |
399 | } | |
28be0b96 | 400 | } |
f734c0ae | 401 | |
28be0b96 SS |
402 | if (strcmp(line, "\n") == 0 && !had_headers) { |
403 | had_headers = 1; | |
404 | while (!had_date || !had_messagid || !had_from) { | |
405 | if (!had_date) { | |
406 | had_date = 1; | |
407 | snprintf(line, sizeof(line), "Date: %s\n", rfc822date()); | |
408 | } else if (!had_messagid) { | |
409 | /* XXX better msgid, assign earlier and log? */ | |
410 | had_messagid = 1; | |
411 | snprintf(line, sizeof(line), "Message-Id: <%s@%s>\n", | |
412 | queue->id, hostname()); | |
413 | } else if (!had_from) { | |
414 | had_from = 1; | |
057afad5 | 415 | snprintf(line, sizeof(line), "From: <%s>\n", queue->sender); |
28be0b96 SS |
416 | } |
417 | if (fwrite(line, strlen(line), 1, queue->mailf) != 1) | |
418 | return (-1); | |
419 | } | |
420 | strcpy(line, "\n"); | |
421 | } | |
422 | if (!nodot && linelen == 2 && line[0] == '.') | |
423 | break; | |
f734c0ae SS |
424 | if (!nocopy) { |
425 | if (fwrite(line, strlen(line), 1, queue->mailf) != 1) | |
426 | return (-1); | |
427 | } | |
28be0b96 SS |
428 | } |
429 | ||
430 | return (0); | |
431 | } |