xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision 3e164d5b)
1 /*
2  * Copyright (c) 1983, 1995 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	8.78 (Berkeley) 06/21/95";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <sysexits.h>
15 /*
16 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
17 **
18 **	Runs through a string and strips off unquoted quote
19 **	characters and quote bits.  This is done in place.
20 **
21 **	Parameters:
22 **		s -- the string to strip.
23 **
24 **	Returns:
25 **		none.
26 **
27 **	Side Effects:
28 **		none.
29 **
30 **	Called By:
31 **		deliver
32 */
33 
34 void
stripquotes(s)35 stripquotes(s)
36 	char *s;
37 {
38 	register char *p;
39 	register char *q;
40 	register char c;
41 
42 	if (s == NULL)
43 		return;
44 
45 	p = q = s;
46 	do
47 	{
48 		c = *p++;
49 		if (c == '\\')
50 			c = *p++;
51 		else if (c == '"')
52 			continue;
53 		*q++ = c;
54 	} while (c != '\0');
55 }
56 /*
57 **  XALLOC -- Allocate memory and bitch wildly on failure.
58 **
59 **	THIS IS A CLUDGE.  This should be made to give a proper
60 **	error -- but after all, what can we do?
61 **
62 **	Parameters:
63 **		sz -- size of area to allocate.
64 **
65 **	Returns:
66 **		pointer to data region.
67 **
68 **	Side Effects:
69 **		Memory is allocated.
70 */
71 
72 char *
xalloc(sz)73 xalloc(sz)
74 	register int sz;
75 {
76 	register char *p;
77 
78 	/* some systems can't handle size zero mallocs */
79 	if (sz <= 0)
80 		sz = 1;
81 
82 	p = malloc((unsigned) sz);
83 	if (p == NULL)
84 	{
85 		syserr("!Out of memory!!");
86 		/* exit(EX_UNAVAILABLE); */
87 	}
88 	return (p);
89 }
90 /*
91 **  COPYPLIST -- copy list of pointers.
92 **
93 **	This routine is the equivalent of newstr for lists of
94 **	pointers.
95 **
96 **	Parameters:
97 **		list -- list of pointers to copy.
98 **			Must be NULL terminated.
99 **		copycont -- if TRUE, copy the contents of the vector
100 **			(which must be a string) also.
101 **
102 **	Returns:
103 **		a copy of 'list'.
104 **
105 **	Side Effects:
106 **		none.
107 */
108 
109 char **
copyplist(list,copycont)110 copyplist(list, copycont)
111 	char **list;
112 	bool copycont;
113 {
114 	register char **vp;
115 	register char **newvp;
116 
117 	for (vp = list; *vp != NULL; vp++)
118 		continue;
119 
120 	vp++;
121 
122 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
123 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
124 
125 	if (copycont)
126 	{
127 		for (vp = newvp; *vp != NULL; vp++)
128 			*vp = newstr(*vp);
129 	}
130 
131 	return (newvp);
132 }
133 /*
134 **  COPYQUEUE -- copy address queue.
135 **
136 **	This routine is the equivalent of newstr for address queues
137 **	addresses marked with QDONTSEND aren't copied
138 **
139 **	Parameters:
140 **		addr -- list of address structures to copy.
141 **
142 **	Returns:
143 **		a copy of 'addr'.
144 **
145 **	Side Effects:
146 **		none.
147 */
148 
149 ADDRESS *
copyqueue(addr)150 copyqueue(addr)
151 	ADDRESS *addr;
152 {
153 	register ADDRESS *newaddr;
154 	ADDRESS *ret;
155 	register ADDRESS **tail = &ret;
156 
157 	while (addr != NULL)
158 	{
159 		if (!bitset(QDONTSEND, addr->q_flags))
160 		{
161 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
162 			STRUCTCOPY(*addr, *newaddr);
163 			*tail = newaddr;
164 			tail = &newaddr->q_next;
165 		}
166 		addr = addr->q_next;
167 	}
168 	*tail = NULL;
169 
170 	return ret;
171 }
172 /*
173 **  PRINTAV -- print argument vector.
174 **
175 **	Parameters:
176 **		av -- argument vector.
177 **
178 **	Returns:
179 **		none.
180 **
181 **	Side Effects:
182 **		prints av.
183 */
184 
185 void
printav(av)186 printav(av)
187 	register char **av;
188 {
189 	while (*av != NULL)
190 	{
191 		if (tTd(0, 44))
192 			printf("\n\t%08x=", *av);
193 		else
194 			(void) putchar(' ');
195 		xputs(*av++);
196 	}
197 	(void) putchar('\n');
198 }
199 /*
200 **  LOWER -- turn letter into lower case.
201 **
202 **	Parameters:
203 **		c -- character to turn into lower case.
204 **
205 **	Returns:
206 **		c, in lower case.
207 **
208 **	Side Effects:
209 **		none.
210 */
211 
212 char
lower(c)213 lower(c)
214 	register char c;
215 {
216 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
217 }
218 /*
219 **  XPUTS -- put string doing control escapes.
220 **
221 **	Parameters:
222 **		s -- string to put.
223 **
224 **	Returns:
225 **		none.
226 **
227 **	Side Effects:
228 **		output to stdout
229 */
230 
231 void
xputs(s)232 xputs(s)
233 	register const char *s;
234 {
235 	register int c;
236 	register struct metamac *mp;
237 	extern struct metamac MetaMacros[];
238 
239 	if (s == NULL)
240 	{
241 		printf("<null>");
242 		return;
243 	}
244 	while ((c = (*s++ & 0377)) != '\0')
245 	{
246 		if (!isascii(c))
247 		{
248 			if (c == MATCHREPL)
249 			{
250 				putchar('$');
251 				continue;
252 			}
253 			if (c == MACROEXPAND)
254 			{
255 				putchar('$');
256 				if (bitset(0200, *s))
257 					printf("{%s}", macname(*s++ & 0377));
258 				continue;
259 			}
260 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
261 			{
262 				if ((mp->metaval & 0377) == c)
263 				{
264 					printf("$%c", mp->metaname);
265 					break;
266 				}
267 			}
268 			if (mp->metaname != '\0')
269 				continue;
270 			(void) putchar('\\');
271 			c &= 0177;
272 		}
273 		if (isprint(c))
274 		{
275 			putchar(c);
276 			continue;
277 		}
278 
279 		/* wasn't a meta-macro -- find another way to print it */
280 		switch (c)
281 		{
282 		  case '\n':
283 			c = 'n';
284 			break;
285 
286 		  case '\r':
287 			c = 'r';
288 			break;
289 
290 		  case '\t':
291 			c = 't';
292 			break;
293 
294 		  default:
295 			(void) putchar('^');
296 			(void) putchar(c ^ 0100);
297 			continue;
298 		}
299 		(void) putchar('\\');
300 		(void) putchar(c);
301 	}
302 	(void) fflush(stdout);
303 }
304 /*
305 **  MAKELOWER -- Translate a line into lower case
306 **
307 **	Parameters:
308 **		p -- the string to translate.  If NULL, return is
309 **			immediate.
310 **
311 **	Returns:
312 **		none.
313 **
314 **	Side Effects:
315 **		String pointed to by p is translated to lower case.
316 **
317 **	Called By:
318 **		parse
319 */
320 
321 void
makelower(p)322 makelower(p)
323 	register char *p;
324 {
325 	register char c;
326 
327 	if (p == NULL)
328 		return;
329 	for (; (c = *p) != '\0'; p++)
330 		if (isascii(c) && isupper(c))
331 			*p = tolower(c);
332 }
333 /*
334 **  BUILDFNAME -- build full name from gecos style entry.
335 **
336 **	This routine interprets the strange entry that would appear
337 **	in the GECOS field of the password file.
338 **
339 **	Parameters:
340 **		p -- name to build.
341 **		login -- the login name of this user (for &).
342 **		buf -- place to put the result.
343 **
344 **	Returns:
345 **		none.
346 **
347 **	Side Effects:
348 **		none.
349 */
350 
351 void
buildfname(gecos,login,buf)352 buildfname(gecos, login, buf)
353 	register char *gecos;
354 	char *login;
355 	char *buf;
356 {
357 	register char *p;
358 	register char *bp = buf;
359 	int l;
360 
361 	if (*gecos == '*')
362 		gecos++;
363 
364 	/* find length of final string */
365 	l = 0;
366 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
367 	{
368 		if (*p == '&')
369 			l += strlen(login);
370 		else
371 			l++;
372 	}
373 
374 	/* now fill in buf */
375 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
376 	{
377 		if (*p == '&')
378 		{
379 			(void) strcpy(bp, login);
380 			*bp = toupper(*bp);
381 			while (*bp != '\0')
382 				bp++;
383 		}
384 		else
385 			*bp++ = *p;
386 	}
387 	*bp = '\0';
388 }
389 /*
390 **  SAFEFILE -- return true if a file exists and is safe for a user.
391 **
392 **	Parameters:
393 **		fn -- filename to check.
394 **		uid -- user id to compare against.
395 **		gid -- group id to compare against.
396 **		uname -- user name to compare against (used for group
397 **			sets).
398 **		flags -- modifiers:
399 **			SFF_MUSTOWN -- "uid" must own this file.
400 **			SFF_NOSLINK -- file cannot be a symbolic link.
401 **		mode -- mode bits that must match.
402 **		st -- if set, points to a stat structure that will
403 **			get the stat info for the file.
404 **
405 **	Returns:
406 **		0 if fn exists, is owned by uid, and matches mode.
407 **		An errno otherwise.  The actual errno is cleared.
408 **
409 **	Side Effects:
410 **		none.
411 */
412 
413 #include <grp.h>
414 
415 #ifndef S_IXOTH
416 # define S_IXOTH	(S_IEXEC >> 6)
417 #endif
418 
419 #ifndef S_IXGRP
420 # define S_IXGRP	(S_IEXEC >> 3)
421 #endif
422 
423 #ifndef S_IXUSR
424 # define S_IXUSR	(S_IEXEC)
425 #endif
426 
427 #define ST_MODE_NOFILE	0171147		/* unlikely to occur */
428 
429 int
safefile(fn,uid,gid,uname,flags,mode,st)430 safefile(fn, uid, gid, uname, flags, mode, st)
431 	char *fn;
432 	uid_t uid;
433 	gid_t gid;
434 	char *uname;
435 	int flags;
436 	int mode;
437 	struct stat *st;
438 {
439 	register char *p;
440 	register struct group *gr = NULL;
441 	struct stat stbuf;
442 
443 	if (tTd(54, 4))
444 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
445 			fn, uid, gid, flags, mode);
446 	errno = 0;
447 	if (st == NULL)
448 		st = &stbuf;
449 
450 	if (!bitset(SFF_NOPATHCHECK, flags) ||
451 	    (uid == 0 && !bitset(SFF_ROOTOK, flags)))
452 	{
453 		/* check the path to the file for acceptability */
454 		for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
455 		{
456 			*p = '\0';
457 			if (stat(fn, &stbuf) < 0)
458 				break;
459 			if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode))
460 				message("051 WARNING: writable directory %s",
461 					fn);
462 			if (uid == 0 && !bitset(SFF_ROOTOK, flags))
463 			{
464 				if (bitset(S_IXOTH, stbuf.st_mode))
465 					continue;
466 				break;
467 			}
468 			if (stbuf.st_uid == uid &&
469 			    bitset(S_IXUSR, stbuf.st_mode))
470 				continue;
471 			if (stbuf.st_gid == gid &&
472 			    bitset(S_IXGRP, stbuf.st_mode))
473 				continue;
474 #ifndef NO_GROUP_SET
475 			if (uname != NULL &&
476 			    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
477 			     (gr = getgrgid(stbuf.st_gid)) != NULL))
478 			{
479 				register char **gp;
480 
481 				for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
482 					if (strcmp(*gp, uname) == 0)
483 						break;
484 				if (gp != NULL && *gp != NULL &&
485 				    bitset(S_IXGRP, stbuf.st_mode))
486 					continue;
487 			}
488 #endif
489 			if (!bitset(S_IXOTH, stbuf.st_mode))
490 				break;
491 		}
492 		if (p != NULL)
493 		{
494 			int ret = errno;
495 
496 			if (ret == 0)
497 				ret = EACCES;
498 			if (tTd(54, 4))
499 				printf("\t[dir %s] %s\n", fn, errstring(ret));
500 			*p = '/';
501 			return ret;
502 		}
503 	}
504 
505 #ifdef HASLSTAT
506 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
507 					: stat(fn, st)) < 0)
508 #else
509 	if (stat(fn, st) < 0)
510 #endif
511 	{
512 		int ret = errno;
513 
514 		if (tTd(54, 4))
515 			printf("\t%s\n", errstring(ret));
516 
517 		errno = 0;
518 		if (!bitset(SFF_CREAT, flags))
519 			return ret;
520 
521 		/* check to see if legal to create the file */
522 		p = strrchr(fn, '/');
523 		if (p == NULL)
524 			return ENOTDIR;
525 		*p = '\0';
526 		if (stat(fn, &stbuf) >= 0)
527 		{
528 			int md = S_IWRITE|S_IEXEC;
529 			if (stbuf.st_uid != uid)
530 				md >>= 6;
531 			if ((stbuf.st_mode & md) != md)
532 				errno = EACCES;
533 		}
534 		ret = errno;
535 		if (tTd(54, 4))
536 			printf("\t[final dir %s uid %d mode %o] %s\n",
537 				fn, stbuf.st_uid, stbuf.st_mode,
538 				errstring(ret));
539 		*p = '/';
540 		st->st_mode = ST_MODE_NOFILE;
541 		return ret;
542 	}
543 
544 #ifdef S_ISLNK
545 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
546 	{
547 		if (tTd(54, 4))
548 			printf("\t[slink mode %o]\tEPERM\n", st->st_mode);
549 		return EPERM;
550 	}
551 #endif
552 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
553 	{
554 		if (tTd(54, 4))
555 			printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode);
556 		return EPERM;
557 	}
558 	if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(0111, st->st_mode))
559 	{
560 		if (tTd(29, 5))
561 			printf("failed (mode %o: x bits)\n", st->st_mode);
562 		return EPERM;
563 	}
564 
565 	if (bitset(SFF_SETUIDOK, flags))
566 	{
567 #ifdef SUID_ROOT_FILES_OK
568 		if (bitset(S_ISUID, st->st_mode))
569 #else
570 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0)
571 #endif
572 		{
573 			uid = st->st_uid;
574 			uname = NULL;
575 		}
576 #ifdef SUID_ROOT_FILES_OK
577 		if (bitset(S_ISGID, st->st_mode))
578 #else
579 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
580 #endif
581 			gid = st->st_gid;
582 	}
583 
584 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
585 		mode >>= 6;
586 	else if (st->st_uid != uid)
587 	{
588 		mode >>= 3;
589 		if (st->st_gid == gid)
590 			;
591 #ifndef NO_GROUP_SET
592 		else if (uname != NULL &&
593 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
594 			  (gr = getgrgid(st->st_gid)) != NULL))
595 		{
596 			register char **gp;
597 
598 			for (gp = gr->gr_mem; *gp != NULL; gp++)
599 				if (strcmp(*gp, uname) == 0)
600 					break;
601 			if (*gp == NULL)
602 				mode >>= 3;
603 		}
604 #endif
605 		else
606 			mode >>= 3;
607 	}
608 	if (tTd(54, 4))
609 		printf("\t[uid %d, stat %o, mode %o] ",
610 			st->st_uid, st->st_mode, mode);
611 	if ((st->st_uid == uid || st->st_uid == 0 ||
612 	     !bitset(SFF_MUSTOWN, flags)) &&
613 	    (st->st_mode & mode) == mode)
614 	{
615 		if (tTd(54, 4))
616 			printf("\tOK\n");
617 		return 0;
618 	}
619 	if (tTd(54, 4))
620 		printf("\tEACCES\n");
621 	return EACCES;
622 }
623 /*
624 **  SAFEFOPEN -- do a file open with extra checking
625 **
626 **	Parameters:
627 **		fn -- the file name to open.
628 **		omode -- the open-style mode flags.
629 **		cmode -- the create-style mode flags.
630 **		sff -- safefile flags.
631 **
632 **	Returns:
633 **		Same as fopen.
634 */
635 
636 #ifndef O_ACCMODE
637 # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
638 #endif
639 
640 FILE *
safefopen(fn,omode,cmode,sff)641 safefopen(fn, omode, cmode, sff)
642 	char *fn;
643 	int omode;
644 	int cmode;
645 	int sff;
646 {
647 	int rval;
648 	FILE *fp;
649 	int smode;
650 	struct stat stb, sta;
651 
652 	if (bitset(O_CREAT, omode))
653 		sff |= SFF_CREAT;
654 	smode = 0;
655 	switch (omode & O_ACCMODE)
656 	{
657 	  case O_RDONLY:
658 		smode = S_IREAD;
659 		break;
660 
661 	  case O_WRONLY:
662 		smode = S_IWRITE;
663 		break;
664 
665 	  case O_RDWR:
666 		smode = S_IREAD|S_IWRITE;
667 		break;
668 
669 	  default:
670 		smode = 0;
671 		break;
672 	}
673 	if (bitset(SFF_OPENASROOT, sff))
674 		rval = safefile(fn, 0, 0, NULL, sff, smode, &stb);
675 	else
676 		rval = safefile(fn, RealUid, RealGid, RealUserName,
677 				sff, smode, &stb);
678 	if (rval != 0)
679 	{
680 		errno = rval;
681 		return NULL;
682 	}
683 	if (stb.st_mode == ST_MODE_NOFILE)
684 		omode |= O_EXCL;
685 
686 	fp = dfopen(fn, omode, cmode);
687 	if (fp == NULL)
688 		return NULL;
689 	if (bitset(O_EXCL, omode))
690 		return fp;
691 	if (fstat(fileno(fp), &sta) < 0 ||
692 	    sta.st_nlink != stb.st_nlink ||
693 	    sta.st_dev != stb.st_dev ||
694 	    sta.st_ino != stb.st_ino ||
695 	    sta.st_uid != stb.st_uid ||
696 	    sta.st_gid != stb.st_gid)
697 	{
698 		syserr("554 cannot open: file %s changed after open", fn);
699 		fclose(fp);
700 		errno = EPERM;
701 		return NULL;
702 	}
703 	return fp;
704 }
705 /*
706 **  FIXCRLF -- fix <CR><LF> in line.
707 **
708 **	Looks for the <CR><LF> combination and turns it into the
709 **	UNIX canonical <NL> character.  It only takes one line,
710 **	i.e., it is assumed that the first <NL> found is the end
711 **	of the line.
712 **
713 **	Parameters:
714 **		line -- the line to fix.
715 **		stripnl -- if true, strip the newline also.
716 **
717 **	Returns:
718 **		none.
719 **
720 **	Side Effects:
721 **		line is changed in place.
722 */
723 
724 void
fixcrlf(line,stripnl)725 fixcrlf(line, stripnl)
726 	char *line;
727 	bool stripnl;
728 {
729 	register char *p;
730 
731 	p = strchr(line, '\n');
732 	if (p == NULL)
733 		return;
734 	if (p > line && p[-1] == '\r')
735 		p--;
736 	if (!stripnl)
737 		*p++ = '\n';
738 	*p = '\0';
739 }
740 /*
741 **  DFOPEN -- determined file open
742 **
743 **	This routine has the semantics of fopen, except that it will
744 **	keep trying a few times to make this happen.  The idea is that
745 **	on very loaded systems, we may run out of resources (inodes,
746 **	whatever), so this tries to get around it.
747 */
748 
749 struct omodes
750 {
751 	int	mask;
752 	int	mode;
753 	char	*farg;
754 } OpenModes[] =
755 {
756 	O_ACCMODE,		O_RDONLY,		"r",
757 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
758 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
759 	O_TRUNC,		0,			"w+",
760 	O_APPEND,		O_APPEND,		"a+",
761 	0,			0,			"r+",
762 };
763 
764 FILE *
dfopen(filename,omode,cmode)765 dfopen(filename, omode, cmode)
766 	char *filename;
767 	int omode;
768 	int cmode;
769 {
770 	register int tries;
771 	int fd;
772 	register struct omodes *om;
773 	struct stat st;
774 
775 	for (om = OpenModes; om->mask != 0; om++)
776 		if ((omode & om->mask) == om->mode)
777 			break;
778 
779 	for (tries = 0; tries < 10; tries++)
780 	{
781 		sleep((unsigned) (10 * tries));
782 		errno = 0;
783 		fd = open(filename, omode, cmode);
784 		if (fd >= 0)
785 			break;
786 		switch (errno)
787 		{
788 		  case ENFILE:		/* system file table full */
789 		  case EINTR:		/* interrupted syscall */
790 #ifdef ETXTBSY
791 		  case ETXTBSY:		/* Apollo: net file locked */
792 #endif
793 			continue;
794 		}
795 		break;
796 	}
797 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
798 	{
799 		int locktype;
800 
801 		/* lock the file to avoid accidental conflicts */
802 		if ((omode & O_ACCMODE) != O_RDONLY)
803 			locktype = LOCK_EX;
804 		else
805 			locktype = LOCK_SH;
806 		(void) lockfile(fd, filename, NULL, locktype);
807 		errno = 0;
808 	}
809 	if (fd < 0)
810 		return NULL;
811 	else
812 		return fdopen(fd, om->farg);
813 }
814 /*
815 **  PUTLINE -- put a line like fputs obeying SMTP conventions
816 **
817 **	This routine always guarantees outputing a newline (or CRLF,
818 **	as appropriate) at the end of the string.
819 **
820 **	Parameters:
821 **		l -- line to put.
822 **		mci -- the mailer connection information.
823 **
824 **	Returns:
825 **		none
826 **
827 **	Side Effects:
828 **		output of l to fp.
829 */
830 
831 void
putline(l,mci)832 putline(l, mci)
833 	register char *l;
834 	register MCI *mci;
835 {
836 	putxline(l, mci, PXLF_MAPFROM);
837 }
838 /*
839 **  PUTXLINE -- putline with flags bits.
840 **
841 **	This routine always guarantees outputing a newline (or CRLF,
842 **	as appropriate) at the end of the string.
843 **
844 **	Parameters:
845 **		l -- line to put.
846 **		mci -- the mailer connection information.
847 **		pxflags -- flag bits:
848 **		    PXLF_MAPFROM -- map From_ to >From_.
849 **		    PXLF_STRIP8BIT -- strip 8th bit.
850 **
851 **	Returns:
852 **		none
853 **
854 **	Side Effects:
855 **		output of l to fp.
856 */
857 
858 void
putxline(l,mci,pxflags)859 putxline(l, mci, pxflags)
860 	register char *l;
861 	register MCI *mci;
862 	int pxflags;
863 {
864 	register char *p;
865 	register char svchar;
866 	int slop = 0;
867 
868 	/* strip out 0200 bits -- these can look like TELNET protocol */
869 	if (bitset(MCIF_7BIT, mci->mci_flags) ||
870 	    bitset(PXLF_STRIP8BIT, pxflags))
871 	{
872 		for (p = l; (svchar = *p) != '\0'; ++p)
873 			if (bitset(0200, svchar))
874 				*p = svchar &~ 0200;
875 	}
876 
877 	do
878 	{
879 		/* find the end of the line */
880 		p = strchr(l, '\n');
881 		if (p == NULL)
882 			p = &l[strlen(l)];
883 
884 		if (TrafficLogFile != NULL)
885 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
886 
887 		/* check for line overflow */
888 		while (mci->mci_mailer->m_linelimit > 0 &&
889 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
890 		{
891 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
892 
893 			svchar = *q;
894 			*q = '\0';
895 			if (l[0] == '.' && slop == 0 &&
896 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
897 			{
898 				(void) putc('.', mci->mci_out);
899 				if (TrafficLogFile != NULL)
900 					(void) putc('.', TrafficLogFile);
901 			}
902 			else if (l[0] == 'F' && slop == 0 &&
903 				 bitset(PXLF_MAPFROM, pxflags) &&
904 				 strncmp(l, "From ", 5) == 0 &&
905 				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
906 			{
907 				(void) putc('>', mci->mci_out);
908 				if (TrafficLogFile != NULL)
909 					(void) putc('>', TrafficLogFile);
910 			}
911 			fputs(l, mci->mci_out);
912 			(void) putc('!', mci->mci_out);
913 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
914 			(void) putc(' ', mci->mci_out);
915 			if (TrafficLogFile != NULL)
916 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
917 					l, getpid());
918 			*q = svchar;
919 			l = q;
920 			slop = 1;
921 		}
922 
923 		/* output last part */
924 		if (l[0] == '.' && slop == 0 &&
925 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
926 		{
927 			(void) putc('.', mci->mci_out);
928 			if (TrafficLogFile != NULL)
929 				(void) putc('.', TrafficLogFile);
930 		}
931 		if (TrafficLogFile != NULL)
932 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
933 		for ( ; l < p; ++l)
934 			(void) putc(*l, mci->mci_out);
935 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
936 		if (*l == '\n')
937 			++l;
938 	} while (l[0] != '\0');
939 }
940 /*
941 **  XUNLINK -- unlink a file, doing logging as appropriate.
942 **
943 **	Parameters:
944 **		f -- name of file to unlink.
945 **
946 **	Returns:
947 **		none.
948 **
949 **	Side Effects:
950 **		f is unlinked.
951 */
952 
953 void
xunlink(f)954 xunlink(f)
955 	char *f;
956 {
957 	register int i;
958 
959 # ifdef LOG
960 	if (LogLevel > 98)
961 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
962 # endif /* LOG */
963 
964 	i = unlink(f);
965 # ifdef LOG
966 	if (i < 0 && LogLevel > 97)
967 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
968 # endif /* LOG */
969 }
970 /*
971 **  XFCLOSE -- close a file, doing logging as appropriate.
972 **
973 **	Parameters:
974 **		fp -- file pointer for the file to close
975 **		a, b -- miscellaneous crud to print for debugging
976 **
977 **	Returns:
978 **		none.
979 **
980 **	Side Effects:
981 **		fp is closed.
982 */
983 
984 void
xfclose(fp,a,b)985 xfclose(fp, a, b)
986 	FILE *fp;
987 	char *a, *b;
988 {
989 	if (tTd(53, 99))
990 		printf("xfclose(%x) %s %s\n", fp, a, b);
991 #if XDEBUG
992 	if (fileno(fp) == 1)
993 		syserr("xfclose(%s %s): fd = 1", a, b);
994 #endif
995 	if (fclose(fp) < 0 && tTd(53, 99))
996 		printf("xfclose FAILURE: %s\n", errstring(errno));
997 }
998 /*
999 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1000 **
1001 **	Parameters:
1002 **		buf -- place to put the input line.
1003 **		siz -- size of buf.
1004 **		fp -- file to read from.
1005 **		timeout -- the timeout before error occurs.
1006 **		during -- what we are trying to read (for error messages).
1007 **
1008 **	Returns:
1009 **		NULL on error (including timeout).  This will also leave
1010 **			buf containing a null string.
1011 **		buf otherwise.
1012 **
1013 **	Side Effects:
1014 **		none.
1015 */
1016 
1017 static jmp_buf	CtxReadTimeout;
1018 static void	readtimeout();
1019 
1020 char *
sfgets(buf,siz,fp,timeout,during)1021 sfgets(buf, siz, fp, timeout, during)
1022 	char *buf;
1023 	int siz;
1024 	FILE *fp;
1025 	time_t timeout;
1026 	char *during;
1027 {
1028 	register EVENT *ev = NULL;
1029 	register char *p;
1030 
1031 	if (fp == NULL)
1032 	{
1033 		buf[0] = '\0';
1034 		return NULL;
1035 	}
1036 
1037 	/* set the timeout */
1038 	if (timeout != 0)
1039 	{
1040 		if (setjmp(CtxReadTimeout) != 0)
1041 		{
1042 # ifdef LOG
1043 			syslog(LOG_NOTICE,
1044 			    "timeout waiting for input from %s during %s\n",
1045 			    CurHostName? CurHostName: "local", during);
1046 # endif
1047 			errno = 0;
1048 			usrerr("451 timeout waiting for input during %s",
1049 				during);
1050 			buf[0] = '\0';
1051 #if XDEBUG
1052 			checkfd012(during);
1053 #endif
1054 			return (NULL);
1055 		}
1056 		ev = setevent(timeout, readtimeout, 0);
1057 	}
1058 
1059 	/* try to read */
1060 	p = NULL;
1061 	while (!feof(fp) && !ferror(fp))
1062 	{
1063 		errno = 0;
1064 		p = fgets(buf, siz, fp);
1065 		if (p != NULL || errno != EINTR)
1066 			break;
1067 		clearerr(fp);
1068 	}
1069 
1070 	/* clear the event if it has not sprung */
1071 	clrevent(ev);
1072 
1073 	/* clean up the books and exit */
1074 	LineNumber++;
1075 	if (p == NULL)
1076 	{
1077 		buf[0] = '\0';
1078 		if (TrafficLogFile != NULL)
1079 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
1080 		return (NULL);
1081 	}
1082 	if (TrafficLogFile != NULL)
1083 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
1084 	if (SevenBitInput)
1085 	{
1086 		for (p = buf; *p != '\0'; p++)
1087 			*p &= ~0200;
1088 	}
1089 	else if (!HasEightBits)
1090 	{
1091 		for (p = buf; *p != '\0'; p++)
1092 		{
1093 			if (bitset(0200, *p))
1094 			{
1095 				HasEightBits = TRUE;
1096 				break;
1097 			}
1098 		}
1099 	}
1100 	return (buf);
1101 }
1102 
1103 static void
readtimeout(timeout)1104 readtimeout(timeout)
1105 	time_t timeout;
1106 {
1107 	longjmp(CtxReadTimeout, 1);
1108 }
1109 /*
1110 **  FGETFOLDED -- like fgets, but know about folded lines.
1111 **
1112 **	Parameters:
1113 **		buf -- place to put result.
1114 **		n -- bytes available.
1115 **		f -- file to read from.
1116 **
1117 **	Returns:
1118 **		input line(s) on success, NULL on error or EOF.
1119 **		This will normally be buf -- unless the line is too
1120 **			long, when it will be xalloc()ed.
1121 **
1122 **	Side Effects:
1123 **		buf gets lines from f, with continuation lines (lines
1124 **		with leading white space) appended.  CRLF's are mapped
1125 **		into single newlines.  Any trailing NL is stripped.
1126 */
1127 
1128 char *
fgetfolded(buf,n,f)1129 fgetfolded(buf, n, f)
1130 	char *buf;
1131 	register int n;
1132 	FILE *f;
1133 {
1134 	register char *p = buf;
1135 	char *bp = buf;
1136 	register int i;
1137 
1138 	n--;
1139 	while ((i = getc(f)) != EOF)
1140 	{
1141 		if (i == '\r')
1142 		{
1143 			i = getc(f);
1144 			if (i != '\n')
1145 			{
1146 				if (i != EOF)
1147 					(void) ungetc(i, f);
1148 				i = '\r';
1149 			}
1150 		}
1151 		if (--n <= 0)
1152 		{
1153 			/* allocate new space */
1154 			char *nbp;
1155 			int nn;
1156 
1157 			nn = (p - bp);
1158 			if (nn < MEMCHUNKSIZE)
1159 				nn *= 2;
1160 			else
1161 				nn += MEMCHUNKSIZE;
1162 			nbp = xalloc(nn);
1163 			bcopy(bp, nbp, p - bp);
1164 			p = &nbp[p - bp];
1165 			if (bp != buf)
1166 				free(bp);
1167 			bp = nbp;
1168 			n = nn - (p - bp);
1169 		}
1170 		*p++ = i;
1171 		if (i == '\n')
1172 		{
1173 			LineNumber++;
1174 			i = getc(f);
1175 			if (i != EOF)
1176 				(void) ungetc(i, f);
1177 			if (i != ' ' && i != '\t')
1178 				break;
1179 		}
1180 	}
1181 	if (p == bp)
1182 		return (NULL);
1183 	if (p[-1] == '\n')
1184 		p--;
1185 	*p = '\0';
1186 	return (bp);
1187 }
1188 /*
1189 **  CURTIME -- return current time.
1190 **
1191 **	Parameters:
1192 **		none.
1193 **
1194 **	Returns:
1195 **		the current time.
1196 **
1197 **	Side Effects:
1198 **		none.
1199 */
1200 
1201 time_t
curtime()1202 curtime()
1203 {
1204 	auto time_t t;
1205 
1206 	(void) time(&t);
1207 	return (t);
1208 }
1209 /*
1210 **  ATOBOOL -- convert a string representation to boolean.
1211 **
1212 **	Defaults to "TRUE"
1213 **
1214 **	Parameters:
1215 **		s -- string to convert.  Takes "tTyY" as true,
1216 **			others as false.
1217 **
1218 **	Returns:
1219 **		A boolean representation of the string.
1220 **
1221 **	Side Effects:
1222 **		none.
1223 */
1224 
1225 bool
atobool(s)1226 atobool(s)
1227 	register char *s;
1228 {
1229 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1230 		return (TRUE);
1231 	return (FALSE);
1232 }
1233 /*
1234 **  ATOOCT -- convert a string representation to octal.
1235 **
1236 **	Parameters:
1237 **		s -- string to convert.
1238 **
1239 **	Returns:
1240 **		An integer representing the string interpreted as an
1241 **		octal number.
1242 **
1243 **	Side Effects:
1244 **		none.
1245 */
1246 
1247 int
atooct(s)1248 atooct(s)
1249 	register char *s;
1250 {
1251 	register int i = 0;
1252 
1253 	while (*s >= '0' && *s <= '7')
1254 		i = (i << 3) | (*s++ - '0');
1255 	return (i);
1256 }
1257 /*
1258 **  WAITFOR -- wait for a particular process id.
1259 **
1260 **	Parameters:
1261 **		pid -- process id to wait for.
1262 **
1263 **	Returns:
1264 **		status of pid.
1265 **		-1 if pid never shows up.
1266 **
1267 **	Side Effects:
1268 **		none.
1269 */
1270 
1271 int
waitfor(pid)1272 waitfor(pid)
1273 	int pid;
1274 {
1275 #ifdef WAITUNION
1276 	union wait st;
1277 #else
1278 	auto int st;
1279 #endif
1280 	int i;
1281 
1282 	do
1283 	{
1284 		errno = 0;
1285 		i = wait(&st);
1286 	} while ((i >= 0 || errno == EINTR) && i != pid);
1287 	if (i < 0)
1288 		return -1;
1289 #ifdef WAITUNION
1290 	return st.w_status;
1291 #else
1292 	return st;
1293 #endif
1294 }
1295 /*
1296 **  BITINTERSECT -- tell if two bitmaps intersect
1297 **
1298 **	Parameters:
1299 **		a, b -- the bitmaps in question
1300 **
1301 **	Returns:
1302 **		TRUE if they have a non-null intersection
1303 **		FALSE otherwise
1304 **
1305 **	Side Effects:
1306 **		none.
1307 */
1308 
1309 bool
bitintersect(a,b)1310 bitintersect(a, b)
1311 	BITMAP a;
1312 	BITMAP b;
1313 {
1314 	int i;
1315 
1316 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1317 		if ((a[i] & b[i]) != 0)
1318 			return (TRUE);
1319 	return (FALSE);
1320 }
1321 /*
1322 **  BITZEROP -- tell if a bitmap is all zero
1323 **
1324 **	Parameters:
1325 **		map -- the bit map to check
1326 **
1327 **	Returns:
1328 **		TRUE if map is all zero.
1329 **		FALSE if there are any bits set in map.
1330 **
1331 **	Side Effects:
1332 **		none.
1333 */
1334 
1335 bool
bitzerop(map)1336 bitzerop(map)
1337 	BITMAP map;
1338 {
1339 	int i;
1340 
1341 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1342 		if (map[i] != 0)
1343 			return (FALSE);
1344 	return (TRUE);
1345 }
1346 /*
1347 **  STRCONTAINEDIN -- tell if one string is contained in another
1348 **
1349 **	Parameters:
1350 **		a -- possible substring.
1351 **		b -- possible superstring.
1352 **
1353 **	Returns:
1354 **		TRUE if a is contained in b.
1355 **		FALSE otherwise.
1356 */
1357 
1358 bool
strcontainedin(a,b)1359 strcontainedin(a, b)
1360 	register char *a;
1361 	register char *b;
1362 {
1363 	int la;
1364 	int lb;
1365 	int c;
1366 
1367 	la = strlen(a);
1368 	lb = strlen(b);
1369 	c = *a;
1370 	if (isascii(c) && isupper(c))
1371 		c = tolower(c);
1372 	for (; lb-- >= la; b++)
1373 	{
1374 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1375 			continue;
1376 		if (strncasecmp(a, b, la) == 0)
1377 			return TRUE;
1378 	}
1379 	return FALSE;
1380 }
1381 /*
1382 **  CHECKFD012 -- check low numbered file descriptors
1383 **
1384 **	File descriptors 0, 1, and 2 should be open at all times.
1385 **	This routine verifies that, and fixes it if not true.
1386 **
1387 **	Parameters:
1388 **		where -- a tag printed if the assertion failed
1389 **
1390 **	Returns:
1391 **		none
1392 */
1393 
1394 void
checkfd012(where)1395 checkfd012(where)
1396 	char *where;
1397 {
1398 #if XDEBUG
1399 	register int i;
1400 	struct stat stbuf;
1401 
1402 	for (i = 0; i < 3; i++)
1403 	{
1404 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
1405 		{
1406 			/* oops.... */
1407 			int fd;
1408 
1409 			syserr("%s: fd %d not open", where, i);
1410 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1411 			if (fd != i)
1412 			{
1413 				(void) dup2(fd, i);
1414 				(void) close(fd);
1415 			}
1416 		}
1417 	}
1418 #endif /* XDEBUG */
1419 }
1420 /*
1421 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1422 **
1423 **	Parameters:
1424 **		logit -- if set, send output to syslog; otherwise
1425 **			print for debugging.
1426 **
1427 **	Returns:
1428 **		none.
1429 */
1430 
1431 #include <arpa/inet.h>
1432 
1433 void
printopenfds(logit)1434 printopenfds(logit)
1435 	bool logit;
1436 {
1437 	register int fd;
1438 	extern int DtableSize;
1439 
1440 	for (fd = 0; fd < DtableSize; fd++)
1441 		dumpfd(fd, FALSE, logit);
1442 }
1443 /*
1444 **  DUMPFD -- dump a file descriptor
1445 **
1446 **	Parameters:
1447 **		fd -- the file descriptor to dump.
1448 **		printclosed -- if set, print a notification even if
1449 **			it is closed; otherwise print nothing.
1450 **		logit -- if set, send output to syslog instead of stdout.
1451 */
1452 
1453 void
dumpfd(fd,printclosed,logit)1454 dumpfd(fd, printclosed, logit)
1455 	int fd;
1456 	bool printclosed;
1457 	bool logit;
1458 {
1459 	register char *p;
1460 	char *hp;
1461 	char *fmtstr;
1462 	SOCKADDR sa;
1463 	auto int slen;
1464 	struct stat st;
1465 	char buf[200];
1466 	extern char *hostnamebyanyaddr();
1467 
1468 	p = buf;
1469 	sprintf(p, "%3d: ", fd);
1470 	p += strlen(p);
1471 
1472 	if (fstat(fd, &st) < 0)
1473 	{
1474 		if (printclosed || errno != EBADF)
1475 		{
1476 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
1477 			goto printit;
1478 		}
1479 		return;
1480 	}
1481 
1482 	slen = fcntl(fd, F_GETFL, NULL);
1483 	if (slen != -1)
1484 	{
1485 		sprintf(p, "fl=0x%x, ", slen);
1486 		p += strlen(p);
1487 	}
1488 
1489 	sprintf(p, "mode=%o: ", st.st_mode);
1490 	p += strlen(p);
1491 	switch (st.st_mode & S_IFMT)
1492 	{
1493 #ifdef S_IFSOCK
1494 	  case S_IFSOCK:
1495 		sprintf(p, "SOCK ");
1496 		p += strlen(p);
1497 		slen = sizeof sa;
1498 		if (getsockname(fd, &sa.sa, &slen) < 0)
1499 			sprintf(p, "(%s)", errstring(errno));
1500 		else
1501 		{
1502 			hp = hostnamebyanyaddr(&sa);
1503 			if (sa.sa.sa_family == AF_INET)
1504 				sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
1505 			else
1506 				sprintf(p, "%s", hp);
1507 		}
1508 		p += strlen(p);
1509 		sprintf(p, "->");
1510 		p += strlen(p);
1511 		slen = sizeof sa;
1512 		if (getpeername(fd, &sa.sa, &slen) < 0)
1513 			sprintf(p, "(%s)", errstring(errno));
1514 		else
1515 		{
1516 			hp = hostnamebyanyaddr(&sa);
1517 			if (sa.sa.sa_family == AF_INET)
1518 				sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
1519 			else
1520 				sprintf(p, "%s", hp);
1521 		}
1522 		break;
1523 #endif
1524 
1525 	  case S_IFCHR:
1526 		sprintf(p, "CHR: ");
1527 		p += strlen(p);
1528 		goto defprint;
1529 
1530 	  case S_IFBLK:
1531 		sprintf(p, "BLK: ");
1532 		p += strlen(p);
1533 		goto defprint;
1534 
1535 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1536 	  case S_IFIFO:
1537 		sprintf(p, "FIFO: ");
1538 		p += strlen(p);
1539 		goto defprint;
1540 #endif
1541 
1542 #ifdef S_IFDIR
1543 	  case S_IFDIR:
1544 		sprintf(p, "DIR: ");
1545 		p += strlen(p);
1546 		goto defprint;
1547 #endif
1548 
1549 #ifdef S_IFLNK
1550 	  case S_IFLNK:
1551 		sprintf(p, "LNK: ");
1552 		p += strlen(p);
1553 		goto defprint;
1554 #endif
1555 
1556 	  default:
1557 defprint:
1558 		if (sizeof st.st_size > sizeof (long))
1559 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
1560 		else
1561 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
1562 		sprintf(p, fmtstr,
1563 			major(st.st_dev), minor(st.st_dev), st.st_ino,
1564 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
1565 		break;
1566 	}
1567 
1568 printit:
1569 #ifdef LOG
1570 	if (logit)
1571 		syslog(LOG_DEBUG, "%s", buf);
1572 	else
1573 #endif
1574 		printf("%s\n", buf);
1575 }
1576 /*
1577 **  SHORTENSTRING -- return short version of a string
1578 **
1579 **	If the string is already short, just return it.  If it is too
1580 **	long, return the head and tail of the string.
1581 **
1582 **	Parameters:
1583 **		s -- the string to shorten.
1584 **		m -- the max length of the string.
1585 **
1586 **	Returns:
1587 **		Either s or a short version of s.
1588 */
1589 
1590 #ifndef MAXSHORTSTR
1591 # define MAXSHORTSTR	203
1592 #endif
1593 
1594 char *
shortenstring(s,m)1595 shortenstring(s, m)
1596 	register const char *s;
1597 	int m;
1598 {
1599 	int l;
1600 	static char buf[MAXSHORTSTR + 1];
1601 
1602 	l = strlen(s);
1603 	if (l < m)
1604 		return (char *) s;
1605 	if (m > MAXSHORTSTR)
1606 		m = MAXSHORTSTR;
1607 	else if (m < 10)
1608 	{
1609 		if (m < 5)
1610 		{
1611 			strncpy(buf, s, m);
1612 			buf[m] = '\0';
1613 			return buf;
1614 		}
1615 		strncpy(buf, s, m - 3);
1616 		strcpy(buf + m - 3, "...");
1617 		return buf;
1618 	}
1619 	m = (m - 3) / 2;
1620 	strncpy(buf, s, m);
1621 	strcpy(buf + m, "...");
1622 	strcpy(buf + m + 3, s + l - m);
1623 	return buf;
1624 }
1625 /*
1626 **  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1627 **
1628 **	Parameters:
1629 **		host -- the host to shorten (stripped in place).
1630 **
1631 **	Returns:
1632 **		none.
1633 */
1634 
1635 void
shorten_hostname(host)1636 shorten_hostname(host)
1637 	char host[];
1638 {
1639 	register char *p;
1640 	char *mydom;
1641 	int i;
1642 	bool canon = FALSE;
1643 
1644 	/* strip off final dot */
1645 	p = &host[strlen(host) - 1];
1646 	if (*p == '.')
1647 	{
1648 		*p = '\0';
1649 		canon = TRUE;
1650 	}
1651 
1652 	/* see if there is any domain at all -- if not, we are done */
1653 	p = strchr(host, '.');
1654 	if (p == NULL)
1655 		return;
1656 
1657 	/* yes, we have a domain -- see if it looks like us */
1658 	mydom = macvalue('m', CurEnv);
1659 	if (mydom == NULL)
1660 		mydom = "";
1661 	i = strlen(++p);
1662 	if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1663 	    (mydom[i] == '.' || mydom[i] == '\0'))
1664 		*--p = '\0';
1665 }
1666 /*
1667 **  PROG_OPEN -- open a program for reading
1668 **
1669 **	Parameters:
1670 **		argv -- the argument list.
1671 **		pfd -- pointer to a place to store the file descriptor.
1672 **		e -- the current envelope.
1673 **
1674 **	Returns:
1675 **		pid of the process -- -1 if it failed.
1676 */
1677 
1678 int
prog_open(argv,pfd,e)1679 prog_open(argv, pfd, e)
1680 	char **argv;
1681 	int *pfd;
1682 	ENVELOPE *e;
1683 {
1684 	int pid;
1685 	int i;
1686 	int saveerrno;
1687 	int fdv[2];
1688 	char *p, *q;
1689 	char buf[MAXLINE + 1];
1690 	extern int DtableSize;
1691 
1692 	if (pipe(fdv) < 0)
1693 	{
1694 		syserr("%s: cannot create pipe for stdout", argv[0]);
1695 		return -1;
1696 	}
1697 	pid = fork();
1698 	if (pid < 0)
1699 	{
1700 		syserr("%s: cannot fork", argv[0]);
1701 		close(fdv[0]);
1702 		close(fdv[1]);
1703 		return -1;
1704 	}
1705 	if (pid > 0)
1706 	{
1707 		/* parent */
1708 		close(fdv[1]);
1709 		*pfd = fdv[0];
1710 		return pid;
1711 	}
1712 
1713 	/* child -- close stdin */
1714 	close(0);
1715 
1716 	/* stdout goes back to parent */
1717 	close(fdv[0]);
1718 	if (dup2(fdv[1], 1) < 0)
1719 	{
1720 		syserr("%s: cannot dup2 for stdout", argv[0]);
1721 		_exit(EX_OSERR);
1722 	}
1723 	close(fdv[1]);
1724 
1725 	/* stderr goes to transcript if available */
1726 	if (e->e_xfp != NULL)
1727 	{
1728 		if (dup2(fileno(e->e_xfp), 2) < 0)
1729 		{
1730 			syserr("%s: cannot dup2 for stderr", argv[0]);
1731 			_exit(EX_OSERR);
1732 		}
1733 	}
1734 
1735 	/* this process has no right to the queue file */
1736 	if (e->e_lockfp != NULL)
1737 		close(fileno(e->e_lockfp));
1738 
1739 	/* run as default user */
1740 	endpwent();
1741 	setgid(DefGid);
1742 	setuid(DefUid);
1743 
1744 	/* run in some directory */
1745 	if (ProgMailer != NULL)
1746 		p = ProgMailer->m_execdir;
1747 	else
1748 		p = NULL;
1749 	for (; p != NULL; p = q)
1750 	{
1751 		q = strchr(p, ':');
1752 		if (q != NULL)
1753 			*q = '\0';
1754 		expand(p, buf, sizeof buf, e);
1755 		if (q != NULL)
1756 			*q++ = ':';
1757 		if (buf[0] != '\0' && chdir(buf) >= 0)
1758 			break;
1759 	}
1760 	if (p == NULL)
1761 	{
1762 		/* backup directories */
1763 		if (chdir("/tmp") < 0)
1764 			(void) chdir("/");
1765 	}
1766 
1767 	/* arrange for all the files to be closed */
1768 	for (i = 3; i < DtableSize; i++)
1769 	{
1770 		register int j;
1771 
1772 		if ((j = fcntl(i, F_GETFD, 0)) != -1)
1773 			(void) fcntl(i, F_SETFD, j | 1);
1774 	}
1775 
1776 	/* now exec the process */
1777 	execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
1778 
1779 	/* woops!  failed */
1780 	saveerrno = errno;
1781 	syserr("%s: cannot exec", argv[0]);
1782 	if (transienterror(saveerrno))
1783 		_exit(EX_OSERR);
1784 	_exit(EX_CONFIG);
1785 }
1786 /*
1787 **  GET_COLUMN  -- look up a Column in a line buffer
1788 **
1789 **	Parameters:
1790 **		line -- the raw text line to search.
1791 **		col -- the column number to fetch.
1792 **		delim -- the delimiter between columns.  If null,
1793 **			use white space.
1794 **		buf -- the output buffer.
1795 **
1796 **	Returns:
1797 **		buf if successful.
1798 **		NULL otherwise.
1799 */
1800 
1801 char *
get_column(line,col,delim,buf)1802 get_column(line, col, delim, buf)
1803 	char line[];
1804 	int col;
1805 	char delim;
1806 	char buf[];
1807 {
1808 	char *p;
1809 	char *begin, *end;
1810 	int i;
1811 	char delimbuf[3];
1812 
1813 	if (delim == '\0')
1814 		strcpy(delimbuf, "\n\t ");
1815 	else
1816 	{
1817 		delimbuf[0] = delim;
1818 		delimbuf[1] = '\0';
1819 	}
1820 
1821 	p = line;
1822 	if (*p == '\0')
1823 		return NULL;			/* line empty */
1824 	if (*p == delim && col == 0)
1825 		return NULL;			/* first column empty */
1826 
1827 	begin = line;
1828 
1829 	if (col == 0 && delim == '\0')
1830 	{
1831 		while (*begin && isspace(*begin))
1832 			begin++;
1833 	}
1834 
1835 	for (i = 0; i < col; i++)
1836 	{
1837 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
1838 			return NULL;		/* no such column */
1839 		begin++;
1840 		if (delim == '\0')
1841 		{
1842 			while (*begin && isspace(*begin))
1843 				begin++;
1844 		}
1845 	}
1846 
1847 	end = strpbrk(begin, delimbuf);
1848 	if (end == NULL)
1849 	{
1850 		strcpy(buf, begin);
1851 	}
1852 	else
1853 	{
1854 		strncpy(buf, begin, end - begin);
1855 		buf[end - begin] = '\0';
1856 	}
1857 	return buf;
1858 }
1859 /*
1860 **  CLEANSTRCPY -- copy string keeping out bogus characters
1861 **
1862 **	Parameters:
1863 **		t -- "to" string.
1864 **		f -- "from" string.
1865 **		l -- length of space available in "to" string.
1866 **
1867 **	Returns:
1868 **		none.
1869 */
1870 
1871 void
cleanstrcpy(t,f,l)1872 cleanstrcpy(t, f, l)
1873 	register char *t;
1874 	register char *f;
1875 	int l;
1876 {
1877 #ifdef LOG
1878 	/* check for newlines and log if necessary */
1879 	(void) denlstring(f, TRUE, TRUE);
1880 #endif
1881 
1882 	l--;
1883 	while (l > 0 && *f != '\0')
1884 	{
1885 		if (isascii(*f) &&
1886 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
1887 		{
1888 			l--;
1889 			*t++ = *f;
1890 		}
1891 		f++;
1892 	}
1893 	*t = '\0';
1894 }
1895 /*
1896 **  DENLSTRING -- convert newlines in a string to spaces
1897 **
1898 **	Parameters:
1899 **		s -- the input string
1900 **		strict -- if set, don't permit continuation lines.
1901 **		logattacks -- if set, log attempted attacks.
1902 **
1903 **	Returns:
1904 **		A pointer to a version of the string with newlines
1905 **		mapped to spaces.  This should be copied.
1906 */
1907 
1908 char *
denlstring(s,strict,logattacks)1909 denlstring(s, strict, logattacks)
1910 	char *s;
1911 	bool strict;
1912 	bool logattacks;
1913 {
1914 	register char *p;
1915 	int l;
1916 	static char *bp = NULL;
1917 	static int bl = 0;
1918 
1919 	p = s;
1920 	while ((p = strchr(p, '\n')) != NULL)
1921 		if (strict || (*++p != ' ' && *p != '\t'))
1922 			break;
1923 	if (p == NULL)
1924 		return s;
1925 
1926 	l = strlen(s) + 1;
1927 	if (bl < l)
1928 	{
1929 		/* allocate more space */
1930 		if (bp != NULL)
1931 			free(bp);
1932 		bp = xalloc(l);
1933 		bl = l;
1934 	}
1935 	strcpy(bp, s);
1936 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
1937 		*p++ = ' ';
1938 
1939 /*
1940 #ifdef LOG
1941 	if (logattacks)
1942 	{
1943 		syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
1944 			RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
1945 			shortenstring(bp, 80));
1946 	}
1947 #endif
1948 */
1949 
1950 	return bp;
1951 }
1952