1 # include <stdio.h>
2 # include <pwd.h>
3 # include <signal.h>
4 # include "dlvrmail.h"
5 # ifdef LOG
6 # include <log.h>
7 # endif LOG
8 
9 static char SccsId[] = "@(#)deliver.c	2.1.1.1	11/20/80";
10 
11 /*
12 **  DELIVER -- Deliver a message to a particular address.
13 **
14 **	Algorithm:
15 **		Compute receiving network (i.e., mailer), host, & user.
16 **		If local, see if this is really a program name.
17 **		Build argument for the mailer.
18 **		Create pipe through edit fcn if appropriate.
19 **		Fork.
20 **			Child: call mailer
21 **		Parent: call editfcn if specified.
22 **		Wait for mailer to finish.
23 **		Interpret exit status.
24 **
25 **	Parameters:
26 **		to -- the address to deliver the message to.
27 **		editfcn -- if non-NULL, we want to call this function
28 **			to output the letter (instead of just out-
29 **			putting it raw).
30 **
31 **	Returns:
32 **		zero -- successfully delivered.
33 **		else -- some failure, see ExitStat for more info.
34 **
35 **	Side Effects:
36 **		The standard input is passed off to someone.
37 **
38 **	WARNING:
39 **		The standard input is shared amongst all children,
40 **		including the file pointer.  It is critical that the
41 **		parent waits for the child to finish before forking
42 **		another child.
43 **
44 **	Called By:
45 **		main
46 **		savemail
47 **
48 **	Files:
49 **		standard input -- must be opened to the message to
50 **			deliver.
51 */
52 
53 deliver(to, editfcn)
54 	addrq *to;
55 	int (*editfcn)();
56 {
57 	register struct mailer *m;
58 	char *host;
59 	char *user;
60 	extern struct passwd *getpwnam();
61 	char **pvp;
62 	extern char **buildargv();
63 	auto int st;
64 	register int i;
65 	register char *p;
66 	int pid;
67 	int pvect[2];
68 	extern FILE *fdopen();
69 	extern int errno;
70 	FILE *mfile;
71 	extern putheader();
72 	extern pipesig();
73 
74 	/*
75 	**  Compute receiving mailer, host, and to addreses.
76 	**	Do some initialization first.  To is the to address
77 	**	for error messages.
78 	*/
79 
80 	To = to->q_paddr;
81 	m = to->q_mailer;
82 	user = to->q_user;
83 	host = to->q_host;
84 	Errors = 0;
85 	errno = 0;
86 # ifdef DEBUG
87 	if (Debug)
88 		printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
89 # endif DEBUG
90 
91 	/*
92 	**  Remove quote bits from user/host.
93 	*/
94 
95 	for (p = user; (*p++ &= 0177) != '\0'; )
96 		continue;
97 	if (host != NULL)
98 		for (p = host; (*p++ &= 0177) != '\0'; )
99 			continue;
100 
101 	/*
102 	**  Strip quote bits from names if the mailer wants it.
103 	*/
104 
105 	if (flagset(M_STRIPQ, m->m_flags))
106 	{
107 		stripquotes(user);
108 		stripquotes(host);
109 	}
110 
111 	/*
112 	**  See if this user name is "special".
113 	**	If the user is a program, diddle with the mailer spec.
114 	**	If the user name has a slash in it, assume that this
115 	**		is a file -- send it off without further ado.
116 	**		Note that this means that editfcn's will not
117 	**		be applied to the message.
118 	*/
119 
120 	if (m == &Mailer[0])
121 	{
122 		if (*user == '|')
123 		{
124 			user++;
125 			m = &Mailer[1];
126 		}
127 		else
128 		{
129 			if (index(user, '/') != NULL)
130 			{
131 				i = mailfile(user);
132 				giveresponse(i, TRUE, m);
133 				return (i);
134 			}
135 		}
136 	}
137 
138 	/*
139 	**  See if the user exists.
140 	**	Strictly, this is only needed to print a pretty
141 	**	error message.
142 	**
143 	**	>>>>>>>>>> This clause assumes that the local mailer
144 	**	>> NOTE >> cannot do any further aliasing; that
145 	**	>>>>>>>>>> function is subsumed by delivermail.
146 	*/
147 
148 	if (m == &Mailer[0])
149 	{
150 		if (getpwnam(user) == NULL)
151 		{
152 			giveresponse(EX_NOUSER, TRUE, m);
153 			return (EX_NOUSER);
154 		}
155 	}
156 
157 	/*
158 	**  If the mailer wants a From line, insert a new editfcn.
159 	*/
160 
161 	if (flagset(M_HDR, m->m_flags) && editfcn == NULL)
162 		editfcn = putheader;
163 
164 	/*
165 	**  Call the mailer.
166 	**	The argument vector gets built, pipes through 'editfcn'
167 	**	are created as necessary, and we fork & exec as
168 	**	appropriate.  In the parent, we call 'editfcn'.
169 	*/
170 
171 	pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
172 	if (pvp == NULL)
173 	{
174 		usrerr("name too long");
175 		return (-1);
176 	}
177 	rewind(stdin);
178 
179 	/* create a pipe if we will need one */
180 	if (editfcn != NULL && pipe(pvect) < 0)
181 	{
182 		syserr("pipe");
183 		return (-1);
184 	}
185 # ifdef VFORK
186 	pid = vfork();
187 # else
188 	pid = fork();
189 # endif
190 	if (pid < 0)
191 	{
192 		syserr("Cannot fork");
193 		if (editfcn != NULL)
194 		{
195 			close(pvect[0]);
196 			close(pvect[1]);
197 		}
198 		return (-1);
199 	}
200 	else if (pid == 0)
201 	{
202 		/* child -- set up input & exec mailer */
203 		/* make diagnostic output be standard output */
204 		close(2);
205 		dup(1);
206 		signal(SIGINT, SIG_IGN);
207 		if (editfcn != NULL)
208 		{
209 			close(0);
210 			if (dup(pvect[0]) < 0)
211 			{
212 				syserr("Cannot dup to zero!");
213 				_exit(EX_OSERR);
214 			}
215 			close(pvect[0]);
216 			close(pvect[1]);
217 		}
218 		if (!flagset(M_RESTR, m->m_flags))
219 			setuid(getuid());
220 # ifdef LOG
221 		initlog(NULL, 0, LOG_CLOSE);
222 # endif LOG
223 # ifndef VFORK
224 		/*
225 		 * We have to be careful with vfork - we can't mung up the
226 		 * memory but we don't want the mailer to inherit any extra
227 		 * open files.  Chances are the mailer won't
228 		 * care about an extra file, but then again you never know.
229 		 * Actually, we would like to close(fileno(pwf)), but it's
230 		 * declared static so we can't.  But if we fclose(pwf), which
231 		 * is what endpwent does, it closes it in the parent too and
232 		 * the next getpwnam will be slower.  If you have a weird mailer
233 		 * that chokes on the extra file you should do the endpwent().
234 		 */
235 		endpwent();
236 # endif
237 		execv(m->m_mailer, pvp);
238 		/* syserr fails because log is closed */
239 		/* syserr("Cannot exec %s", m->m_mailer); */
240 		_exit(EX_UNAVAILABLE);
241 	}
242 
243 	/* arrange to write out header message if error */
244 	if (editfcn != NULL)
245 	{
246 		close(pvect[0]);
247 		signal(SIGPIPE, pipesig);
248 		mfile = fdopen(pvect[1], "w");
249 		(*editfcn)(mfile);
250 		fclose(mfile);
251 	}
252 
253 	/*
254 	**  Wait for child to die and report status.
255 	**	We should never get fatal errors (e.g., segmentation
256 	**	violation), so we report those specially.  For other
257 	**	errors, we choose a status message (into statmsg),
258 	**	and if it represents an error, we print it.
259 	*/
260 
261 	while ((i = wait(&st)) > 0 && i != pid)
262 		continue;
263 	if (i < 0)
264 	{
265 		syserr("wait");
266 		return (-1);
267 	}
268 	if ((st & 0377) != 0)
269 	{
270 		syserr("%s: stat %o", pvp[0], st);
271 		ExitStat = EX_UNAVAILABLE;
272 		return (-1);
273 	}
274 	i = (st >> 8) & 0377;
275 	giveresponse(i, FALSE, m);
276 	return (i);
277 }
278 /*
279 **  GIVERESPONSE -- Interpret an error response from a mailer
280 **
281 **	Parameters:
282 **		stat -- the status code from the mailer (high byte
283 **			only; core dumps must have been taken care of
284 **			already).
285 **		force -- if set, force an error message output, even
286 **			if the mailer seems to like to print its own
287 **			messages.
288 **		m -- the mailer descriptor for this mailer.
289 **
290 **	Returns:
291 **		none.
292 **
293 **	Side Effects:
294 **		Errors may be incremented.
295 **		ExitStat may be set.
296 **
297 **	Called By:
298 **		deliver
299 */
300 
301 giveresponse(stat, force, m)
302 	int stat;
303 	int force;
304 	register struct mailer *m;
305 {
306 	register char *statmsg;
307 	extern char *SysExMsg[];
308 	register int i;
309 	extern int N_SysEx;
310 	extern long MsgSize;
311 	char buf[30];
312 
313 	i = stat - EX__BASE;
314 	if (i < 0 || i > N_SysEx)
315 		statmsg = NULL;
316 	else
317 		statmsg = SysExMsg[i];
318 	if (stat == 0)
319 		statmsg = "ok";
320 	else
321 	{
322 		Errors++;
323 		if (statmsg == NULL && m->m_badstat != 0)
324 		{
325 			stat = m->m_badstat;
326 			i = stat - EX__BASE;
327 # ifdef DEBUG
328 			if (i < 0 || i >= N_SysEx)
329 				syserr("Bad m_badstat %d", stat);
330 			else
331 # endif DEBUG
332 			statmsg = SysExMsg[i];
333 		}
334 		if (statmsg == NULL)
335 			usrerr("unknown mailer response %d", stat);
336 		else if (force || !flagset(M_QUIET, m->m_flags))
337 			usrerr("%s", statmsg);
338 	}
339 
340 	/*
341 	**  Final cleanup.
342 	**	Log a record of the transaction.  Compute the new
343 	**	ExitStat -- if we already had an error, stick with
344 	**	that.
345 	*/
346 
347 	if (statmsg == NULL)
348 	{
349 		sprintf(buf, "error %d", stat);
350 		statmsg = buf;
351 	}
352 
353 # ifdef LOG
354 	logmsg(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg);
355 # endif LOG
356 	setstat(stat);
357 	return (stat);
358 }
359 /*
360 **  PUTHEADER -- insert the From header into some mail
361 **
362 **	For mailers such as 'msgs' that want the header inserted
363 **	into the mail, this edit filter inserts the From line and
364 **	then passes the rest of the message through.  If we have
365 **	managed to extract a date already, use that; otherwise,
366 **	use the current date/time.
367 **
368 **	Parameters:
369 **		fp -- the file pointer for the output.
370 **
371 **	Returns:
372 **		none
373 **
374 **	Side Effects:
375 **		Puts a "From" line in UNIX format, and then
376 **			outputs the rest of the message.
377 **
378 **	Called By:
379 **		deliver
380 */
381 
382 putheader(fp)
383 	register FILE *fp;
384 {
385 	char buf[MAXLINE + 1];
386 	long tim;
387 	extern char *ctime();
388 	extern char SentDate[];
389 
390 	fprintf(fp, "From %s ", From.q_paddr);
391 	if (SentDate[0] == '\0')
392 	{
393 		time(&tim);
394 		fprintf(fp, "%s", ctime(&tim));
395 	}
396 	else
397 		fprintf(fp, "%s", SentDate);
398 	while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
399 		fputs(buf, fp);
400 	if (ferror(fp))
401 	{
402 		syserr("putheader: write error");
403 		setstat(EX_IOERR);
404 	}
405 }
406 /*
407 **  PIPESIG -- Handle broken pipe signals
408 **
409 **	This just logs an error.
410 **
411 **	Parameters:
412 **		none
413 **
414 **	Returns:
415 **		none
416 **
417 **	Side Effects:
418 **		logs an error message.
419 */
420 
421 pipesig()
422 {
423 	syserr("Broken pipe");
424 	signal(SIGPIPE, SIG_IGN);
425 }
426 /*
427 **  SENDTO -- Designate a send list.
428 **
429 **	The parameter is a comma-separated list of people to send to.
430 **	This routine arranges to send to all of them.
431 **
432 **	Parameters:
433 **		list -- the send list.
434 **		copyf -- the copy flag; passed to parse.
435 **
436 **	Returns:
437 **		none
438 **
439 **	Side Effects:
440 **		none.
441 **
442 **	Called By:
443 **		main
444 **		alias
445 */
446 
447 sendto(list, copyf)
448 	char *list;
449 	int copyf;
450 {
451 	register char *p;
452 	register char *q;
453 	register char c;
454 	addrq *a;
455 	extern addrq *parse();
456 	bool more;
457 
458 	/* more keeps track of what the previous delimiter was */
459 	more = TRUE;
460 	for (p = list; more; )
461 	{
462 		/* find the end of this address */
463 		q = p;
464 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
465 			continue;
466 		more = c != '\0';
467 		*--p = '\0';
468 		if (more)
469 			p++;
470 
471 		/* parse the address */
472 		if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
473 			continue;
474 
475 		/* arrange to send to this person */
476 		recipient(a, &SendQ);
477 	}
478 	To = NULL;
479 }
480 /*
481 **  RECIPIENT -- Designate a message recipient
482 **
483 **	Saves the named person for future mailing.
484 **
485 **	Designates a person as a recipient.  This routine
486 **	does the initial parsing, and checks to see if
487 **	this person has already received the mail.
488 **	It also supresses local network names and turns them into
489 **	local names.
490 **
491 **	Parameters:
492 **		a -- the (preparsed) address header for the recipient.
493 **		targetq -- the queue to add the name to.
494 **
495 **	Returns:
496 **		none.
497 **
498 **	Side Effects:
499 **		none.
500 **
501 **	Called By:
502 **		sendto
503 **		main
504 */
505 
506 recipient(a, targetq)
507 	register addrq *a;
508 	addrq *targetq;
509 {
510 	register addrq *q;
511 	register struct mailer *m;
512 	register char **pvp;
513 	extern char *xalloc();
514 	extern bool forward();
515 	extern int errno;
516 	extern bool sameaddr();
517 
518 	To = a->q_paddr;
519 	m = a->q_mailer;
520 	errno = 0;
521 # ifdef DEBUG
522 	if (Debug)
523 		printf("recipient(%s)\n", To);
524 # endif DEBUG
525 
526 	/*
527 	**  Look up this person in the recipient list.  If they
528 	**  are there already, return, otherwise continue.
529 	*/
530 
531 	if (!ForceMail)
532 	{
533 		for (q = &SendQ; (q = nxtinq(q)) != NULL; )
534 			if (sameaddr(q, a, FALSE))
535 			{
536 # ifdef DEBUG
537 				if (Debug)
538 					printf("(%s in SendQ)\n", a->q_paddr);
539 # endif DEBUG
540 				return;
541 			}
542 		for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
543 			if (sameaddr(q, a, FALSE))
544 			{
545 # ifdef DEBUG
546 				if (Debug)
547 					printf("(%s in AliasQ)\n", a->q_paddr);
548 # endif DEBUG
549 				return;
550 			}
551 	}
552 
553 	/*
554 	**  See if the user wants hir mail forwarded.
555 	**	`Forward' must do the forwarding recursively.
556 	*/
557 
558 	if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
559 		return;
560 
561 	/*
562 	**  Put the user onto the target queue.
563 	*/
564 
565 	if (targetq != NULL)
566 	{
567 		putonq(a, targetq);
568 	}
569 
570 	return;
571 }
572 /*
573 **  BUILDARGV -- Build an argument vector for a mail server.
574 **
575 **	Using a template defined in config.c, an argv is built.
576 **	The format of the template is already a vector.  The
577 **	items of this vector are copied, unless a dollar sign
578 **	is encountered.  In this case, the next character
579 **	specifies something else to copy in.  These can be
580 **		$f	The from address.
581 **		$h	The host.
582 **		$u	The user.
583 **		$c	The hop count.
584 **	The vector is built in a local buffer.  A pointer to
585 **	the static argv is returned.
586 **
587 **	Parameters:
588 **		tmplt -- a template for an argument vector.
589 **		flags -- the flags for this server.
590 **		host -- the host name to send to.
591 **		user -- the user name to send to.
592 **		from -- the person this mail is from.
593 **
594 **	Returns:
595 **		A pointer to an argv.
596 **
597 **	Side Effects:
598 **		none
599 **
600 **	WARNING:
601 **		Since the argv is staticly allocated, any subsequent
602 **		calls will clobber the old argv.
603 **
604 **	Called By:
605 **		deliver
606 */
607 
608 char **
609 buildargv(tmplt, flags, host, user, from)
610 	char **tmplt;
611 	int flags;
612 	char *host;
613 	char *user;
614 	char *from;
615 {
616 	register char *p;
617 	register char *q;
618 	static char *pv[MAXPV+1];
619 	char **pvp;
620 	char **mvp;
621 	static char buf[512];
622 	register char *bp;
623 	char pbuf[30];
624 
625 	/*
626 	**  Do initial argv setup.
627 	**	Insert the mailer name.  Notice that $x expansion is
628 	**	NOT done on the mailer name.  Then, if the mailer has
629 	**	a picky -f flag, we insert it as appropriate.  This
630 	**	code does not check for 'pv' overflow; this places a
631 	**	manifest lower limit of 4 for MAXPV.
632 	*/
633 
634 	pvp = pv;
635 	bp = buf;
636 
637 	*pvp++ = tmplt[0];
638 
639 	/* insert -f or -r flag as appropriate */
640 	if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
641 	{
642 		if (flagset(M_FOPT, flags))
643 			*pvp++ = "-f";
644 		else
645 			*pvp++ = "-r";
646 		*pvp++ = From.q_paddr;
647 	}
648 
649 	/*
650 	**  Build the rest of argv.
651 	**	For each prototype parameter, the prototype is
652 	**	scanned character at a time.  If a dollar-sign is
653 	**	found, 'q' is set to the appropriate expansion,
654 	**	otherwise it is null.  Then either the string
655 	**	pointed to by q, or the original character, is
656 	**	interpolated into the buffer.  Buffer overflow is
657 	**	checked.
658 	*/
659 
660 	for (mvp = tmplt; (p = *++mvp) != NULL; )
661 	{
662 		if (pvp >= &pv[MAXPV])
663 		{
664 			syserr("Too many parameters to %s", pv[0]);
665 			return (NULL);
666 		}
667 		*pvp++ = bp;
668 		for (; *p != '\0'; p++)
669 		{
670 			/* q will be the interpolated quantity */
671 			q = NULL;
672 			if (*p == '$')
673 			{
674 				switch (*++p)
675 				{
676 				  case 'f':	/* from person */
677 					q = from;
678 					break;
679 
680 				  case 'u':	/* user */
681 					q = user;
682 					break;
683 
684 				  case 'h':	/* host */
685 					q = host;
686 					break;
687 
688 				  case 'c':	/* hop count */
689 					sprintf(pbuf, "%d", HopCount);
690 					q = pbuf;
691 					break;
692 				}
693 			}
694 
695 			/*
696 			**  Interpolate q or output one character
697 			**	Strip quote bits as we proceed.....
698 			*/
699 
700 			if (q != NULL)
701 			{
702 				while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
703 					continue;
704 				bp--;
705 			}
706 			else if (bp < &buf[sizeof buf - 1])
707 				*bp++ = *p;
708 		}
709 		*bp++ = '\0';
710 		if (bp >= &buf[sizeof buf - 1])
711 			return (NULL);
712 	}
713 	*pvp = NULL;
714 
715 # ifdef DEBUG
716 	if (Debug)
717 	{
718 		printf("Interpolated argv is:\n");
719 		for (mvp = pv; *mvp != NULL; mvp++)
720 			printf("\t%s\n", *mvp);
721 	}
722 # endif DEBUG
723 
724 	return (pv);
725 }
726 /*
727 **  MAILFILE -- Send a message to a file.
728 **
729 **	Parameters:
730 **		filename -- the name of the file to send to.
731 **
732 **	Returns:
733 **		The exit code associated with the operation.
734 **
735 **	Side Effects:
736 **		none.
737 **
738 **	Called By:
739 **		deliver
740 */
741 
742 mailfile(filename)
743 	char *filename;
744 {
745 	char buf[MAXLINE];
746 	register FILE *f;
747 	auto long tim;
748 	extern char *ctime();
749 
750 	f = fopen(filename, "a");
751 	if (f == NULL)
752 		return (EX_CANTCREAT);
753 
754 	/* output the timestamp */
755 	time(&tim);
756 	fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
757 	rewind(stdin);
758 	while (fgets(buf, sizeof buf, stdin) != NULL)
759 	{
760 		fputs(buf, f);
761 		if (ferror(f))
762 		{
763 			fclose(f);
764 			return (EX_IOERR);
765 		}
766 	}
767 	fputs("\n", f);
768 	fclose(f);
769 	return (EX_OK);
770 }
771