more cleanup for DSN drafts
[unix-history] / usr / src / usr.sbin / sendmail / src / savemail.c
CommitLineData
b2a81223 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
24634489
KB
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
bee79b64 5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64 7 */
b2a81223
DF
8
9#ifndef lint
66d16835 10static char sccsid[] = "@(#)savemail.c 8.60 (Berkeley) %G%";
bee79b64 11#endif /* not lint */
b2a81223 12
96faada8 13# include "sendmail.h"
e6cb9fc4 14# include <pwd.h>
b3cbe40f
EA
15
16/*
17** SAVEMAIL -- Save mail on error
18**
7338e3d4 19** If mailing back errors, mail it back to the originator
b3cbe40f
EA
20** together with an error message; otherwise, just put it in
21** dead.letter in the user's home directory (if he exists on
22** this machine).
23**
24** Parameters:
e6f08ab1 25** e -- the envelope containing the message in error.
c8df0ab6
EA
26** sendbody -- if TRUE, also send back the body of the
27** message; otherwise just send the header.
b3cbe40f
EA
28**
29** Returns:
30** none
31**
32** Side Effects:
33** Saves the letter, by writing or mailing it back to the
34** sender, or by putting it in dead.letter in her home
35** directory.
b3cbe40f
EA
36*/
37
2e3062fe
EA
38/* defines for state machine */
39# define ESM_REPORT 0 /* report to sender's terminal */
40# define ESM_MAIL 1 /* mail back to sender */
41# define ESM_QUIET 2 /* messages have already been returned */
42# define ESM_DEADLETTER 3 /* save in ~/dead.letter */
43# define ESM_POSTMASTER 4 /* return to postmaster */
44# define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */
45# define ESM_PANIC 6 /* leave the locked queue/transcript files */
46# define ESM_DONE 7 /* the message is successfully delivered */
47
b1b2b4a2
EA
48# ifndef _PATH_VARTMP
49# define _PATH_VARTMP "/usr/tmp/"
50# endif
51
2e3062fe 52
c8df0ab6 53savemail(e, sendbody)
e6f08ab1 54 register ENVELOPE *e;
c8df0ab6 55 bool sendbody;
b3cbe40f
EA
56{
57 register struct passwd *pw;
2e3062fe
EA
58 register FILE *fp;
59 int state;
83a871a1 60 auto ADDRESS *q = NULL;
23fafb99
EA
61 register char *p;
62 MCI mcibuf;
b3cbe40f 63 char buf[MAXLINE+1];
b3cbe40f 64 extern struct passwd *getpwnam();
b3cbe40f 65 extern char *ttypath();
40e27d12 66 typedef int (*fnptr)();
06b12081 67 extern bool writable();
b3cbe40f 68
9678c96d 69 if (tTd(6, 1))
bc854e30 70 {
8a1f82bc
EA
71 printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=",
72 e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
73 ExitStat);
bc854e30
EA
74 printaddr(&e->e_from, FALSE);
75 }
ae945718 76
c87034b7
EA
77 if (e->e_id == NULL)
78 {
79 /* can't return a message with no id */
80 return;
81 }
82
b3cbe40f
EA
83 /*
84 ** In the unhappy event we don't know who to return the mail
85 ** to, make someone up.
86 */
87
1bcdf0a2 88 if (CurEnv->e_returnto == NULL)
b3cbe40f 89 {
1bcdf0a2
EA
90 CurEnv->e_returnto = parse("root", (ADDRESS *) NULL, 0);
91 if (CurEnv->e_returnto == NULL)
b3cbe40f 92 {
e0b81669 93 syserr("553 Cannot parse Postmaster!");
b3cbe40f
EA
94 ExitStat = EX_SOFTWARE;
95 finis();
96 }
97 }
e6f08ab1 98 e->e_to = NULL;
b3cbe40f
EA
99
100 /*
2e3062fe
EA
101 ** Basic state machine.
102 **
103 ** This machine runs through the following states:
104 **
105 ** ESM_QUIET Errors have already been printed iff the
106 ** sender is local.
107 ** ESM_REPORT Report directly to the sender's terminal.
108 ** ESM_MAIL Mail response to the sender.
109 ** ESM_DEADLETTER Save response in ~/dead.letter.
110 ** ESM_POSTMASTER Mail response to the postmaster.
111 ** ESM_PANIC Save response anywhere possible.
b3cbe40f
EA
112 */
113
2e3062fe 114 /* determine starting state */
8c8e8e94 115 switch (e->e_errormode)
b3cbe40f 116 {
2e3062fe
EA
117 case EM_WRITE:
118 state = ESM_REPORT;
119 break;
120
121 case EM_BERKNET:
122 /* mail back, but return o.k. exit status */
3b661a43 123 ExitStat = EX_OK;
b3cbe40f 124
2e3062fe
EA
125 /* fall through.... */
126
127 case EM_MAIL:
128 state = ESM_MAIL;
129 break;
130
131 case EM_PRINT:
f4b05990 132 case '\0':
2e3062fe
EA
133 state = ESM_QUIET;
134 break;
b3cbe40f 135
2e3062fe
EA
136 case EM_QUIET:
137 /* no need to return anything at all */
138 return;
f4b05990
EA
139
140 default:
8c8e8e94 141 syserr("554 savemail: bogus errormode x%x\n", e->e_errormode);
f4b05990
EA
142 state = ESM_MAIL;
143 break;
2e3062fe
EA
144 }
145
d6776f09
EA
146 /* if this is already an error response, send to postmaster */
147 if (bitset(EF_RESPONSE, e->e_flags))
148 {
149 if (e->e_parent != NULL &&
150 bitset(EF_RESPONSE, e->e_parent->e_flags))
151 {
152 /* got an error sending a response -- can it */
153 return;
154 }
155 state = ESM_POSTMASTER;
156 }
157
2e3062fe 158 while (state != ESM_DONE)
b3cbe40f 159 {
f4b05990
EA
160 if (tTd(6, 5))
161 printf(" state %d\n", state);
f4b05990 162
2e3062fe 163 switch (state)
b3cbe40f 164 {
f4b05990 165 case ESM_QUIET:
2bade550 166 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
f4b05990
EA
167 state = ESM_DEADLETTER;
168 else
169 state = ESM_MAIL;
170 break;
171
2e3062fe
EA
172 case ESM_REPORT:
173
174 /*
175 ** If the user is still logged in on the same terminal,
176 ** then write the error messages back to hir (sic).
177 */
178
179 p = ttypath();
180 if (p == NULL || freopen(p, "w", stdout) == NULL)
181 {
182 state = ESM_MAIL;
183 break;
184 }
185
832e8a27 186 expand("\201n", buf, sizeof buf, e);
7338e3d4
EA
187 printf("\r\nMessage from %s...\r\n", buf);
188 printf("Errors occurred while sending mail.\r\n");
912acb74 189 if (e->e_xfp != NULL)
7338e3d4 190 {
912acb74 191 (void) fflush(e->e_xfp);
2e3062fe 192 fp = fopen(queuename(e, 'x'), "r");
7338e3d4
EA
193 }
194 else
2e3062fe
EA
195 fp = NULL;
196 if (fp == NULL)
7338e3d4 197 {
e6f08ab1 198 syserr("Cannot open %s", queuename(e, 'x'));
7338e3d4
EA
199 printf("Transcript of session is unavailable.\r\n");
200 }
201 else
202 {
203 printf("Transcript follows:\r\n");
2e3062fe 204 while (fgets(buf, sizeof buf, fp) != NULL &&
7338e3d4
EA
205 !ferror(stdout))
206 fputs(buf, stdout);
bc854e30 207 (void) xfclose(fp, "savemail transcript", e->e_id);
7338e3d4 208 }
2e3062fe 209 printf("Original message will be saved in dead.letter.\r\n");
2e3062fe
EA
210 state = ESM_DEADLETTER;
211 break;
b3cbe40f 212
2e3062fe 213 case ESM_MAIL:
2e3062fe
EA
214 /*
215 ** If mailing back, do it.
216 ** Throw away all further output. Don't alias,
217 ** since this could cause loops, e.g., if joe
218 ** mails to joe@x, and for some reason the network
219 ** for @x is down, then the response gets sent to
220 ** joe@x, which gives a response, etc. Also force
221 ** the mail to be delivered even if a version of
222 ** it has already been sent to the sender.
d385f2ac
EA
223 **
224 ** If this is a configuration or local software
225 ** error, send to the local postmaster as well,
226 ** since the originator can't do anything
227 ** about it anyway. Note that this is a full
228 ** copy of the message (intentionally) so that
229 ** the Postmaster can forward things along.
2e3062fe 230 */
b3cbe40f 231
d385f2ac
EA
232 if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
233 {
234 (void) sendtolist("postmaster",
f8c2f9fd 235 NULLADDR, &e->e_errorqueue, 0, e);
d385f2ac 236 }
f43d47fc 237 if (!emptyaddr(&e->e_from))
d385f2ac 238 {
bc854e30 239 (void) sendtolist(e->e_from.q_paddr,
f8c2f9fd 240 NULLADDR, &e->e_errorqueue, 0, e);
d385f2ac
EA
241 }
242
243 /*
244 ** Deliver a non-delivery report to the
245 ** Postmaster-designate (not necessarily
246 ** Postmaster). This does not include the
247 ** body of the message, for privacy reasons.
248 ** You really shouldn't need this.
249 */
bc854e30 250
37288e8e 251 e->e_flags |= EF_PM_NOTIFY;
d385f2ac 252
feed5c80
EA
253 /* check to see if there are any good addresses */
254 for (q = e->e_errorqueue; q != NULL; q = q->q_next)
255 if (!bitset(QBADADDR|QDONTSEND, q->q_flags))
256 break;
bc854e30
EA
257 if (q == NULL)
258 {
259 /* this is an error-error */
260 state = ESM_POSTMASTER;
261 break;
262 }
e7f42bdd 263 if (returntosender(e->e_message, e->e_errorqueue,
c8df0ab6 264 sendbody, e) == 0)
bc854e30
EA
265 {
266 state = ESM_DONE;
267 break;
268 }
a0225d08 269
bc854e30
EA
270 /* didn't work -- return to postmaster */
271 state = ESM_POSTMASTER;
272 break;
ab0b3f7d 273
bc854e30
EA
274 case ESM_POSTMASTER:
275 /*
276 ** Similar to previous case, but to system postmaster.
277 */
278
f553db48 279 q = NULL;
f8c2f9fd 280 if (sendtolist("postmaster", NULL, &q, 0, e) <= 0)
2e3062fe 281 {
bc854e30
EA
282 syserr("553 cannot parse postmaster!");
283 ExitStat = EX_SOFTWARE;
284 state = ESM_USRTMP;
285 break;
2e3062fe 286 }
c8df0ab6 287 if (returntosender(e->e_message, q, sendbody, e) == 0)
2e3062fe
EA
288 {
289 state = ESM_DONE;
290 break;
291 }
b3cbe40f 292
bc854e30
EA
293 /* didn't work -- last resort */
294 state = ESM_USRTMP;
2e3062fe 295 break;
b3cbe40f 296
2e3062fe
EA
297 case ESM_DEADLETTER:
298 /*
299 ** Save the message in dead.letter.
300 ** If we weren't mailing back, and the user is
301 ** local, we should save the message in
302 ** ~/dead.letter so that the poor person doesn't
303 ** have to type it over again -- and we all know
304 ** what poor typists UNIX users are.
305 */
b3cbe40f 306
2e3062fe 307 p = NULL;
2bade550 308 if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
2e3062fe
EA
309 {
310 if (e->e_from.q_home != NULL)
311 p = e->e_from.q_home;
312 else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
313 p = pw->pw_dir;
314 }
315 if (p == NULL)
316 {
0f8f3490 317 /* no local directory */
2e3062fe
EA
318 state = ESM_MAIL;
319 break;
320 }
321 if (e->e_dfp != NULL)
322 {
2e3062fe
EA
323 bool oldverb = Verbose;
324
325 /* we have a home directory; open dead.letter */
326 define('z', p, e);
832e8a27 327 expand("\201z/dead.letter", buf, sizeof buf, e);
2e3062fe 328 Verbose = TRUE;
b6edea3d 329 message("Saving message in %s", buf);
2e3062fe
EA
330 Verbose = oldverb;
331 e->e_to = buf;
332 q = NULL;
f8c2f9fd 333 (void) sendtolist(buf, &e->e_from, &q, 0, e);
626f8601
EA
334 if (q != NULL &&
335 !bitset(QBADADDR, q->q_flags) &&
336 deliver(e, q) == 0)
2e3062fe
EA
337 state = ESM_DONE;
338 else
339 state = ESM_MAIL;
340 }
577bce94
EA
341 else
342 {
343 /* no data file -- try mailing back */
344 state = ESM_MAIL;
345 }
2e3062fe
EA
346 break;
347
348 case ESM_USRTMP:
349 /*
350 ** Log the mail in /usr/tmp/dead.letter.
351 */
352
abccefc9
EA
353 if (e->e_class < 0)
354 {
355 state = ESM_DONE;
356 break;
357 }
358
99fd9645
EA
359 if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
360 {
361 state = ESM_PANIC;
362 break;
363 }
364
b1b2b4a2
EA
365 strcpy(buf, _PATH_VARTMP);
366 strcat(buf, "dead.letter");
d52f8819 367 if (!writable(buf, NULLADDR, SFF_NOSLINK|SFF_CREAT))
06b12081
EA
368 {
369 state = ESM_PANIC;
370 break;
371 }
d52f8819 372 fp = safefopen(buf, O_WRONLY|O_CREAT|O_APPEND,
51cc57b9 373 FileMode, SFF_NOSLINK|SFF_REGONLY);
2e3062fe
EA
374 if (fp == NULL)
375 {
376 state = ESM_PANIC;
377 break;
378 }
379
23fafb99
EA
380 bzero(&mcibuf, sizeof mcibuf);
381 mcibuf.mci_out = fp;
382 mcibuf.mci_mailer = FileMailer;
383 if (bitnset(M_7BITS, FileMailer->m_flags))
384 mcibuf.mci_flags |= MCIF_7BIT;
385
386 putfromline(&mcibuf, e);
51d448e5
EA
387 (*e->e_puthdr)(&mcibuf, e->e_header, e);
388 (*e->e_putbody)(&mcibuf, e, NULL);
23fafb99 389 putline("\n", &mcibuf);
2e3062fe
EA
390 (void) fflush(fp);
391 state = ferror(fp) ? ESM_PANIC : ESM_DONE;
3a2b5eec 392 (void) xfclose(fp, "savemail", buf);
2e3062fe
EA
393 break;
394
395 default:
b6edea3d 396 syserr("554 savemail: unknown state %d", state);
2e3062fe
EA
397
398 /* fall through ... */
399
400 case ESM_PANIC:
2e3062fe 401 /* leave the locked queue & transcript files around */
99fd9645 402 loseqfile(e, "savemail panic");
48fb557e 403 syserr("!554 savemail: cannot save rejected email anywhere");
2e3062fe
EA
404 }
405 }
b3cbe40f
EA
406}
407\f/*
aba51985
EA
408** RETURNTOSENDER -- return a message to the sender with an error.
409**
410** Parameters:
411** msg -- the explanatory message.
7a079701 412** returnq -- the queue of people to send the message to.
79bd7c07
EA
413** sendbody -- if TRUE, also send back the body of the
414** message; otherwise just send the header.
a4076aed 415** e -- the current envelope.
aba51985
EA
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
3c7fe765 426#define MAXRETURNS 6 /* max depth of returning messages */
0f104940 427#define ERRORFUDGE 100 /* nominal size of error message text */
3c7fe765 428
a4076aed 429returntosender(msg, returnq, sendbody, e)
aba51985 430 char *msg;
7a079701 431 ADDRESS *returnq;
79bd7c07 432 bool sendbody;
a4076aed 433 register ENVELOPE *e;
aba51985 434{
8446c922 435 char buf[MAXNAME + 1];
dd1fe05b
EA
436 extern putheader(), errbody();
437 register ENVELOPE *ee;
bc854e30 438 ENVELOPE *oldcur = CurEnv;
dd1fe05b 439 ENVELOPE errenvelope;
3c7fe765 440 static int returndepth;
7338e3d4 441 register ADDRESS *q;
51d448e5 442 char *p;
3c7fe765 443
cbfdff7c
EA
444 if (returnq == NULL)
445 return (-1);
446
35b698c6
EA
447 if (msg == NULL)
448 msg = "Unable to deliver mail";
449
9678c96d 450 if (tTd(6, 1))
e4d966b5 451 {
3b6c2253 452 printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=",
a4076aed 453 msg, returndepth, e);
7a079701 454 printaddr(returnq, TRUE);
3b6c2253
EA
455 if (tTd(6, 20))
456 {
457 printf("Sendq=");
458 printaddr(e->e_sendqueue, TRUE);
459 }
e4d966b5 460 }
e4d966b5 461
3c7fe765
EA
462 if (++returndepth >= MAXRETURNS)
463 {
464 if (returndepth != MAXRETURNS)
b6edea3d 465 syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr);
3c7fe765
EA
466 /* don't "unrecurse" and fake a clean exit */
467 /* returndepth--; */
468 return (0);
469 }
aba51985 470
bc854e30 471 define('g', e->e_from.q_paddr, e);
659d287a 472 define('u', NULL, e);
68d9129a
EA
473
474 /* initialize error envelope */
fda58daa 475 ee = newenvelope(&errenvelope, e);
2bee003d 476 define('a', "\201b", ee);
0257ba6d
EA
477 define('r', "internal", ee);
478 define('s', "localhost", ee);
479 define('_', "localhost", ee);
dd1fe05b
EA
480 ee->e_puthdr = putheader;
481 ee->e_putbody = errbody;
61e75b16 482 ee->e_flags |= EF_RESPONSE|EF_METOO;
a4076aed 483 if (!bitset(EF_OLDSTYLE, e->e_flags))
839ae7fa 484 ee->e_flags &= ~EF_OLDSTYLE;
7a079701 485 ee->e_sendqueue = returnq;
54e571e9 486 ee->e_msgsize = ERRORFUDGE;
c7a0eaaf 487 if (sendbody)
54e571e9 488 ee->e_msgsize += e->e_msgsize;
5af8d6d0 489 initsys(ee);
7a079701 490 for (q = returnq; q != NULL; q = q->q_next)
7338e3d4 491 {
68491765 492 if (bitset(QBADADDR, q->q_flags))
0f104940
EA
493 continue;
494
63d473ff 495 if (!DontPruneRoutes && pruneroute(q->q_paddr))
d952f310
EA
496 {
497 register ADDRESS *p;
498
28f94061 499 parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e);
d952f310
EA
500 for (p = returnq; p != NULL; p = p->q_next)
501 {
502 if (p != q && sameaddr(p, q))
503 q->q_flags |= QDONTSEND;
504 }
505 }
506
507 if (!bitset(QDONTSEND, q->q_flags))
508 ee->e_nrcpts++;
63d473ff 509
7338e3d4 510 if (q->q_alias == NULL)
c23930c0 511 addheader("To", q->q_paddr, &ee->e_header);
7338e3d4 512 }
2e3062fe 513
6e99f903 514# ifdef LOG
68f7099c 515 if (LogLevel > 5)
25414557 516 syslog(LOG_INFO, "%s: %s: returntosender: %s",
6e99f903
EA
517 e->e_id, ee->e_id, msg);
518# endif
519
25414557
EA
520 if (strncasecmp(msg, "warning:", 8) != 0)
521 {
522 (void) sprintf(buf, "Returned mail: %s", msg);
523 msg = buf;
524 }
525 addheader("Subject", msg, ee);
aba51985
EA
526
527 /* fake up an address header for the from person */
832e8a27 528 expand("\201n", buf, sizeof buf, e);
28f94061 529 if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL)
aba51985 530 {
b6edea3d 531 syserr("553 Can't parse myself!");
aba51985 532 ExitStat = EX_SOFTWARE;
3c7fe765 533 returndepth--;
aba51985
EA
534 return (-1);
535 }
4a2da288 536 ee->e_sender = ee->e_from.q_paddr;
79bd7c07 537
dd1fe05b
EA
538 /* push state into submessage */
539 CurEnv = ee;
2bee003d 540 define('f', "\201n", ee);
7338e3d4 541 define('x', "Mail Delivery Subsystem", ee);
959cf51d 542 eatheader(ee, TRUE);
dd1fe05b 543
8e5c6745 544 /* mark statistics */
28f94061 545 markstats(ee, NULLADDR);
8e5c6745 546
dd1fe05b 547 /* actually deliver the error message */
f7e74083 548 sendall(ee, SM_DEFAULT);
dd1fe05b 549
dd1fe05b 550 /* restore state */
2cce0c26 551 dropenvelope(ee);
bc854e30 552 CurEnv = oldcur;
3c7fe765 553 returndepth--;
79bd7c07 554
3c7fe765 555 /* should check for delivery errors here */
aba51985
EA
556 return (0);
557}
558\f/*
dd1fe05b 559** ERRBODY -- output the body of an error message.
b3cbe40f 560**
dd1fe05b
EA
561** Typically this is a copy of the transcript plus a copy of the
562** original offending message.
b3cbe40f 563**
b3cbe40f 564** Parameters:
23fafb99 565** mci -- the mailer connection information.
912acb74 566** e -- the envelope we are working in.
c23930c0 567** separator -- any possible MIME separator.
a579cea5 568** flags -- to modify the behaviour.
b3cbe40f
EA
569**
570** Returns:
571** none
572**
573** Side Effects:
dd1fe05b 574** Outputs the body of an error message.
b3cbe40f
EA
575*/
576
51d448e5 577errbody(mci, e, separator)
23fafb99 578 register MCI *mci;
912acb74 579 register ENVELOPE *e;
c23930c0 580 char *separator;
b3cbe40f 581{
9e3c0a28 582 register FILE *xfile;
e6f08ab1 583 char *p;
7800dbe0
EA
584 register ADDRESS *q;
585 bool printheader;
c7a0eaaf 586 bool sendbody;
7800dbe0 587 char buf[MAXLINE];
51d448e5 588 extern char *xtextify();
b3cbe40f 589
c23930c0
EA
590 if (bitset(MCIF_INHEADER, mci->mci_flags))
591 {
592 putline("", mci);
593 mci->mci_flags &= ~MCIF_INHEADER;
594 }
bc854e30
EA
595 if (e->e_parent == NULL)
596 {
597 syserr("errbody: null parent");
23fafb99 598 putline(" ----- Original message lost -----\n", mci);
bc854e30
EA
599 return;
600 }
601
2f6a8a78
EA
602 /*
603 ** Output MIME header.
604 */
605
606 if (e->e_msgboundary != NULL)
607 {
23fafb99
EA
608 putline("This is a MIME-encapsulated message", mci);
609 putline("", mci);
2f6a8a78 610 (void) sprintf(buf, "--%s", e->e_msgboundary);
23fafb99
EA
611 putline(buf, mci);
612 putline("", mci);
2f6a8a78
EA
613 }
614
329d6249
EA
615 /*
616 ** Output introductory information.
617 */
618
fe3849ea
EA
619 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
620 if (bitset(QBADADDR, q->q_flags))
621 break;
763a56e2
EA
622 if (q == NULL &&
623 !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
fe3849ea
EA
624 {
625 putline(" **********************************************",
23fafb99 626 mci);
fe3849ea 627 putline(" ** THIS IS A WARNING MESSAGE ONLY **",
23fafb99 628 mci);
fe3849ea 629 putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **",
23fafb99 630 mci);
fe3849ea 631 putline(" **********************************************",
23fafb99
EA
632 mci);
633 putline("", mci);
fe3849ea
EA
634 }
635 sprintf(buf, "The original message was received at %s",
636 arpadate(ctime(&e->e_parent->e_ctime)));
23fafb99 637 putline(buf, mci);
832e8a27 638 expand("from \201_", buf, sizeof buf, e->e_parent);
23fafb99
EA
639 putline(buf, mci);
640 putline("", mci);
329d6249 641
44967af8
EA
642 /*
643 ** Output error message header (if specified and available).
644 */
645
8a684747 646 if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
44967af8
EA
647 {
648 if (*ErrMsgFile == '/')
649 {
650 xfile = fopen(ErrMsgFile, "r");
651 if (xfile != NULL)
652 {
653 while (fgets(buf, sizeof buf, xfile) != NULL)
5d99721e 654 {
832e8a27 655 expand(buf, buf, sizeof buf, e);
23fafb99 656 putline(buf, mci);
5d99721e 657 }
44967af8 658 (void) fclose(xfile);
23fafb99 659 putline("\n", mci);
44967af8
EA
660 }
661 }
662 else
663 {
832e8a27 664 expand(ErrMsgFile, buf, sizeof buf, e);
23fafb99
EA
665 putline(buf, mci);
666 putline("", mci);
44967af8
EA
667 }
668 }
669
7800dbe0
EA
670 /*
671 ** Output message introduction
672 */
673
674 printheader = TRUE;
675 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
676 {
66d16835 677 if (bitset(QBADADDR|QREPORT|QRELAYED|QEXPLODED, q->q_flags))
7800dbe0 678 {
37288e8e 679 strcpy(buf, q->q_paddr);
fd57f063 680 if (bitset(QBADADDR, q->q_flags))
37288e8e 681 strcat(buf, " (unrecoverable error)");
66d16835
EA
682 else if (!bitset(QPRIMARY, q->q_flags))
683 continue;
c8df0ab6
EA
684 else if (bitset(QRELAYED, q->q_flags))
685 strcat(buf, " (relayed to non-DSN-aware mailer)");
68d9129a
EA
686 else if (bitset(QSENT, q->q_flags))
687 strcat(buf, " (successfully delivered)");
66d16835
EA
688 else if (bitset(QEXPLODED, q->q_flags))
689 strcat(buf, " (expanded by mailing list)");
9cdb0b30 690 else
37288e8e 691 strcat(buf, " (transient failure)");
66d16835
EA
692 if (printheader)
693 {
694 putline(" ----- The following addresses have delivery notifications -----",
695 mci);
696 printheader = FALSE;
697 }
23fafb99 698 putline(buf, mci);
37288e8e
EA
699 if (q->q_alias != NULL)
700 {
701 strcpy(buf, " (expanded from: ");
702 strcat(buf, q->q_alias->q_paddr);
703 strcat(buf, ")");
23fafb99 704 putline(buf, mci);
37288e8e 705 }
7800dbe0
EA
706 }
707 }
708 if (!printheader)
23fafb99 709 putline("\n", mci);
7800dbe0 710
74c5fe7c
EA
711 /*
712 ** Output transcript of errors
713 */
714
be2fcca9 715 (void) fflush(stdout);
912acb74 716 p = queuename(e->e_parent, 'x');
e6f08ab1 717 if ((xfile = fopen(p, "r")) == NULL)
be2fcca9 718 {
e6f08ab1 719 syserr("Cannot open %s", p);
23fafb99 720 putline(" ----- Transcript of session is unavailable -----\n", mci);
be2fcca9
EA
721 }
722 else
723 {
23fafb99 724 putline(" ----- Transcript of session follows -----\n", mci);
912acb74
EA
725 if (e->e_xfp != NULL)
726 (void) fflush(e->e_xfp);
be2fcca9 727 while (fgets(buf, sizeof buf, xfile) != NULL)
23fafb99 728 putline(buf, mci);
bc854e30 729 (void) xfclose(xfile, "errbody xscript", p);
be2fcca9
EA
730 }
731 errno = 0;
74c5fe7c 732
1722f27e 733#ifdef DSN
68d9129a
EA
734 /*
735 ** Output machine-readable version.
736 */
737
738 if (e->e_msgboundary != NULL)
739 {
740 putline("", mci);
741 (void) sprintf(buf, "--%s", e->e_msgboundary);
742 putline(buf, mci);
51d448e5 743 putline("Content-Type: message/X-delivery-status-2 (Draft of 20 January 1995)", mci);
68d9129a
EA
744 putline("", mci);
745
746 /*
747 ** Output per-message information.
748 */
749
68d9129a
EA
750 /* original envelope id from MAIL FROM: line */
751 if (e->e_parent->e_envid != NULL)
752 {
753 (void) sprintf(buf, "Original-Envelope-Id: %s",
51d448e5 754 xtextify(e->e_parent->e_envid));
68d9129a
EA
755 putline(buf, mci);
756 }
757
51d448e5
EA
758 /* Reporting-MTA: is us (required) */
759 p = e->e_parent->e_from.q_mailer->m_mtatype;
760 if (p == NULL)
761 p = "dns";
8b6906e3
EA
762 (void) sprintf(buf, "Reporting-MTA: %s; %s", p,
763 xtextify(MyHostName));
68d9129a
EA
764 putline(buf, mci);
765
51d448e5 766 /* Received-From-MTA: shows where we got this message from */
d51d925d
EA
767 if (RealHostName != NULL)
768 {
51d448e5
EA
769 /* XXX use $s for type? */
770 p = e->e_parent->e_from.q_mailer->m_mtatype;
771 if (p == NULL)
772 p = "dns";
773 (void) sprintf(buf, "Received-From-MTA: %s; %s",
8b6906e3 774 p, xtextify(RealHostName));
d51d925d
EA
775 putline(buf, mci);
776 }
82e3dc75
EA
777
778 /* Arrival-Date: -- when it arrived here */
779 (void) sprintf(buf, "Arrival-Date: %s",
780 arpadate(ctime(&e->e_parent->e_ctime)));
781 putline(buf, mci);
782
68d9129a
EA
783 /*
784 ** Output per-address information.
785 */
786
787 for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
788 {
789 register ADDRESS *r;
66d16835 790 char *action;
68d9129a 791
66d16835
EA
792 if (bitset(QBADADDR, q->q_flags))
793 action = "failure";
794 else if (!bitset(QPRIMARY, q->q_flags))
795 continue;
796 else if (bitset(QRELAYED, q->q_flags))
797 action = "relayed";
798 else if (bitset(QEXPLODED, q->q_flags))
799 action = "delivered (to mailing list)";
800 else if (bitset(QSENT, q->q_flags) &&
801 bitnset(M_LOCALMAILER, q->q_mailer->m_flags))
802 action = "delivered (final delivery)";
803 else if (bitset(QREPORT, q->q_flags))
804 action = "delayed";
805 else
68d9129a 806 continue;
66d16835 807
68d9129a
EA
808 putline("", mci);
809
51d448e5
EA
810 /* Original-Recipient: -- passed from on high */
811 if (q->q_orcpt != NULL)
812 {
813 (void) sprintf(buf, "Original-Recipient: %s",
814 xtextify(q->q_orcpt));
815 putline(buf, mci);
816 }
817
818 /* Final-Recipient: -- the name from the RCPT command */
819 p = e->e_parent->e_from.q_mailer->m_addrtype;
820 if (p == NULL)
821 p = "rfc822";
822 for (r = q; r->q_alias != NULL; r = r->q_alias)
823 continue;
824 if (strchr(r->q_user, '@') == NULL)
8b6906e3
EA
825 {
826 (void) sprintf(buf, "Final-Recipient: %s; %s@",
827 p, xtextify(r->q_user));
828 strcat(buf, xtextify(MyHostName));
829 }
a542cc51 830 else
8b6906e3 831 {
51d448e5
EA
832 (void) sprintf(buf, "Final-Recipient: %s; %s",
833 p, xtextify(r->q_user));
8b6906e3 834 }
68d9129a
EA
835 putline(buf, mci);
836
66d16835
EA
837 /* X-Actual-Recipient: -- the real problem address */
838 if (r != q)
839 {
840 if (strchr(q->q_user, '@') == NULL)
841 {
842 (void) sprintf(buf, "X-Actual-Recipient: %s; %s@",
843 p, xtextify(q->q_user));
844 strcat(buf, xtextify(MyHostName));
845 }
846 else
847 {
848 (void) sprintf(buf, "X-Actual-Recipient: %s; %s",
849 p, xtextify(q->q_user));
850 }
851 putline(buf, mci);
852 }
853
68d9129a 854 /* Action: -- what happened? */
66d16835
EA
855 sprintf(buf, "Action: %s", action);
856 putline(buf, mci);
68d9129a
EA
857
858 /* Status: -- what _really_ happened? */
859 strcpy(buf, "Status: ");
860 if (q->q_status != NULL)
861 strcat(buf, q->q_status);
862 else if (bitset(QBADADDR, q->q_flags))
51d448e5 863 strcat(buf, "5.0.0");
68d9129a 864 else if (bitset(QQUEUEUP, q->q_flags))
51d448e5 865 strcat(buf, "4.0.0");
68d9129a 866 else
51d448e5 867 strcat(buf, "2.0.0");
68d9129a
EA
868 putline(buf, mci);
869
51d448e5
EA
870 /* Remote-MTA: -- who was I talking to? */
871 p = q->q_mailer->m_mtatype;
872 if (p == NULL)
873 p = "dns";
874 (void) sprintf(buf, "Remote-MTA: %s; ", p);
875 if (q->q_statmta != NULL)
876 p = q->q_statmta;
66d16835 877 else if (q->q_host != NULL && q->q_host[0] != '\0')
51d448e5
EA
878 p = q->q_host;
879 else
880 p = NULL;
881 if (p != NULL)
882 {
883 strcat(buf, p);
884 p = &buf[strlen(buf) - 1];
885 if (*p == '.')
886 *p = '\0';
887 putline(buf, mci);
888 }
889
890 /* Diagnostic-Code: -- actual result from other end */
891 if (q->q_rstatus != NULL)
892 {
893 p = q->q_mailer->m_diagtype;
894 if (p == NULL)
895 p = "smtp";
896 (void) sprintf(buf, "Diagnostic-Code: %s; %s",
66d16835 897 p, q->q_rstatus);
51d448e5
EA
898 putline(buf, mci);
899 }
900
901 /* Last-Attempt-Date: -- fine granularity */
68d9129a
EA
902 if (q->q_statdate == (time_t) 0L)
903 q->q_statdate = curtime();
51d448e5 904 (void) sprintf(buf, "Last-Attempt-Date: %s",
68d9129a
EA
905 arpadate(ctime(&q->q_statdate)));
906 putline(buf, mci);
907
82e3dc75
EA
908 /* Expiry-Date: -- for delayed messages only */
909 if (bitset(QQUEUEUP, q->q_flags) &&
910 !bitset(QBADADDR, q->q_flags))
911 {
912 time_t xdate;
913
914 xdate = e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass];
915 sprintf(buf, "Expiry-Date: %s",
916 arpadate(ctime(&xdate)));
917 putline(buf, mci);
918 }
68d9129a
EA
919 }
920 }
1722f27e 921#endif
68d9129a 922
74c5fe7c
EA
923 /*
924 ** Output text of original message
925 */
926
23fafb99 927 putline("", mci);
18a81ac0 928 if (bitset(EF_HAS_DF, e->e_parent->e_flags))
738043e8 929 {
c7a0eaaf
EA
930 sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags);
931
68d9129a
EA
932 if (e->e_msgboundary == NULL)
933 {
c7a0eaaf 934 if (sendbody)
68d9129a
EA
935 putline(" ----- Original message follows -----\n", mci);
936 else
937 putline(" ----- Message header follows -----\n", mci);
938 (void) fflush(mci->mci_out);
939 }
79bd7c07 940 else
2f6a8a78 941 {
2f6a8a78 942 (void) sprintf(buf, "--%s", e->e_msgboundary);
23fafb99 943 putline(buf, mci);
51d448e5 944 (void) sprintf(buf, "Content-Type: message/rfc822%s",
c7a0eaaf 945 mci, sendbody ? "" : "-headers");
51d448e5 946 putline(buf, mci);
2f6a8a78 947 }
c8df0ab6 948 putline("", mci);
51d448e5 949 putheader(mci, e->e_parent->e_header, e->e_parent);
c7a0eaaf 950 if (sendbody)
51d448e5
EA
951 putbody(mci, e->e_parent, e->e_msgboundary);
952 else if (e->e_msgboundary == NULL)
c23930c0
EA
953 {
954 putline("", mci);
23fafb99 955 putline(" ----- Message body suppressed -----", mci);
c23930c0 956 }
738043e8 957 }
51d448e5 958 else if (e->e_msgboundary == NULL)
4db45bf1 959 {
23fafb99 960 putline(" ----- No message was collected -----\n", mci);
4db45bf1 961 }
74c5fe7c 962
cbfdff7c
EA
963 if (e->e_msgboundary != NULL)
964 {
23fafb99 965 putline("", mci);
cbfdff7c 966 (void) sprintf(buf, "--%s--", e->e_msgboundary);
23fafb99 967 putline(buf, mci);
cbfdff7c 968 }
23fafb99 969 putline("", mci);
2f6a8a78 970
74c5fe7c
EA
971 /*
972 ** Cleanup and exit
973 */
974
b3cbe40f 975 if (errno != 0)
dd1fe05b 976 syserr("errbody: I/O error");
b3cbe40f 977}
63d473ff 978\f/*
51d448e5
EA
979** SMTPTODSN -- convert SMTP to DSN status code
980**
981** Parameters:
982** smtpstat -- the smtp status code (e.g., 550).
983**
984** Returns:
985** The DSN version of the status code.
986*/
987
988char *
989smtptodsn(smtpstat)
990 int smtpstat;
991{
992 switch (smtpstat)
993 {
994 case 450: /* Req mail action not taken: mailbox unavailable */
995 return "4.2.0";
996
997 case 451: /* Req action aborted: local error in processing */
998 return "4.3.0";
999
1000 case 452: /* Req action not taken: insufficient sys storage */
1001 return "4.3.1";
1002
1003 case 500: /* Syntax error, command unrecognized */
1004 return "5.5.2";
1005
1006 case 501: /* Syntax error in parameters or arguments */
1007 return "5.5.4";
1008
1009 case 502: /* Command not implemented */
1010 return "5.5.1";
1011
1012 case 503: /* Bad sequence of commands */
1013 return "5.5.1";
1014
1015 case 504: /* Command parameter not implemented */
1016 return "5.5.4";
1017
1018 case 550: /* Req mail action not taken: mailbox unavailable */
1019 return "5.2.0";
1020
1021 case 551: /* User not local; please try <...> */
1022 return "5.1.6";
1023
1024 case 552: /* Req mail action aborted: exceeded storage alloc */
1025 return "5.2.2";
1026
1027 case 553: /* Req action not taken: mailbox name not allowed */
1028 return "5.1.3";
1029
1030 case 554: /* Transaction failed */
1031 return "5.0.0";
1032 }
1033
1034 if ((smtpstat / 100) == 2)
1035 return "2.0.0";
1036 if ((smtpstat / 100) == 4)
1037 return "4.0.0";
1038 return "5.0.0";
1039}
1040\f/*
1041** XTEXTIFY -- take regular text and turn it into DSN-style xtext
1042**
1043** Parameters:
1044** t -- the text to convert.
1045**
1046** Returns:
1047** The xtext-ified version of the same string.
1048*/
1049
1050char *
1051xtextify(t)
1052 register char *t;
1053{
1054 register char *p;
1055 int l;
1056 int nbogus;
1057 static char *bp = NULL;
1058 static int bplen = 0;
1059
1060 /* figure out how long this xtext will have to be */
1061 nbogus = l = 0;
1062 for (p = t; *p != '\0'; p++)
1063 {
1064 register int c = (*p & 0xff);
1065
1066 /* ASCII dependence here -- this is the way the spec words it */
66d16835
EA
1067 if ((c < ' ' || c > '~' || c == '+' || c == '\\' || c == '(') &&
1068 c != '\t')
51d448e5
EA
1069 nbogus++;
1070 l++;
1071 }
1072 if (nbogus == 0)
1073 return t;
1074 l += nbogus * 2 + 1;
1075
1076 /* now allocate space if necessary for the new string */
1077 if (l > bplen)
1078 {
1079 if (bp != NULL)
1080 free(bp);
1081 bp = xalloc(l);
1082 bplen = l;
1083 }
1084
1085 /* ok, copy the text with byte expansion */
1086 for (p = bp; *t != '\0'; )
1087 {
1088 register int c = (*t++ & 0xff);
1089
1090 /* ASCII dependence here -- this is the way the spec words it */
1091 if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(')
1092 {
1093 *p++ = '+';
1094 *p++ = "0123456789abcdef"[c >> 4];
1095 *p++ = "0123456789abcdef"[c & 0xf];
1096 }
1097 else
1098 *p++ = c;
1099 }
1100 *p = '\0';
1101 return bp;
1102}
1103\f/*
8b6906e3
EA
1104** XTEXTOK -- check if a string is legal xtext
1105**
1106** Xtext is used in Delivery Status Notifications. The spec was
1107** taken from draft-ietf-notary-mime-delivery-04.txt.
1108**
1109** Parameters:
1110** s -- the string to check.
1111**
1112** Returns:
1113** TRUE -- if 's' is legal xtext.
1114** FALSE -- if it has any illegal characters in it.
1115*/
1116
1117bool
1118xtextok(s)
1119 char *s;
1120{
1121 int c;
1122
1123 while ((c = *s++) != '\0')
1124 {
1125 if (c == '+')
1126 {
1127 c = *s++;
1128 if (!isascii(c) || !isxdigit(c))
1129 return FALSE;
1130 c = *s++;
1131 if (!isascii(c) || !isxdigit(c))
1132 return FALSE;
1133 }
1134 else if (c < '!' || c > '~' || c == '\\' || c == '(')
1135 return FALSE;
1136 }
1137 return TRUE;
1138}
1139\f/*
63d473ff
EA
1140** PRUNEROUTE -- prune an RFC-822 source route
1141**
1142** Trims down a source route to the last internet-registered hop.
1143** This is encouraged by RFC 1123 section 5.3.3.
1144**
1145** Parameters:
1146** addr -- the address
1147**
1148** Returns:
1149** TRUE -- address was modified
1150** FALSE -- address could not be pruned
1151**
1152** Side Effects:
1153** modifies addr in-place
1154*/
1155
1156pruneroute(addr)
1157 char *addr;
1158{
efe7f723 1159#if NAMED_BIND
63d473ff
EA
1160 char *start, *at, *comma;
1161 char c;
1162 int rcode;
1163 char hostbuf[BUFSIZ];
1164 char *mxhosts[MAXMXHOSTS + 1];
1165
1166 /* check to see if this is really a route-addr */
1167 if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
1168 return FALSE;
1169 start = strchr(addr, ':');
1170 at = strrchr(addr, '@');
1171 if (start == NULL || at == NULL || at < start)
1172 return FALSE;
1173
1174 /* slice off the angle brackets */
1175 strcpy(hostbuf, at + 1);
1176 hostbuf[strlen(hostbuf) - 1] = '\0';
1177
1178 while (start)
1179 {
c3577cd6 1180 if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0)
63d473ff
EA
1181 {
1182 strcpy(addr + 1, start + 1);
1183 return TRUE;
1184 }
1185 c = *start;
1186 *start = '\0';
1187 comma = strrchr(addr, ',');
1188 if (comma && comma[1] == '@')
1189 strcpy(hostbuf, comma + 2);
1190 else
1191 comma = 0;
1192 *start = c;
1193 start = comma;
1194 }
1195#endif
1196 return FALSE;
1197}