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