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