xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 64735)
122717Sdist /*
242833Sbostic  * Copyright (c) 1983 Eric P. Allman
363589Sbostic  * Copyright (c) 1988, 1993
463589Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642833Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822717Sdist 
922717Sdist #ifndef lint
10*64735Seric static char sccsid[] = "@(#)util.c	8.13 (Berkeley) 10/21/93";
1133731Sbostic #endif /* not lint */
1222717Sdist 
1358332Seric # include "sendmail.h"
14298Seric # include <sysexits.h>
1557135Seric /*
16298Seric **  STRIPQUOTES -- Strip quotes & quote bits from a string.
17298Seric **
18298Seric **	Runs through a string and strips off unquoted quote
19298Seric **	characters and quote bits.  This is done in place.
20298Seric **
21298Seric **	Parameters:
22298Seric **		s -- the string to strip.
23298Seric **
24298Seric **	Returns:
25298Seric **		none.
26298Seric **
27298Seric **	Side Effects:
28298Seric **		none.
29298Seric **
30298Seric **	Called By:
31298Seric **		deliver
32298Seric */
33298Seric 
3454983Seric stripquotes(s)
35298Seric 	char *s;
36298Seric {
37298Seric 	register char *p;
38298Seric 	register char *q;
39298Seric 	register char c;
40298Seric 
414101Seric 	if (s == NULL)
424101Seric 		return;
434101Seric 
4454983Seric 	p = q = s;
4554983Seric 	do
46298Seric 	{
4754983Seric 		c = *p++;
4854983Seric 		if (c == '\\')
4954983Seric 			c = *p++;
5054983Seric 		else if (c == '"')
5154983Seric 			continue;
5254983Seric 		*q++ = c;
5354983Seric 	} while (c != '\0');
54298Seric }
55298Seric /*
56298Seric **  XALLOC -- Allocate memory and bitch wildly on failure.
57298Seric **
58298Seric **	THIS IS A CLUDGE.  This should be made to give a proper
59298Seric **	error -- but after all, what can we do?
60298Seric **
61298Seric **	Parameters:
62298Seric **		sz -- size of area to allocate.
63298Seric **
64298Seric **	Returns:
65298Seric **		pointer to data region.
66298Seric **
67298Seric **	Side Effects:
68298Seric **		Memory is allocated.
69298Seric */
70298Seric 
71298Seric char *
72298Seric xalloc(sz)
737007Seric 	register int sz;
74298Seric {
75298Seric 	register char *p;
76298Seric 
7723121Seric 	p = malloc((unsigned) sz);
78298Seric 	if (p == NULL)
79298Seric 	{
80298Seric 		syserr("Out of memory!!");
8110685Seric 		abort();
8210685Seric 		/* exit(EX_UNAVAILABLE); */
83298Seric 	}
84298Seric 	return (p);
85298Seric }
86298Seric /*
873151Seric **  COPYPLIST -- copy list of pointers.
883151Seric **
893151Seric **	This routine is the equivalent of newstr for lists of
903151Seric **	pointers.
913151Seric **
923151Seric **	Parameters:
933151Seric **		list -- list of pointers to copy.
943151Seric **			Must be NULL terminated.
953151Seric **		copycont -- if TRUE, copy the contents of the vector
963151Seric **			(which must be a string) also.
973151Seric **
983151Seric **	Returns:
993151Seric **		a copy of 'list'.
1003151Seric **
1013151Seric **	Side Effects:
1023151Seric **		none.
1033151Seric */
1043151Seric 
1053151Seric char **
1063151Seric copyplist(list, copycont)
1073151Seric 	char **list;
1083151Seric 	bool copycont;
1093151Seric {
1103151Seric 	register char **vp;
1113151Seric 	register char **newvp;
1123151Seric 
1133151Seric 	for (vp = list; *vp != NULL; vp++)
1143151Seric 		continue;
1153151Seric 
1163151Seric 	vp++;
1173151Seric 
11816897Seric 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
11916897Seric 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
1203151Seric 
1213151Seric 	if (copycont)
1223151Seric 	{
1233151Seric 		for (vp = newvp; *vp != NULL; vp++)
1243151Seric 			*vp = newstr(*vp);
1253151Seric 	}
1263151Seric 
1273151Seric 	return (newvp);
1283151Seric }
1293151Seric /*
13058170Seric **  COPYQUEUE -- copy address queue.
13158170Seric **
13258170Seric **	This routine is the equivalent of newstr for address queues
13358170Seric **	addresses marked with QDONTSEND aren't copied
13458170Seric **
13558170Seric **	Parameters:
13658170Seric **		addr -- list of address structures to copy.
13758170Seric **
13858170Seric **	Returns:
13958170Seric **		a copy of 'addr'.
14058170Seric **
14158170Seric **	Side Effects:
14258170Seric **		none.
14358170Seric */
14458170Seric 
14558170Seric ADDRESS *
14658170Seric copyqueue(addr)
14758170Seric 	ADDRESS *addr;
14858170Seric {
14958170Seric 	register ADDRESS *newaddr;
15058170Seric 	ADDRESS *ret;
15158170Seric 	register ADDRESS **tail = &ret;
15258170Seric 
15358170Seric 	while (addr != NULL)
15458170Seric 	{
15558170Seric 		if (!bitset(QDONTSEND, addr->q_flags))
15658170Seric 		{
15758170Seric 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
15858170Seric 			STRUCTCOPY(*addr, *newaddr);
15958170Seric 			*tail = newaddr;
16058170Seric 			tail = &newaddr->q_next;
16158170Seric 		}
16258170Seric 		addr = addr->q_next;
16358170Seric 	}
16458170Seric 	*tail = NULL;
16558170Seric 
16658170Seric 	return ret;
16758170Seric }
16858170Seric /*
1693151Seric **  PRINTAV -- print argument vector.
1703151Seric **
1713151Seric **	Parameters:
1723151Seric **		av -- argument vector.
1733151Seric **
1743151Seric **	Returns:
1753151Seric **		none.
1763151Seric **
1773151Seric **	Side Effects:
1783151Seric **		prints av.
1793151Seric */
1803151Seric 
1813151Seric printav(av)
1823151Seric 	register char **av;
1833151Seric {
1843151Seric 	while (*av != NULL)
1853151Seric 	{
1868063Seric 		if (tTd(0, 44))
1878063Seric 			printf("\n\t%08x=", *av);
1888063Seric 		else
18923105Seric 			(void) putchar(' ');
1903151Seric 		xputs(*av++);
1913151Seric 	}
19223105Seric 	(void) putchar('\n');
1933151Seric }
1943151Seric /*
1953151Seric **  LOWER -- turn letter into lower case.
1963151Seric **
1973151Seric **	Parameters:
1983151Seric **		c -- character to turn into lower case.
1993151Seric **
2003151Seric **	Returns:
2013151Seric **		c, in lower case.
2023151Seric **
2033151Seric **	Side Effects:
2043151Seric **		none.
2053151Seric */
2063151Seric 
2073151Seric char
2083151Seric lower(c)
2093151Seric 	register char c;
2103151Seric {
21158050Seric 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
2123151Seric }
2133151Seric /*
2143151Seric **  XPUTS -- put string doing control escapes.
2153151Seric **
2163151Seric **	Parameters:
2173151Seric **		s -- string to put.
2183151Seric **
2193151Seric **	Returns:
2203151Seric **		none.
2213151Seric **
2223151Seric **	Side Effects:
2233151Seric **		output to stdout
2243151Seric */
2253151Seric 
2263151Seric xputs(s)
2273151Seric 	register char *s;
2283151Seric {
22958050Seric 	register int c;
23051781Seric 	register struct metamac *mp;
23151781Seric 	extern struct metamac MetaMacros[];
2323151Seric 
2338055Seric 	if (s == NULL)
2348055Seric 	{
2358055Seric 		printf("<null>");
2368055Seric 		return;
2378055Seric 	}
23858050Seric 	while ((c = (*s++ & 0377)) != '\0')
2393151Seric 	{
2403151Seric 		if (!isascii(c))
2413151Seric 		{
24258050Seric 			if (c == MATCHREPL || c == MACROEXPAND)
24358050Seric 			{
24458050Seric 				putchar('$');
24558050Seric 				continue;
24658050Seric 			}
24758050Seric 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
24858050Seric 			{
24958050Seric 				if ((mp->metaval & 0377) == c)
25058050Seric 				{
25158050Seric 					printf("$%c", mp->metaname);
25258050Seric 					break;
25358050Seric 				}
25458050Seric 			}
25558050Seric 			if (mp->metaname != '\0')
25658050Seric 				continue;
25723105Seric 			(void) putchar('\\');
2583151Seric 			c &= 0177;
2593151Seric 		}
26057589Seric 		if (isprint(c))
2613151Seric 		{
26257589Seric 			putchar(c);
26357589Seric 			continue;
26457589Seric 		}
26552050Seric 
26657589Seric 		/* wasn't a meta-macro -- find another way to print it */
26757589Seric 		switch (c)
26857589Seric 		{
26957589Seric 		  case '\0':
27057589Seric 			continue;
27152050Seric 
27257589Seric 		  case '\n':
27357589Seric 			c = 'n';
27457589Seric 			break;
27552050Seric 
27657589Seric 		  case '\r':
27757589Seric 			c = 'r';
27857589Seric 			break;
27952637Seric 
28057589Seric 		  case '\t':
28157589Seric 			c = 't';
28257589Seric 			break;
28357589Seric 
28457589Seric 		  default:
28557589Seric 			(void) putchar('^');
28657589Seric 			(void) putchar(c ^ 0100);
28757589Seric 			continue;
2883151Seric 		}
2893151Seric 	}
2904086Seric 	(void) fflush(stdout);
2913151Seric }
2923151Seric /*
2933151Seric **  MAKELOWER -- Translate a line into lower case
2943151Seric **
2953151Seric **	Parameters:
2963151Seric **		p -- the string to translate.  If NULL, return is
2973151Seric **			immediate.
2983151Seric **
2993151Seric **	Returns:
3003151Seric **		none.
3013151Seric **
3023151Seric **	Side Effects:
3033151Seric **		String pointed to by p is translated to lower case.
3043151Seric **
3053151Seric **	Called By:
3063151Seric **		parse
3073151Seric */
3083151Seric 
3093151Seric makelower(p)
3103151Seric 	register char *p;
3113151Seric {
3123151Seric 	register char c;
3133151Seric 
3143151Seric 	if (p == NULL)
3153151Seric 		return;
3163151Seric 	for (; (c = *p) != '\0'; p++)
3173151Seric 		if (isascii(c) && isupper(c))
31833724Sbostic 			*p = tolower(c);
3193151Seric }
3204059Seric /*
3215196Seric **  BUILDFNAME -- build full name from gecos style entry.
3224375Seric **
3235196Seric **	This routine interprets the strange entry that would appear
3245196Seric **	in the GECOS field of the password file.
3255196Seric **
3264375Seric **	Parameters:
3275196Seric **		p -- name to build.
3285196Seric **		login -- the login name of this user (for &).
3295196Seric **		buf -- place to put the result.
3304375Seric **
3314375Seric **	Returns:
3324375Seric **		none.
3334375Seric **
3344375Seric **	Side Effects:
3354375Seric **		none.
3364375Seric */
3374375Seric 
33854984Seric buildfname(gecos, login, buf)
33954984Seric 	register char *gecos;
3405196Seric 	char *login;
3414375Seric 	char *buf;
3424375Seric {
34354984Seric 	register char *p;
3444375Seric 	register char *bp = buf;
34554984Seric 	int l;
3464375Seric 
34754984Seric 	if (*gecos == '*')
34854984Seric 		gecos++;
34954984Seric 
35057123Seric 	/* find length of final string */
35154984Seric 	l = 0;
35254984Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
35354984Seric 	{
35454984Seric 		if (*p == '&')
35554984Seric 			l += strlen(login);
35654984Seric 		else
35754984Seric 			l++;
35854984Seric 	}
35954984Seric 
36054984Seric 	/* now fill in buf */
36155193Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
3624375Seric 	{
3634375Seric 		if (*p == '&')
3644375Seric 		{
3655196Seric 			(void) strcpy(bp, login);
3664375Seric 			*bp = toupper(*bp);
3674375Seric 			while (*bp != '\0')
3684375Seric 				bp++;
3694375Seric 		}
3704375Seric 		else
37155193Seric 			*bp++ = *p;
3724375Seric 	}
3734375Seric 	*bp = '\0';
3744375Seric }
3754375Seric /*
3764538Seric **  SAFEFILE -- return true if a file exists and is safe for a user.
3774538Seric **
3784538Seric **	Parameters:
3794538Seric **		fn -- filename to check.
38064083Seric **		uid -- user id to compare against.
38164083Seric **		gid -- group id to compare against.
38264083Seric **		uname -- user name to compare against (used for group
38364083Seric **			sets).
38463753Seric **		mustown -- to be safe, this uid must own the file.
3854538Seric **		mode -- mode bits that must match.
3864538Seric **
3874538Seric **	Returns:
38858247Seric **		0 if fn exists, is owned by uid, and matches mode.
38958247Seric **		An errno otherwise.  The actual errno is cleared.
3904538Seric **
3914538Seric **	Side Effects:
3924538Seric **		none.
3934538Seric */
3944538Seric 
39564083Seric #include <grp.h>
39664083Seric 
39763581Seric #ifndef S_IXOTH
39863581Seric # define S_IXOTH	(S_IEXEC >> 6)
39963581Seric #endif
40063581Seric 
40164083Seric #ifndef S_IXGRP
40264083Seric # define S_IXGRP	(S_IEXEC >> 3)
40364083Seric #endif
40464083Seric 
40563753Seric #ifndef S_IXUSR
40663753Seric # define S_IXUSR	(S_IEXEC)
40763753Seric #endif
40863753Seric 
40958247Seric int
41064083Seric safefile(fn, uid, gid, uname, mustown, mode)
4114538Seric 	char *fn;
41255372Seric 	uid_t uid;
41364083Seric 	gid_t gid;
41464083Seric 	char *uname;
41563753Seric 	bool mustown;
4164538Seric 	int mode;
4174538Seric {
41863581Seric 	register char *p;
41964083Seric 	register struct group *gr = NULL;
4204538Seric 	struct stat stbuf;
4214538Seric 
42263581Seric 	if (tTd(54, 4))
42364083Seric 		printf("safefile(%s, uid=%d, gid=%d, mustown=%d, mode=%o):\n",
42464083Seric 			fn, uid, gid, mustown, mode);
42563581Seric 	errno = 0;
42663581Seric 
42763581Seric 	for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
42863581Seric 	{
42963581Seric 		*p = '\0';
43064083Seric 		if (stat(fn, &stbuf) < 0)
43164083Seric 			break;
43264362Seric 		if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
43364362Seric 			continue;
43464083Seric 		if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
43564083Seric 			continue;
43664084Seric #ifndef NO_GROUP_SET
43764083Seric 		if (uname != NULL &&
43864083Seric 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
43964083Seric 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
44063581Seric 		{
44164083Seric 			register char **gp;
44263581Seric 
44364083Seric 			for (gp = gr->gr_mem; *gp != NULL; gp++)
44464083Seric 				if (strcmp(*gp, uname) == 0)
44564083Seric 					break;
44664362Seric 			if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
44764362Seric 				continue;
44863581Seric 		}
44964084Seric #endif
45064083Seric 		if (!bitset(S_IXOTH, stbuf.st_mode))
45164083Seric 			break;
45263581Seric 	}
45364083Seric 	if (p != NULL)
45464083Seric 	{
45564083Seric 		int ret = errno;
45663581Seric 
45764083Seric 		if (ret == 0)
45864083Seric 			ret = EACCES;
45964083Seric 		if (tTd(54, 4))
46064083Seric 			printf("\t[dir %s] %s\n", fn, errstring(ret));
46164083Seric 		*p = '/';
46264083Seric 		return ret;
46364083Seric 	}
46464083Seric 
46558247Seric 	if (stat(fn, &stbuf) < 0)
46658247Seric 	{
46758247Seric 		int ret = errno;
46858247Seric 
46963581Seric 		if (tTd(54, 4))
47064083Seric 			printf("\t%s\n", errstring(ret));
47163581Seric 
47258247Seric 		errno = 0;
47358247Seric 		return ret;
47458247Seric 	}
47564084Seric 	if (uid == 0)
47663581Seric 		mode >>= 6;
47764084Seric 	else if (stbuf.st_uid != uid)
47864084Seric 	{
47964084Seric 		mode >>= 3;
48064084Seric 		if (stbuf.st_gid == gid)
48164084Seric 			;
48264084Seric #ifndef NO_GROUP_SET
48364084Seric 		else if (uname != NULL &&
48464084Seric 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
48564084Seric 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
48664084Seric 		{
48764084Seric 			register char **gp;
48864084Seric 
48964084Seric 			for (gp = gr->gr_mem; *gp != NULL; gp++)
49064084Seric 				if (strcmp(*gp, uname) == 0)
49164084Seric 					break;
49264084Seric 			if (*gp == NULL)
49364084Seric 				mode >>= 3;
49464084Seric 		}
49564084Seric #endif
49664084Seric 		else
49764084Seric 			mode >>= 3;
49864084Seric 	}
49963581Seric 	if (tTd(54, 4))
50064084Seric 		printf("\t[uid %d, stat %o, mode %o] ",
50164084Seric 			stbuf.st_uid, stbuf.st_mode, mode);
50263753Seric 	if ((stbuf.st_uid == uid || uid == 0 || !mustown) &&
50363581Seric 	    (stbuf.st_mode & mode) == mode)
50463581Seric 	{
50563581Seric 		if (tTd(54, 4))
50664083Seric 			printf("\tOK\n");
50758247Seric 		return 0;
50863581Seric 	}
50963581Seric 	if (tTd(54, 4))
51064083Seric 		printf("\tEACCES\n");
51163581Seric 	return EACCES;
5124538Seric }
5134538Seric /*
5144557Seric **  FIXCRLF -- fix <CR><LF> in line.
5154557Seric **
5164557Seric **	Looks for the <CR><LF> combination and turns it into the
5174557Seric **	UNIX canonical <NL> character.  It only takes one line,
5184557Seric **	i.e., it is assumed that the first <NL> found is the end
5194557Seric **	of the line.
5204557Seric **
5214557Seric **	Parameters:
5224557Seric **		line -- the line to fix.
5234557Seric **		stripnl -- if true, strip the newline also.
5244557Seric **
5254557Seric **	Returns:
5264557Seric **		none.
5274557Seric **
5284557Seric **	Side Effects:
5294557Seric **		line is changed in place.
5304557Seric */
5314557Seric 
5324557Seric fixcrlf(line, stripnl)
5334557Seric 	char *line;
5344557Seric 	bool stripnl;
5354557Seric {
5364557Seric 	register char *p;
5374557Seric 
53856795Seric 	p = strchr(line, '\n');
5394557Seric 	if (p == NULL)
5404557Seric 		return;
54136291Sbostic 	if (p > line && p[-1] == '\r')
5424557Seric 		p--;
5434557Seric 	if (!stripnl)
5444557Seric 		*p++ = '\n';
5454557Seric 	*p = '\0';
5464557Seric }
5474557Seric /*
5486890Seric **  DFOPEN -- determined file open
5496890Seric **
5506890Seric **	This routine has the semantics of fopen, except that it will
5516890Seric **	keep trying a few times to make this happen.  The idea is that
5526890Seric **	on very loaded systems, we may run out of resources (inodes,
5536890Seric **	whatever), so this tries to get around it.
5546890Seric */
5556890Seric 
55663753Seric #ifndef O_ACCMODE
55763753Seric # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
55863753Seric #endif
55963753Seric 
56059745Seric struct omodes
56159745Seric {
56259745Seric 	int	mask;
56359745Seric 	int	mode;
56459745Seric 	char	*farg;
56559745Seric } OpenModes[] =
56659745Seric {
56759745Seric 	O_ACCMODE,		O_RDONLY,		"r",
56859745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
56959745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
57059745Seric 	O_TRUNC,		0,			"w+",
57159745Seric 	O_APPEND,		O_APPEND,		"a+",
57259745Seric 	0,			0,			"r+",
57359745Seric };
57459745Seric 
5756890Seric FILE *
57659745Seric dfopen(filename, omode, cmode)
5776890Seric 	char *filename;
57859745Seric 	int omode;
57959745Seric 	int cmode;
5806890Seric {
5816890Seric 	register int tries;
58259745Seric 	int fd;
58359745Seric 	register struct omodes *om;
58459431Seric 	struct stat st;
5856890Seric 
58659745Seric 	for (om = OpenModes; om->mask != 0; om++)
58759745Seric 		if ((omode & om->mask) == om->mode)
58859745Seric 			break;
58959745Seric 
5906890Seric 	for (tries = 0; tries < 10; tries++)
5916890Seric 	{
59225618Seric 		sleep((unsigned) (10 * tries));
5936890Seric 		errno = 0;
59459745Seric 		fd = open(filename, omode, cmode);
59559745Seric 		if (fd >= 0)
5966890Seric 			break;
5979376Seric 		if (errno != ENFILE && errno != EINTR)
5989376Seric 			break;
5996890Seric 	}
60059745Seric 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
60156328Seric 	{
60256328Seric 		int locktype;
60356328Seric 
60456328Seric 		/* lock the file to avoid accidental conflicts */
60559745Seric 		if ((omode & O_ACCMODE) != O_RDONLY)
60656328Seric 			locktype = LOCK_EX;
60756328Seric 		else
60856328Seric 			locktype = LOCK_SH;
60964335Seric 		(void) lockfile(fd, filename, NULL, locktype);
61056328Seric 		errno = 0;
61156328Seric 	}
61263787Seric 	if (fd < 0)
61363787Seric 		return NULL;
61463787Seric 	else
61563787Seric 		return fdopen(fd, om->farg);
6166890Seric }
6177124Seric /*
6187124Seric **  PUTLINE -- put a line like fputs obeying SMTP conventions
6197124Seric **
6207753Seric **	This routine always guarantees outputing a newline (or CRLF,
6217753Seric **	as appropriate) at the end of the string.
6227753Seric **
6237124Seric **	Parameters:
6247124Seric **		l -- line to put.
6257124Seric **		fp -- file to put it onto.
62610172Seric **		m -- the mailer used to control output.
6277124Seric **
6287124Seric **	Returns:
6297124Seric **		none
6307124Seric **
6317124Seric **	Side Effects:
6327124Seric **		output of l to fp.
6337124Seric */
6347124Seric 
63510172Seric putline(l, fp, m)
6367753Seric 	register char *l;
6377124Seric 	FILE *fp;
63810172Seric 	MAILER *m;
6397124Seric {
6407124Seric 	register char *p;
64147157Sbostic 	register char svchar;
6427124Seric 
64311275Seric 	/* strip out 0200 bits -- these can look like TELNET protocol */
64452106Seric 	if (bitnset(M_7BITS, m->m_flags))
64511275Seric 	{
64661707Seric 		for (p = l; (svchar = *p) != '\0'; ++p)
64761707Seric 			if (bitset(0200, svchar))
64847157Sbostic 				*p = svchar &~ 0200;
64911275Seric 	}
65011275Seric 
6517753Seric 	do
6527124Seric 	{
6537753Seric 		/* find the end of the line */
65456795Seric 		p = strchr(l, '\n');
6557753Seric 		if (p == NULL)
6567753Seric 			p = &l[strlen(l)];
6577124Seric 
65863753Seric 		if (TrafficLogFile != NULL)
65963753Seric 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
66063753Seric 
6617753Seric 		/* check for line overflow */
66252106Seric 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
6637753Seric 		{
66452106Seric 			register char *q = &l[m->m_linelimit - 1];
6657124Seric 
6667753Seric 			svchar = *q;
6677753Seric 			*q = '\0';
66810685Seric 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
66963753Seric 			{
67023105Seric 				(void) putc('.', fp);
67163753Seric 				if (TrafficLogFile != NULL)
67263753Seric 					(void) putc('.', TrafficLogFile);
67363753Seric 			}
6747753Seric 			fputs(l, fp);
67523105Seric 			(void) putc('!', fp);
67610326Seric 			fputs(m->m_eol, fp);
67763753Seric 			if (TrafficLogFile != NULL)
67863753Seric 				fprintf(TrafficLogFile, "%s!\n%05d >>> ",
67963753Seric 					l, getpid());
6807753Seric 			*q = svchar;
6817753Seric 			l = q;
6827753Seric 		}
6837124Seric 
6847753Seric 		/* output last part */
68510685Seric 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
68663753Seric 		{
68723105Seric 			(void) putc('.', fp);
68863753Seric 			if (TrafficLogFile != NULL)
68963753Seric 				(void) putc('.', TrafficLogFile);
69063753Seric 		}
69163753Seric 		if (TrafficLogFile != NULL)
69263753Seric 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
69347157Sbostic 		for ( ; l < p; ++l)
69447157Sbostic 			(void) putc(*l, fp);
69510326Seric 		fputs(m->m_eol, fp);
6967753Seric 		if (*l == '\n')
69747157Sbostic 			++l;
6987753Seric 	} while (l[0] != '\0');
6997124Seric }
7007676Seric /*
7017676Seric **  XUNLINK -- unlink a file, doing logging as appropriate.
7027676Seric **
7037676Seric **	Parameters:
7047676Seric **		f -- name of file to unlink.
7057676Seric **
7067676Seric **	Returns:
7077676Seric **		none.
7087676Seric **
7097676Seric **	Side Effects:
7107676Seric **		f is unlinked.
7117676Seric */
7127676Seric 
7137676Seric xunlink(f)
7147676Seric 	char *f;
7157676Seric {
7167676Seric 	register int i;
7177676Seric 
7187676Seric # ifdef LOG
71958020Seric 	if (LogLevel > 98)
72058020Seric 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
72156795Seric # endif /* LOG */
7227676Seric 
7237676Seric 	i = unlink(f);
7247676Seric # ifdef LOG
72558020Seric 	if (i < 0 && LogLevel > 97)
7267942Seric 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
72756795Seric # endif /* LOG */
7287676Seric }
7297685Seric /*
73058680Seric **  XFCLOSE -- close a file, doing logging as appropriate.
73158680Seric **
73258680Seric **	Parameters:
73358680Seric **		fp -- file pointer for the file to close
73458680Seric **		a, b -- miscellaneous crud to print for debugging
73558680Seric **
73658680Seric **	Returns:
73758680Seric **		none.
73858680Seric **
73958680Seric **	Side Effects:
74058680Seric **		fp is closed.
74158680Seric */
74258680Seric 
74358680Seric xfclose(fp, a, b)
74458680Seric 	FILE *fp;
74558680Seric 	char *a, *b;
74658680Seric {
74758796Seric 	if (tTd(53, 99))
74858680Seric 		printf("xfclose(%x) %s %s\n", fp, a, b);
74964401Seric #ifdef XDEBUG
75064401Seric 	if (fileno(fp) == 1)
75164401Seric 		syserr("xfclose(%s %s): fd = 1", a, b);
75264401Seric #endif
75358796Seric 	if (fclose(fp) < 0 && tTd(53, 99))
75458680Seric 		printf("xfclose FAILURE: %s\n", errstring(errno));
75558680Seric }
75658680Seric /*
75714885Seric **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
7587685Seric **
7597685Seric **	Parameters:
7607685Seric **		buf -- place to put the input line.
7617685Seric **		siz -- size of buf.
7627685Seric **		fp -- file to read from.
76357384Seric **		timeout -- the timeout before error occurs.
76461093Seric **		during -- what we are trying to read (for error messages).
7657685Seric **
7667685Seric **	Returns:
76715533Seric **		NULL on error (including timeout).  This will also leave
76815533Seric **			buf containing a null string.
7697685Seric **		buf otherwise.
7707685Seric **
7717685Seric **	Side Effects:
7727685Seric **		none.
7737685Seric */
7747685Seric 
77514885Seric static jmp_buf	CtxReadTimeout;
77663937Seric static int	readtimeout();
7777685Seric 
7787685Seric char *
77961093Seric sfgets(buf, siz, fp, timeout, during)
7807685Seric 	char *buf;
7817685Seric 	int siz;
7827685Seric 	FILE *fp;
78357384Seric 	time_t timeout;
78461093Seric 	char *during;
7857685Seric {
7867942Seric 	register EVENT *ev = NULL;
7877685Seric 	register char *p;
7887685Seric 
78914885Seric 	/* set the timeout */
79057384Seric 	if (timeout != 0)
79114885Seric 	{
79214885Seric 		if (setjmp(CtxReadTimeout) != 0)
79314885Seric 		{
79436233Skarels # ifdef LOG
79536230Skarels 			syslog(LOG_NOTICE,
79661093Seric 			    "timeout waiting for input from %s during %s\n",
79761093Seric 			    CurHostName? CurHostName: "local", during);
79836233Skarels # endif
79936230Skarels 			errno = 0;
80061093Seric 			usrerr("451 timeout waiting for input during %s",
80161093Seric 				during);
80219037Seric 			buf[0] = '\0';
80363753Seric #ifdef XDEBUG
80463753Seric 			checkfd012(during);
80563753Seric #endif
80614885Seric 			return (NULL);
80714885Seric 		}
80857384Seric 		ev = setevent(timeout, readtimeout, 0);
80914885Seric 	}
81014885Seric 
81114885Seric 	/* try to read */
81215533Seric 	p = NULL;
81315533Seric 	while (p == NULL && !feof(fp) && !ferror(fp))
8147942Seric 	{
8157942Seric 		errno = 0;
8167942Seric 		p = fgets(buf, siz, fp);
81715533Seric 		if (errno == EINTR)
81815533Seric 			clearerr(fp);
81915533Seric 	}
82014885Seric 
82114885Seric 	/* clear the event if it has not sprung */
8227685Seric 	clrevent(ev);
82314885Seric 
82414885Seric 	/* clean up the books and exit */
8258055Seric 	LineNumber++;
82615533Seric 	if (p == NULL)
82716880Seric 	{
82815533Seric 		buf[0] = '\0';
82963753Seric 		if (TrafficLogFile != NULL)
83063753Seric 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
83116880Seric 		return (NULL);
83216880Seric 	}
83363753Seric 	if (TrafficLogFile != NULL)
83463753Seric 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
83559709Seric 	if (SevenBit)
83652106Seric 		for (p = buf; *p != '\0'; p++)
83752106Seric 			*p &= ~0200;
83816880Seric 	return (buf);
8397685Seric }
8407685Seric 
8417685Seric static
8427685Seric readtimeout()
8437685Seric {
84414885Seric 	longjmp(CtxReadTimeout, 1);
8457685Seric }
8467786Seric /*
8477786Seric **  FGETFOLDED -- like fgets, but know about folded lines.
8487786Seric **
8497786Seric **	Parameters:
8507786Seric **		buf -- place to put result.
8517786Seric **		n -- bytes available.
8527786Seric **		f -- file to read from.
8537786Seric **
8547786Seric **	Returns:
85557135Seric **		input line(s) on success, NULL on error or EOF.
85657135Seric **		This will normally be buf -- unless the line is too
85757135Seric **			long, when it will be xalloc()ed.
8587786Seric **
8597786Seric **	Side Effects:
8607786Seric **		buf gets lines from f, with continuation lines (lines
8617786Seric **		with leading white space) appended.  CRLF's are mapped
8627786Seric **		into single newlines.  Any trailing NL is stripped.
8637786Seric */
8647786Seric 
8657786Seric char *
8667786Seric fgetfolded(buf, n, f)
8677786Seric 	char *buf;
8687786Seric 	register int n;
8697786Seric 	FILE *f;
8707786Seric {
8717786Seric 	register char *p = buf;
87257135Seric 	char *bp = buf;
8737786Seric 	register int i;
8747786Seric 
8757786Seric 	n--;
87617350Seric 	while ((i = getc(f)) != EOF)
8777786Seric 	{
87817350Seric 		if (i == '\r')
87917350Seric 		{
88017350Seric 			i = getc(f);
88117350Seric 			if (i != '\n')
88217350Seric 			{
88317350Seric 				if (i != EOF)
88423105Seric 					(void) ungetc(i, f);
88517350Seric 				i = '\r';
88617350Seric 			}
88717350Seric 		}
88857135Seric 		if (--n <= 0)
88957135Seric 		{
89057135Seric 			/* allocate new space */
89157135Seric 			char *nbp;
89257135Seric 			int nn;
89357135Seric 
89457135Seric 			nn = (p - bp);
89557232Seric 			if (nn < MEMCHUNKSIZE)
89657135Seric 				nn *= 2;
89757135Seric 			else
89857232Seric 				nn += MEMCHUNKSIZE;
89957135Seric 			nbp = xalloc(nn);
90057135Seric 			bcopy(bp, nbp, p - bp);
90157135Seric 			p = &nbp[p - bp];
90257135Seric 			if (bp != buf)
90357135Seric 				free(bp);
90457135Seric 			bp = nbp;
90557135Seric 			n = nn - (p - bp);
90657135Seric 		}
90757135Seric 		*p++ = i;
90817350Seric 		if (i == '\n')
90917350Seric 		{
91017350Seric 			LineNumber++;
91117350Seric 			i = getc(f);
91217350Seric 			if (i != EOF)
91323105Seric 				(void) ungetc(i, f);
91417350Seric 			if (i != ' ' && i != '\t')
91552647Seric 				break;
91617350Seric 		}
9177786Seric 	}
91857135Seric 	if (p == bp)
91952647Seric 		return (NULL);
92052647Seric 	*--p = '\0';
92157135Seric 	return (bp);
9227786Seric }
9237860Seric /*
9247886Seric **  CURTIME -- return current time.
9257886Seric **
9267886Seric **	Parameters:
9277886Seric **		none.
9287886Seric **
9297886Seric **	Returns:
9307886Seric **		the current time.
9317886Seric **
9327886Seric **	Side Effects:
9337886Seric **		none.
9347886Seric */
9357886Seric 
9367886Seric time_t
9377886Seric curtime()
9387886Seric {
9397886Seric 	auto time_t t;
9407886Seric 
9417886Seric 	(void) time(&t);
9427886Seric 	return (t);
9437886Seric }
9448264Seric /*
9458264Seric **  ATOBOOL -- convert a string representation to boolean.
9468264Seric **
9478264Seric **	Defaults to "TRUE"
9488264Seric **
9498264Seric **	Parameters:
9508264Seric **		s -- string to convert.  Takes "tTyY" as true,
9518264Seric **			others as false.
9528264Seric **
9538264Seric **	Returns:
9548264Seric **		A boolean representation of the string.
9558264Seric **
9568264Seric **	Side Effects:
9578264Seric **		none.
9588264Seric */
9598264Seric 
9608264Seric bool
9618264Seric atobool(s)
9628264Seric 	register char *s;
9638264Seric {
96463833Seric 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
9658264Seric 		return (TRUE);
9668264Seric 	return (FALSE);
9678264Seric }
9689048Seric /*
9699048Seric **  ATOOCT -- convert a string representation to octal.
9709048Seric **
9719048Seric **	Parameters:
9729048Seric **		s -- string to convert.
9739048Seric **
9749048Seric **	Returns:
9759048Seric **		An integer representing the string interpreted as an
9769048Seric **		octal number.
9779048Seric **
9789048Seric **	Side Effects:
9799048Seric **		none.
9809048Seric */
9819048Seric 
9829048Seric atooct(s)
9839048Seric 	register char *s;
9849048Seric {
9859048Seric 	register int i = 0;
9869048Seric 
9879048Seric 	while (*s >= '0' && *s <= '7')
9889048Seric 		i = (i << 3) | (*s++ - '0');
9899048Seric 	return (i);
9909048Seric }
9919376Seric /*
9929376Seric **  WAITFOR -- wait for a particular process id.
9939376Seric **
9949376Seric **	Parameters:
9959376Seric **		pid -- process id to wait for.
9969376Seric **
9979376Seric **	Returns:
9989376Seric **		status of pid.
9999376Seric **		-1 if pid never shows up.
10009376Seric **
10019376Seric **	Side Effects:
10029376Seric **		none.
10039376Seric */
10049376Seric 
100564562Seric int
10069376Seric waitfor(pid)
10079376Seric 	int pid;
10089376Seric {
100964562Seric #ifdef WAITUNION
101064562Seric 	union wait st;
101164562Seric #else
10129376Seric 	auto int st;
101364562Seric #endif
10149376Seric 	int i;
10159376Seric 
10169376Seric 	do
10179376Seric 	{
10189376Seric 		errno = 0;
10199376Seric 		i = wait(&st);
10209376Seric 	} while ((i >= 0 || errno == EINTR) && i != pid);
10219376Seric 	if (i < 0)
102264562Seric 		return -1;
102364562Seric #ifdef WAITUNION
102464562Seric 	return st.w_status;
102564562Seric #else
102664562Seric 	return st;
102764562Seric #endif
10289376Seric }
10299376Seric /*
103010685Seric **  BITINTERSECT -- tell if two bitmaps intersect
103110685Seric **
103210685Seric **	Parameters:
103310685Seric **		a, b -- the bitmaps in question
103410685Seric **
103510685Seric **	Returns:
103610685Seric **		TRUE if they have a non-null intersection
103710685Seric **		FALSE otherwise
103810685Seric **
103910685Seric **	Side Effects:
104010685Seric **		none.
104110685Seric */
104210685Seric 
104310685Seric bool
104410685Seric bitintersect(a, b)
104510685Seric 	BITMAP a;
104610685Seric 	BITMAP b;
104710685Seric {
104810685Seric 	int i;
104910685Seric 
105010685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
105110685Seric 		if ((a[i] & b[i]) != 0)
105210685Seric 			return (TRUE);
105310685Seric 	return (FALSE);
105410685Seric }
105510685Seric /*
105610685Seric **  BITZEROP -- tell if a bitmap is all zero
105710685Seric **
105810685Seric **	Parameters:
105910685Seric **		map -- the bit map to check
106010685Seric **
106110685Seric **	Returns:
106210685Seric **		TRUE if map is all zero.
106310685Seric **		FALSE if there are any bits set in map.
106410685Seric **
106510685Seric **	Side Effects:
106610685Seric **		none.
106710685Seric */
106810685Seric 
106910685Seric bool
107010685Seric bitzerop(map)
107110685Seric 	BITMAP map;
107210685Seric {
107310685Seric 	int i;
107410685Seric 
107510685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
107610685Seric 		if (map[i] != 0)
107710685Seric 			return (FALSE);
107810685Seric 	return (TRUE);
107910685Seric }
108058247Seric /*
108158318Seric **  STRCONTAINEDIN -- tell if one string is contained in another
108258318Seric **
108358318Seric **	Parameters:
108458318Seric **		a -- possible substring.
108558318Seric **		b -- possible superstring.
108658318Seric **
108758318Seric **	Returns:
108858318Seric **		TRUE if a is contained in b.
108958318Seric **		FALSE otherwise.
109058318Seric */
109158318Seric 
109258318Seric bool
109358318Seric strcontainedin(a, b)
109458318Seric 	register char *a;
109558318Seric 	register char *b;
109658318Seric {
109758318Seric 	int l;
109858318Seric 
109958318Seric 	l = strlen(a);
110058318Seric 	for (;;)
110158318Seric 	{
110258318Seric 		b = strchr(b, a[0]);
110358318Seric 		if (b == NULL)
110458318Seric 			return FALSE;
110558318Seric 		if (strncmp(a, b, l) == 0)
110658318Seric 			return TRUE;
110758318Seric 		b++;
110858318Seric 	}
110958318Seric }
111063753Seric /*
111163753Seric **  CHECKFD012 -- check low numbered file descriptors
111263753Seric **
111363753Seric **	File descriptors 0, 1, and 2 should be open at all times.
111463753Seric **	This routine verifies that, and fixes it if not true.
111563753Seric **
111663753Seric **	Parameters:
111763753Seric **		where -- a tag printed if the assertion failed
111863753Seric **
111963753Seric **	Returns:
112063753Seric **		none
112163753Seric */
112263753Seric 
112363753Seric checkfd012(where)
112463753Seric 	char *where;
112563753Seric {
112663753Seric #ifdef XDEBUG
112763753Seric 	register int i;
112863753Seric 	struct stat stbuf;
112963753Seric 
113063753Seric 	for (i = 0; i < 3; i++)
113163753Seric 	{
1132*64735Seric 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
113363753Seric 		{
113463753Seric 			/* oops.... */
113563753Seric 			int fd;
113663753Seric 
113763753Seric 			syserr("%s: fd %d not open", where, i);
113863753Seric 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
113963753Seric 			if (fd != i)
114063753Seric 			{
114163753Seric 				(void) dup2(fd, i);
114263753Seric 				(void) close(fd);
114363753Seric 			}
114463753Seric 		}
114563753Seric 	}
114663937Seric #endif /* XDEBUG */
114763753Seric }
114864725Seric /*
114964725Seric **  PRINTOPENFDS -- print the open file descriptors (for debugging)
115064725Seric **
115164725Seric **	Parameters:
115264725Seric **		logit -- if set, send output to syslog; otherwise
115364725Seric **			print for debugging.
115464725Seric **
115564725Seric **	Returns:
115664725Seric **		none.
115764725Seric */
115864725Seric 
115964725Seric #include <netdb.h>
116064725Seric #include <arpa/inet.h>
116164725Seric 
116264725Seric printopenfds(logit)
116364725Seric 	bool logit;
116464725Seric {
116564725Seric 	register int fd;
116664725Seric 	struct stat st;
116764725Seric 	register struct hostent *hp;
116864725Seric 	register char *p;
116964725Seric 	char buf[200];
117064725Seric 	extern int DtableSize;
117164725Seric 
117264725Seric 	for (fd = 0; fd < DtableSize; fd++)
117364725Seric 	{
117464725Seric 		struct sockaddr_in sin;
117564725Seric 		auto int slen;
117664725Seric 
117764725Seric 		if (fstat(fd, &st) < 0)
117864725Seric 			continue;
117964725Seric 
118064725Seric 		p = buf;
118164725Seric 		sprintf(p, "%3d: mode=%o: ", fd, st.st_mode);
118264725Seric 		p += strlen(p);
118364725Seric 		switch (st.st_mode & S_IFMT)
118464725Seric 		{
118564725Seric 		  case S_IFSOCK:
118664725Seric 			sprintf(p, "SOCK ");
118764725Seric 			p += strlen(p);
118864725Seric 			slen = sizeof sin;
118964725Seric 			if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
119064725Seric 				sprintf(p, "(badsock)");
119164725Seric 			else
119264725Seric 			{
119364725Seric 				hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
119464725Seric 				sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
119564725Seric 							   : hp->h_name, ntohs(sin.sin_port));
119664725Seric 			}
119764725Seric 			p += strlen(p);
119864725Seric 			sprintf(p, "->");
119964725Seric 			p += strlen(p);
120064725Seric 			slen = sizeof sin;
120164725Seric 			if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
120264725Seric 				sprintf(p, "(badsock)");
120364725Seric 			else
120464725Seric 			{
120564725Seric 				hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
120664725Seric 				sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
120764725Seric 							   : hp->h_name, ntohs(sin.sin_port));
120864725Seric 			}
120964725Seric 			break;
121064725Seric 
121164725Seric 		  case S_IFCHR:
121264725Seric 			sprintf(p, "CHR: ");
121364725Seric 			p += strlen(p);
121464725Seric 			goto defprint;
121564725Seric 
121664725Seric 		  case S_IFBLK:
121764725Seric 			sprintf(p, "BLK: ");
121864725Seric 			p += strlen(p);
121964725Seric 			goto defprint;
122064725Seric 
122164725Seric 		  default:
122264725Seric defprint:
122364725Seric 			sprintf(p, "rdev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld",
122464725Seric 				major(st.st_rdev), minor(st.st_rdev), st.st_ino,
122564725Seric 				st.st_nlink, st.st_uid, st.st_gid, st.st_size);
122664725Seric 			break;
122764725Seric 		}
122864725Seric 
122964725Seric 		if (logit)
123064725Seric 			syslog(LOG_INFO, "%s", buf);
123164725Seric 		else
123264725Seric 			printf("%s\n", buf);
123364725Seric 	}
123464725Seric }
1235