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