new copyright notice
[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
417f7a11 10static 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
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;
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 347static bool SendBody;
aba51985 348
3c7fe765
EA
349#define MAXRETURNS 6 /* max depth of returning messages */
350
7a079701 351returntosender(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 445errbody(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}