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