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.15 (Berkeley) 12/15/91"; 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') == 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); 210 211 /* deliver a cc: to the postmaster if desired */ 212 if (PostMasterCopy != NULL) 213 sendtolist(PostMasterCopy, 214 (ADDRESS *) NULL, 215 &e->e_errorqueue); 216 q = e->e_errorqueue; 217 } 218 else 219 { 220 if (parseaddr("postmaster", q, 0, '\0') == 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) == 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); 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); 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 ** 337 ** Returns: 338 ** zero -- if everything went ok. 339 ** else -- some error. 340 ** 341 ** Side Effects: 342 ** Returns the current message to the sender via 343 ** mail. 344 */ 345 346 static bool SendBody; 347 348 #define MAXRETURNS 6 /* max depth of returning messages */ 349 350 returntosender(msg, returnq, sendbody) 351 char *msg; 352 ADDRESS *returnq; 353 bool sendbody; 354 { 355 char buf[MAXNAME]; 356 extern putheader(), errbody(); 357 register ENVELOPE *ee; 358 extern ENVELOPE *newenvelope(); 359 ENVELOPE errenvelope; 360 static int returndepth; 361 register ADDRESS *q; 362 363 if (tTd(6, 1)) 364 { 365 printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n", 366 msg, returndepth, CurEnv); 367 printf("\treturnq="); 368 printaddr(returnq, TRUE); 369 } 370 371 if (++returndepth >= MAXRETURNS) 372 { 373 if (returndepth != MAXRETURNS) 374 syserr("returntosender: infinite recursion on %s", returnq->q_paddr); 375 /* don't "unrecurse" and fake a clean exit */ 376 /* returndepth--; */ 377 return (0); 378 } 379 380 SendBody = sendbody; 381 define('g', "\001f", CurEnv); 382 define('<', "\001f", CurEnv); 383 ee = newenvelope(&errenvelope); 384 define('a', "\001b", ee); 385 ee->e_puthdr = putheader; 386 ee->e_putbody = errbody; 387 ee->e_flags |= EF_RESPONSE; 388 if (!bitset(EF_OLDSTYLE, CurEnv->e_flags)) 389 ee->e_flags &= ~EF_OLDSTYLE; 390 ee->e_sendqueue = returnq; 391 openxscript(ee); 392 for (q = returnq; q != NULL; q = q->q_next) 393 { 394 if (q->q_alias == NULL) 395 addheader("to", q->q_paddr, ee); 396 } 397 398 (void) sprintf(buf, "Returned mail: %s", msg); 399 addheader("subject", buf, ee); 400 401 /* fake up an address header for the from person */ 402 expand("\001n", buf, &buf[sizeof buf - 1], CurEnv); 403 if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL) 404 { 405 syserr("Can't parse myself!"); 406 ExitStat = EX_SOFTWARE; 407 returndepth--; 408 return (-1); 409 } 410 loweraddr(&ee->e_from); 411 412 /* push state into submessage */ 413 CurEnv = ee; 414 define('f', "\001n", ee); 415 define('x', "Mail Delivery Subsystem", ee); 416 eatheader(ee); 417 418 /* actually deliver the error message */ 419 sendall(ee, SM_DEFAULT); 420 421 /* restore state */ 422 dropenvelope(ee); 423 CurEnv = CurEnv->e_parent; 424 returndepth--; 425 426 /* should check for delivery errors here */ 427 return (0); 428 } 429 /* 430 ** ERRBODY -- output the body of an error message. 431 ** 432 ** Typically this is a copy of the transcript plus a copy of the 433 ** original offending message. 434 ** 435 ** Parameters: 436 ** fp -- the output file. 437 ** m -- the mailer to output to. 438 ** e -- the envelope we are working in. 439 ** 440 ** Returns: 441 ** none 442 ** 443 ** Side Effects: 444 ** Outputs the body of an error message. 445 */ 446 447 errbody(fp, m, e) 448 register FILE *fp; 449 register struct mailer *m; 450 register ENVELOPE *e; 451 { 452 register FILE *xfile; 453 char buf[MAXLINE]; 454 char *p; 455 456 /* 457 ** Output transcript of errors 458 */ 459 460 (void) fflush(stdout); 461 p = queuename(e->e_parent, 'x'); 462 if ((xfile = fopen(p, "r")) == NULL) 463 { 464 syserr("Cannot open %s", p); 465 fprintf(fp, " ----- Transcript of session is unavailable -----\n"); 466 } 467 else 468 { 469 fprintf(fp, " ----- Transcript of session follows -----\n"); 470 if (e->e_xfp != NULL) 471 (void) fflush(e->e_xfp); 472 while (fgets(buf, sizeof buf, xfile) != NULL) 473 putline(buf, fp, m); 474 (void) fclose(xfile); 475 } 476 errno = 0; 477 478 /* 479 ** Output text of original message 480 */ 481 482 if (NoReturn) 483 fprintf(fp, "\n ----- Return message suppressed -----\n\n"); 484 else if (e->e_parent->e_dfp != NULL) 485 { 486 if (SendBody) 487 { 488 putline("\n", fp, m); 489 putline(" ----- Unsent message follows -----\n", fp, m); 490 (void) fflush(fp); 491 putheader(fp, m, e->e_parent); 492 putline("\n", fp, m); 493 putbody(fp, m, e->e_parent); 494 } 495 else 496 { 497 putline("\n", fp, m); 498 putline(" ----- Message header follows -----\n", fp, m); 499 (void) fflush(fp); 500 putheader(fp, m, e->e_parent); 501 } 502 } 503 else 504 { 505 putline("\n", fp, m); 506 putline(" ----- No message was collected -----\n", fp, m); 507 putline("\n", fp, m); 508 } 509 510 /* 511 ** Cleanup and exit 512 */ 513 514 if (errno != 0) 515 syserr("errbody: I/O error"); 516 } 517