Commit | Line | Data |
---|---|---|
b2a81223 | 1 | /* |
dc45ba8c | 2 | * Copyright (c) 1983 Eric P. Allman |
bee79b64 KB |
3 | * Copyright (c) 1988 Regents of the University of California. |
4 | * All rights reserved. | |
5 | * | |
417f7a11 | 6 | * %sccs.include.redist.c% |
bee79b64 | 7 | */ |
b2a81223 DF |
8 | |
9 | #ifndef lint | |
417f7a11 | 10 | static char sccsid[] = "@(#)savemail.c 5.13 (Berkeley) %G%"; |
bee79b64 | 11 | #endif /* not lint */ |
b2a81223 | 12 | |
611050b6 | 13 | # include <sys/types.h> |
b3cbe40f | 14 | # include <pwd.h> |
96faada8 | 15 | # include "sendmail.h" |
b3cbe40f EA |
16 | |
17 | /* | |
18 | ** SAVEMAIL -- Save mail on error | |
19 | ** | |
7338e3d4 | 20 | ** If mailing back errors, mail it back to the originator |
b3cbe40f EA |
21 | ** together with an error message; otherwise, just put it in |
22 | ** dead.letter in the user's home directory (if he exists on | |
23 | ** this machine). | |
24 | ** | |
25 | ** Parameters: | |
e6f08ab1 | 26 | ** e -- the envelope containing the message in error. |
b3cbe40f EA |
27 | ** |
28 | ** Returns: | |
29 | ** none | |
30 | ** | |
31 | ** Side Effects: | |
32 | ** Saves the letter, by writing or mailing it back to the | |
33 | ** sender, or by putting it in dead.letter in her home | |
34 | ** directory. | |
b3cbe40f EA |
35 | */ |
36 | ||
2e3062fe EA |
37 | /* defines for state machine */ |
38 | # define ESM_REPORT 0 /* report to sender's terminal */ | |
39 | # define ESM_MAIL 1 /* mail back to sender */ | |
40 | # define ESM_QUIET 2 /* messages have already been returned */ | |
41 | # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ | |
42 | # define ESM_POSTMASTER 4 /* return to postmaster */ | |
43 | # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ | |
44 | # define ESM_PANIC 6 /* leave the locked queue/transcript files */ | |
45 | # define ESM_DONE 7 /* the message is successfully delivered */ | |
46 | ||
47 | ||
e6f08ab1 EA |
48 | savemail(e) |
49 | register ENVELOPE *e; | |
b3cbe40f EA |
50 | { |
51 | register struct passwd *pw; | |
2e3062fe EA |
52 | register FILE *fp; |
53 | int state; | |
54 | auto ADDRESS *q; | |
b3cbe40f | 55 | char buf[MAXLINE+1]; |
b3cbe40f EA |
56 | extern struct passwd *getpwnam(); |
57 | register char *p; | |
b3cbe40f | 58 | extern char *ttypath(); |
40e27d12 | 59 | typedef int (*fnptr)(); |
b3cbe40f | 60 | |
9678c96d | 61 | if (tTd(6, 1)) |
f4b05990 | 62 | printf("\nsavemail, ErrorMode = %c\n", ErrorMode); |
ae945718 | 63 | |
7338e3d4 | 64 | if (bitset(EF_RESPONSE, e->e_flags)) |
b3cbe40f | 65 | return; |
e6f08ab1 | 66 | if (e->e_class < 0) |
dd1fe05b | 67 | { |
91f69adf | 68 | message(Arpa_Info, "Dumping junk mail"); |
dd1fe05b EA |
69 | return; |
70 | } | |
91f69adf | 71 | ForceMail = TRUE; |
e6f08ab1 | 72 | e->e_flags &= ~EF_FATALERRS; |
b3cbe40f EA |
73 | |
74 | /* | |
75 | ** In the unhappy event we don't know who to return the mail | |
76 | ** to, make someone up. | |
77 | */ | |
78 | ||
1bcdf0a2 | 79 | if (CurEnv->e_returnto == NULL) |
b3cbe40f | 80 | { |
1bcdf0a2 EA |
81 | CurEnv->e_returnto = parse("root", (ADDRESS *) NULL, 0); |
82 | if (CurEnv->e_returnto == NULL) | |
b3cbe40f EA |
83 | { |
84 | syserr("Cannot parse root!"); | |
85 | ExitStat = EX_SOFTWARE; | |
86 | finis(); | |
87 | } | |
88 | } | |
e6f08ab1 | 89 | e->e_to = NULL; |
b3cbe40f EA |
90 | |
91 | /* | |
2e3062fe EA |
92 | ** Basic state machine. |
93 | ** | |
94 | ** This machine runs through the following states: | |
95 | ** | |
96 | ** ESM_QUIET Errors have already been printed iff the | |
97 | ** sender is local. | |
98 | ** ESM_REPORT Report directly to the sender's terminal. | |
99 | ** ESM_MAIL Mail response to the sender. | |
100 | ** ESM_DEADLETTER Save response in ~/dead.letter. | |
101 | ** ESM_POSTMASTER Mail response to the postmaster. | |
102 | ** ESM_PANIC Save response anywhere possible. | |
b3cbe40f EA |
103 | */ |
104 | ||
2e3062fe EA |
105 | /* determine starting state */ |
106 | switch (ErrorMode) | |
b3cbe40f | 107 | { |
2e3062fe EA |
108 | case EM_WRITE: |
109 | state = ESM_REPORT; | |
110 | break; | |
111 | ||
112 | case EM_BERKNET: | |
113 | /* mail back, but return o.k. exit status */ | |
3b661a43 | 114 | ExitStat = EX_OK; |
b3cbe40f | 115 | |
2e3062fe EA |
116 | /* fall through.... */ |
117 | ||
118 | case EM_MAIL: | |
119 | state = ESM_MAIL; | |
120 | break; | |
121 | ||
122 | case EM_PRINT: | |
f4b05990 | 123 | case '\0': |
2e3062fe EA |
124 | state = ESM_QUIET; |
125 | break; | |
b3cbe40f | 126 | |
2e3062fe EA |
127 | case EM_QUIET: |
128 | /* no need to return anything at all */ | |
129 | return; | |
f4b05990 EA |
130 | |
131 | default: | |
132 | syserr("savemail: ErrorMode x%x\n"); | |
133 | state = ESM_MAIL; | |
134 | break; | |
2e3062fe EA |
135 | } |
136 | ||
137 | while (state != ESM_DONE) | |
b3cbe40f | 138 | { |
f4b05990 EA |
139 | if (tTd(6, 5)) |
140 | printf(" state %d\n", state); | |
f4b05990 | 141 | |
2e3062fe | 142 | switch (state) |
b3cbe40f | 143 | { |
f4b05990 EA |
144 | case ESM_QUIET: |
145 | if (e->e_from.q_mailer == LocalMailer) | |
146 | state = ESM_DEADLETTER; | |
147 | else | |
148 | state = ESM_MAIL; | |
149 | break; | |
150 | ||
2e3062fe EA |
151 | case ESM_REPORT: |
152 | ||
153 | /* | |
154 | ** If the user is still logged in on the same terminal, | |
155 | ** then write the error messages back to hir (sic). | |
156 | */ | |
157 | ||
158 | p = ttypath(); | |
159 | if (p == NULL || freopen(p, "w", stdout) == NULL) | |
160 | { | |
161 | state = ESM_MAIL; | |
162 | break; | |
163 | } | |
164 | ||
a73ae8ac | 165 | expand("\001n", buf, &buf[sizeof buf - 1], e); |
7338e3d4 EA |
166 | printf("\r\nMessage from %s...\r\n", buf); |
167 | printf("Errors occurred while sending mail.\r\n"); | |
912acb74 | 168 | if (e->e_xfp != NULL) |
7338e3d4 | 169 | { |
912acb74 | 170 | (void) fflush(e->e_xfp); |
2e3062fe | 171 | fp = fopen(queuename(e, 'x'), "r"); |
7338e3d4 EA |
172 | } |
173 | else | |
2e3062fe EA |
174 | fp = NULL; |
175 | if (fp == NULL) | |
7338e3d4 | 176 | { |
e6f08ab1 | 177 | syserr("Cannot open %s", queuename(e, 'x')); |
7338e3d4 EA |
178 | printf("Transcript of session is unavailable.\r\n"); |
179 | } | |
180 | else | |
181 | { | |
182 | printf("Transcript follows:\r\n"); | |
2e3062fe | 183 | while (fgets(buf, sizeof buf, fp) != NULL && |
7338e3d4 EA |
184 | !ferror(stdout)) |
185 | fputs(buf, stdout); | |
2e3062fe | 186 | (void) fclose(fp); |
7338e3d4 | 187 | } |
2e3062fe | 188 | printf("Original message will be saved in dead.letter.\r\n"); |
2e3062fe EA |
189 | state = ESM_DEADLETTER; |
190 | break; | |
b3cbe40f | 191 | |
2e3062fe EA |
192 | case ESM_MAIL: |
193 | case ESM_POSTMASTER: | |
194 | /* | |
195 | ** If mailing back, do it. | |
196 | ** Throw away all further output. Don't alias, | |
197 | ** since this could cause loops, e.g., if joe | |
198 | ** mails to joe@x, and for some reason the network | |
199 | ** for @x is down, then the response gets sent to | |
200 | ** joe@x, which gives a response, etc. Also force | |
201 | ** the mail to be delivered even if a version of | |
202 | ** it has already been sent to the sender. | |
203 | */ | |
b3cbe40f | 204 | |
2e3062fe EA |
205 | if (state == ESM_MAIL) |
206 | { | |
207 | if (e->e_errorqueue == NULL) | |
208 | sendtolist(e->e_from.q_paddr, | |
209 | (ADDRESS *) NULL, | |
210 | &e->e_errorqueue); | |
a0225d08 EA |
211 | |
212 | /* deliver a cc: to the postmaster if desired */ | |
213 | if (PostMasterCopy != NULL) | |
214 | sendtolist(PostMasterCopy, | |
215 | (ADDRESS *) NULL, | |
216 | &e->e_errorqueue); | |
2e3062fe EA |
217 | q = e->e_errorqueue; |
218 | } | |
219 | else | |
220 | { | |
8ff78b51 | 221 | if (parseaddr("postmaster", q, 0, '\0') == NULL) |
2e3062fe EA |
222 | { |
223 | syserr("cannot parse postmaster!"); | |
224 | ExitStat = EX_SOFTWARE; | |
225 | state = ESM_USRTMP; | |
226 | break; | |
227 | } | |
228 | } | |
229 | if (returntosender(e->e_message != NULL ? e->e_message : | |
230 | "Unable to deliver mail", | |
231 | q, TRUE) == 0) | |
232 | { | |
233 | state = ESM_DONE; | |
234 | break; | |
235 | } | |
b3cbe40f | 236 | |
2e3062fe EA |
237 | state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP; |
238 | break; | |
b3cbe40f | 239 | |
2e3062fe EA |
240 | case ESM_DEADLETTER: |
241 | /* | |
242 | ** Save the message in dead.letter. | |
243 | ** If we weren't mailing back, and the user is | |
244 | ** local, we should save the message in | |
245 | ** ~/dead.letter so that the poor person doesn't | |
246 | ** have to type it over again -- and we all know | |
247 | ** what poor typists UNIX users are. | |
248 | */ | |
b3cbe40f | 249 | |
2e3062fe EA |
250 | p = NULL; |
251 | if (e->e_from.q_mailer == LocalMailer) | |
252 | { | |
253 | if (e->e_from.q_home != NULL) | |
254 | p = e->e_from.q_home; | |
255 | else if ((pw = getpwnam(e->e_from.q_user)) != NULL) | |
256 | p = pw->pw_dir; | |
257 | } | |
258 | if (p == NULL) | |
259 | { | |
260 | syserr("Can't return mail to %s", e->e_from.q_paddr); | |
261 | state = ESM_MAIL; | |
262 | break; | |
263 | } | |
264 | if (e->e_dfp != NULL) | |
265 | { | |
266 | auto ADDRESS *q; | |
267 | bool oldverb = Verbose; | |
268 | ||
269 | /* we have a home directory; open dead.letter */ | |
270 | define('z', p, e); | |
271 | expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e); | |
272 | Verbose = TRUE; | |
273 | message(Arpa_Info, "Saving message in %s", buf); | |
274 | Verbose = oldverb; | |
275 | e->e_to = buf; | |
276 | q = NULL; | |
277 | sendtolist(buf, (ADDRESS *) NULL, &q); | |
278 | if (deliver(e, q) == 0) | |
279 | state = ESM_DONE; | |
280 | else | |
281 | state = ESM_MAIL; | |
282 | } | |
577bce94 EA |
283 | else |
284 | { | |
285 | /* no data file -- try mailing back */ | |
286 | state = ESM_MAIL; | |
287 | } | |
2e3062fe EA |
288 | break; |
289 | ||
290 | case ESM_USRTMP: | |
291 | /* | |
292 | ** Log the mail in /usr/tmp/dead.letter. | |
293 | */ | |
294 | ||
295 | fp = dfopen("/usr/tmp/dead.letter", "a"); | |
296 | if (fp == NULL) | |
297 | { | |
298 | state = ESM_PANIC; | |
299 | break; | |
300 | } | |
301 | ||
302 | putfromline(fp, ProgMailer); | |
303 | (*e->e_puthdr)(fp, ProgMailer, e); | |
304 | putline("\n", fp, ProgMailer); | |
305 | (*e->e_putbody)(fp, ProgMailer, e); | |
306 | putline("\n", fp, ProgMailer); | |
307 | (void) fflush(fp); | |
308 | state = ferror(fp) ? ESM_PANIC : ESM_DONE; | |
309 | (void) fclose(fp); | |
310 | break; | |
311 | ||
312 | default: | |
313 | syserr("savemail: unknown state %d", state); | |
314 | ||
315 | /* fall through ... */ | |
316 | ||
317 | case ESM_PANIC: | |
318 | syserr("savemail: HELP!!!!"); | |
319 | # ifdef LOG | |
320 | if (LogLevel >= 1) | |
321 | syslog(LOG_ALERT, "savemail: HELP!!!!"); | |
322 | # endif LOG | |
323 | ||
324 | /* leave the locked queue & transcript files around */ | |
325 | exit(EX_SOFTWARE); | |
326 | } | |
327 | } | |
b3cbe40f EA |
328 | } |
329 | \f/* | |
aba51985 EA |
330 | ** RETURNTOSENDER -- return a message to the sender with an error. |
331 | ** | |
332 | ** Parameters: | |
333 | ** msg -- the explanatory message. | |
7a079701 | 334 | ** returnq -- the queue of people to send the message to. |
79bd7c07 EA |
335 | ** sendbody -- if TRUE, also send back the body of the |
336 | ** message; otherwise just send the header. | |
aba51985 EA |
337 | ** |
338 | ** Returns: | |
339 | ** zero -- if everything went ok. | |
340 | ** else -- some error. | |
341 | ** | |
342 | ** Side Effects: | |
343 | ** Returns the current message to the sender via | |
344 | ** mail. | |
345 | */ | |
346 | ||
79bd7c07 | 347 | static bool SendBody; |
aba51985 | 348 | |
3c7fe765 EA |
349 | #define MAXRETURNS 6 /* max depth of returning messages */ |
350 | ||
7a079701 | 351 | returntosender(msg, returnq, sendbody) |
aba51985 | 352 | char *msg; |
7a079701 | 353 | ADDRESS *returnq; |
79bd7c07 | 354 | bool sendbody; |
aba51985 | 355 | { |
aba51985 | 356 | char buf[MAXNAME]; |
dd1fe05b EA |
357 | extern putheader(), errbody(); |
358 | register ENVELOPE *ee; | |
359 | extern ENVELOPE *newenvelope(); | |
360 | ENVELOPE errenvelope; | |
3c7fe765 | 361 | static int returndepth; |
7338e3d4 | 362 | register ADDRESS *q; |
3c7fe765 | 363 | |
9678c96d | 364 | if (tTd(6, 1)) |
e4d966b5 EA |
365 | { |
366 | printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n", | |
367 | msg, returndepth, CurEnv); | |
2e3062fe | 368 | printf("\treturnq="); |
7a079701 | 369 | printaddr(returnq, TRUE); |
e4d966b5 | 370 | } |
e4d966b5 | 371 | |
3c7fe765 EA |
372 | if (++returndepth >= MAXRETURNS) |
373 | { | |
374 | if (returndepth != MAXRETURNS) | |
7a079701 | 375 | syserr("returntosender: infinite recursion on %s", returnq->q_paddr); |
3c7fe765 EA |
376 | /* don't "unrecurse" and fake a clean exit */ |
377 | /* returndepth--; */ | |
378 | return (0); | |
379 | } | |
aba51985 | 380 | |
79bd7c07 | 381 | SendBody = sendbody; |
a73ae8ac | 382 | define('g', "\001f", CurEnv); |
dd1fe05b | 383 | ee = newenvelope(&errenvelope); |
2e3062fe | 384 | define('a', "\001b", ee); |
dd1fe05b EA |
385 | ee->e_puthdr = putheader; |
386 | ee->e_putbody = errbody; | |
7338e3d4 | 387 | ee->e_flags |= EF_RESPONSE; |
7a079701 | 388 | ee->e_sendqueue = returnq; |
912acb74 | 389 | openxscript(ee); |
7a079701 | 390 | for (q = returnq; q != NULL; q = q->q_next) |
7338e3d4 EA |
391 | { |
392 | if (q->q_alias == NULL) | |
393 | addheader("to", q->q_paddr, ee); | |
394 | } | |
2e3062fe | 395 | |
e7d41cfe | 396 | (void) sprintf(buf, "Returned mail: %s", msg); |
baa0c390 | 397 | addheader("subject", buf, ee); |
aba51985 EA |
398 | |
399 | /* fake up an address header for the from person */ | |
a73ae8ac | 400 | expand("\001n", buf, &buf[sizeof buf - 1], CurEnv); |
d3f52e20 | 401 | if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL) |
aba51985 EA |
402 | { |
403 | syserr("Can't parse myself!"); | |
404 | ExitStat = EX_SOFTWARE; | |
3c7fe765 | 405 | returndepth--; |
aba51985 EA |
406 | return (-1); |
407 | } | |
0fe3917f | 408 | loweraddr(&ee->e_from); |
79bd7c07 | 409 | |
dd1fe05b EA |
410 | /* push state into submessage */ |
411 | CurEnv = ee; | |
a73ae8ac | 412 | define('f', "\001n", ee); |
7338e3d4 | 413 | define('x', "Mail Delivery Subsystem", ee); |
4e8842eb | 414 | eatheader(ee); |
dd1fe05b EA |
415 | |
416 | /* actually deliver the error message */ | |
f7e74083 | 417 | sendall(ee, SM_DEFAULT); |
dd1fe05b | 418 | |
dd1fe05b | 419 | /* restore state */ |
2cce0c26 | 420 | dropenvelope(ee); |
dd1fe05b | 421 | CurEnv = CurEnv->e_parent; |
3c7fe765 | 422 | returndepth--; |
79bd7c07 | 423 | |
3c7fe765 | 424 | /* should check for delivery errors here */ |
aba51985 EA |
425 | return (0); |
426 | } | |
427 | \f/* | |
dd1fe05b | 428 | ** ERRBODY -- output the body of an error message. |
b3cbe40f | 429 | ** |
dd1fe05b EA |
430 | ** Typically this is a copy of the transcript plus a copy of the |
431 | ** original offending message. | |
b3cbe40f | 432 | ** |
b3cbe40f | 433 | ** Parameters: |
b3cbe40f | 434 | ** fp -- the output file. |
4db45bf1 | 435 | ** m -- the mailer to output to. |
912acb74 | 436 | ** e -- the envelope we are working in. |
b3cbe40f EA |
437 | ** |
438 | ** Returns: | |
439 | ** none | |
440 | ** | |
441 | ** Side Effects: | |
dd1fe05b | 442 | ** Outputs the body of an error message. |
b3cbe40f EA |
443 | */ |
444 | ||
4db45bf1 | 445 | errbody(fp, m, e) |
b3cbe40f | 446 | register FILE *fp; |
74c5fe7c | 447 | register struct mailer *m; |
912acb74 | 448 | register ENVELOPE *e; |
b3cbe40f | 449 | { |
9e3c0a28 | 450 | register FILE *xfile; |
dd1fe05b | 451 | char buf[MAXLINE]; |
e6f08ab1 | 452 | char *p; |
b3cbe40f | 453 | |
74c5fe7c EA |
454 | /* |
455 | ** Output transcript of errors | |
456 | */ | |
457 | ||
be2fcca9 | 458 | (void) fflush(stdout); |
912acb74 | 459 | p = queuename(e->e_parent, 'x'); |
e6f08ab1 | 460 | if ((xfile = fopen(p, "r")) == NULL) |
be2fcca9 | 461 | { |
e6f08ab1 | 462 | syserr("Cannot open %s", p); |
be2fcca9 EA |
463 | fprintf(fp, " ----- Transcript of session is unavailable -----\n"); |
464 | } | |
465 | else | |
466 | { | |
467 | fprintf(fp, " ----- Transcript of session follows -----\n"); | |
912acb74 EA |
468 | if (e->e_xfp != NULL) |
469 | (void) fflush(e->e_xfp); | |
be2fcca9 | 470 | while (fgets(buf, sizeof buf, xfile) != NULL) |
4db45bf1 | 471 | putline(buf, fp, m); |
be2fcca9 EA |
472 | (void) fclose(xfile); |
473 | } | |
474 | errno = 0; | |
74c5fe7c EA |
475 | |
476 | /* | |
477 | ** Output text of original message | |
478 | */ | |
479 | ||
46b14b48 EA |
480 | if (NoReturn) |
481 | fprintf(fp, "\n ----- Return message suppressed -----\n\n"); | |
912acb74 | 482 | else if (e->e_parent->e_dfp != NULL) |
738043e8 | 483 | { |
79bd7c07 EA |
484 | if (SendBody) |
485 | { | |
4db45bf1 EA |
486 | putline("\n", fp, m); |
487 | putline(" ----- Unsent message follows -----\n", fp, m); | |
79bd7c07 | 488 | (void) fflush(fp); |
4db45bf1 EA |
489 | putheader(fp, m, e->e_parent); |
490 | putline("\n", fp, m); | |
491 | putbody(fp, m, e->e_parent); | |
79bd7c07 EA |
492 | } |
493 | else | |
494 | { | |
4db45bf1 EA |
495 | putline("\n", fp, m); |
496 | putline(" ----- Message header follows -----\n", fp, m); | |
79bd7c07 | 497 | (void) fflush(fp); |
4db45bf1 | 498 | putheader(fp, m, e->e_parent); |
79bd7c07 | 499 | } |
738043e8 EA |
500 | } |
501 | else | |
4db45bf1 EA |
502 | { |
503 | putline("\n", fp, m); | |
504 | putline(" ----- No message was collected -----\n", fp, m); | |
505 | putline("\n", fp, m); | |
506 | } | |
74c5fe7c EA |
507 | |
508 | /* | |
509 | ** Cleanup and exit | |
510 | */ | |
511 | ||
b3cbe40f | 512 | if (errno != 0) |
dd1fe05b | 513 | syserr("errbody: I/O error"); |
b3cbe40f | 514 | } |