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