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