1 | /* |
---|
2 | * Copyright (c) 1998 Sendmail, Inc. All rights reserved. |
---|
3 | * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. |
---|
4 | * Copyright (c) 1988, 1993 |
---|
5 | * The Regents of the University of California. All rights reserved. |
---|
6 | * |
---|
7 | * By using this file, you agree to the terms and conditions set |
---|
8 | * forth in the LICENSE file which can be found at the top level of |
---|
9 | * the sendmail distribution. |
---|
10 | * |
---|
11 | */ |
---|
12 | |
---|
13 | #ifndef lint |
---|
14 | static char sccsid[] = "@(#)savemail.c 8.140 (Berkeley) 1/18/1999"; |
---|
15 | #endif /* not lint */ |
---|
16 | |
---|
17 | # include "sendmail.h" |
---|
18 | |
---|
19 | /* |
---|
20 | ** SAVEMAIL -- Save mail on error |
---|
21 | ** |
---|
22 | ** If mailing back errors, mail it back to the originator |
---|
23 | ** together with an error message; otherwise, just put it in |
---|
24 | ** dead.letter in the user's home directory (if he exists on |
---|
25 | ** this machine). |
---|
26 | ** |
---|
27 | ** Parameters: |
---|
28 | ** e -- the envelope containing the message in error. |
---|
29 | ** sendbody -- if TRUE, also send back the body of the |
---|
30 | ** message; otherwise just send the header. |
---|
31 | ** |
---|
32 | ** Returns: |
---|
33 | ** none |
---|
34 | ** |
---|
35 | ** Side Effects: |
---|
36 | ** Saves the letter, by writing or mailing it back to the |
---|
37 | ** sender, or by putting it in dead.letter in her home |
---|
38 | ** directory. |
---|
39 | */ |
---|
40 | |
---|
41 | /* defines for state machine */ |
---|
42 | # define ESM_REPORT 0 /* report to sender's terminal */ |
---|
43 | # define ESM_MAIL 1 /* mail back to sender */ |
---|
44 | # define ESM_QUIET 2 /* messages have already been returned */ |
---|
45 | # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ |
---|
46 | # define ESM_POSTMASTER 4 /* return to postmaster */ |
---|
47 | # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ |
---|
48 | # define ESM_PANIC 6 /* leave the locked queue/transcript files */ |
---|
49 | # define ESM_DONE 7 /* the message is successfully delivered */ |
---|
50 | |
---|
51 | |
---|
52 | void |
---|
53 | savemail(e, sendbody) |
---|
54 | register ENVELOPE *e; |
---|
55 | bool sendbody; |
---|
56 | { |
---|
57 | register struct passwd *pw; |
---|
58 | register FILE *fp; |
---|
59 | int state; |
---|
60 | auto ADDRESS *q = NULL; |
---|
61 | register char *p; |
---|
62 | MCI mcibuf; |
---|
63 | int flags; |
---|
64 | char buf[MAXLINE+1]; |
---|
65 | extern char *ttypath __P((void)); |
---|
66 | extern bool writable __P((char *, ADDRESS *, int)); |
---|
67 | |
---|
68 | if (tTd(6, 1)) |
---|
69 | { |
---|
70 | printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", |
---|
71 | e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, |
---|
72 | ExitStat); |
---|
73 | printaddr(&e->e_from, FALSE); |
---|
74 | } |
---|
75 | |
---|
76 | if (e->e_id == NULL) |
---|
77 | { |
---|
78 | /* can't return a message with no id */ |
---|
79 | return; |
---|
80 | } |
---|
81 | |
---|
82 | /* |
---|
83 | ** In the unhappy event we don't know who to return the mail |
---|
84 | ** to, make someone up. |
---|
85 | */ |
---|
86 | |
---|
87 | if (e->e_from.q_paddr == NULL) |
---|
88 | { |
---|
89 | e->e_sender = "Postmaster"; |
---|
90 | if (parseaddr(e->e_sender, &e->e_from, |
---|
91 | RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) |
---|
92 | { |
---|
93 | syserr("553 Cannot parse Postmaster!"); |
---|
94 | finis(TRUE, EX_SOFTWARE); |
---|
95 | } |
---|
96 | } |
---|
97 | e->e_to = NULL; |
---|
98 | |
---|
99 | /* |
---|
100 | ** Basic state machine. |
---|
101 | ** |
---|
102 | ** This machine runs through the following states: |
---|
103 | ** |
---|
104 | ** ESM_QUIET Errors have already been printed iff the |
---|
105 | ** sender is local. |
---|
106 | ** ESM_REPORT Report directly to the sender's terminal. |
---|
107 | ** ESM_MAIL Mail response to the sender. |
---|
108 | ** ESM_DEADLETTER Save response in ~/dead.letter. |
---|
109 | ** ESM_POSTMASTER Mail response to the postmaster. |
---|
110 | ** ESM_PANIC Save response anywhere possible. |
---|
111 | */ |
---|
112 | |
---|
113 | /* determine starting state */ |
---|
114 | switch (e->e_errormode) |
---|
115 | { |
---|
116 | case EM_WRITE: |
---|
117 | state = ESM_REPORT; |
---|
118 | break; |
---|
119 | |
---|
120 | case EM_BERKNET: |
---|
121 | case EM_MAIL: |
---|
122 | state = ESM_MAIL; |
---|
123 | break; |
---|
124 | |
---|
125 | case EM_PRINT: |
---|
126 | case '\0': |
---|
127 | state = ESM_QUIET; |
---|
128 | break; |
---|
129 | |
---|
130 | case EM_QUIET: |
---|
131 | /* no need to return anything at all */ |
---|
132 | return; |
---|
133 | |
---|
134 | default: |
---|
135 | syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); |
---|
136 | state = ESM_MAIL; |
---|
137 | break; |
---|
138 | } |
---|
139 | |
---|
140 | /* if this is already an error response, send to postmaster */ |
---|
141 | if (bitset(EF_RESPONSE, e->e_flags)) |
---|
142 | { |
---|
143 | if (e->e_parent != NULL && |
---|
144 | bitset(EF_RESPONSE, e->e_parent->e_flags)) |
---|
145 | { |
---|
146 | /* got an error sending a response -- can it */ |
---|
147 | return; |
---|
148 | } |
---|
149 | state = ESM_POSTMASTER; |
---|
150 | } |
---|
151 | |
---|
152 | while (state != ESM_DONE) |
---|
153 | { |
---|
154 | if (tTd(6, 5)) |
---|
155 | printf(" state %d\n", state); |
---|
156 | |
---|
157 | switch (state) |
---|
158 | { |
---|
159 | case ESM_QUIET: |
---|
160 | if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) |
---|
161 | state = ESM_DEADLETTER; |
---|
162 | else |
---|
163 | state = ESM_MAIL; |
---|
164 | break; |
---|
165 | |
---|
166 | case ESM_REPORT: |
---|
167 | |
---|
168 | /* |
---|
169 | ** If the user is still logged in on the same terminal, |
---|
170 | ** then write the error messages back to hir (sic). |
---|
171 | */ |
---|
172 | |
---|
173 | p = ttypath(); |
---|
174 | if (p == NULL || freopen(p, "w", stdout) == NULL) |
---|
175 | { |
---|
176 | state = ESM_MAIL; |
---|
177 | break; |
---|
178 | } |
---|
179 | |
---|
180 | expand("\201n", buf, sizeof buf, e); |
---|
181 | printf("\r\nMessage from %s...\r\n", buf); |
---|
182 | printf("Errors occurred while sending mail.\r\n"); |
---|
183 | if (e->e_xfp != NULL) |
---|
184 | { |
---|
185 | (void) fflush(e->e_xfp); |
---|
186 | fp = fopen(queuename(e, 'x'), "r"); |
---|
187 | } |
---|
188 | else |
---|
189 | fp = NULL; |
---|
190 | if (fp == NULL) |
---|
191 | { |
---|
192 | syserr("Cannot open %s", queuename(e, 'x')); |
---|
193 | printf("Transcript of session is unavailable.\r\n"); |
---|
194 | } |
---|
195 | else |
---|
196 | { |
---|
197 | printf("Transcript follows:\r\n"); |
---|
198 | while (fgets(buf, sizeof buf, fp) != NULL && |
---|
199 | !ferror(stdout)) |
---|
200 | fputs(buf, stdout); |
---|
201 | (void) xfclose(fp, "savemail transcript", e->e_id); |
---|
202 | } |
---|
203 | printf("Original message will be saved in dead.letter.\r\n"); |
---|
204 | state = ESM_DEADLETTER; |
---|
205 | break; |
---|
206 | |
---|
207 | case ESM_MAIL: |
---|
208 | /* |
---|
209 | ** If mailing back, do it. |
---|
210 | ** Throw away all further output. Don't alias, |
---|
211 | ** since this could cause loops, e.g., if joe |
---|
212 | ** mails to joe@x, and for some reason the network |
---|
213 | ** for @x is down, then the response gets sent to |
---|
214 | ** joe@x, which gives a response, etc. Also force |
---|
215 | ** the mail to be delivered even if a version of |
---|
216 | ** it has already been sent to the sender. |
---|
217 | ** |
---|
218 | ** If this is a configuration or local software |
---|
219 | ** error, send to the local postmaster as well, |
---|
220 | ** since the originator can't do anything |
---|
221 | ** about it anyway. Note that this is a full |
---|
222 | ** copy of the message (intentionally) so that |
---|
223 | ** the Postmaster can forward things along. |
---|
224 | */ |
---|
225 | |
---|
226 | if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) |
---|
227 | { |
---|
228 | (void) sendtolist("postmaster", |
---|
229 | NULLADDR, &e->e_errorqueue, 0, e); |
---|
230 | } |
---|
231 | if (!emptyaddr(&e->e_from)) |
---|
232 | { |
---|
233 | char from[TOBUFSIZE]; |
---|
234 | extern bool pruneroute __P((char *)); |
---|
235 | |
---|
236 | if (strlen(e->e_from.q_paddr) + 1 > sizeof from) |
---|
237 | { |
---|
238 | state = ESM_POSTMASTER; |
---|
239 | break; |
---|
240 | } |
---|
241 | strcpy(from, e->e_from.q_paddr); |
---|
242 | |
---|
243 | if (!DontPruneRoutes && pruneroute(from)) |
---|
244 | { |
---|
245 | ADDRESS *a; |
---|
246 | |
---|
247 | for (a = e->e_errorqueue; a != NULL; |
---|
248 | a = a->q_next) |
---|
249 | { |
---|
250 | if (sameaddr(a, &e->e_from)) |
---|
251 | a->q_flags |= QDONTSEND; |
---|
252 | } |
---|
253 | } |
---|
254 | (void) sendtolist(from, NULLADDR, |
---|
255 | &e->e_errorqueue, 0, e); |
---|
256 | } |
---|
257 | |
---|
258 | /* |
---|
259 | ** Deliver a non-delivery report to the |
---|
260 | ** Postmaster-designate (not necessarily |
---|
261 | ** Postmaster). This does not include the |
---|
262 | ** body of the message, for privacy reasons. |
---|
263 | ** You really shouldn't need this. |
---|
264 | */ |
---|
265 | |
---|
266 | e->e_flags |= EF_PM_NOTIFY; |
---|
267 | |
---|
268 | /* check to see if there are any good addresses */ |
---|
269 | for (q = e->e_errorqueue; q != NULL; q = q->q_next) |
---|
270 | if (!bitset(QBADADDR|QDONTSEND, q->q_flags)) |
---|
271 | break; |
---|
272 | if (q == NULL) |
---|
273 | { |
---|
274 | /* this is an error-error */ |
---|
275 | state = ESM_POSTMASTER; |
---|
276 | break; |
---|
277 | } |
---|
278 | if (returntosender(e->e_message, e->e_errorqueue, |
---|
279 | sendbody ? RTSF_SEND_BODY |
---|
280 | : RTSF_NO_BODY, |
---|
281 | e) == 0) |
---|
282 | { |
---|
283 | state = ESM_DONE; |
---|
284 | break; |
---|
285 | } |
---|
286 | |
---|
287 | /* didn't work -- return to postmaster */ |
---|
288 | state = ESM_POSTMASTER; |
---|
289 | break; |
---|
290 | |
---|
291 | case ESM_POSTMASTER: |
---|
292 | /* |
---|
293 | ** Similar to previous case, but to system postmaster. |
---|
294 | */ |
---|
295 | |
---|
296 | q = NULL; |
---|
297 | if (sendtolist(DoubleBounceAddr, |
---|
298 | NULLADDR, &q, 0, e) <= 0) |
---|
299 | { |
---|
300 | syserr("553 cannot parse %s!", DoubleBounceAddr); |
---|
301 | ExitStat = EX_SOFTWARE; |
---|
302 | state = ESM_USRTMP; |
---|
303 | break; |
---|
304 | } |
---|
305 | flags = RTSF_PM_BOUNCE; |
---|
306 | if (sendbody) |
---|
307 | flags |= RTSF_SEND_BODY; |
---|
308 | if (returntosender(e->e_message, q, flags, e) == 0) |
---|
309 | { |
---|
310 | state = ESM_DONE; |
---|
311 | break; |
---|
312 | } |
---|
313 | |
---|
314 | /* didn't work -- last resort */ |
---|
315 | state = ESM_USRTMP; |
---|
316 | break; |
---|
317 | |
---|
318 | case ESM_DEADLETTER: |
---|
319 | /* |
---|
320 | ** Save the message in dead.letter. |
---|
321 | ** If we weren't mailing back, and the user is |
---|
322 | ** local, we should save the message in |
---|
323 | ** ~/dead.letter so that the poor person doesn't |
---|
324 | ** have to type it over again -- and we all know |
---|
325 | ** what poor typists UNIX users are. |
---|
326 | */ |
---|
327 | |
---|
328 | p = NULL; |
---|
329 | if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) |
---|
330 | { |
---|
331 | if (e->e_from.q_home != NULL) |
---|
332 | p = e->e_from.q_home; |
---|
333 | else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL) |
---|
334 | p = pw->pw_dir; |
---|
335 | } |
---|
336 | if (p == NULL || e->e_dfp == NULL) |
---|
337 | { |
---|
338 | /* no local directory or no data file */ |
---|
339 | state = ESM_MAIL; |
---|
340 | break; |
---|
341 | } |
---|
342 | |
---|
343 | /* we have a home directory; write dead.letter */ |
---|
344 | define('z', p, e); |
---|
345 | expand("\201z/dead.letter", buf, sizeof buf, e); |
---|
346 | flags = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; |
---|
347 | if (RealUid == 0) |
---|
348 | flags |= SFF_ROOTOK; |
---|
349 | e->e_to = buf; |
---|
350 | if (mailfile(buf, FileMailer, NULL, flags, e) == EX_OK) |
---|
351 | { |
---|
352 | int oldverb = Verbose; |
---|
353 | |
---|
354 | Verbose = 1; |
---|
355 | message("Saved message in %s", buf); |
---|
356 | Verbose = oldverb; |
---|
357 | state = ESM_DONE; |
---|
358 | break; |
---|
359 | } |
---|
360 | state = ESM_MAIL; |
---|
361 | break; |
---|
362 | |
---|
363 | case ESM_USRTMP: |
---|
364 | /* |
---|
365 | ** Log the mail in /usr/tmp/dead.letter. |
---|
366 | */ |
---|
367 | |
---|
368 | if (e->e_class < 0) |
---|
369 | { |
---|
370 | state = ESM_DONE; |
---|
371 | break; |
---|
372 | } |
---|
373 | |
---|
374 | if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') || |
---|
375 | DeadLetterDrop == NULL || DeadLetterDrop[0] == '\0') |
---|
376 | { |
---|
377 | state = ESM_PANIC; |
---|
378 | break; |
---|
379 | } |
---|
380 | |
---|
381 | flags = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN; |
---|
382 | if (!writable(DeadLetterDrop, NULL, flags) || |
---|
383 | (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND, |
---|
384 | FileMode, flags)) == NULL) |
---|
385 | { |
---|
386 | state = ESM_PANIC; |
---|
387 | break; |
---|
388 | } |
---|
389 | |
---|
390 | bzero(&mcibuf, sizeof mcibuf); |
---|
391 | mcibuf.mci_out = fp; |
---|
392 | mcibuf.mci_mailer = FileMailer; |
---|
393 | if (bitnset(M_7BITS, FileMailer->m_flags)) |
---|
394 | mcibuf.mci_flags |= MCIF_7BIT; |
---|
395 | mcibuf.mci_contentlen = 0; |
---|
396 | |
---|
397 | putfromline(&mcibuf, e); |
---|
398 | (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); |
---|
399 | (*e->e_putbody)(&mcibuf, e, NULL); |
---|
400 | putline("\n", &mcibuf); |
---|
401 | (void) fflush(fp); |
---|
402 | if (ferror(fp)) |
---|
403 | state = ESM_PANIC; |
---|
404 | else |
---|
405 | { |
---|
406 | int oldverb = Verbose; |
---|
407 | |
---|
408 | Verbose = 1; |
---|
409 | message("Saved message in %s", DeadLetterDrop); |
---|
410 | Verbose = oldverb; |
---|
411 | if (LogLevel > 3) |
---|
412 | sm_syslog(LOG_NOTICE, e->e_id, |
---|
413 | "Saved message in %s", |
---|
414 | DeadLetterDrop); |
---|
415 | state = ESM_DONE; |
---|
416 | } |
---|
417 | (void) xfclose(fp, "savemail", DeadLetterDrop); |
---|
418 | break; |
---|
419 | |
---|
420 | default: |
---|
421 | syserr("554 savemail: unknown state %d", state); |
---|
422 | |
---|
423 | /* fall through ... */ |
---|
424 | |
---|
425 | case ESM_PANIC: |
---|
426 | /* leave the locked queue & transcript files around */ |
---|
427 | loseqfile(e, "savemail panic"); |
---|
428 | syserr("!554 savemail: cannot save rejected email anywhere"); |
---|
429 | } |
---|
430 | } |
---|
431 | } |
---|
432 | /* |
---|
433 | ** RETURNTOSENDER -- return a message to the sender with an error. |
---|
434 | ** |
---|
435 | ** Parameters: |
---|
436 | ** msg -- the explanatory message. |
---|
437 | ** returnq -- the queue of people to send the message to. |
---|
438 | ** flags -- flags tweaking the operation: |
---|
439 | ** RTSF_SENDBODY -- include body of message (otherwise |
---|
440 | ** just send the header). |
---|
441 | ** RTSF_PMBOUNCE -- this is a postmaster bounce. |
---|
442 | ** e -- the current envelope. |
---|
443 | ** |
---|
444 | ** Returns: |
---|
445 | ** zero -- if everything went ok. |
---|
446 | ** else -- some error. |
---|
447 | ** |
---|
448 | ** Side Effects: |
---|
449 | ** Returns the current message to the sender via |
---|
450 | ** mail. |
---|
451 | */ |
---|
452 | |
---|
453 | #define MAXRETURNS 6 /* max depth of returning messages */ |
---|
454 | #define ERRORFUDGE 100 /* nominal size of error message text */ |
---|
455 | |
---|
456 | int |
---|
457 | returntosender(msg, returnq, flags, e) |
---|
458 | char *msg; |
---|
459 | ADDRESS *returnq; |
---|
460 | int flags; |
---|
461 | register ENVELOPE *e; |
---|
462 | { |
---|
463 | register ENVELOPE *ee; |
---|
464 | ENVELOPE *oldcur = CurEnv; |
---|
465 | ENVELOPE errenvelope; |
---|
466 | static int returndepth = 0; |
---|
467 | register ADDRESS *q; |
---|
468 | char *p; |
---|
469 | char buf[MAXNAME + 1]; |
---|
470 | extern void errbody __P((MCI *, ENVELOPE *, char *)); |
---|
471 | |
---|
472 | if (returnq == NULL) |
---|
473 | return (-1); |
---|
474 | |
---|
475 | if (msg == NULL) |
---|
476 | msg = "Unable to deliver mail"; |
---|
477 | |
---|
478 | if (tTd(6, 1)) |
---|
479 | { |
---|
480 | printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=", |
---|
481 | msg, returndepth, (u_long) e); |
---|
482 | printaddr(returnq, TRUE); |
---|
483 | if (tTd(6, 20)) |
---|
484 | { |
---|
485 | printf("Sendq="); |
---|
486 | printaddr(e->e_sendqueue, TRUE); |
---|
487 | } |
---|
488 | } |
---|
489 | |
---|
490 | if (++returndepth >= MAXRETURNS) |
---|
491 | { |
---|
492 | if (returndepth != MAXRETURNS) |
---|
493 | syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); |
---|
494 | /* don't "unrecurse" and fake a clean exit */ |
---|
495 | /* returndepth--; */ |
---|
496 | return (0); |
---|
497 | } |
---|
498 | |
---|
499 | define('g', e->e_from.q_paddr, e); |
---|
500 | define('u', NULL, e); |
---|
501 | |
---|
502 | /* initialize error envelope */ |
---|
503 | ee = newenvelope(&errenvelope, e); |
---|
504 | define('a', "\201b", ee); |
---|
505 | define('r', "internal", ee); |
---|
506 | define('s', "localhost", ee); |
---|
507 | define('_', "localhost", ee); |
---|
508 | ee->e_puthdr = putheader; |
---|
509 | ee->e_putbody = errbody; |
---|
510 | ee->e_flags |= EF_RESPONSE|EF_METOO; |
---|
511 | if (!bitset(EF_OLDSTYLE, e->e_flags)) |
---|
512 | ee->e_flags &= ~EF_OLDSTYLE; |
---|
513 | ee->e_sendqueue = returnq; |
---|
514 | ee->e_msgsize = ERRORFUDGE; |
---|
515 | if (bitset(RTSF_SEND_BODY, flags)) |
---|
516 | ee->e_msgsize += e->e_msgsize; |
---|
517 | else |
---|
518 | ee->e_flags |= EF_NO_BODY_RETN; |
---|
519 | initsys(ee); |
---|
520 | for (q = returnq; q != NULL; q = q->q_next) |
---|
521 | { |
---|
522 | if (bitset(QBADADDR, q->q_flags)) |
---|
523 | continue; |
---|
524 | |
---|
525 | q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); |
---|
526 | q->q_flags |= QPINGONFAILURE; |
---|
527 | |
---|
528 | if (!bitset(QDONTSEND, q->q_flags)) |
---|
529 | ee->e_nrcpts++; |
---|
530 | |
---|
531 | if (q->q_alias == NULL) |
---|
532 | addheader("To", q->q_paddr, &ee->e_header); |
---|
533 | } |
---|
534 | |
---|
535 | if (LogLevel > 5) |
---|
536 | { |
---|
537 | if (bitset(EF_RESPONSE|EF_WARNING, e->e_flags)) |
---|
538 | p = "return to sender"; |
---|
539 | else if (bitset(RTSF_PM_BOUNCE, flags)) |
---|
540 | p = "postmaster notify"; |
---|
541 | else |
---|
542 | p = "DSN"; |
---|
543 | sm_syslog(LOG_INFO, e->e_id, |
---|
544 | "%s: %s: %s", |
---|
545 | ee->e_id, p, shortenstring(msg, MAXSHORTSTR)); |
---|
546 | } |
---|
547 | |
---|
548 | if (SendMIMEErrors) |
---|
549 | { |
---|
550 | addheader("MIME-Version", "1.0", &ee->e_header); |
---|
551 | |
---|
552 | (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s", |
---|
553 | ee->e_id, curtime(), MyHostName); |
---|
554 | ee->e_msgboundary = newstr(buf); |
---|
555 | (void) snprintf(buf, sizeof buf, |
---|
556 | #if DSN |
---|
557 | "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", |
---|
558 | #else |
---|
559 | "multipart/mixed; boundary=\"%s\"", |
---|
560 | #endif |
---|
561 | ee->e_msgboundary); |
---|
562 | addheader("Content-Type", buf, &ee->e_header); |
---|
563 | |
---|
564 | p = hvalue("Content-Transfer-Encoding", e->e_header); |
---|
565 | if (p != NULL && strcasecmp(p, "binary") != 0) |
---|
566 | p = NULL; |
---|
567 | if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) |
---|
568 | p = "8bit"; |
---|
569 | if (p != NULL) |
---|
570 | addheader("Content-Transfer-Encoding", p, &ee->e_header); |
---|
571 | } |
---|
572 | if (strncmp(msg, "Warning:", 8) == 0) |
---|
573 | { |
---|
574 | addheader("Subject", msg, &ee->e_header); |
---|
575 | p = "warning-timeout"; |
---|
576 | } |
---|
577 | else if (strncmp(msg, "Postmaster warning:", 19) == 0) |
---|
578 | { |
---|
579 | addheader("Subject", msg, &ee->e_header); |
---|
580 | p = "postmaster-warning"; |
---|
581 | } |
---|
582 | else if (strcmp(msg, "Return receipt") == 0) |
---|
583 | { |
---|
584 | addheader("Subject", msg, &ee->e_header); |
---|
585 | p = "return-receipt"; |
---|
586 | } |
---|
587 | else if (bitset(RTSF_PM_BOUNCE, flags)) |
---|
588 | { |
---|
589 | snprintf(buf, sizeof buf, "Postmaster notify: %.*s", |
---|
590 | sizeof buf - 20, msg); |
---|
591 | addheader("Subject", buf, &ee->e_header); |
---|
592 | p = "postmaster-notification"; |
---|
593 | } |
---|
594 | else |
---|
595 | { |
---|
596 | snprintf(buf, sizeof buf, "Returned mail: %.*s", |
---|
597 | sizeof buf - 20, msg); |
---|
598 | addheader("Subject", buf, &ee->e_header); |
---|
599 | p = "failure"; |
---|
600 | } |
---|
601 | (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p); |
---|
602 | addheader("Auto-Submitted", buf, &ee->e_header); |
---|
603 | |
---|
604 | /* fake up an address header for the from person */ |
---|
605 | expand("\201n", buf, sizeof buf, e); |
---|
606 | if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) |
---|
607 | { |
---|
608 | syserr("553 Can't parse myself!"); |
---|
609 | ExitStat = EX_SOFTWARE; |
---|
610 | returndepth--; |
---|
611 | return (-1); |
---|
612 | } |
---|
613 | ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); |
---|
614 | ee->e_from.q_flags |= QPINGONFAILURE; |
---|
615 | ee->e_sender = ee->e_from.q_paddr; |
---|
616 | |
---|
617 | /* push state into submessage */ |
---|
618 | CurEnv = ee; |
---|
619 | define('f', "\201n", ee); |
---|
620 | define('x', "Mail Delivery Subsystem", ee); |
---|
621 | eatheader(ee, TRUE); |
---|
622 | |
---|
623 | /* mark statistics */ |
---|
624 | markstats(ee, NULLADDR, FALSE); |
---|
625 | |
---|
626 | /* actually deliver the error message */ |
---|
627 | sendall(ee, SM_DELIVER); |
---|
628 | |
---|
629 | /* restore state */ |
---|
630 | dropenvelope(ee, TRUE); |
---|
631 | CurEnv = oldcur; |
---|
632 | returndepth--; |
---|
633 | |
---|
634 | /* check for delivery errors */ |
---|
635 | if (ee->e_parent == NULL || !bitset(EF_RESPONSE, ee->e_parent->e_flags)) |
---|
636 | return 0; |
---|
637 | for (q = ee->e_sendqueue; q != NULL; q = q->q_next) |
---|
638 | { |
---|
639 | if (bitset(QQUEUEUP|QSENT, q->q_flags)) |
---|
640 | return 0; |
---|
641 | } |
---|
642 | return -1; |
---|
643 | } |
---|
644 | /* |
---|
645 | ** ERRBODY -- output the body of an error message. |
---|
646 | ** |
---|
647 | ** Typically this is a copy of the transcript plus a copy of the |
---|
648 | ** original offending message. |
---|
649 | ** |
---|
650 | ** Parameters: |
---|
651 | ** mci -- the mailer connection information. |
---|
652 | ** e -- the envelope we are working in. |
---|
653 | ** separator -- any possible MIME separator. |
---|
654 | ** |
---|
655 | ** Returns: |
---|
656 | ** none |
---|
657 | ** |
---|
658 | ** Side Effects: |
---|
659 | ** Outputs the body of an error message. |
---|
660 | */ |
---|
661 | |
---|
662 | void |
---|
663 | errbody(mci, e, separator) |
---|
664 | register MCI *mci; |
---|
665 | register ENVELOPE *e; |
---|
666 | char *separator; |
---|
667 | { |
---|
668 | register FILE *xfile; |
---|
669 | char *p; |
---|
670 | register ADDRESS *q = NULL; |
---|
671 | bool printheader; |
---|
672 | bool sendbody; |
---|
673 | bool pm_notify; |
---|
674 | char buf[MAXLINE]; |
---|
675 | |
---|
676 | if (bitset(MCIF_INHEADER, mci->mci_flags)) |
---|
677 | { |
---|
678 | putline("", mci); |
---|
679 | mci->mci_flags &= ~MCIF_INHEADER; |
---|
680 | } |
---|
681 | if (e->e_parent == NULL) |
---|
682 | { |
---|
683 | syserr("errbody: null parent"); |
---|
684 | putline(" ----- Original message lost -----\n", mci); |
---|
685 | return; |
---|
686 | } |
---|
687 | |
---|
688 | /* |
---|
689 | ** Output MIME header. |
---|
690 | */ |
---|
691 | |
---|
692 | if (e->e_msgboundary != NULL) |
---|
693 | { |
---|
694 | putline("This is a MIME-encapsulated message", mci); |
---|
695 | putline("", mci); |
---|
696 | (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); |
---|
697 | putline(buf, mci); |
---|
698 | putline("", mci); |
---|
699 | } |
---|
700 | |
---|
701 | /* |
---|
702 | ** Output introductory information. |
---|
703 | */ |
---|
704 | |
---|
705 | pm_notify = FALSE; |
---|
706 | p = hvalue("subject", e->e_header); |
---|
707 | if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) |
---|
708 | pm_notify = TRUE; |
---|
709 | else |
---|
710 | { |
---|
711 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
712 | if (bitset(QBADADDR, q->q_flags)) |
---|
713 | break; |
---|
714 | } |
---|
715 | if (!pm_notify && q == NULL && |
---|
716 | !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) |
---|
717 | { |
---|
718 | putline(" **********************************************", |
---|
719 | mci); |
---|
720 | putline(" ** THIS IS A WARNING MESSAGE ONLY **", |
---|
721 | mci); |
---|
722 | putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", |
---|
723 | mci); |
---|
724 | putline(" **********************************************", |
---|
725 | mci); |
---|
726 | putline("", mci); |
---|
727 | } |
---|
728 | snprintf(buf, sizeof buf, "The original message was received at %s", |
---|
729 | arpadate(ctime(&e->e_parent->e_ctime))); |
---|
730 | putline(buf, mci); |
---|
731 | expand("from \201_", buf, sizeof buf, e->e_parent); |
---|
732 | putline(buf, mci); |
---|
733 | putline("", mci); |
---|
734 | |
---|
735 | /* |
---|
736 | ** Output error message header (if specified and available). |
---|
737 | */ |
---|
738 | |
---|
739 | if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) |
---|
740 | { |
---|
741 | if (*ErrMsgFile == '/') |
---|
742 | { |
---|
743 | int sff = SFF_ROOTOK|SFF_REGONLY; |
---|
744 | |
---|
745 | if (DontLockReadFiles) |
---|
746 | sff |= SFF_NOLOCK; |
---|
747 | if (!bitset(DBS_ERRORHEADERINUNSAFEDIRPATH, DontBlameSendmail)) |
---|
748 | sff |= SFF_SAFEDIRPATH; |
---|
749 | xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); |
---|
750 | if (xfile != NULL) |
---|
751 | { |
---|
752 | while (fgets(buf, sizeof buf, xfile) != NULL) |
---|
753 | { |
---|
754 | extern void translate_dollars __P((char *)); |
---|
755 | |
---|
756 | translate_dollars(buf); |
---|
757 | expand(buf, buf, sizeof buf, e); |
---|
758 | putline(buf, mci); |
---|
759 | } |
---|
760 | (void) fclose(xfile); |
---|
761 | putline("\n", mci); |
---|
762 | } |
---|
763 | } |
---|
764 | else |
---|
765 | { |
---|
766 | expand(ErrMsgFile, buf, sizeof buf, e); |
---|
767 | putline(buf, mci); |
---|
768 | putline("", mci); |
---|
769 | } |
---|
770 | } |
---|
771 | |
---|
772 | /* |
---|
773 | ** Output message introduction |
---|
774 | */ |
---|
775 | |
---|
776 | printheader = TRUE; |
---|
777 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
778 | { |
---|
779 | if (!bitset(QBADADDR, q->q_flags) || |
---|
780 | !bitset(QPINGONFAILURE, q->q_flags)) |
---|
781 | continue; |
---|
782 | |
---|
783 | if (printheader) |
---|
784 | { |
---|
785 | putline(" ----- The following addresses had permanent fatal errors -----", |
---|
786 | mci); |
---|
787 | printheader = FALSE; |
---|
788 | } |
---|
789 | |
---|
790 | snprintf(buf, sizeof buf, "%s", |
---|
791 | shortenstring(q->q_paddr, MAXSHORTSTR)); |
---|
792 | putline(buf, mci); |
---|
793 | if (q->q_alias != NULL) |
---|
794 | { |
---|
795 | snprintf(buf, sizeof buf, " (expanded from: %s)", |
---|
796 | shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); |
---|
797 | putline(buf, mci); |
---|
798 | } |
---|
799 | } |
---|
800 | if (!printheader) |
---|
801 | putline("", mci); |
---|
802 | |
---|
803 | printheader = TRUE; |
---|
804 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
805 | { |
---|
806 | if (bitset(QBADADDR, q->q_flags) || |
---|
807 | !bitset(QPRIMARY, q->q_flags) || |
---|
808 | !bitset(QDELAYED, q->q_flags)) |
---|
809 | continue; |
---|
810 | |
---|
811 | if (printheader) |
---|
812 | { |
---|
813 | putline(" ----- The following addresses had transient non-fatal errors -----", |
---|
814 | mci); |
---|
815 | printheader = FALSE; |
---|
816 | } |
---|
817 | |
---|
818 | snprintf(buf, sizeof buf, "%s", |
---|
819 | shortenstring(q->q_paddr, MAXSHORTSTR)); |
---|
820 | putline(buf, mci); |
---|
821 | if (q->q_alias != NULL) |
---|
822 | { |
---|
823 | snprintf(buf, sizeof buf, " (expanded from: %s)", |
---|
824 | shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); |
---|
825 | putline(buf, mci); |
---|
826 | } |
---|
827 | } |
---|
828 | if (!printheader) |
---|
829 | putline("", mci); |
---|
830 | |
---|
831 | printheader = TRUE; |
---|
832 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
833 | { |
---|
834 | if (bitset(QBADADDR, q->q_flags) || |
---|
835 | !bitset(QPRIMARY, q->q_flags) || |
---|
836 | bitset(QDELAYED, q->q_flags)) |
---|
837 | continue; |
---|
838 | else if (!bitset(QPINGONSUCCESS, q->q_flags)) |
---|
839 | continue; |
---|
840 | else if (bitset(QRELAYED, q->q_flags)) |
---|
841 | p = "relayed to non-DSN-aware mailer"; |
---|
842 | else if (bitset(QDELIVERED, q->q_flags)) |
---|
843 | { |
---|
844 | if (bitset(QEXPANDED, q->q_flags)) |
---|
845 | p = "successfully delivered to mailing list"; |
---|
846 | else |
---|
847 | p = "successfully delivered to mailbox"; |
---|
848 | } |
---|
849 | else if (bitset(QEXPANDED, q->q_flags)) |
---|
850 | p = "expanded by alias"; |
---|
851 | else |
---|
852 | continue; |
---|
853 | |
---|
854 | if (printheader) |
---|
855 | { |
---|
856 | putline(" ----- The following addresses had successful delivery notifications -----", |
---|
857 | mci); |
---|
858 | printheader = FALSE; |
---|
859 | } |
---|
860 | |
---|
861 | snprintf(buf, sizeof buf, "%s (%s)", |
---|
862 | shortenstring(q->q_paddr, MAXSHORTSTR), p); |
---|
863 | putline(buf, mci); |
---|
864 | if (q->q_alias != NULL) |
---|
865 | { |
---|
866 | snprintf(buf, sizeof buf, " (expanded from: %s)", |
---|
867 | shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); |
---|
868 | putline(buf, mci); |
---|
869 | } |
---|
870 | } |
---|
871 | if (!printheader) |
---|
872 | putline("", mci); |
---|
873 | |
---|
874 | /* |
---|
875 | ** Output transcript of errors |
---|
876 | */ |
---|
877 | |
---|
878 | (void) fflush(stdout); |
---|
879 | p = queuename(e->e_parent, 'x'); |
---|
880 | if ((xfile = fopen(p, "r")) == NULL) |
---|
881 | { |
---|
882 | syserr("Cannot open %s", p); |
---|
883 | putline(" ----- Transcript of session is unavailable -----\n", mci); |
---|
884 | } |
---|
885 | else |
---|
886 | { |
---|
887 | printheader = TRUE; |
---|
888 | if (e->e_xfp != NULL) |
---|
889 | (void) fflush(e->e_xfp); |
---|
890 | while (fgets(buf, sizeof buf, xfile) != NULL) |
---|
891 | { |
---|
892 | if (printheader) |
---|
893 | putline(" ----- Transcript of session follows -----\n", mci); |
---|
894 | printheader = FALSE; |
---|
895 | putline(buf, mci); |
---|
896 | } |
---|
897 | (void) xfclose(xfile, "errbody xscript", p); |
---|
898 | } |
---|
899 | errno = 0; |
---|
900 | |
---|
901 | #if DSN |
---|
902 | /* |
---|
903 | ** Output machine-readable version. |
---|
904 | */ |
---|
905 | |
---|
906 | if (e->e_msgboundary != NULL) |
---|
907 | { |
---|
908 | putline("", mci); |
---|
909 | (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); |
---|
910 | putline(buf, mci); |
---|
911 | putline("Content-Type: message/delivery-status", mci); |
---|
912 | putline("", mci); |
---|
913 | |
---|
914 | /* |
---|
915 | ** Output per-message information. |
---|
916 | */ |
---|
917 | |
---|
918 | /* original envelope id from MAIL FROM: line */ |
---|
919 | if (e->e_parent->e_envid != NULL) |
---|
920 | { |
---|
921 | (void) snprintf(buf, sizeof buf, "Original-Envelope-Id: %.800s", |
---|
922 | xuntextify(e->e_parent->e_envid)); |
---|
923 | putline(buf, mci); |
---|
924 | } |
---|
925 | |
---|
926 | /* Reporting-MTA: is us (required) */ |
---|
927 | (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName); |
---|
928 | putline(buf, mci); |
---|
929 | |
---|
930 | /* DSN-Gateway: not relevant since we are not translating */ |
---|
931 | |
---|
932 | /* Received-From-MTA: shows where we got this message from */ |
---|
933 | if (RealHostName != NULL) |
---|
934 | { |
---|
935 | /* XXX use $s for type? */ |
---|
936 | if (e->e_parent->e_from.q_mailer == NULL || |
---|
937 | (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) |
---|
938 | p = "dns"; |
---|
939 | (void) snprintf(buf, sizeof buf, "Received-From-MTA: %s; %.800s", |
---|
940 | p, RealHostName); |
---|
941 | putline(buf, mci); |
---|
942 | } |
---|
943 | |
---|
944 | /* Arrival-Date: -- when it arrived here */ |
---|
945 | (void) snprintf(buf, sizeof buf, "Arrival-Date: %s", |
---|
946 | arpadate(ctime(&e->e_parent->e_ctime))); |
---|
947 | putline(buf, mci); |
---|
948 | |
---|
949 | /* |
---|
950 | ** Output per-address information. |
---|
951 | */ |
---|
952 | |
---|
953 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) |
---|
954 | { |
---|
955 | register ADDRESS *r; |
---|
956 | char *action; |
---|
957 | |
---|
958 | if (bitset(QBADADDR, q->q_flags)) |
---|
959 | action = "failed"; |
---|
960 | else if (!bitset(QPRIMARY, q->q_flags)) |
---|
961 | continue; |
---|
962 | else if (bitset(QDELIVERED, q->q_flags)) |
---|
963 | { |
---|
964 | if (bitset(QEXPANDED, q->q_flags)) |
---|
965 | action = "delivered (to mailing list)"; |
---|
966 | else |
---|
967 | action = "delivered (to mailbox)"; |
---|
968 | } |
---|
969 | else if (bitset(QRELAYED, q->q_flags)) |
---|
970 | action = "relayed (to non-DSN-aware mailer)"; |
---|
971 | else if (bitset(QEXPANDED, q->q_flags)) |
---|
972 | action = "expanded (to multi-recipient alias)"; |
---|
973 | else if (bitset(QDELAYED, q->q_flags)) |
---|
974 | action = "delayed"; |
---|
975 | else |
---|
976 | continue; |
---|
977 | |
---|
978 | putline("", mci); |
---|
979 | |
---|
980 | /* Original-Recipient: -- passed from on high */ |
---|
981 | if (q->q_orcpt != NULL) |
---|
982 | { |
---|
983 | (void) snprintf(buf, sizeof buf, "Original-Recipient: %.800s", |
---|
984 | q->q_orcpt); |
---|
985 | putline(buf, mci); |
---|
986 | } |
---|
987 | |
---|
988 | /* Final-Recipient: -- the name from the RCPT command */ |
---|
989 | p = e->e_parent->e_from.q_mailer->m_addrtype; |
---|
990 | if (p == NULL) |
---|
991 | p = "rfc822"; |
---|
992 | for (r = q; r->q_alias != NULL; r = r->q_alias) |
---|
993 | continue; |
---|
994 | if (strchr(r->q_user, '@') != NULL) |
---|
995 | { |
---|
996 | (void) snprintf(buf, sizeof buf, |
---|
997 | "Final-Recipient: %s; %.800s", |
---|
998 | p, r->q_user); |
---|
999 | } |
---|
1000 | else if (strchr(r->q_paddr, '@') != NULL) |
---|
1001 | { |
---|
1002 | (void) snprintf(buf, sizeof buf, |
---|
1003 | "Final-Recipient: %s; %.800s", |
---|
1004 | p, r->q_paddr); |
---|
1005 | } |
---|
1006 | else |
---|
1007 | { |
---|
1008 | (void) snprintf(buf, sizeof buf, |
---|
1009 | "Final-Recipient: %s; %.700s@%.100s", |
---|
1010 | p, r->q_user, MyHostName); |
---|
1011 | } |
---|
1012 | putline(buf, mci); |
---|
1013 | |
---|
1014 | /* X-Actual-Recipient: -- the real problem address */ |
---|
1015 | if (r != q && q->q_user[0] != '\0') |
---|
1016 | { |
---|
1017 | if (strchr(q->q_user, '@') == NULL) |
---|
1018 | { |
---|
1019 | (void) snprintf(buf, sizeof buf, |
---|
1020 | "X-Actual-Recipient: %s; %.700s@%.100s", |
---|
1021 | p, q->q_user, MyHostName); |
---|
1022 | } |
---|
1023 | else |
---|
1024 | { |
---|
1025 | (void) snprintf(buf, sizeof buf, |
---|
1026 | "X-Actual-Recipient: %s; %.800s", |
---|
1027 | p, q->q_user); |
---|
1028 | } |
---|
1029 | putline(buf, mci); |
---|
1030 | } |
---|
1031 | |
---|
1032 | /* Action: -- what happened? */ |
---|
1033 | snprintf(buf, sizeof buf, "Action: %s", action); |
---|
1034 | putline(buf, mci); |
---|
1035 | |
---|
1036 | /* Status: -- what _really_ happened? */ |
---|
1037 | if (q->q_status != NULL) |
---|
1038 | p = q->q_status; |
---|
1039 | else if (bitset(QBADADDR, q->q_flags)) |
---|
1040 | p = "5.0.0"; |
---|
1041 | else if (bitset(QQUEUEUP, q->q_flags)) |
---|
1042 | p = "4.0.0"; |
---|
1043 | else |
---|
1044 | p = "2.0.0"; |
---|
1045 | snprintf(buf, sizeof buf, "Status: %s", p); |
---|
1046 | putline(buf, mci); |
---|
1047 | |
---|
1048 | /* Remote-MTA: -- who was I talking to? */ |
---|
1049 | if (q->q_statmta != NULL) |
---|
1050 | { |
---|
1051 | if (q->q_mailer == NULL || |
---|
1052 | (p = q->q_mailer->m_mtatype) == NULL) |
---|
1053 | p = "dns"; |
---|
1054 | (void) snprintf(buf, sizeof buf, |
---|
1055 | "Remote-MTA: %s; %.800s", |
---|
1056 | p, q->q_statmta); |
---|
1057 | p = &buf[strlen(buf) - 1]; |
---|
1058 | if (*p == '.') |
---|
1059 | *p = '\0'; |
---|
1060 | putline(buf, mci); |
---|
1061 | } |
---|
1062 | |
---|
1063 | /* Diagnostic-Code: -- actual result from other end */ |
---|
1064 | if (q->q_rstatus != NULL) |
---|
1065 | { |
---|
1066 | p = q->q_mailer->m_diagtype; |
---|
1067 | if (p == NULL) |
---|
1068 | p = "smtp"; |
---|
1069 | (void) snprintf(buf, sizeof buf, |
---|
1070 | "Diagnostic-Code: %s; %.800s", |
---|
1071 | p, q->q_rstatus); |
---|
1072 | putline(buf, mci); |
---|
1073 | } |
---|
1074 | |
---|
1075 | /* Last-Attempt-Date: -- fine granularity */ |
---|
1076 | if (q->q_statdate == (time_t) 0L) |
---|
1077 | q->q_statdate = curtime(); |
---|
1078 | (void) snprintf(buf, sizeof buf, |
---|
1079 | "Last-Attempt-Date: %s", |
---|
1080 | arpadate(ctime(&q->q_statdate))); |
---|
1081 | putline(buf, mci); |
---|
1082 | |
---|
1083 | /* Will-Retry-Until: -- for delayed messages only */ |
---|
1084 | if (bitset(QQUEUEUP, q->q_flags) && |
---|
1085 | !bitset(QBADADDR, q->q_flags)) |
---|
1086 | { |
---|
1087 | time_t xdate; |
---|
1088 | |
---|
1089 | xdate = e->e_parent->e_ctime + |
---|
1090 | TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; |
---|
1091 | snprintf(buf, sizeof buf, |
---|
1092 | "Will-Retry-Until: %s", |
---|
1093 | arpadate(ctime(&xdate))); |
---|
1094 | putline(buf, mci); |
---|
1095 | } |
---|
1096 | } |
---|
1097 | } |
---|
1098 | #endif |
---|
1099 | |
---|
1100 | /* |
---|
1101 | ** Output text of original message |
---|
1102 | */ |
---|
1103 | |
---|
1104 | putline("", mci); |
---|
1105 | if (bitset(EF_HAS_DF, e->e_parent->e_flags)) |
---|
1106 | { |
---|
1107 | sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && |
---|
1108 | !bitset(EF_NO_BODY_RETN, e->e_flags); |
---|
1109 | |
---|
1110 | if (e->e_msgboundary == NULL) |
---|
1111 | { |
---|
1112 | if (sendbody) |
---|
1113 | putline(" ----- Original message follows -----\n", mci); |
---|
1114 | else |
---|
1115 | putline(" ----- Message header follows -----\n", mci); |
---|
1116 | (void) fflush(mci->mci_out); |
---|
1117 | } |
---|
1118 | else |
---|
1119 | { |
---|
1120 | (void) snprintf(buf, sizeof buf, "--%s", |
---|
1121 | e->e_msgboundary); |
---|
1122 | |
---|
1123 | putline(buf, mci); |
---|
1124 | (void) snprintf(buf, sizeof buf, "Content-Type: %s", |
---|
1125 | sendbody ? "message/rfc822" |
---|
1126 | : "text/rfc822-headers"); |
---|
1127 | putline(buf, mci); |
---|
1128 | |
---|
1129 | p = hvalue("Content-Transfer-Encoding", e->e_parent->e_header); |
---|
1130 | if (p != NULL && strcasecmp(p, "binary") != 0) |
---|
1131 | p = NULL; |
---|
1132 | if (p == NULL && bitset(EF_HAS8BIT, e->e_parent->e_flags)) |
---|
1133 | p = "8bit"; |
---|
1134 | if (p != NULL) |
---|
1135 | { |
---|
1136 | (void) snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", |
---|
1137 | p); |
---|
1138 | putline(buf, mci); |
---|
1139 | } |
---|
1140 | } |
---|
1141 | putline("", mci); |
---|
1142 | putheader(mci, e->e_parent->e_header, e->e_parent, M87F_OUTER); |
---|
1143 | if (sendbody) |
---|
1144 | putbody(mci, e->e_parent, e->e_msgboundary); |
---|
1145 | else if (e->e_msgboundary == NULL) |
---|
1146 | { |
---|
1147 | putline("", mci); |
---|
1148 | putline(" ----- Message body suppressed -----", mci); |
---|
1149 | } |
---|
1150 | } |
---|
1151 | else if (e->e_msgboundary == NULL) |
---|
1152 | { |
---|
1153 | putline(" ----- No message was collected -----\n", mci); |
---|
1154 | } |
---|
1155 | |
---|
1156 | if (e->e_msgboundary != NULL) |
---|
1157 | { |
---|
1158 | putline("", mci); |
---|
1159 | (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary); |
---|
1160 | putline(buf, mci); |
---|
1161 | } |
---|
1162 | putline("", mci); |
---|
1163 | |
---|
1164 | /* |
---|
1165 | ** Cleanup and exit |
---|
1166 | */ |
---|
1167 | |
---|
1168 | if (errno != 0) |
---|
1169 | syserr("errbody: I/O error"); |
---|
1170 | } |
---|
1171 | /* |
---|
1172 | ** SMTPTODSN -- convert SMTP to DSN status code |
---|
1173 | ** |
---|
1174 | ** Parameters: |
---|
1175 | ** smtpstat -- the smtp status code (e.g., 550). |
---|
1176 | ** |
---|
1177 | ** Returns: |
---|
1178 | ** The DSN version of the status code. |
---|
1179 | */ |
---|
1180 | |
---|
1181 | char * |
---|
1182 | smtptodsn(smtpstat) |
---|
1183 | int smtpstat; |
---|
1184 | { |
---|
1185 | if (smtpstat < 0) |
---|
1186 | return "4.4.2"; |
---|
1187 | |
---|
1188 | switch (smtpstat) |
---|
1189 | { |
---|
1190 | case 450: /* Req mail action not taken: mailbox unavailable */ |
---|
1191 | return "4.2.0"; |
---|
1192 | |
---|
1193 | case 451: /* Req action aborted: local error in processing */ |
---|
1194 | return "4.3.0"; |
---|
1195 | |
---|
1196 | case 452: /* Req action not taken: insufficient sys storage */ |
---|
1197 | return "4.3.1"; |
---|
1198 | |
---|
1199 | case 500: /* Syntax error, command unrecognized */ |
---|
1200 | return "5.5.2"; |
---|
1201 | |
---|
1202 | case 501: /* Syntax error in parameters or arguments */ |
---|
1203 | return "5.5.4"; |
---|
1204 | |
---|
1205 | case 502: /* Command not implemented */ |
---|
1206 | return "5.5.1"; |
---|
1207 | |
---|
1208 | case 503: /* Bad sequence of commands */ |
---|
1209 | return "5.5.1"; |
---|
1210 | |
---|
1211 | case 504: /* Command parameter not implemented */ |
---|
1212 | return "5.5.4"; |
---|
1213 | |
---|
1214 | case 550: /* Req mail action not taken: mailbox unavailable */ |
---|
1215 | return "5.2.0"; |
---|
1216 | |
---|
1217 | case 551: /* User not local; please try <...> */ |
---|
1218 | return "5.1.6"; |
---|
1219 | |
---|
1220 | case 552: /* Req mail action aborted: exceeded storage alloc */ |
---|
1221 | return "5.2.2"; |
---|
1222 | |
---|
1223 | case 553: /* Req action not taken: mailbox name not allowed */ |
---|
1224 | return "5.1.0"; |
---|
1225 | |
---|
1226 | case 554: /* Transaction failed */ |
---|
1227 | return "5.0.0"; |
---|
1228 | } |
---|
1229 | |
---|
1230 | if ((smtpstat / 100) == 2) |
---|
1231 | return "2.0.0"; |
---|
1232 | if ((smtpstat / 100) == 4) |
---|
1233 | return "4.0.0"; |
---|
1234 | return "5.0.0"; |
---|
1235 | } |
---|
1236 | /* |
---|
1237 | ** XTEXTIFY -- take regular text and turn it into DSN-style xtext |
---|
1238 | ** |
---|
1239 | ** Parameters: |
---|
1240 | ** t -- the text to convert. |
---|
1241 | ** taboo -- additional characters that must be encoded. |
---|
1242 | ** |
---|
1243 | ** Returns: |
---|
1244 | ** The xtext-ified version of the same string. |
---|
1245 | */ |
---|
1246 | |
---|
1247 | char * |
---|
1248 | xtextify(t, taboo) |
---|
1249 | register char *t; |
---|
1250 | char *taboo; |
---|
1251 | { |
---|
1252 | register char *p; |
---|
1253 | int l; |
---|
1254 | int nbogus; |
---|
1255 | static char *bp = NULL; |
---|
1256 | static int bplen = 0; |
---|
1257 | |
---|
1258 | if (taboo == NULL) |
---|
1259 | taboo = ""; |
---|
1260 | |
---|
1261 | /* figure out how long this xtext will have to be */ |
---|
1262 | nbogus = l = 0; |
---|
1263 | for (p = t; *p != '\0'; p++) |
---|
1264 | { |
---|
1265 | register int c = (*p & 0xff); |
---|
1266 | |
---|
1267 | /* ASCII dependence here -- this is the way the spec words it */ |
---|
1268 | if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || |
---|
1269 | strchr(taboo, c) != NULL) |
---|
1270 | nbogus++; |
---|
1271 | l++; |
---|
1272 | } |
---|
1273 | if (nbogus == 0) |
---|
1274 | return t; |
---|
1275 | l += nbogus * 2 + 1; |
---|
1276 | |
---|
1277 | /* now allocate space if necessary for the new string */ |
---|
1278 | if (l > bplen) |
---|
1279 | { |
---|
1280 | if (bp != NULL) |
---|
1281 | free(bp); |
---|
1282 | bp = xalloc(l); |
---|
1283 | bplen = l; |
---|
1284 | } |
---|
1285 | |
---|
1286 | /* ok, copy the text with byte expansion */ |
---|
1287 | for (p = bp; *t != '\0'; ) |
---|
1288 | { |
---|
1289 | register int c = (*t++ & 0xff); |
---|
1290 | |
---|
1291 | /* ASCII dependence here -- this is the way the spec words it */ |
---|
1292 | if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || |
---|
1293 | strchr(taboo, c) != NULL) |
---|
1294 | { |
---|
1295 | *p++ = '+'; |
---|
1296 | *p++ = "0123456789abcdef"[c >> 4]; |
---|
1297 | *p++ = "0123456789abcdef"[c & 0xf]; |
---|
1298 | } |
---|
1299 | else |
---|
1300 | *p++ = c; |
---|
1301 | } |
---|
1302 | *p = '\0'; |
---|
1303 | return bp; |
---|
1304 | } |
---|
1305 | /* |
---|
1306 | ** XUNTEXTIFY -- take xtext and turn it into plain text |
---|
1307 | ** |
---|
1308 | ** Parameters: |
---|
1309 | ** t -- the xtextified text. |
---|
1310 | ** |
---|
1311 | ** Returns: |
---|
1312 | ** The decoded text. No attempt is made to deal with |
---|
1313 | ** null strings in the resulting text. |
---|
1314 | */ |
---|
1315 | |
---|
1316 | char * |
---|
1317 | xuntextify(t) |
---|
1318 | register char *t; |
---|
1319 | { |
---|
1320 | register char *p; |
---|
1321 | int l; |
---|
1322 | static char *bp = NULL; |
---|
1323 | static int bplen = 0; |
---|
1324 | |
---|
1325 | /* heuristic -- if no plus sign, just return the input */ |
---|
1326 | if (strchr(t, '+') == NULL) |
---|
1327 | return t; |
---|
1328 | |
---|
1329 | /* xtext is always longer than decoded text */ |
---|
1330 | l = strlen(t); |
---|
1331 | if (l > bplen) |
---|
1332 | { |
---|
1333 | if (bp != NULL) |
---|
1334 | free(bp); |
---|
1335 | bp = xalloc(l); |
---|
1336 | bplen = l; |
---|
1337 | } |
---|
1338 | |
---|
1339 | /* ok, copy the text with byte compression */ |
---|
1340 | for (p = bp; *t != '\0'; t++) |
---|
1341 | { |
---|
1342 | register int c = *t & 0xff; |
---|
1343 | |
---|
1344 | if (c != '+') |
---|
1345 | { |
---|
1346 | *p++ = c; |
---|
1347 | continue; |
---|
1348 | } |
---|
1349 | |
---|
1350 | c = *++t & 0xff; |
---|
1351 | if (!isascii(c) || !isxdigit(c)) |
---|
1352 | { |
---|
1353 | /* error -- first digit is not hex */ |
---|
1354 | usrerr("bogus xtext: +%c", c); |
---|
1355 | t--; |
---|
1356 | continue; |
---|
1357 | } |
---|
1358 | if (isdigit(c)) |
---|
1359 | c -= '0'; |
---|
1360 | else if (isupper(c)) |
---|
1361 | c -= 'A' - 10; |
---|
1362 | else |
---|
1363 | c -= 'a' - 10; |
---|
1364 | *p = c << 4; |
---|
1365 | |
---|
1366 | c = *++t & 0xff; |
---|
1367 | if (!isascii(c) || !isxdigit(c)) |
---|
1368 | { |
---|
1369 | /* error -- second digit is not hex */ |
---|
1370 | usrerr("bogus xtext: +%x%c", *p >> 4, c); |
---|
1371 | t--; |
---|
1372 | continue; |
---|
1373 | } |
---|
1374 | if (isdigit(c)) |
---|
1375 | c -= '0'; |
---|
1376 | else if (isupper(c)) |
---|
1377 | c -= 'A' - 10; |
---|
1378 | else |
---|
1379 | c -= 'a' - 10; |
---|
1380 | *p++ |= c; |
---|
1381 | } |
---|
1382 | *p = '\0'; |
---|
1383 | return bp; |
---|
1384 | } |
---|
1385 | /* |
---|
1386 | ** XTEXTOK -- check if a string is legal xtext |
---|
1387 | ** |
---|
1388 | ** Xtext is used in Delivery Status Notifications. The spec was |
---|
1389 | ** taken from RFC 1891, ``SMTP Service Extension for Delivery |
---|
1390 | ** Status Notifications''. |
---|
1391 | ** |
---|
1392 | ** Parameters: |
---|
1393 | ** s -- the string to check. |
---|
1394 | ** |
---|
1395 | ** Returns: |
---|
1396 | ** TRUE -- if 's' is legal xtext. |
---|
1397 | ** FALSE -- if it has any illegal characters in it. |
---|
1398 | */ |
---|
1399 | |
---|
1400 | bool |
---|
1401 | xtextok(s) |
---|
1402 | char *s; |
---|
1403 | { |
---|
1404 | int c; |
---|
1405 | |
---|
1406 | while ((c = *s++) != '\0') |
---|
1407 | { |
---|
1408 | if (c == '+') |
---|
1409 | { |
---|
1410 | c = *s++; |
---|
1411 | if (!isascii(c) || !isxdigit(c)) |
---|
1412 | return FALSE; |
---|
1413 | c = *s++; |
---|
1414 | if (!isascii(c) || !isxdigit(c)) |
---|
1415 | return FALSE; |
---|
1416 | } |
---|
1417 | else if (c < '!' || c > '~' || c == '=') |
---|
1418 | return FALSE; |
---|
1419 | } |
---|
1420 | return TRUE; |
---|
1421 | } |
---|
1422 | /* |
---|
1423 | ** PRUNEROUTE -- prune an RFC-822 source route |
---|
1424 | ** |
---|
1425 | ** Trims down a source route to the last internet-registered hop. |
---|
1426 | ** This is encouraged by RFC 1123 section 5.3.3. |
---|
1427 | ** |
---|
1428 | ** Parameters: |
---|
1429 | ** addr -- the address |
---|
1430 | ** |
---|
1431 | ** Returns: |
---|
1432 | ** TRUE -- address was modified |
---|
1433 | ** FALSE -- address could not be pruned |
---|
1434 | ** |
---|
1435 | ** Side Effects: |
---|
1436 | ** modifies addr in-place |
---|
1437 | */ |
---|
1438 | |
---|
1439 | bool |
---|
1440 | pruneroute(addr) |
---|
1441 | char *addr; |
---|
1442 | { |
---|
1443 | #if NAMED_BIND |
---|
1444 | char *start, *at, *comma; |
---|
1445 | char c; |
---|
1446 | int rcode; |
---|
1447 | int i; |
---|
1448 | char hostbuf[BUFSIZ]; |
---|
1449 | char *mxhosts[MAXMXHOSTS + 1]; |
---|
1450 | |
---|
1451 | /* check to see if this is really a route-addr */ |
---|
1452 | if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') |
---|
1453 | return FALSE; |
---|
1454 | start = strchr(addr, ':'); |
---|
1455 | at = strrchr(addr, '@'); |
---|
1456 | if (start == NULL || at == NULL || at < start) |
---|
1457 | return FALSE; |
---|
1458 | |
---|
1459 | /* slice off the angle brackets */ |
---|
1460 | i = strlen(at + 1); |
---|
1461 | if (i >= (SIZE_T) sizeof hostbuf) |
---|
1462 | return FALSE; |
---|
1463 | strcpy(hostbuf, at + 1); |
---|
1464 | hostbuf[i - 1] = '\0'; |
---|
1465 | |
---|
1466 | while (start) |
---|
1467 | { |
---|
1468 | if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) |
---|
1469 | { |
---|
1470 | strcpy(addr + 1, start + 1); |
---|
1471 | return TRUE; |
---|
1472 | } |
---|
1473 | c = *start; |
---|
1474 | *start = '\0'; |
---|
1475 | comma = strrchr(addr, ','); |
---|
1476 | if (comma != NULL && comma[1] == '@' && |
---|
1477 | strlen(comma + 2) < (SIZE_T) sizeof hostbuf) |
---|
1478 | strcpy(hostbuf, comma + 2); |
---|
1479 | else |
---|
1480 | comma = NULL; |
---|
1481 | *start = c; |
---|
1482 | start = comma; |
---|
1483 | } |
---|
1484 | #endif |
---|
1485 | return FALSE; |
---|
1486 | } |
---|