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