1 /* 2 ** Sendmail 3 ** Copyright (c) 1983 Eric P. Allman 4 ** Berkeley, California 5 ** 6 ** Copyright (c) 1983 Regents of the University of California. 7 ** All rights reserved. The Berkeley software License Agreement 8 ** specifies the terms and conditions for redistribution. 9 */ 10 11 #ifndef lint 12 static char SccsId[] = "@(#)savemail.c 5.1 (Berkeley) 06/07/85"; 13 #endif not lint 14 15 # include <pwd.h> 16 # include "sendmail.h" 17 18 SCCSID(@(#)savemail.c 5.1 06/07/85); 19 20 /* 21 ** SAVEMAIL -- Save mail on error 22 ** 23 ** If mailing back errors, mail it back to the originator 24 ** together with an error message; otherwise, just put it in 25 ** dead.letter in the user's home directory (if he exists on 26 ** this machine). 27 ** 28 ** Parameters: 29 ** e -- the envelope containing the message in error. 30 ** 31 ** Returns: 32 ** none 33 ** 34 ** Side Effects: 35 ** Saves the letter, by writing or mailing it back to the 36 ** sender, or by putting it in dead.letter in her home 37 ** directory. 38 */ 39 40 savemail(e) 41 register ENVELOPE *e; 42 { 43 register struct passwd *pw; 44 register FILE *xfile; 45 char buf[MAXLINE+1]; 46 extern struct passwd *getpwnam(); 47 register char *p; 48 extern char *ttypath(); 49 typedef int (*fnptr)(); 50 51 # ifdef DEBUG 52 if (tTd(6, 1)) 53 printf("\nsavemail\n"); 54 # endif DEBUG 55 56 if (bitset(EF_RESPONSE, e->e_flags)) 57 return; 58 if (e->e_class < 0) 59 { 60 message(Arpa_Info, "Dumping junk mail"); 61 return; 62 } 63 ForceMail = TRUE; 64 e->e_flags &= ~EF_FATALERRS; 65 66 /* 67 ** In the unhappy event we don't know who to return the mail 68 ** to, make someone up. 69 */ 70 71 if (e->e_from.q_paddr == NULL) 72 { 73 if (parseaddr("root", &e->e_from, 0, '\0') == NULL) 74 { 75 syserr("Cannot parse root!"); 76 ExitStat = EX_SOFTWARE; 77 finis(); 78 } 79 } 80 e->e_to = NULL; 81 82 /* 83 ** If called from Eric Schmidt's network, do special mailback. 84 ** Fundamentally, this is the mailback case except that 85 ** it returns an OK exit status (assuming the return 86 ** worked). 87 ** Also, if the from address is not local, mail it back. 88 */ 89 90 if (ErrorMode == EM_BERKNET) 91 { 92 ExitStat = EX_OK; 93 ErrorMode = EM_MAIL; 94 } 95 if (!bitnset(M_LOCAL, e->e_from.q_mailer->m_flags)) 96 ErrorMode = EM_MAIL; 97 98 /* 99 ** If writing back, do it. 100 ** If the user is still logged in on the same terminal, 101 ** then write the error messages back to hir (sic). 102 ** If not, mail back instead. 103 */ 104 105 if (ErrorMode == EM_WRITE) 106 { 107 p = ttypath(); 108 if (p == NULL || freopen(p, "w", stdout) == NULL) 109 { 110 ErrorMode = EM_MAIL; 111 errno = 0; 112 } 113 else 114 { 115 expand("\001n", buf, &buf[sizeof buf - 1], e); 116 printf("\r\nMessage from %s...\r\n", buf); 117 printf("Errors occurred while sending mail.\r\n"); 118 if (e->e_xfp != NULL) 119 { 120 (void) fflush(e->e_xfp); 121 xfile = fopen(queuename(e, 'x'), "r"); 122 } 123 else 124 xfile = NULL; 125 if (xfile == NULL) 126 { 127 syserr("Cannot open %s", queuename(e, 'x')); 128 printf("Transcript of session is unavailable.\r\n"); 129 } 130 else 131 { 132 printf("Transcript follows:\r\n"); 133 while (fgets(buf, sizeof buf, xfile) != NULL && 134 !ferror(stdout)) 135 fputs(buf, stdout); 136 (void) fclose(xfile); 137 } 138 if (ferror(stdout)) 139 (void) syserr("savemail: stdout: write err"); 140 } 141 } 142 143 /* 144 ** If mailing back, do it. 145 ** Throw away all further output. Don't do aliases, since 146 ** this could cause loops, e.g., if joe mails to x:joe, 147 ** and for some reason the network for x: is down, then 148 ** the response gets sent to x:joe, which gives a 149 ** response, etc. Also force the mail to be delivered 150 ** even if a version of it has already been sent to the 151 ** sender. 152 */ 153 154 if (ErrorMode == EM_MAIL) 155 { 156 if (e->e_errorqueue == NULL) 157 sendtolist(e->e_from.q_paddr, (ADDRESS *) NULL, 158 &e->e_errorqueue); 159 if (returntosender(e->e_message != NULL ? e->e_message : 160 "Unable to deliver mail", 161 e->e_errorqueue, TRUE) == 0) 162 return; 163 } 164 165 /* 166 ** Save the message in dead.letter. 167 ** If we weren't mailing back, and the user is local, we 168 ** should save the message in dead.letter so that the 169 ** poor person doesn't have to type it over again -- 170 ** and we all know what poor typists programmers are. 171 */ 172 173 p = NULL; 174 if (e->e_from.q_mailer == LocalMailer) 175 { 176 if (e->e_from.q_home != NULL) 177 p = e->e_from.q_home; 178 else if ((pw = getpwnam(e->e_from.q_user)) != NULL) 179 p = pw->pw_dir; 180 } 181 if (p == NULL) 182 { 183 syserr("Can't return mail to %s", e->e_from.q_paddr); 184 # ifdef DEBUG 185 p = "/usr/tmp"; 186 # endif 187 } 188 if (p != NULL && e->e_dfp != NULL) 189 { 190 auto ADDRESS *q; 191 bool oldverb = Verbose; 192 193 /* we have a home directory; open dead.letter */ 194 define('z', p, e); 195 expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e); 196 Verbose = TRUE; 197 message(Arpa_Info, "Saving message in %s", buf); 198 Verbose = oldverb; 199 e->e_to = buf; 200 q = NULL; 201 sendtolist(buf, (ADDRESS *) NULL, &q); 202 (void) deliver(e, q); 203 } 204 205 /* add terminator to writeback message */ 206 if (ErrorMode == EM_WRITE) 207 printf("-----\r\n"); 208 } 209 /* 210 ** RETURNTOSENDER -- return a message to the sender with an error. 211 ** 212 ** Parameters: 213 ** msg -- the explanatory message. 214 ** returnq -- the queue of people to send the message to. 215 ** sendbody -- if TRUE, also send back the body of the 216 ** message; otherwise just send the header. 217 ** 218 ** Returns: 219 ** zero -- if everything went ok. 220 ** else -- some error. 221 ** 222 ** Side Effects: 223 ** Returns the current message to the sender via 224 ** mail. 225 */ 226 227 static bool SendBody; 228 229 #define MAXRETURNS 6 /* max depth of returning messages */ 230 231 returntosender(msg, returnq, sendbody) 232 char *msg; 233 ADDRESS *returnq; 234 bool sendbody; 235 { 236 char buf[MAXNAME]; 237 extern putheader(), errbody(); 238 register ENVELOPE *ee; 239 extern ENVELOPE *newenvelope(); 240 ENVELOPE errenvelope; 241 static int returndepth; 242 register ADDRESS *q; 243 244 # ifdef DEBUG 245 if (tTd(6, 1)) 246 { 247 printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n", 248 msg, returndepth, CurEnv); 249 printf("\treturnto="); 250 printaddr(returnq, TRUE); 251 } 252 # endif DEBUG 253 254 if (++returndepth >= MAXRETURNS) 255 { 256 if (returndepth != MAXRETURNS) 257 syserr("returntosender: infinite recursion on %s", returnq->q_paddr); 258 /* don't "unrecurse" and fake a clean exit */ 259 /* returndepth--; */ 260 return (0); 261 } 262 263 SendBody = sendbody; 264 define('g', "\001f", CurEnv); 265 ee = newenvelope(&errenvelope); 266 ee->e_puthdr = putheader; 267 ee->e_putbody = errbody; 268 ee->e_flags |= EF_RESPONSE; 269 ee->e_sendqueue = returnq; 270 openxscript(ee); 271 for (q = returnq; q != NULL; q = q->q_next) 272 { 273 if (q->q_alias == NULL) 274 addheader("to", q->q_paddr, ee); 275 } 276 (void) sprintf(buf, "Returned mail: %s", msg); 277 addheader("subject", buf, ee); 278 279 /* fake up an address header for the from person */ 280 expand("\001n", buf, &buf[sizeof buf - 1], CurEnv); 281 if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL) 282 { 283 syserr("Can't parse myself!"); 284 ExitStat = EX_SOFTWARE; 285 returndepth--; 286 return (-1); 287 } 288 loweraddr(&ee->e_from); 289 290 /* push state into submessage */ 291 CurEnv = ee; 292 define('f', "\001n", ee); 293 define('x', "Mail Delivery Subsystem", ee); 294 eatheader(ee); 295 296 /* actually deliver the error message */ 297 sendall(ee, SM_DEFAULT); 298 299 /* restore state */ 300 dropenvelope(ee); 301 CurEnv = CurEnv->e_parent; 302 returndepth--; 303 304 /* should check for delivery errors here */ 305 return (0); 306 } 307 /* 308 ** ERRBODY -- output the body of an error message. 309 ** 310 ** Typically this is a copy of the transcript plus a copy of the 311 ** original offending message. 312 ** 313 ** Parameters: 314 ** fp -- the output file. 315 ** m -- the mailer to output to. 316 ** e -- the envelope we are working in. 317 ** 318 ** Returns: 319 ** none 320 ** 321 ** Side Effects: 322 ** Outputs the body of an error message. 323 */ 324 325 errbody(fp, m, e) 326 register FILE *fp; 327 register struct mailer *m; 328 register ENVELOPE *e; 329 { 330 register FILE *xfile; 331 char buf[MAXLINE]; 332 char *p; 333 334 /* 335 ** Output transcript of errors 336 */ 337 338 (void) fflush(stdout); 339 p = queuename(e->e_parent, 'x'); 340 if ((xfile = fopen(p, "r")) == NULL) 341 { 342 syserr("Cannot open %s", p); 343 fprintf(fp, " ----- Transcript of session is unavailable -----\n"); 344 } 345 else 346 { 347 fprintf(fp, " ----- Transcript of session follows -----\n"); 348 if (e->e_xfp != NULL) 349 (void) fflush(e->e_xfp); 350 while (fgets(buf, sizeof buf, xfile) != NULL) 351 putline(buf, fp, m); 352 (void) fclose(xfile); 353 } 354 errno = 0; 355 356 /* 357 ** Output text of original message 358 */ 359 360 if (NoReturn) 361 fprintf(fp, "\n ----- Return message suppressed -----\n\n"); 362 else if (e->e_parent->e_dfp != NULL) 363 { 364 if (SendBody) 365 { 366 putline("\n", fp, m); 367 putline(" ----- Unsent message follows -----\n", fp, m); 368 (void) fflush(fp); 369 putheader(fp, m, e->e_parent); 370 putline("\n", fp, m); 371 putbody(fp, m, e->e_parent); 372 } 373 else 374 { 375 putline("\n", fp, m); 376 putline(" ----- Message header follows -----\n", fp, m); 377 (void) fflush(fp); 378 putheader(fp, m, e->e_parent); 379 } 380 } 381 else 382 { 383 putline("\n", fp, m); 384 putline(" ----- No message was collected -----\n", fp, m); 385 putline("\n", fp, m); 386 } 387 388 /* 389 ** Cleanup and exit 390 */ 391 392 if (errno != 0) 393 syserr("errbody: I/O error"); 394 } 395