Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1983 Eric P. Allman | |
6f14531a RG |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
15637ed4 RG |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. All advertising materials mentioning features or use of this software | |
15 | * must display the following acknowledgement: | |
16 | * This product includes software developed by the University of | |
17 | * California, Berkeley and its contributors. | |
18 | * 4. Neither the name of the University nor the names of its contributors | |
19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #ifndef lint | |
69fc843f | 36 | static char sccsid[] = "@(#)savemail.c 8.17 (Berkeley) 10/31/93"; |
15637ed4 RG |
37 | #endif /* not lint */ |
38 | ||
15637ed4 | 39 | # include "sendmail.h" |
d747e748 | 40 | # include <pwd.h> |
15637ed4 RG |
41 | |
42 | /* | |
43 | ** SAVEMAIL -- Save mail on error | |
44 | ** | |
45 | ** If mailing back errors, mail it back to the originator | |
46 | ** together with an error message; otherwise, just put it in | |
47 | ** dead.letter in the user's home directory (if he exists on | |
48 | ** this machine). | |
49 | ** | |
50 | ** Parameters: | |
51 | ** e -- the envelope containing the message in error. | |
52 | ** | |
53 | ** Returns: | |
54 | ** none | |
55 | ** | |
56 | ** Side Effects: | |
57 | ** Saves the letter, by writing or mailing it back to the | |
58 | ** sender, or by putting it in dead.letter in her home | |
59 | ** directory. | |
60 | */ | |
61 | ||
62 | /* defines for state machine */ | |
63 | # define ESM_REPORT 0 /* report to sender's terminal */ | |
64 | # define ESM_MAIL 1 /* mail back to sender */ | |
65 | # define ESM_QUIET 2 /* messages have already been returned */ | |
66 | # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ | |
67 | # define ESM_POSTMASTER 4 /* return to postmaster */ | |
68 | # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ | |
69 | # define ESM_PANIC 6 /* leave the locked queue/transcript files */ | |
70 | # define ESM_DONE 7 /* the message is successfully delivered */ | |
71 | ||
72 | ||
73 | savemail(e) | |
74 | register ENVELOPE *e; | |
75 | { | |
76 | register struct passwd *pw; | |
77 | register FILE *fp; | |
78 | int state; | |
6f14531a | 79 | auto ADDRESS *q = NULL; |
15637ed4 RG |
80 | char buf[MAXLINE+1]; |
81 | extern struct passwd *getpwnam(); | |
82 | register char *p; | |
83 | extern char *ttypath(); | |
84 | typedef int (*fnptr)(); | |
85 | ||
86 | if (tTd(6, 1)) | |
6f14531a RG |
87 | { |
88 | printf("\nsavemail, errormode = %c, id = %s\n e_from=", | |
89 | e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id); | |
90 | printaddr(&e->e_from, FALSE); | |
91 | } | |
15637ed4 | 92 | |
6f14531a | 93 | if (e->e_id == NULL) |
15637ed4 | 94 | { |
6f14531a | 95 | /* can't return a message with no id */ |
15637ed4 RG |
96 | return; |
97 | } | |
6f14531a | 98 | |
15637ed4 RG |
99 | /* |
100 | ** In the unhappy event we don't know who to return the mail | |
101 | ** to, make someone up. | |
102 | */ | |
103 | ||
104 | if (e->e_from.q_paddr == NULL) | |
105 | { | |
6f14531a | 106 | e->e_sender = "Postmaster"; |
d747e748 JH |
107 | if (parseaddr(e->e_sender, &e->e_from, |
108 | RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) | |
15637ed4 | 109 | { |
6f14531a | 110 | syserr("553 Cannot parse Postmaster!"); |
15637ed4 RG |
111 | ExitStat = EX_SOFTWARE; |
112 | finis(); | |
113 | } | |
114 | } | |
115 | e->e_to = NULL; | |
116 | ||
117 | /* | |
118 | ** Basic state machine. | |
119 | ** | |
120 | ** This machine runs through the following states: | |
121 | ** | |
122 | ** ESM_QUIET Errors have already been printed iff the | |
123 | ** sender is local. | |
124 | ** ESM_REPORT Report directly to the sender's terminal. | |
125 | ** ESM_MAIL Mail response to the sender. | |
126 | ** ESM_DEADLETTER Save response in ~/dead.letter. | |
127 | ** ESM_POSTMASTER Mail response to the postmaster. | |
128 | ** ESM_PANIC Save response anywhere possible. | |
129 | */ | |
130 | ||
131 | /* determine starting state */ | |
6f14531a | 132 | switch (e->e_errormode) |
15637ed4 RG |
133 | { |
134 | case EM_WRITE: | |
135 | state = ESM_REPORT; | |
136 | break; | |
137 | ||
138 | case EM_BERKNET: | |
139 | /* mail back, but return o.k. exit status */ | |
140 | ExitStat = EX_OK; | |
141 | ||
142 | /* fall through.... */ | |
143 | ||
144 | case EM_MAIL: | |
145 | state = ESM_MAIL; | |
146 | break; | |
147 | ||
148 | case EM_PRINT: | |
149 | case '\0': | |
150 | state = ESM_QUIET; | |
151 | break; | |
152 | ||
153 | case EM_QUIET: | |
154 | /* no need to return anything at all */ | |
155 | return; | |
156 | ||
157 | default: | |
6f14531a | 158 | syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); |
15637ed4 RG |
159 | state = ESM_MAIL; |
160 | break; | |
161 | } | |
162 | ||
6f14531a RG |
163 | /* if this is already an error response, send to postmaster */ |
164 | if (bitset(EF_RESPONSE, e->e_flags)) | |
165 | { | |
166 | if (e->e_parent != NULL && | |
167 | bitset(EF_RESPONSE, e->e_parent->e_flags)) | |
168 | { | |
169 | /* got an error sending a response -- can it */ | |
170 | return; | |
171 | } | |
172 | state = ESM_POSTMASTER; | |
173 | } | |
174 | ||
15637ed4 RG |
175 | while (state != ESM_DONE) |
176 | { | |
177 | if (tTd(6, 5)) | |
178 | printf(" state %d\n", state); | |
179 | ||
180 | switch (state) | |
181 | { | |
182 | case ESM_QUIET: | |
183 | if (e->e_from.q_mailer == LocalMailer) | |
184 | state = ESM_DEADLETTER; | |
185 | else | |
186 | state = ESM_MAIL; | |
187 | break; | |
188 | ||
189 | case ESM_REPORT: | |
190 | ||
191 | /* | |
192 | ** If the user is still logged in on the same terminal, | |
193 | ** then write the error messages back to hir (sic). | |
194 | */ | |
195 | ||
196 | p = ttypath(); | |
197 | if (p == NULL || freopen(p, "w", stdout) == NULL) | |
198 | { | |
199 | state = ESM_MAIL; | |
200 | break; | |
201 | } | |
202 | ||
6f14531a | 203 | expand("\201n", buf, &buf[sizeof buf - 1], e); |
15637ed4 RG |
204 | printf("\r\nMessage from %s...\r\n", buf); |
205 | printf("Errors occurred while sending mail.\r\n"); | |
206 | if (e->e_xfp != NULL) | |
207 | { | |
208 | (void) fflush(e->e_xfp); | |
209 | fp = fopen(queuename(e, 'x'), "r"); | |
210 | } | |
211 | else | |
212 | fp = NULL; | |
213 | if (fp == NULL) | |
214 | { | |
215 | syserr("Cannot open %s", queuename(e, 'x')); | |
216 | printf("Transcript of session is unavailable.\r\n"); | |
217 | } | |
218 | else | |
219 | { | |
220 | printf("Transcript follows:\r\n"); | |
221 | while (fgets(buf, sizeof buf, fp) != NULL && | |
222 | !ferror(stdout)) | |
223 | fputs(buf, stdout); | |
6f14531a | 224 | (void) xfclose(fp, "savemail transcript", e->e_id); |
15637ed4 RG |
225 | } |
226 | printf("Original message will be saved in dead.letter.\r\n"); | |
227 | state = ESM_DEADLETTER; | |
228 | break; | |
229 | ||
230 | case ESM_MAIL: | |
15637ed4 RG |
231 | /* |
232 | ** If mailing back, do it. | |
233 | ** Throw away all further output. Don't alias, | |
234 | ** since this could cause loops, e.g., if joe | |
235 | ** mails to joe@x, and for some reason the network | |
236 | ** for @x is down, then the response gets sent to | |
237 | ** joe@x, which gives a response, etc. Also force | |
238 | ** the mail to be delivered even if a version of | |
239 | ** it has already been sent to the sender. | |
d747e748 JH |
240 | ** |
241 | ** If this is a configuration or local software | |
242 | ** error, send to the local postmaster as well, | |
243 | ** since the originator can't do anything | |
244 | ** about it anyway. Note that this is a full | |
245 | ** copy of the message (intentionally) so that | |
246 | ** the Postmaster can forward things along. | |
15637ed4 RG |
247 | */ |
248 | ||
d747e748 JH |
249 | if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) |
250 | { | |
251 | (void) sendtolist("postmaster", | |
252 | NULLADDR, &e->e_errorqueue, e); | |
253 | } | |
6f14531a | 254 | if (strcmp(e->e_from.q_paddr, "<>") != 0) |
d747e748 | 255 | { |
6f14531a | 256 | (void) sendtolist(e->e_from.q_paddr, |
d747e748 JH |
257 | NULLADDR, &e->e_errorqueue, e); |
258 | } | |
6f14531a | 259 | |
d747e748 JH |
260 | /* |
261 | ** Deliver a non-delivery report to the | |
262 | ** Postmaster-designate (not necessarily | |
263 | ** Postmaster). This does not include the | |
264 | ** body of the message, for privacy reasons. | |
265 | ** You really shouldn't need this. | |
266 | */ | |
267 | ||
268 | e->e_flags |= EF_PM_NOTIFY; | |
6f14531a | 269 | |
69fc843f AM |
270 | /* check to see if there are any good addresses */ |
271 | for (q = e->e_errorqueue; q != NULL; q = q->q_next) | |
272 | if (!bitset(QBADADDR|QDONTSEND, q->q_flags)) | |
273 | break; | |
6f14531a | 274 | if (q == NULL) |
15637ed4 | 275 | { |
6f14531a RG |
276 | /* this is an error-error */ |
277 | state = ESM_POSTMASTER; | |
278 | break; | |
15637ed4 | 279 | } |
6f14531a RG |
280 | if (returntosender(e->e_message, |
281 | q, (e->e_class >= 0), e) == 0) | |
15637ed4 RG |
282 | { |
283 | state = ESM_DONE; | |
284 | break; | |
285 | } | |
286 | ||
6f14531a RG |
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("postmaster", NULL, &q, e) <= 0) | |
298 | { | |
299 | syserr("553 cannot parse postmaster!"); | |
300 | ExitStat = EX_SOFTWARE; | |
301 | state = ESM_USRTMP; | |
302 | break; | |
303 | } | |
304 | if (returntosender(e->e_message, | |
305 | q, (e->e_class >= 0), e) == 0) | |
306 | { | |
307 | state = ESM_DONE; | |
308 | break; | |
309 | } | |
310 | ||
311 | /* didn't work -- last resort */ | |
312 | state = ESM_USRTMP; | |
15637ed4 RG |
313 | break; |
314 | ||
315 | case ESM_DEADLETTER: | |
316 | /* | |
317 | ** Save the message in dead.letter. | |
318 | ** If we weren't mailing back, and the user is | |
319 | ** local, we should save the message in | |
320 | ** ~/dead.letter so that the poor person doesn't | |
321 | ** have to type it over again -- and we all know | |
322 | ** what poor typists UNIX users are. | |
323 | */ | |
324 | ||
325 | p = NULL; | |
326 | if (e->e_from.q_mailer == LocalMailer) | |
327 | { | |
328 | if (e->e_from.q_home != NULL) | |
329 | p = e->e_from.q_home; | |
330 | else if ((pw = getpwnam(e->e_from.q_user)) != NULL) | |
331 | p = pw->pw_dir; | |
332 | } | |
333 | if (p == NULL) | |
334 | { | |
6f14531a | 335 | /* no local directory */ |
15637ed4 RG |
336 | state = ESM_MAIL; |
337 | break; | |
338 | } | |
339 | if (e->e_dfp != NULL) | |
340 | { | |
15637ed4 RG |
341 | bool oldverb = Verbose; |
342 | ||
343 | /* we have a home directory; open dead.letter */ | |
344 | define('z', p, e); | |
6f14531a | 345 | expand("\201z/dead.letter", buf, &buf[sizeof buf - 1], e); |
15637ed4 | 346 | Verbose = TRUE; |
6f14531a | 347 | message("Saving message in %s", buf); |
15637ed4 RG |
348 | Verbose = oldverb; |
349 | e->e_to = buf; | |
350 | q = NULL; | |
6f14531a | 351 | (void) sendtolist(buf, &e->e_from, &q, e); |
d747e748 JH |
352 | if (q != NULL && |
353 | !bitset(QBADADDR, q->q_flags) && | |
354 | deliver(e, q) == 0) | |
15637ed4 RG |
355 | state = ESM_DONE; |
356 | else | |
357 | state = ESM_MAIL; | |
358 | } | |
359 | else | |
360 | { | |
361 | /* no data file -- try mailing back */ | |
362 | state = ESM_MAIL; | |
363 | } | |
364 | break; | |
365 | ||
366 | case ESM_USRTMP: | |
367 | /* | |
368 | ** Log the mail in /usr/tmp/dead.letter. | |
369 | */ | |
370 | ||
6f14531a RG |
371 | if (e->e_class < 0) |
372 | { | |
373 | state = ESM_DONE; | |
374 | break; | |
375 | } | |
376 | ||
377 | fp = dfopen("/usr/tmp/dead.letter", | |
378 | O_WRONLY|O_CREAT|O_APPEND, FileMode); | |
15637ed4 RG |
379 | if (fp == NULL) |
380 | { | |
381 | state = ESM_PANIC; | |
382 | break; | |
383 | } | |
384 | ||
6f14531a RG |
385 | putfromline(fp, FileMailer, e); |
386 | (*e->e_puthdr)(fp, FileMailer, e); | |
387 | putline("\n", fp, FileMailer); | |
388 | (*e->e_putbody)(fp, FileMailer, e, NULL); | |
389 | putline("\n", fp, FileMailer); | |
15637ed4 RG |
390 | (void) fflush(fp); |
391 | state = ferror(fp) ? ESM_PANIC : ESM_DONE; | |
6f14531a | 392 | (void) xfclose(fp, "savemail", "/usr/tmp/dead.letter"); |
15637ed4 RG |
393 | break; |
394 | ||
395 | default: | |
6f14531a | 396 | syserr("554 savemail: unknown state %d", state); |
15637ed4 RG |
397 | |
398 | /* fall through ... */ | |
399 | ||
400 | case ESM_PANIC: | |
15637ed4 | 401 | /* leave the locked queue & transcript files around */ |
6f14531a | 402 | syserr("554 savemail: cannot save rejected email anywhere"); |
15637ed4 RG |
403 | exit(EX_SOFTWARE); |
404 | } | |
405 | } | |
406 | } | |
407 | \f/* | |
408 | ** RETURNTOSENDER -- return a message to the sender with an error. | |
409 | ** | |
410 | ** Parameters: | |
411 | ** msg -- the explanatory message. | |
412 | ** returnq -- the queue of people to send the message to. | |
413 | ** sendbody -- if TRUE, also send back the body of the | |
414 | ** message; otherwise just send the header. | |
6f14531a | 415 | ** e -- the current envelope. |
15637ed4 RG |
416 | ** |
417 | ** Returns: | |
418 | ** zero -- if everything went ok. | |
419 | ** else -- some error. | |
420 | ** | |
421 | ** Side Effects: | |
422 | ** Returns the current message to the sender via | |
423 | ** mail. | |
424 | */ | |
425 | ||
426 | static bool SendBody; | |
427 | ||
428 | #define MAXRETURNS 6 /* max depth of returning messages */ | |
6f14531a | 429 | #define ERRORFUDGE 100 /* nominal size of error message text */ |
15637ed4 | 430 | |
6f14531a | 431 | returntosender(msg, returnq, sendbody, e) |
15637ed4 RG |
432 | char *msg; |
433 | ADDRESS *returnq; | |
434 | bool sendbody; | |
6f14531a | 435 | register ENVELOPE *e; |
15637ed4 RG |
436 | { |
437 | char buf[MAXNAME]; | |
438 | extern putheader(), errbody(); | |
439 | register ENVELOPE *ee; | |
6f14531a | 440 | ENVELOPE *oldcur = CurEnv; |
15637ed4 RG |
441 | ENVELOPE errenvelope; |
442 | static int returndepth; | |
443 | register ADDRESS *q; | |
444 | ||
6f14531a RG |
445 | if (returnq == NULL) |
446 | return (-1); | |
447 | ||
448 | if (msg == NULL) | |
449 | msg = "Unable to deliver mail"; | |
450 | ||
15637ed4 RG |
451 | if (tTd(6, 1)) |
452 | { | |
6f14531a RG |
453 | printf("Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=", |
454 | msg, returndepth, e); | |
15637ed4 RG |
455 | printaddr(returnq, TRUE); |
456 | } | |
457 | ||
458 | if (++returndepth >= MAXRETURNS) | |
459 | { | |
460 | if (returndepth != MAXRETURNS) | |
6f14531a | 461 | syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); |
15637ed4 RG |
462 | /* don't "unrecurse" and fake a clean exit */ |
463 | /* returndepth--; */ | |
464 | return (0); | |
465 | } | |
466 | ||
467 | SendBody = sendbody; | |
6f14531a RG |
468 | define('g', e->e_from.q_paddr, e); |
469 | ee = newenvelope(&errenvelope, e); | |
470 | define('a', "\201b", ee); | |
471 | define('r', "internal", ee); | |
472 | define('s', "localhost", ee); | |
473 | define('_', "localhost", ee); | |
15637ed4 RG |
474 | ee->e_puthdr = putheader; |
475 | ee->e_putbody = errbody; | |
d747e748 | 476 | ee->e_flags |= EF_RESPONSE|EF_METOO; |
6f14531a | 477 | if (!bitset(EF_OLDSTYLE, e->e_flags)) |
15637ed4 RG |
478 | ee->e_flags &= ~EF_OLDSTYLE; |
479 | ee->e_sendqueue = returnq; | |
d747e748 JH |
480 | ee->e_msgsize = ERRORFUDGE; |
481 | if (!NoReturn) | |
482 | ee->e_msgsize += e->e_msgsize; | |
483 | initsys(ee); | |
15637ed4 RG |
484 | for (q = returnq; q != NULL; q = q->q_next) |
485 | { | |
6f14531a RG |
486 | if (bitset(QBADADDR, q->q_flags)) |
487 | continue; | |
488 | ||
489 | if (!bitset(QDONTSEND, q->q_flags)) | |
490 | ee->e_nrcpts++; | |
491 | ||
492 | if (!DontPruneRoutes && pruneroute(q->q_paddr)) | |
d747e748 | 493 | parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e); |
6f14531a | 494 | |
15637ed4 | 495 | if (q->q_alias == NULL) |
6f14531a | 496 | addheader("To", q->q_paddr, ee); |
15637ed4 RG |
497 | } |
498 | ||
6f14531a RG |
499 | # ifdef LOG |
500 | if (LogLevel > 5) | |
501 | syslog(LOG_INFO, "%s: %s: return to sender: %s", | |
502 | e->e_id, ee->e_id, msg); | |
503 | # endif | |
504 | ||
15637ed4 | 505 | (void) sprintf(buf, "Returned mail: %s", msg); |
6f14531a RG |
506 | addheader("Subject", buf, ee); |
507 | if (SendMIMEErrors) | |
508 | { | |
509 | addheader("MIME-Version", "1.0", ee); | |
510 | (void) sprintf(buf, "%s.%ld/%s", | |
511 | ee->e_id, curtime(), MyHostName); | |
512 | ee->e_msgboundary = newstr(buf); | |
513 | (void) sprintf(buf, "multipart/mixed; boundary=\"%s\"", | |
514 | ee->e_msgboundary); | |
515 | addheader("Content-Type", buf, ee); | |
516 | } | |
15637ed4 RG |
517 | |
518 | /* fake up an address header for the from person */ | |
6f14531a | 519 | expand("\201n", buf, &buf[sizeof buf - 1], e); |
d747e748 | 520 | if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) |
15637ed4 | 521 | { |
6f14531a | 522 | syserr("553 Can't parse myself!"); |
15637ed4 RG |
523 | ExitStat = EX_SOFTWARE; |
524 | returndepth--; | |
525 | return (-1); | |
526 | } | |
6f14531a | 527 | ee->e_sender = ee->e_from.q_paddr; |
15637ed4 RG |
528 | |
529 | /* push state into submessage */ | |
530 | CurEnv = ee; | |
6f14531a | 531 | define('f', "\201n", ee); |
15637ed4 | 532 | define('x', "Mail Delivery Subsystem", ee); |
6f14531a | 533 | eatheader(ee, TRUE); |
15637ed4 | 534 | |
3a363396 | 535 | /* mark statistics */ |
d747e748 | 536 | markstats(ee, NULLADDR); |
3a363396 | 537 | |
15637ed4 RG |
538 | /* actually deliver the error message */ |
539 | sendall(ee, SM_DEFAULT); | |
540 | ||
541 | /* restore state */ | |
542 | dropenvelope(ee); | |
6f14531a | 543 | CurEnv = oldcur; |
15637ed4 RG |
544 | returndepth--; |
545 | ||
546 | /* should check for delivery errors here */ | |
547 | return (0); | |
548 | } | |
549 | \f/* | |
550 | ** ERRBODY -- output the body of an error message. | |
551 | ** | |
552 | ** Typically this is a copy of the transcript plus a copy of the | |
553 | ** original offending message. | |
554 | ** | |
555 | ** Parameters: | |
556 | ** fp -- the output file. | |
557 | ** m -- the mailer to output to. | |
558 | ** e -- the envelope we are working in. | |
559 | ** | |
560 | ** Returns: | |
561 | ** none | |
562 | ** | |
563 | ** Side Effects: | |
564 | ** Outputs the body of an error message. | |
565 | */ | |
566 | ||
567 | errbody(fp, m, e) | |
568 | register FILE *fp; | |
569 | register struct mailer *m; | |
570 | register ENVELOPE *e; | |
571 | { | |
572 | register FILE *xfile; | |
15637ed4 | 573 | char *p; |
6f14531a RG |
574 | register ADDRESS *q; |
575 | bool printheader; | |
576 | char buf[MAXLINE]; | |
577 | ||
578 | if (e->e_parent == NULL) | |
579 | { | |
580 | syserr("errbody: null parent"); | |
581 | putline(" ----- Original message lost -----\n", fp, m); | |
582 | return; | |
583 | } | |
584 | ||
585 | /* | |
586 | ** Output MIME header. | |
587 | */ | |
588 | ||
589 | if (e->e_msgboundary != NULL) | |
590 | { | |
591 | putline("This is a MIME-encapsulated message", fp, m); | |
592 | putline("", fp, m); | |
593 | (void) sprintf(buf, "--%s", e->e_msgboundary); | |
594 | putline(buf, fp, m); | |
595 | putline("", fp, m); | |
596 | } | |
597 | ||
d747e748 JH |
598 | /* |
599 | ** Output introductory information. | |
600 | */ | |
601 | ||
602 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) | |
603 | if (bitset(QBADADDR, q->q_flags)) | |
604 | break; | |
605 | if (q == NULL && !bitset(EF_FATALERRS, e->e_parent->e_flags)) | |
606 | { | |
607 | putline(" **********************************************", | |
608 | fp, m); | |
609 | putline(" ** THIS IS A WARNING MESSAGE ONLY **", | |
610 | fp, m); | |
611 | putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", | |
612 | fp, m); | |
613 | putline(" **********************************************", | |
614 | fp, m); | |
615 | putline("", fp, m); | |
616 | } | |
617 | sprintf(buf, "The original message was received at %s", | |
618 | arpadate(ctime(&e->e_parent->e_ctime))); | |
619 | putline(buf, fp, m); | |
620 | expand("from \201_", buf, &buf[sizeof buf - 1], e->e_parent); | |
621 | putline(buf, fp, m); | |
622 | putline("", fp, m); | |
623 | ||
6f14531a RG |
624 | /* |
625 | ** Output error message header (if specified and available). | |
626 | */ | |
627 | ||
628 | if (ErrMsgFile != NULL) | |
629 | { | |
630 | if (*ErrMsgFile == '/') | |
631 | { | |
632 | xfile = fopen(ErrMsgFile, "r"); | |
633 | if (xfile != NULL) | |
634 | { | |
635 | while (fgets(buf, sizeof buf, xfile) != NULL) | |
636 | { | |
637 | expand(buf, buf, &buf[sizeof buf - 1], e); | |
638 | putline(buf, fp, m); | |
639 | } | |
640 | (void) fclose(xfile); | |
641 | putline("\n", fp, m); | |
642 | } | |
643 | } | |
644 | else | |
645 | { | |
646 | expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e); | |
647 | putline(buf, fp, m); | |
648 | putline("", fp, m); | |
649 | } | |
650 | } | |
651 | ||
652 | /* | |
653 | ** Output message introduction | |
654 | */ | |
655 | ||
656 | printheader = TRUE; | |
657 | for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) | |
658 | { | |
3a363396 | 659 | if (bitset(QBADADDR|QREPORT, q->q_flags)) |
6f14531a RG |
660 | { |
661 | if (printheader) | |
662 | { | |
3a363396 | 663 | putline(" ----- The following addresses had delivery problems -----", |
6f14531a RG |
664 | fp, m); |
665 | printheader = FALSE; | |
666 | } | |
d747e748 | 667 | strcpy(buf, q->q_paddr); |
3a363396 | 668 | if (bitset(QBADADDR, q->q_flags)) |
d747e748 | 669 | strcat(buf, " (unrecoverable error)"); |
3a363396 | 670 | else |
d747e748 | 671 | strcat(buf, " (transient failure)"); |
3a363396 | 672 | putline(buf, fp, m); |
d747e748 JH |
673 | if (q->q_alias != NULL) |
674 | { | |
675 | strcpy(buf, " (expanded from: "); | |
676 | strcat(buf, q->q_alias->q_paddr); | |
677 | strcat(buf, ")"); | |
678 | putline(buf, fp, m); | |
679 | } | |
6f14531a RG |
680 | } |
681 | } | |
682 | if (!printheader) | |
683 | putline("\n", fp, m); | |
15637ed4 RG |
684 | |
685 | /* | |
686 | ** Output transcript of errors | |
687 | */ | |
688 | ||
689 | (void) fflush(stdout); | |
690 | p = queuename(e->e_parent, 'x'); | |
691 | if ((xfile = fopen(p, "r")) == NULL) | |
692 | { | |
693 | syserr("Cannot open %s", p); | |
6f14531a | 694 | putline(" ----- Transcript of session is unavailable -----\n", fp, m); |
15637ed4 RG |
695 | } |
696 | else | |
697 | { | |
6f14531a | 698 | putline(" ----- Transcript of session follows -----\n", fp, m); |
15637ed4 RG |
699 | if (e->e_xfp != NULL) |
700 | (void) fflush(e->e_xfp); | |
701 | while (fgets(buf, sizeof buf, xfile) != NULL) | |
702 | putline(buf, fp, m); | |
6f14531a | 703 | (void) xfclose(xfile, "errbody xscript", p); |
15637ed4 RG |
704 | } |
705 | errno = 0; | |
706 | ||
707 | /* | |
708 | ** Output text of original message | |
709 | */ | |
710 | ||
711 | if (NoReturn) | |
6f14531a RG |
712 | SendBody = FALSE; |
713 | putline("", fp, m); | |
714 | if (e->e_parent->e_df != NULL) | |
15637ed4 RG |
715 | { |
716 | if (SendBody) | |
d747e748 | 717 | putline(" ----- Original message follows -----\n", fp, m); |
15637ed4 | 718 | else |
6f14531a RG |
719 | putline(" ----- Message header follows -----\n", fp, m); |
720 | (void) fflush(fp); | |
721 | ||
722 | if (e->e_msgboundary != NULL) | |
15637ed4 | 723 | { |
6f14531a RG |
724 | putline("", fp, m); |
725 | (void) sprintf(buf, "--%s", e->e_msgboundary); | |
726 | putline(buf, fp, m); | |
727 | putline("Content-Type: message/rfc822", fp, m); | |
728 | putline("", fp, m); | |
15637ed4 | 729 | } |
6f14531a RG |
730 | putheader(fp, m, e->e_parent); |
731 | putline("", fp, m); | |
732 | if (SendBody) | |
733 | putbody(fp, m, e->e_parent, e->e_msgboundary); | |
734 | else | |
735 | putline(" ----- Message body suppressed -----", fp, m); | |
15637ed4 RG |
736 | } |
737 | else | |
738 | { | |
15637ed4 | 739 | putline(" ----- No message was collected -----\n", fp, m); |
15637ed4 RG |
740 | } |
741 | ||
6f14531a RG |
742 | if (e->e_msgboundary != NULL) |
743 | { | |
744 | putline("", fp, m); | |
745 | (void) sprintf(buf, "--%s--", e->e_msgboundary); | |
746 | putline(buf, fp, m); | |
747 | } | |
748 | putline("", fp, m); | |
749 | ||
15637ed4 RG |
750 | /* |
751 | ** Cleanup and exit | |
752 | */ | |
753 | ||
754 | if (errno != 0) | |
755 | syserr("errbody: I/O error"); | |
756 | } | |
6f14531a RG |
757 | \f/* |
758 | ** PRUNEROUTE -- prune an RFC-822 source route | |
759 | ** | |
760 | ** Trims down a source route to the last internet-registered hop. | |
761 | ** This is encouraged by RFC 1123 section 5.3.3. | |
762 | ** | |
763 | ** Parameters: | |
764 | ** addr -- the address | |
765 | ** | |
766 | ** Returns: | |
767 | ** TRUE -- address was modified | |
768 | ** FALSE -- address could not be pruned | |
769 | ** | |
770 | ** Side Effects: | |
771 | ** modifies addr in-place | |
772 | */ | |
773 | ||
774 | pruneroute(addr) | |
775 | char *addr; | |
776 | { | |
777 | #ifdef NAMED_BIND | |
778 | char *start, *at, *comma; | |
779 | char c; | |
780 | int rcode; | |
781 | char hostbuf[BUFSIZ]; | |
782 | char *mxhosts[MAXMXHOSTS + 1]; | |
783 | ||
784 | /* check to see if this is really a route-addr */ | |
785 | if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') | |
786 | return FALSE; | |
787 | start = strchr(addr, ':'); | |
788 | at = strrchr(addr, '@'); | |
789 | if (start == NULL || at == NULL || at < start) | |
790 | return FALSE; | |
791 | ||
792 | /* slice off the angle brackets */ | |
793 | strcpy(hostbuf, at + 1); | |
794 | hostbuf[strlen(hostbuf) - 1] = '\0'; | |
795 | ||
796 | while (start) | |
797 | { | |
798 | if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) | |
799 | { | |
800 | strcpy(addr + 1, start + 1); | |
801 | return TRUE; | |
802 | } | |
803 | c = *start; | |
804 | *start = '\0'; | |
805 | comma = strrchr(addr, ','); | |
806 | if (comma && comma[1] == '@') | |
807 | strcpy(hostbuf, comma + 2); | |
808 | else | |
809 | comma = 0; | |
810 | *start = c; | |
811 | start = comma; | |
812 | } | |
813 | #endif | |
814 | return FALSE; | |
815 | } |