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.13 (Berkeley) 06/01/90"; 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 ee = newenvelope(&errenvelope); 383 define('a', "\001b", ee); 384 ee->e_puthdr = putheader; 385 ee->e_putbody = errbody; 386 ee->e_flags |= EF_RESPONSE; 387 ee->e_sendqueue = returnq; 388 openxscript(ee); 389 for (q = returnq; q != NULL; q = q->q_next) 390 { 391 if (q->q_alias == NULL) 392 addheader("to", q->q_paddr, ee); 393 } 394 395 (void) sprintf(buf, "Returned mail: %s", msg); 396 addheader("subject", buf, ee); 397 398 /* fake up an address header for the from person */ 399 expand("\001n", buf, &buf[sizeof buf - 1], CurEnv); 400 if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL) 401 { 402 syserr("Can't parse myself!"); 403 ExitStat = EX_SOFTWARE; 404 returndepth--; 405 return (-1); 406 } 407 loweraddr(&ee->e_from); 408 409 /* push state into submessage */ 410 CurEnv = ee; 411 define('f', "\001n", ee); 412 define('x', "Mail Delivery Subsystem", ee); 413 eatheader(ee); 414 415 /* actually deliver the error message */ 416 sendall(ee, SM_DEFAULT); 417 418 /* restore state */ 419 dropenvelope(ee); 420 CurEnv = CurEnv->e_parent; 421 returndepth--; 422 423 /* should check for delivery errors here */ 424 return (0); 425 } 426 /* 427 ** ERRBODY -- output the body of an error message. 428 ** 429 ** Typically this is a copy of the transcript plus a copy of the 430 ** original offending message. 431 ** 432 ** Parameters: 433 ** fp -- the output file. 434 ** m -- the mailer to output to. 435 ** e -- the envelope we are working in. 436 ** 437 ** Returns: 438 ** none 439 ** 440 ** Side Effects: 441 ** Outputs the body of an error message. 442 */ 443 444 errbody(fp, m, e) 445 register FILE *fp; 446 register struct mailer *m; 447 register ENVELOPE *e; 448 { 449 register FILE *xfile; 450 char buf[MAXLINE]; 451 char *p; 452 453 /* 454 ** Output transcript of errors 455 */ 456 457 (void) fflush(stdout); 458 p = queuename(e->e_parent, 'x'); 459 if ((xfile = fopen(p, "r")) == NULL) 460 { 461 syserr("Cannot open %s", p); 462 fprintf(fp, " ----- Transcript of session is unavailable -----\n"); 463 } 464 else 465 { 466 fprintf(fp, " ----- Transcript of session follows -----\n"); 467 if (e->e_xfp != NULL) 468 (void) fflush(e->e_xfp); 469 while (fgets(buf, sizeof buf, xfile) != NULL) 470 putline(buf, fp, m); 471 (void) fclose(xfile); 472 } 473 errno = 0; 474 475 /* 476 ** Output text of original message 477 */ 478 479 if (NoReturn) 480 fprintf(fp, "\n ----- Return message suppressed -----\n\n"); 481 else if (e->e_parent->e_dfp != NULL) 482 { 483 if (SendBody) 484 { 485 putline("\n", fp, m); 486 putline(" ----- Unsent message follows -----\n", fp, m); 487 (void) fflush(fp); 488 putheader(fp, m, e->e_parent); 489 putline("\n", fp, m); 490 putbody(fp, m, e->e_parent); 491 } 492 else 493 { 494 putline("\n", fp, m); 495 putline(" ----- Message header follows -----\n", fp, m); 496 (void) fflush(fp); 497 putheader(fp, m, e->e_parent); 498 } 499 } 500 else 501 { 502 putline("\n", fp, m); 503 putline(" ----- No message was collected -----\n", fp, m); 504 putline("\n", fp, m); 505 } 506 507 /* 508 ** Cleanup and exit 509 */ 510 511 if (errno != 0) 512 syserr("errbody: I/O error"); 513 } 514