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