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