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