Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/dma/mail.c
39475 views
1
/*
2
* Copyright (c) 2008-2014, Simon Schubert <[email protected]>.
3
* Copyright (c) 2008 The DragonFly Project. All rights reserved.
4
*
5
* This code is derived from software contributed to The DragonFly Project
6
* by Simon Schubert <[email protected]>.
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 <errno.h>
37
#include <inttypes.h>
38
#include <malloc_np.h>
39
#include <signal.h>
40
#include <strings.h>
41
#include <string.h>
42
#include <syslog.h>
43
#include <unistd.h>
44
45
#include "dma.h"
46
47
#define MAX_LINE_RFC822 999 /* 998 characters plus \n */
48
49
void
50
bounce(struct qitem *it, const char *reason)
51
{
52
struct queue bounceq;
53
char line[1000];
54
size_t pos;
55
int error;
56
57
/* Don't bounce bounced mails */
58
if (it->sender[0] == 0) {
59
syslog(LOG_INFO, "can not bounce a bounce message, discarding");
60
exit(EX_SOFTWARE);
61
}
62
63
bzero(&bounceq, sizeof(bounceq));
64
LIST_INIT(&bounceq.queue);
65
bounceq.sender = "";
66
if (add_recp(&bounceq, it->sender, EXPAND_WILDCARD) != 0)
67
goto fail;
68
69
if (newspoolf(&bounceq) != 0)
70
goto fail;
71
72
syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id);
73
setlogident("%s", bounceq.id);
74
75
error = fprintf(bounceq.mailf,
76
"Received: from MAILER-DAEMON\n"
77
"\tid %s\n"
78
"\tby %s (%s on %s);\n"
79
"\t%s\n"
80
"X-Original-To: <%s>\n"
81
"From: MAILER-DAEMON <>\n"
82
"To: %s\n"
83
"Subject: Mail delivery failed\n"
84
"Message-Id: <%s@%s>\n"
85
"Date: %s\n"
86
"\n"
87
"This is the %s at %s.\n"
88
"\n"
89
"There was an error delivering your mail to <%s>.\n"
90
"\n"
91
"%s\n"
92
"\n"
93
"%s\n"
94
"\n",
95
bounceq.id,
96
hostname(), VERSION, systemhostname(),
97
rfc822date(),
98
it->addr,
99
it->sender,
100
bounceq.id, hostname(),
101
rfc822date(),
102
VERSION, hostname(),
103
it->addr,
104
reason,
105
config.features & FULLBOUNCE ?
106
"Original message follows." :
107
"Message headers follow.");
108
if (error < 0)
109
goto fail;
110
111
if (fseek(it->mailf, 0, SEEK_SET) != 0)
112
goto fail;
113
if (config.features & FULLBOUNCE) {
114
while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
115
if (fwrite(line, 1, pos, bounceq.mailf) != pos)
116
goto fail;
117
}
118
} else {
119
while (!feof(it->mailf)) {
120
if (fgets(line, sizeof(line), it->mailf) == NULL)
121
break;
122
if (line[0] == '\n')
123
break;
124
if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1)
125
goto fail;
126
}
127
}
128
129
if (linkspool(&bounceq) != 0)
130
goto fail;
131
/* bounce is safe */
132
133
delqueue(it);
134
135
run_queue(&bounceq);
136
/* NOTREACHED */
137
138
fail:
139
syslog(LOG_CRIT, "error creating bounce: %m");
140
delqueue(it);
141
exit(EX_IOERR);
142
}
143
144
struct parse_state {
145
char addr[1000];
146
int pos;
147
148
enum {
149
NONE = 0,
150
START,
151
MAIN,
152
EOL,
153
QUIT
154
} state;
155
int comment;
156
int quote;
157
int brackets;
158
int esc;
159
};
160
161
/*
162
* Simplified RFC2822 header/address parsing.
163
* We copy escapes and quoted strings directly, since
164
* we have to pass them like this to the mail server anyways.
165
* XXX local addresses will need treatment
166
*/
167
static int
168
parse_addrs(struct parse_state *ps, char *s, struct queue *queue)
169
{
170
char *addr;
171
172
again:
173
switch (ps->state) {
174
case NONE:
175
return (-1);
176
177
case START:
178
/* init our data */
179
bzero(ps, sizeof(*ps));
180
181
/* skip over header name */
182
while (*s != ':')
183
s++;
184
s++;
185
ps->state = MAIN;
186
break;
187
188
case MAIN:
189
/* all fine */
190
break;
191
192
case EOL:
193
switch (*s) {
194
case ' ':
195
case '\t':
196
ps->state = MAIN;
197
break;
198
199
default:
200
ps->state = QUIT;
201
if (ps->pos != 0)
202
goto newaddr;
203
return (0);
204
}
205
break;
206
207
case QUIT:
208
return (0);
209
}
210
211
for (; *s != 0; s++) {
212
if (ps->esc) {
213
ps->esc = 0;
214
215
switch (*s) {
216
case '\r':
217
case '\n':
218
goto err;
219
220
default:
221
goto copy;
222
}
223
}
224
225
if (ps->quote) {
226
switch (*s) {
227
case '"':
228
ps->quote = 0;
229
goto copy;
230
231
case '\\':
232
ps->esc = 1;
233
goto copy;
234
235
case '\r':
236
case '\n':
237
goto eol;
238
239
default:
240
goto copy;
241
}
242
}
243
244
switch (*s) {
245
case '(':
246
ps->comment++;
247
break;
248
249
case ')':
250
if (ps->comment)
251
ps->comment--;
252
else
253
goto err;
254
goto skip;
255
256
case '"':
257
ps->quote = 1;
258
goto copy;
259
260
case '\\':
261
ps->esc = 1;
262
goto copy;
263
264
case '\r':
265
case '\n':
266
goto eol;
267
}
268
269
if (ps->comment)
270
goto skip;
271
272
switch (*s) {
273
case ' ':
274
case '\t':
275
/* ignore whitespace */
276
goto skip;
277
278
case '<':
279
/* this is the real address now */
280
ps->brackets = 1;
281
ps->pos = 0;
282
goto skip;
283
284
case '>':
285
if (!ps->brackets)
286
goto err;
287
ps->brackets = 0;
288
289
s++;
290
goto newaddr;
291
292
case ':':
293
/* group - ignore */
294
ps->pos = 0;
295
goto skip;
296
297
case ',':
298
case ';':
299
/*
300
* Next address, copy previous one.
301
* However, we might be directly after
302
* a <address>, or have two consecutive
303
* commas.
304
* Skip the comma unless there is
305
* really something to copy.
306
*/
307
if (ps->pos == 0)
308
goto skip;
309
s++;
310
goto newaddr;
311
312
default:
313
goto copy;
314
}
315
316
copy:
317
if (ps->comment)
318
goto skip;
319
320
if (ps->pos + 1 == sizeof(ps->addr))
321
goto err;
322
ps->addr[ps->pos++] = *s;
323
324
skip:
325
;
326
}
327
328
eol:
329
ps->state = EOL;
330
return (0);
331
332
err:
333
ps->state = QUIT;
334
return (-1);
335
336
newaddr:
337
ps->addr[ps->pos] = 0;
338
ps->pos = 0;
339
addr = strdup(ps->addr);
340
if (addr == NULL)
341
errlog(EX_SOFTWARE, "strdup");
342
343
if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
344
errlogx(EX_DATAERR, "invalid recipient `%s'", addr);
345
346
goto again;
347
}
348
349
static int
350
writeline(struct queue *queue, const char *line, ssize_t linelen)
351
{
352
ssize_t len;
353
354
while (linelen > 0) {
355
len = linelen;
356
if (linelen > MAX_LINE_RFC822) {
357
len = MAX_LINE_RFC822 - 10;
358
}
359
360
if (fwrite(line, len, 1, queue->mailf) != 1)
361
return (-1);
362
363
if (linelen <= MAX_LINE_RFC822)
364
break;
365
366
if (fwrite("\n", 1, 1, queue->mailf) != 1)
367
return (-1);
368
369
line += MAX_LINE_RFC822 - 10;
370
linelen = strlen(line);
371
}
372
return (0);
373
}
374
375
int
376
readmail(struct queue *queue, int nodot, int recp_from_header)
377
{
378
struct parse_state parse_state;
379
char *line = NULL;
380
ssize_t linelen;
381
size_t linecap = 0;
382
char newline[MAX_LINE_RFC822 + 1];
383
size_t error;
384
int had_headers = 0;
385
int had_from = 0;
386
int had_messagid = 0;
387
int had_date = 0;
388
int had_first_line = 0;
389
int had_last_line = 0;
390
int nocopy = 0;
391
int ret = -1;
392
393
parse_state.state = NONE;
394
395
error = fprintf(queue->mailf,
396
"Received: from %s (uid %d)\n"
397
"\t(envelope-from %s)\n"
398
"\tid %s\n"
399
"\tby %s (%s on %s);\n"
400
"\t%s\n",
401
username, useruid,
402
queue->sender,
403
queue->id,
404
hostname(), VERSION, systemhostname(),
405
rfc822date());
406
if ((ssize_t)error < 0)
407
return (-1);
408
409
while ((linelen = getline(&line, &linecap, stdin)) > 0) {
410
newline[0] = '\0';
411
if (had_last_line)
412
errlogx(EX_DATAERR, "bad mail input format:"
413
" from %s (uid %d) (envelope-from %s)",
414
username, useruid, queue->sender);
415
linelen = strlen(line);
416
if (linelen == 0 || line[linelen - 1] != '\n') {
417
/*
418
* This line did not end with a newline character.
419
* If we fix it, it better be the last line of
420
* the file.
421
*/
422
if ((size_t)linelen + 1 > linecap) {
423
line = realloc(line, linelen + 2);
424
if (line == NULL)
425
errlogx(EX_SOFTWARE, "realloc");
426
linecap = malloc_usable_size(line);
427
}
428
line[linelen++] = '\n';
429
line[linelen] = 0;
430
had_last_line = 1;
431
}
432
if (!had_first_line) {
433
/*
434
* Ignore a leading RFC-976 From_ or >From_ line mistakenly
435
* inserted by some programs.
436
*/
437
if (strprefixcmp(line, "From ") == 0 || strprefixcmp(line, ">From ") == 0)
438
continue;
439
had_first_line = 1;
440
}
441
if (!had_headers) {
442
if (linelen > MAX_LINE_RFC822) {
443
/* XXX also split headers */
444
errlogx(EX_DATAERR, "bad mail input format:"
445
" from %s (uid %d) (envelope-from %s)",
446
username, useruid, queue->sender);
447
}
448
/*
449
* Unless this is a continuation, switch of
450
* the Bcc: nocopy flag.
451
*/
452
if (!(line[0] == ' ' || line[0] == '\t'))
453
nocopy = 0;
454
455
if (strprefixcmp(line, "Date:") == 0)
456
had_date = 1;
457
else if (strprefixcmp(line, "Message-Id:") == 0)
458
had_messagid = 1;
459
else if (strprefixcmp(line, "From:") == 0)
460
had_from = 1;
461
else if (strprefixcmp(line, "Bcc:") == 0)
462
nocopy = 1;
463
464
if (parse_state.state != NONE) {
465
if (parse_addrs(&parse_state, line, queue) < 0) {
466
errlogx(EX_DATAERR, "invalid address in header\n");
467
/* NOTREACHED */
468
}
469
}
470
471
if (recp_from_header && (
472
strprefixcmp(line, "To:") == 0 ||
473
strprefixcmp(line, "Cc:") == 0 ||
474
strprefixcmp(line, "Bcc:") == 0)) {
475
parse_state.state = START;
476
if (parse_addrs(&parse_state, line, queue) < 0) {
477
errlogx(EX_DATAERR, "invalid address in header\n");
478
/* NOTREACHED */
479
}
480
}
481
}
482
483
if (strcmp(line, "\n") == 0 && !had_headers) {
484
had_headers = 1;
485
while (!had_date || !had_messagid || !had_from) {
486
if (!had_date) {
487
had_date = 1;
488
snprintf(newline, sizeof(newline), "Date: %s\n", rfc822date());
489
} else if (!had_messagid) {
490
/* XXX msgid, assign earlier and log? */
491
had_messagid = 1;
492
snprintf(newline, sizeof(newline), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
493
(uintmax_t)time(NULL),
494
queue->id,
495
(uintmax_t)random(),
496
hostname());
497
} else if (!had_from) {
498
had_from = 1;
499
snprintf(newline, sizeof(newline), "From: <%s>\n", queue->sender);
500
}
501
if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
502
goto fail;
503
}
504
strlcpy(newline, "\n", sizeof(newline));
505
}
506
if (!nodot && linelen == 2 && line[0] == '.')
507
break;
508
if (!nocopy) {
509
if (newline[0]) {
510
if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
511
goto fail;
512
} else {
513
if (writeline(queue, line, linelen) != 0)
514
goto fail;
515
}
516
}
517
}
518
if (ferror(stdin) == 0)
519
ret = 0;
520
fail:
521
free(line);
522
return (ret);
523
}
524
525