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