xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 69963)
122717Sdist /*
268839Seric  * Copyright (c) 1983, 1995 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*69963Seric static char sccsid[] = "@(#)util.c	8.78 (Berkeley) 06/21/95";
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 
3469748Seric void
stripquotes(s)3554983Seric stripquotes(s)
36298Seric 	char *s;
37298Seric {
38298Seric 	register char *p;
39298Seric 	register char *q;
40298Seric 	register char c;
41298Seric 
424101Seric 	if (s == NULL)
434101Seric 		return;
444101Seric 
4554983Seric 	p = q = s;
4654983Seric 	do
47298Seric 	{
4854983Seric 		c = *p++;
4954983Seric 		if (c == '\\')
5054983Seric 			c = *p++;
5154983Seric 		else if (c == '"')
5254983Seric 			continue;
5354983Seric 		*q++ = c;
5454983Seric 	} while (c != '\0');
55298Seric }
56298Seric /*
57298Seric **  XALLOC -- Allocate memory and bitch wildly on failure.
58298Seric **
59298Seric **	THIS IS A CLUDGE.  This should be made to give a proper
60298Seric **	error -- but after all, what can we do?
61298Seric **
62298Seric **	Parameters:
63298Seric **		sz -- size of area to allocate.
64298Seric **
65298Seric **	Returns:
66298Seric **		pointer to data region.
67298Seric **
68298Seric **	Side Effects:
69298Seric **		Memory is allocated.
70298Seric */
71298Seric 
72298Seric char *
xalloc(sz)73298Seric xalloc(sz)
747007Seric 	register int sz;
75298Seric {
76298Seric 	register char *p;
77298Seric 
7866747Seric 	/* some systems can't handle size zero mallocs */
7966747Seric 	if (sz <= 0)
8066747Seric 		sz = 1;
8166747Seric 
8223121Seric 	p = malloc((unsigned) sz);
83298Seric 	if (p == NULL)
84298Seric 	{
8569800Seric 		syserr("!Out of memory!!");
8610685Seric 		/* exit(EX_UNAVAILABLE); */
87298Seric 	}
88298Seric 	return (p);
89298Seric }
90298Seric /*
913151Seric **  COPYPLIST -- copy list of pointers.
923151Seric **
933151Seric **	This routine is the equivalent of newstr for lists of
943151Seric **	pointers.
953151Seric **
963151Seric **	Parameters:
973151Seric **		list -- list of pointers to copy.
983151Seric **			Must be NULL terminated.
993151Seric **		copycont -- if TRUE, copy the contents of the vector
1003151Seric **			(which must be a string) also.
1013151Seric **
1023151Seric **	Returns:
1033151Seric **		a copy of 'list'.
1043151Seric **
1053151Seric **	Side Effects:
1063151Seric **		none.
1073151Seric */
1083151Seric 
1093151Seric char **
copyplist(list,copycont)1103151Seric copyplist(list, copycont)
1113151Seric 	char **list;
1123151Seric 	bool copycont;
1133151Seric {
1143151Seric 	register char **vp;
1153151Seric 	register char **newvp;
1163151Seric 
1173151Seric 	for (vp = list; *vp != NULL; vp++)
1183151Seric 		continue;
1193151Seric 
1203151Seric 	vp++;
1213151Seric 
12216897Seric 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
12316897Seric 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
1243151Seric 
1253151Seric 	if (copycont)
1263151Seric 	{
1273151Seric 		for (vp = newvp; *vp != NULL; vp++)
1283151Seric 			*vp = newstr(*vp);
1293151Seric 	}
1303151Seric 
1313151Seric 	return (newvp);
1323151Seric }
1333151Seric /*
13458170Seric **  COPYQUEUE -- copy address queue.
13558170Seric **
13658170Seric **	This routine is the equivalent of newstr for address queues
13758170Seric **	addresses marked with QDONTSEND aren't copied
13858170Seric **
13958170Seric **	Parameters:
14058170Seric **		addr -- list of address structures to copy.
14158170Seric **
14258170Seric **	Returns:
14358170Seric **		a copy of 'addr'.
14458170Seric **
14558170Seric **	Side Effects:
14658170Seric **		none.
14758170Seric */
14858170Seric 
14958170Seric ADDRESS *
copyqueue(addr)15058170Seric copyqueue(addr)
15158170Seric 	ADDRESS *addr;
15258170Seric {
15358170Seric 	register ADDRESS *newaddr;
15458170Seric 	ADDRESS *ret;
15558170Seric 	register ADDRESS **tail = &ret;
15658170Seric 
15758170Seric 	while (addr != NULL)
15858170Seric 	{
15958170Seric 		if (!bitset(QDONTSEND, addr->q_flags))
16058170Seric 		{
16158170Seric 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
16258170Seric 			STRUCTCOPY(*addr, *newaddr);
16358170Seric 			*tail = newaddr;
16458170Seric 			tail = &newaddr->q_next;
16558170Seric 		}
16658170Seric 		addr = addr->q_next;
16758170Seric 	}
16858170Seric 	*tail = NULL;
16958170Seric 
17058170Seric 	return ret;
17158170Seric }
17258170Seric /*
1733151Seric **  PRINTAV -- print argument vector.
1743151Seric **
1753151Seric **	Parameters:
1763151Seric **		av -- argument vector.
1773151Seric **
1783151Seric **	Returns:
1793151Seric **		none.
1803151Seric **
1813151Seric **	Side Effects:
1823151Seric **		prints av.
1833151Seric */
1843151Seric 
18569748Seric void
printav(av)1863151Seric printav(av)
1873151Seric 	register char **av;
1883151Seric {
1893151Seric 	while (*av != NULL)
1903151Seric 	{
1918063Seric 		if (tTd(0, 44))
1928063Seric 			printf("\n\t%08x=", *av);
1938063Seric 		else
19423105Seric 			(void) putchar(' ');
1953151Seric 		xputs(*av++);
1963151Seric 	}
19723105Seric 	(void) putchar('\n');
1983151Seric }
1993151Seric /*
2003151Seric **  LOWER -- turn letter into lower case.
2013151Seric **
2023151Seric **	Parameters:
2033151Seric **		c -- character to turn into lower case.
2043151Seric **
2053151Seric **	Returns:
2063151Seric **		c, in lower case.
2073151Seric **
2083151Seric **	Side Effects:
2093151Seric **		none.
2103151Seric */
2113151Seric 
2123151Seric char
lower(c)2133151Seric lower(c)
2143151Seric 	register char c;
2153151Seric {
21658050Seric 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
2173151Seric }
2183151Seric /*
2193151Seric **  XPUTS -- put string doing control escapes.
2203151Seric **
2213151Seric **	Parameters:
2223151Seric **		s -- string to put.
2233151Seric **
2243151Seric **	Returns:
2253151Seric **		none.
2263151Seric **
2273151Seric **	Side Effects:
2283151Seric **		output to stdout
2293151Seric */
2303151Seric 
23169748Seric void
xputs(s)2323151Seric xputs(s)
23369748Seric 	register const char *s;
2343151Seric {
23558050Seric 	register int c;
23651781Seric 	register struct metamac *mp;
23751781Seric 	extern struct metamac MetaMacros[];
2383151Seric 
2398055Seric 	if (s == NULL)
2408055Seric 	{
2418055Seric 		printf("<null>");
2428055Seric 		return;
2438055Seric 	}
24458050Seric 	while ((c = (*s++ & 0377)) != '\0')
2453151Seric 	{
2463151Seric 		if (!isascii(c))
2473151Seric 		{
24868481Seric 			if (c == MATCHREPL)
24958050Seric 			{
25058050Seric 				putchar('$');
25158050Seric 				continue;
25258050Seric 			}
25368481Seric 			if (c == MACROEXPAND)
25468481Seric 			{
25568481Seric 				putchar('$');
25668481Seric 				if (bitset(0200, *s))
25768481Seric 					printf("{%s}", macname(*s++ & 0377));
25868481Seric 				continue;
25968481Seric 			}
26058050Seric 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
26158050Seric 			{
26258050Seric 				if ((mp->metaval & 0377) == c)
26358050Seric 				{
26458050Seric 					printf("$%c", mp->metaname);
26558050Seric 					break;
26658050Seric 				}
26758050Seric 			}
26858050Seric 			if (mp->metaname != '\0')
26958050Seric 				continue;
27023105Seric 			(void) putchar('\\');
2713151Seric 			c &= 0177;
2723151Seric 		}
27357589Seric 		if (isprint(c))
2743151Seric 		{
27557589Seric 			putchar(c);
27657589Seric 			continue;
27757589Seric 		}
27852050Seric 
27957589Seric 		/* wasn't a meta-macro -- find another way to print it */
28057589Seric 		switch (c)
28157589Seric 		{
28257589Seric 		  case '\n':
28357589Seric 			c = 'n';
28457589Seric 			break;
28552050Seric 
28657589Seric 		  case '\r':
28757589Seric 			c = 'r';
28857589Seric 			break;
28952637Seric 
29057589Seric 		  case '\t':
29157589Seric 			c = 't';
29257589Seric 			break;
29357589Seric 
29457589Seric 		  default:
29557589Seric 			(void) putchar('^');
29657589Seric 			(void) putchar(c ^ 0100);
29757589Seric 			continue;
2983151Seric 		}
29968500Seric 		(void) putchar('\\');
30068500Seric 		(void) putchar(c);
3013151Seric 	}
3024086Seric 	(void) fflush(stdout);
3033151Seric }
3043151Seric /*
3053151Seric **  MAKELOWER -- Translate a line into lower case
3063151Seric **
3073151Seric **	Parameters:
3083151Seric **		p -- the string to translate.  If NULL, return is
3093151Seric **			immediate.
3103151Seric **
3113151Seric **	Returns:
3123151Seric **		none.
3133151Seric **
3143151Seric **	Side Effects:
3153151Seric **		String pointed to by p is translated to lower case.
3163151Seric **
3173151Seric **	Called By:
3183151Seric **		parse
3193151Seric */
3203151Seric 
32168481Seric void
makelower(p)3223151Seric makelower(p)
3233151Seric 	register char *p;
3243151Seric {
3253151Seric 	register char c;
3263151Seric 
3273151Seric 	if (p == NULL)
3283151Seric 		return;
3293151Seric 	for (; (c = *p) != '\0'; p++)
3303151Seric 		if (isascii(c) && isupper(c))
33133724Sbostic 			*p = tolower(c);
3323151Seric }
3334059Seric /*
3345196Seric **  BUILDFNAME -- build full name from gecos style entry.
3354375Seric **
3365196Seric **	This routine interprets the strange entry that would appear
3375196Seric **	in the GECOS field of the password file.
3385196Seric **
3394375Seric **	Parameters:
3405196Seric **		p -- name to build.
3415196Seric **		login -- the login name of this user (for &).
3425196Seric **		buf -- place to put the result.
3434375Seric **
3444375Seric **	Returns:
34565006Seric **		none.
3464375Seric **
3474375Seric **	Side Effects:
3484375Seric **		none.
3494375Seric */
3504375Seric 
35169748Seric void
buildfname(gecos,login,buf)35254984Seric buildfname(gecos, login, buf)
35365006Seric 	register char *gecos;
35465006Seric 	char *login;
35565006Seric 	char *buf;
3564375Seric {
35765006Seric 	register char *p;
35865006Seric 	register char *bp = buf;
35965006Seric 	int l;
3604375Seric 
36165006Seric 	if (*gecos == '*')
36265006Seric 		gecos++;
36365006Seric 
36465006Seric 	/* find length of final string */
36565006Seric 	l = 0;
36665006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
36754984Seric 	{
36865006Seric 		if (*p == '&')
36965006Seric 			l += strlen(login);
37065006Seric 		else
37165006Seric 			l++;
37254984Seric 	}
37365006Seric 
37465006Seric 	/* now fill in buf */
37565006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
3764375Seric 	{
37765006Seric 		if (*p == '&')
3784375Seric 		{
37965006Seric 			(void) strcpy(bp, login);
38065006Seric 			*bp = toupper(*bp);
38165006Seric 			while (*bp != '\0')
38265006Seric 				bp++;
3834375Seric 		}
38465006Seric 		else
38565006Seric 			*bp++ = *p;
3864375Seric 	}
3874375Seric 	*bp = '\0';
3884375Seric }
3894375Seric /*
3904538Seric **  SAFEFILE -- return true if a file exists and is safe for a user.
3914538Seric **
3924538Seric **	Parameters:
3934538Seric **		fn -- filename to check.
39464083Seric **		uid -- user id to compare against.
39564083Seric **		gid -- group id to compare against.
39664083Seric **		uname -- user name to compare against (used for group
39764083Seric **			sets).
39864944Seric **		flags -- modifiers:
39965064Seric **			SFF_MUSTOWN -- "uid" must own this file.
40065064Seric **			SFF_NOSLINK -- file cannot be a symbolic link.
4014538Seric **		mode -- mode bits that must match.
40268494Seric **		st -- if set, points to a stat structure that will
40368494Seric **			get the stat info for the file.
4044538Seric **
4054538Seric **	Returns:
40658247Seric **		0 if fn exists, is owned by uid, and matches mode.
40758247Seric **		An errno otherwise.  The actual errno is cleared.
4084538Seric **
4094538Seric **	Side Effects:
4104538Seric **		none.
4114538Seric */
4124538Seric 
41364083Seric #include <grp.h>
41464083Seric 
41563581Seric #ifndef S_IXOTH
41663581Seric # define S_IXOTH	(S_IEXEC >> 6)
41763581Seric #endif
41863581Seric 
41964083Seric #ifndef S_IXGRP
42064083Seric # define S_IXGRP	(S_IEXEC >> 3)
42164083Seric #endif
42264083Seric 
42363753Seric #ifndef S_IXUSR
42463753Seric # define S_IXUSR	(S_IEXEC)
42563753Seric #endif
42663753Seric 
42768494Seric #define ST_MODE_NOFILE	0171147		/* unlikely to occur */
42868494Seric 
42958247Seric int
safefile(fn,uid,gid,uname,flags,mode,st)43068494Seric safefile(fn, uid, gid, uname, flags, mode, st)
4314538Seric 	char *fn;
43255372Seric 	uid_t uid;
43364083Seric 	gid_t gid;
43464083Seric 	char *uname;
43564944Seric 	int flags;
4364538Seric 	int mode;
43768494Seric 	struct stat *st;
4384538Seric {
43963581Seric 	register char *p;
44064083Seric 	register struct group *gr = NULL;
4414538Seric 	struct stat stbuf;
4424538Seric 
44363581Seric 	if (tTd(54, 4))
44464944Seric 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
44564944Seric 			fn, uid, gid, flags, mode);
44663581Seric 	errno = 0;
44768494Seric 	if (st == NULL)
44868494Seric 		st = &stbuf;
44963581Seric 
45068481Seric 	if (!bitset(SFF_NOPATHCHECK, flags) ||
45168481Seric 	    (uid == 0 && !bitset(SFF_ROOTOK, flags)))
45263581Seric 	{
45368481Seric 		/* check the path to the file for acceptability */
45468481Seric 		for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
45565225Seric 		{
45668481Seric 			*p = '\0';
45768481Seric 			if (stat(fn, &stbuf) < 0)
45868481Seric 				break;
45968513Seric 			if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode))
46068513Seric 				message("051 WARNING: writable directory %s",
46168513Seric 					fn);
46268481Seric 			if (uid == 0 && !bitset(SFF_ROOTOK, flags))
46368481Seric 			{
46468481Seric 				if (bitset(S_IXOTH, stbuf.st_mode))
46568481Seric 					continue;
46668481Seric 				break;
46768481Seric 			}
46868481Seric 			if (stbuf.st_uid == uid &&
46968481Seric 			    bitset(S_IXUSR, stbuf.st_mode))
47065225Seric 				continue;
47168481Seric 			if (stbuf.st_gid == gid &&
47268481Seric 			    bitset(S_IXGRP, stbuf.st_mode))
47368481Seric 				continue;
47468481Seric #ifndef NO_GROUP_SET
47568481Seric 			if (uname != NULL &&
47668481Seric 			    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
47768481Seric 			     (gr = getgrgid(stbuf.st_gid)) != NULL))
47868481Seric 			{
47968481Seric 				register char **gp;
48068481Seric 
48168481Seric 				for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
48268481Seric 					if (strcmp(*gp, uname) == 0)
48368481Seric 						break;
48468481Seric 				if (gp != NULL && *gp != NULL &&
48568481Seric 				    bitset(S_IXGRP, stbuf.st_mode))
48668481Seric 					continue;
48768481Seric 			}
48868481Seric #endif
48968481Seric 			if (!bitset(S_IXOTH, stbuf.st_mode))
49068481Seric 				break;
49168478Seric 		}
49268481Seric 		if (p != NULL)
49363581Seric 		{
49468481Seric 			int ret = errno;
49563581Seric 
49668481Seric 			if (ret == 0)
49768481Seric 				ret = EACCES;
49868481Seric 			if (tTd(54, 4))
49968481Seric 				printf("\t[dir %s] %s\n", fn, errstring(ret));
50068481Seric 			*p = '/';
50168481Seric 			return ret;
50263581Seric 		}
50363581Seric 	}
50463581Seric 
50564944Seric #ifdef HASLSTAT
50668494Seric 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
50768494Seric 					: stat(fn, st)) < 0)
50864944Seric #else
50968494Seric 	if (stat(fn, st) < 0)
51064944Seric #endif
51158247Seric 	{
51258247Seric 		int ret = errno;
51358247Seric 
51463581Seric 		if (tTd(54, 4))
51564083Seric 			printf("\t%s\n", errstring(ret));
51663581Seric 
51758247Seric 		errno = 0;
51868494Seric 		if (!bitset(SFF_CREAT, flags))
51968494Seric 			return ret;
52068494Seric 
52168494Seric 		/* check to see if legal to create the file */
52268494Seric 		p = strrchr(fn, '/');
52368494Seric 		if (p == NULL)
52468494Seric 			return ENOTDIR;
52568494Seric 		*p = '\0';
52668494Seric 		if (stat(fn, &stbuf) >= 0)
52768494Seric 		{
52868494Seric 			int md = S_IWRITE|S_IEXEC;
52968494Seric 			if (stbuf.st_uid != uid)
53068494Seric 				md >>= 6;
53168494Seric 			if ((stbuf.st_mode & md) != md)
53268494Seric 				errno = EACCES;
53368494Seric 		}
53468494Seric 		ret = errno;
53568494Seric 		if (tTd(54, 4))
53668494Seric 			printf("\t[final dir %s uid %d mode %o] %s\n",
53768494Seric 				fn, stbuf.st_uid, stbuf.st_mode,
53868494Seric 				errstring(ret));
53968494Seric 		*p = '/';
54068494Seric 		st->st_mode = ST_MODE_NOFILE;
54158247Seric 		return ret;
54258247Seric 	}
54364944Seric 
54464944Seric #ifdef S_ISLNK
54568494Seric 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
54664944Seric 	{
54764944Seric 		if (tTd(54, 4))
54868494Seric 			printf("\t[slink mode %o]\tEPERM\n", st->st_mode);
54964944Seric 		return EPERM;
55064944Seric 	}
55164944Seric #endif
55268513Seric 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
55368513Seric 	{
55468513Seric 		if (tTd(54, 4))
55568513Seric 			printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode);
55668513Seric 		return EPERM;
55768513Seric 	}
55868494Seric 	if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(0111, st->st_mode))
55968494Seric 	{
56068494Seric 		if (tTd(29, 5))
56168494Seric 			printf("failed (mode %o: x bits)\n", st->st_mode);
56268513Seric 		return EPERM;
56368494Seric 	}
56468494Seric 
56568494Seric 	if (bitset(SFF_SETUIDOK, flags))
56668494Seric 	{
56769952Seric #ifdef SUID_ROOT_FILES_OK
56869952Seric 		if (bitset(S_ISUID, st->st_mode))
56969952Seric #else
57069952Seric 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0)
57169952Seric #endif
57268494Seric 		{
57368494Seric 			uid = st->st_uid;
57468494Seric 			uname = NULL;
57568494Seric 		}
57669952Seric #ifdef SUID_ROOT_FILES_OK
57769952Seric 		if (bitset(S_ISGID, st->st_mode))
57869952Seric #else
57969952Seric 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
58069952Seric #endif
58168494Seric 			gid = st->st_gid;
58268494Seric 	}
58368494Seric 
58465139Seric 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
58563581Seric 		mode >>= 6;
58668494Seric 	else if (st->st_uid != uid)
58764084Seric 	{
58864084Seric 		mode >>= 3;
58968494Seric 		if (st->st_gid == gid)
59064084Seric 			;
59164084Seric #ifndef NO_GROUP_SET
59264084Seric 		else if (uname != NULL &&
59368494Seric 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
59468494Seric 			  (gr = getgrgid(st->st_gid)) != NULL))
59564084Seric 		{
59664084Seric 			register char **gp;
59764084Seric 
59864084Seric 			for (gp = gr->gr_mem; *gp != NULL; gp++)
59964084Seric 				if (strcmp(*gp, uname) == 0)
60064084Seric 					break;
60164084Seric 			if (*gp == NULL)
60264084Seric 				mode >>= 3;
60364084Seric 		}
60464084Seric #endif
60564084Seric 		else
60664084Seric 			mode >>= 3;
60764084Seric 	}
60863581Seric 	if (tTd(54, 4))
60964084Seric 		printf("\t[uid %d, stat %o, mode %o] ",
61068494Seric 			st->st_uid, st->st_mode, mode);
61168494Seric 	if ((st->st_uid == uid || st->st_uid == 0 ||
61265064Seric 	     !bitset(SFF_MUSTOWN, flags)) &&
61368494Seric 	    (st->st_mode & mode) == mode)
61463581Seric 	{
61563581Seric 		if (tTd(54, 4))
61664083Seric 			printf("\tOK\n");
61758247Seric 		return 0;
61863581Seric 	}
61963581Seric 	if (tTd(54, 4))
62064083Seric 		printf("\tEACCES\n");
62163581Seric 	return EACCES;
6224538Seric }
6234538Seric /*
62468494Seric **  SAFEFOPEN -- do a file open with extra checking
62568494Seric **
62668494Seric **	Parameters:
62768494Seric **		fn -- the file name to open.
62868494Seric **		omode -- the open-style mode flags.
62968494Seric **		cmode -- the create-style mode flags.
63068494Seric **		sff -- safefile flags.
63168494Seric **
63268494Seric **	Returns:
63368494Seric **		Same as fopen.
63468494Seric */
63568494Seric 
63668703Seric #ifndef O_ACCMODE
63768703Seric # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
63868703Seric #endif
63968703Seric 
64068494Seric FILE *
safefopen(fn,omode,cmode,sff)64168494Seric safefopen(fn, omode, cmode, sff)
64268494Seric 	char *fn;
64368494Seric 	int omode;
64468494Seric 	int cmode;
64568494Seric 	int sff;
64668494Seric {
64768494Seric 	int rval;
64868494Seric 	FILE *fp;
64968494Seric 	int smode;
65068494Seric 	struct stat stb, sta;
65168494Seric 
65268494Seric 	if (bitset(O_CREAT, omode))
65368494Seric 		sff |= SFF_CREAT;
65468494Seric 	smode = 0;
65568494Seric 	switch (omode & O_ACCMODE)
65668494Seric 	{
65768494Seric 	  case O_RDONLY:
65868494Seric 		smode = S_IREAD;
65968494Seric 		break;
66068494Seric 
66168494Seric 	  case O_WRONLY:
66268494Seric 		smode = S_IWRITE;
66368494Seric 		break;
66468494Seric 
66568494Seric 	  case O_RDWR:
66668494Seric 		smode = S_IREAD|S_IWRITE;
66768494Seric 		break;
66868494Seric 
66968494Seric 	  default:
67068494Seric 		smode = 0;
67168494Seric 		break;
67268494Seric 	}
67368513Seric 	if (bitset(SFF_OPENASROOT, sff))
67468513Seric 		rval = safefile(fn, 0, 0, NULL, sff, smode, &stb);
67568513Seric 	else
67668513Seric 		rval = safefile(fn, RealUid, RealGid, RealUserName,
67768513Seric 				sff, smode, &stb);
67868494Seric 	if (rval != 0)
67968494Seric 	{
68068494Seric 		errno = rval;
68168494Seric 		return NULL;
68268494Seric 	}
68368494Seric 	if (stb.st_mode == ST_MODE_NOFILE)
68468494Seric 		omode |= O_EXCL;
68568494Seric 
68668494Seric 	fp = dfopen(fn, omode, cmode);
68768494Seric 	if (fp == NULL)
68868494Seric 		return NULL;
68968494Seric 	if (bitset(O_EXCL, omode))
69068494Seric 		return fp;
69168494Seric 	if (fstat(fileno(fp), &sta) < 0 ||
69268494Seric 	    sta.st_nlink != stb.st_nlink ||
69368494Seric 	    sta.st_dev != stb.st_dev ||
69468494Seric 	    sta.st_ino != stb.st_ino ||
69568494Seric 	    sta.st_uid != stb.st_uid ||
69668494Seric 	    sta.st_gid != stb.st_gid)
69768494Seric 	{
69868494Seric 		syserr("554 cannot open: file %s changed after open", fn);
69968513Seric 		fclose(fp);
70068494Seric 		errno = EPERM;
70168494Seric 		return NULL;
70268494Seric 	}
70368494Seric 	return fp;
70468494Seric }
70568494Seric /*
7064557Seric **  FIXCRLF -- fix <CR><LF> in line.
7074557Seric **
7084557Seric **	Looks for the <CR><LF> combination and turns it into the
7094557Seric **	UNIX canonical <NL> character.  It only takes one line,
7104557Seric **	i.e., it is assumed that the first <NL> found is the end
7114557Seric **	of the line.
7124557Seric **
7134557Seric **	Parameters:
7144557Seric **		line -- the line to fix.
7154557Seric **		stripnl -- if true, strip the newline also.
7164557Seric **
7174557Seric **	Returns:
7184557Seric **		none.
7194557Seric **
7204557Seric **	Side Effects:
7214557Seric **		line is changed in place.
7224557Seric */
7234557Seric 
72469748Seric void
fixcrlf(line,stripnl)7254557Seric fixcrlf(line, stripnl)
7264557Seric 	char *line;
7274557Seric 	bool stripnl;
7284557Seric {
7294557Seric 	register char *p;
7304557Seric 
73156795Seric 	p = strchr(line, '\n');
7324557Seric 	if (p == NULL)
7334557Seric 		return;
73436291Sbostic 	if (p > line && p[-1] == '\r')
7354557Seric 		p--;
7364557Seric 	if (!stripnl)
7374557Seric 		*p++ = '\n';
7384557Seric 	*p = '\0';
7394557Seric }
7404557Seric /*
7416890Seric **  DFOPEN -- determined file open
7426890Seric **
7436890Seric **	This routine has the semantics of fopen, except that it will
7446890Seric **	keep trying a few times to make this happen.  The idea is that
7456890Seric **	on very loaded systems, we may run out of resources (inodes,
7466890Seric **	whatever), so this tries to get around it.
7476890Seric */
7486890Seric 
74959745Seric struct omodes
75059745Seric {
75159745Seric 	int	mask;
75259745Seric 	int	mode;
75359745Seric 	char	*farg;
75459745Seric } OpenModes[] =
75559745Seric {
75659745Seric 	O_ACCMODE,		O_RDONLY,		"r",
75759745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
75859745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
75959745Seric 	O_TRUNC,		0,			"w+",
76059745Seric 	O_APPEND,		O_APPEND,		"a+",
76159745Seric 	0,			0,			"r+",
76259745Seric };
76359745Seric 
7646890Seric FILE *
dfopen(filename,omode,cmode)76559745Seric dfopen(filename, omode, cmode)
7666890Seric 	char *filename;
76759745Seric 	int omode;
76859745Seric 	int cmode;
7696890Seric {
7706890Seric 	register int tries;
77159745Seric 	int fd;
77259745Seric 	register struct omodes *om;
77359431Seric 	struct stat st;
7746890Seric 
77559745Seric 	for (om = OpenModes; om->mask != 0; om++)
77659745Seric 		if ((omode & om->mask) == om->mode)
77759745Seric 			break;
77859745Seric 
7796890Seric 	for (tries = 0; tries < 10; tries++)
7806890Seric 	{
78125618Seric 		sleep((unsigned) (10 * tries));
7826890Seric 		errno = 0;
78359745Seric 		fd = open(filename, omode, cmode);
78459745Seric 		if (fd >= 0)
7856890Seric 			break;
78666017Seric 		switch (errno)
78766017Seric 		{
78866017Seric 		  case ENFILE:		/* system file table full */
78966017Seric 		  case EINTR:		/* interrupted syscall */
79066017Seric #ifdef ETXTBSY
79166017Seric 		  case ETXTBSY:		/* Apollo: net file locked */
79266017Seric #endif
79366017Seric 			continue;
79466017Seric 		}
79566017Seric 		break;
7966890Seric 	}
79759745Seric 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
79856328Seric 	{
79956328Seric 		int locktype;
80056328Seric 
80156328Seric 		/* lock the file to avoid accidental conflicts */
80259745Seric 		if ((omode & O_ACCMODE) != O_RDONLY)
80356328Seric 			locktype = LOCK_EX;
80456328Seric 		else
80556328Seric 			locktype = LOCK_SH;
80664335Seric 		(void) lockfile(fd, filename, NULL, locktype);
80756328Seric 		errno = 0;
80856328Seric 	}
80963787Seric 	if (fd < 0)
81063787Seric 		return NULL;
81163787Seric 	else
81263787Seric 		return fdopen(fd, om->farg);
8136890Seric }
8147124Seric /*
8157124Seric **  PUTLINE -- put a line like fputs obeying SMTP conventions
8167124Seric **
8177753Seric **	This routine always guarantees outputing a newline (or CRLF,
8187753Seric **	as appropriate) at the end of the string.
8197753Seric **
8207124Seric **	Parameters:
8217124Seric **		l -- line to put.
82265870Seric **		mci -- the mailer connection information.
8237124Seric **
8247124Seric **	Returns:
8257124Seric **		none
8267124Seric **
8277124Seric **	Side Effects:
8287124Seric **		output of l to fp.
8297124Seric */
8307124Seric 
83169748Seric void
putline(l,mci)83265870Seric putline(l, mci)
8337753Seric 	register char *l;
83465870Seric 	register MCI *mci;
8357124Seric {
83668847Seric 	putxline(l, mci, PXLF_MAPFROM);
83768515Seric }
83868847Seric /*
83968847Seric **  PUTXLINE -- putline with flags bits.
84068847Seric **
84168847Seric **	This routine always guarantees outputing a newline (or CRLF,
84268847Seric **	as appropriate) at the end of the string.
84368847Seric **
84468847Seric **	Parameters:
84568847Seric **		l -- line to put.
84668847Seric **		mci -- the mailer connection information.
84768847Seric **		pxflags -- flag bits:
84868847Seric **		    PXLF_MAPFROM -- map From_ to >From_.
84968847Seric **		    PXLF_STRIP8BIT -- strip 8th bit.
85068847Seric **
85168847Seric **	Returns:
85268847Seric **		none
85368847Seric **
85468847Seric **	Side Effects:
85568847Seric **		output of l to fp.
85668847Seric */
85768515Seric 
85868515Seric void
putxline(l,mci,pxflags)85968847Seric putxline(l, mci, pxflags)
86068515Seric 	register char *l;
86168515Seric 	register MCI *mci;
86268847Seric 	int pxflags;
86368515Seric {
8647124Seric 	register char *p;
86547157Sbostic 	register char svchar;
86666004Seric 	int slop = 0;
8677124Seric 
86811275Seric 	/* strip out 0200 bits -- these can look like TELNET protocol */
86968847Seric 	if (bitset(MCIF_7BIT, mci->mci_flags) ||
87068847Seric 	    bitset(PXLF_STRIP8BIT, pxflags))
87111275Seric 	{
87261707Seric 		for (p = l; (svchar = *p) != '\0'; ++p)
87361707Seric 			if (bitset(0200, svchar))
87447157Sbostic 				*p = svchar &~ 0200;
87511275Seric 	}
87611275Seric 
8777753Seric 	do
8787124Seric 	{
8797753Seric 		/* find the end of the line */
88056795Seric 		p = strchr(l, '\n');
8817753Seric 		if (p == NULL)
8827753Seric 			p = &l[strlen(l)];
8837124Seric 
88463753Seric 		if (TrafficLogFile != NULL)
88563753Seric 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
88663753Seric 
8877753Seric 		/* check for line overflow */
88865870Seric 		while (mci->mci_mailer->m_linelimit > 0 &&
88966004Seric 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
8907753Seric 		{
89166004Seric 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
8927124Seric 
8937753Seric 			svchar = *q;
8947753Seric 			*q = '\0';
89566004Seric 			if (l[0] == '.' && slop == 0 &&
89666004Seric 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
89763753Seric 			{
89865870Seric 				(void) putc('.', mci->mci_out);
89963753Seric 				if (TrafficLogFile != NULL)
90063753Seric 					(void) putc('.', TrafficLogFile);
90163753Seric 			}
90268847Seric 			else if (l[0] == 'F' && slop == 0 &&
90368847Seric 				 bitset(PXLF_MAPFROM, pxflags) &&
90468847Seric 				 strncmp(l, "From ", 5) == 0 &&
90568515Seric 				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
90668515Seric 			{
90768515Seric 				(void) putc('>', mci->mci_out);
90868515Seric 				if (TrafficLogFile != NULL)
90968515Seric 					(void) putc('>', TrafficLogFile);
91068515Seric 			}
91165870Seric 			fputs(l, mci->mci_out);
91265870Seric 			(void) putc('!', mci->mci_out);
91365870Seric 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
91466004Seric 			(void) putc(' ', mci->mci_out);
91563753Seric 			if (TrafficLogFile != NULL)
91666004Seric 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
91763753Seric 					l, getpid());
9187753Seric 			*q = svchar;
9197753Seric 			l = q;
92066004Seric 			slop = 1;
9217753Seric 		}
9227124Seric 
9237753Seric 		/* output last part */
92466004Seric 		if (l[0] == '.' && slop == 0 &&
92566004Seric 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
92663753Seric 		{
92765870Seric 			(void) putc('.', mci->mci_out);
92863753Seric 			if (TrafficLogFile != NULL)
92963753Seric 				(void) putc('.', TrafficLogFile);
93063753Seric 		}
93163753Seric 		if (TrafficLogFile != NULL)
93263753Seric 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
93347157Sbostic 		for ( ; l < p; ++l)
93465870Seric 			(void) putc(*l, mci->mci_out);
93565870Seric 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
9367753Seric 		if (*l == '\n')
93747157Sbostic 			++l;
9387753Seric 	} while (l[0] != '\0');
9397124Seric }
9407676Seric /*
9417676Seric **  XUNLINK -- unlink a file, doing logging as appropriate.
9427676Seric **
9437676Seric **	Parameters:
9447676Seric **		f -- name of file to unlink.
9457676Seric **
9467676Seric **	Returns:
9477676Seric **		none.
9487676Seric **
9497676Seric **	Side Effects:
9507676Seric **		f is unlinked.
9517676Seric */
9527676Seric 
95369748Seric void
xunlink(f)9547676Seric xunlink(f)
9557676Seric 	char *f;
9567676Seric {
9577676Seric 	register int i;
9587676Seric 
9597676Seric # ifdef LOG
96058020Seric 	if (LogLevel > 98)
96158020Seric 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
96256795Seric # endif /* LOG */
9637676Seric 
9647676Seric 	i = unlink(f);
9657676Seric # ifdef LOG
96658020Seric 	if (i < 0 && LogLevel > 97)
9677942Seric 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
96856795Seric # endif /* LOG */
9697676Seric }
9707685Seric /*
97158680Seric **  XFCLOSE -- close a file, doing logging as appropriate.
97258680Seric **
97358680Seric **	Parameters:
97458680Seric **		fp -- file pointer for the file to close
97558680Seric **		a, b -- miscellaneous crud to print for debugging
97658680Seric **
97758680Seric **	Returns:
97858680Seric **		none.
97958680Seric **
98058680Seric **	Side Effects:
98158680Seric **		fp is closed.
98258680Seric */
98358680Seric 
98469748Seric void
xfclose(fp,a,b)98558680Seric xfclose(fp, a, b)
98658680Seric 	FILE *fp;
98758680Seric 	char *a, *b;
98858680Seric {
98958796Seric 	if (tTd(53, 99))
99058680Seric 		printf("xfclose(%x) %s %s\n", fp, a, b);
99169881Seric #if XDEBUG
99264401Seric 	if (fileno(fp) == 1)
99364401Seric 		syserr("xfclose(%s %s): fd = 1", a, b);
99464401Seric #endif
99558796Seric 	if (fclose(fp) < 0 && tTd(53, 99))
99658680Seric 		printf("xfclose FAILURE: %s\n", errstring(errno));
99758680Seric }
99858680Seric /*
99914885Seric **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
10007685Seric **
10017685Seric **	Parameters:
10027685Seric **		buf -- place to put the input line.
10037685Seric **		siz -- size of buf.
10047685Seric **		fp -- file to read from.
100557384Seric **		timeout -- the timeout before error occurs.
100661093Seric **		during -- what we are trying to read (for error messages).
10077685Seric **
10087685Seric **	Returns:
100915533Seric **		NULL on error (including timeout).  This will also leave
101015533Seric **			buf containing a null string.
10117685Seric **		buf otherwise.
10127685Seric **
10137685Seric **	Side Effects:
10147685Seric **		none.
10157685Seric */
10167685Seric 
101714885Seric static jmp_buf	CtxReadTimeout;
101868481Seric static void	readtimeout();
10197685Seric 
10207685Seric char *
sfgets(buf,siz,fp,timeout,during)102161093Seric sfgets(buf, siz, fp, timeout, during)
10227685Seric 	char *buf;
10237685Seric 	int siz;
10247685Seric 	FILE *fp;
102557384Seric 	time_t timeout;
102661093Seric 	char *during;
10277685Seric {
10287942Seric 	register EVENT *ev = NULL;
10297685Seric 	register char *p;
10307685Seric 
103166332Seric 	if (fp == NULL)
103266332Seric 	{
103366332Seric 		buf[0] = '\0';
103466332Seric 		return NULL;
103566332Seric 	}
103666332Seric 
103714885Seric 	/* set the timeout */
103857384Seric 	if (timeout != 0)
103914885Seric 	{
104014885Seric 		if (setjmp(CtxReadTimeout) != 0)
104114885Seric 		{
104236233Skarels # ifdef LOG
104336230Skarels 			syslog(LOG_NOTICE,
104461093Seric 			    "timeout waiting for input from %s during %s\n",
104561093Seric 			    CurHostName? CurHostName: "local", during);
104636233Skarels # endif
104736230Skarels 			errno = 0;
104861093Seric 			usrerr("451 timeout waiting for input during %s",
104961093Seric 				during);
105019037Seric 			buf[0] = '\0';
105169881Seric #if XDEBUG
105263753Seric 			checkfd012(during);
105363753Seric #endif
105414885Seric 			return (NULL);
105514885Seric 		}
105668481Seric 		ev = setevent(timeout, readtimeout, 0);
105714885Seric 	}
105814885Seric 
105914885Seric 	/* try to read */
106015533Seric 	p = NULL;
106165190Seric 	while (!feof(fp) && !ferror(fp))
10627942Seric 	{
10637942Seric 		errno = 0;
10647942Seric 		p = fgets(buf, siz, fp);
106565190Seric 		if (p != NULL || errno != EINTR)
106665186Seric 			break;
106765190Seric 		clearerr(fp);
106815533Seric 	}
106914885Seric 
107014885Seric 	/* clear the event if it has not sprung */
107168481Seric 	clrevent(ev);
107214885Seric 
107314885Seric 	/* clean up the books and exit */
10748055Seric 	LineNumber++;
107515533Seric 	if (p == NULL)
107616880Seric 	{
107715533Seric 		buf[0] = '\0';
107863753Seric 		if (TrafficLogFile != NULL)
107963753Seric 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
108016880Seric 		return (NULL);
108116880Seric 	}
108263753Seric 	if (TrafficLogFile != NULL)
108363753Seric 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
108468481Seric 	if (SevenBitInput)
108568481Seric 	{
108652106Seric 		for (p = buf; *p != '\0'; p++)
108752106Seric 			*p &= ~0200;
108867546Seric 	}
108968481Seric 	else if (!HasEightBits)
109067546Seric 	{
109168481Seric 		for (p = buf; *p != '\0'; p++)
109268481Seric 		{
109368481Seric 			if (bitset(0200, *p))
109468481Seric 			{
109568481Seric 				HasEightBits = TRUE;
109668481Seric 				break;
109768481Seric 			}
109868481Seric 		}
109967546Seric 	}
110068481Seric 	return (buf);
11017685Seric }
11027685Seric 
110368481Seric static void
readtimeout(timeout)110466765Seric readtimeout(timeout)
110566765Seric 	time_t timeout;
11067685Seric {
110768481Seric 	longjmp(CtxReadTimeout, 1);
11087685Seric }
11097786Seric /*
11107786Seric **  FGETFOLDED -- like fgets, but know about folded lines.
11117786Seric **
11127786Seric **	Parameters:
11137786Seric **		buf -- place to put result.
11147786Seric **		n -- bytes available.
11157786Seric **		f -- file to read from.
11167786Seric **
11177786Seric **	Returns:
111857135Seric **		input line(s) on success, NULL on error or EOF.
111957135Seric **		This will normally be buf -- unless the line is too
112057135Seric **			long, when it will be xalloc()ed.
11217786Seric **
11227786Seric **	Side Effects:
11237786Seric **		buf gets lines from f, with continuation lines (lines
11247786Seric **		with leading white space) appended.  CRLF's are mapped
11257786Seric **		into single newlines.  Any trailing NL is stripped.
11267786Seric */
11277786Seric 
11287786Seric char *
fgetfolded(buf,n,f)11297786Seric fgetfolded(buf, n, f)
11307786Seric 	char *buf;
11317786Seric 	register int n;
11327786Seric 	FILE *f;
11337786Seric {
11347786Seric 	register char *p = buf;
113557135Seric 	char *bp = buf;
11367786Seric 	register int i;
11377786Seric 
11387786Seric 	n--;
113917350Seric 	while ((i = getc(f)) != EOF)
11407786Seric 	{
114117350Seric 		if (i == '\r')
114217350Seric 		{
114317350Seric 			i = getc(f);
114417350Seric 			if (i != '\n')
114517350Seric 			{
114617350Seric 				if (i != EOF)
114723105Seric 					(void) ungetc(i, f);
114817350Seric 				i = '\r';
114917350Seric 			}
115017350Seric 		}
115157135Seric 		if (--n <= 0)
115257135Seric 		{
115357135Seric 			/* allocate new space */
115457135Seric 			char *nbp;
115557135Seric 			int nn;
115657135Seric 
115757135Seric 			nn = (p - bp);
115857232Seric 			if (nn < MEMCHUNKSIZE)
115957135Seric 				nn *= 2;
116057135Seric 			else
116157232Seric 				nn += MEMCHUNKSIZE;
116257135Seric 			nbp = xalloc(nn);
116357135Seric 			bcopy(bp, nbp, p - bp);
116457135Seric 			p = &nbp[p - bp];
116557135Seric 			if (bp != buf)
116657135Seric 				free(bp);
116757135Seric 			bp = nbp;
116857135Seric 			n = nn - (p - bp);
116957135Seric 		}
117057135Seric 		*p++ = i;
117117350Seric 		if (i == '\n')
117217350Seric 		{
117317350Seric 			LineNumber++;
117417350Seric 			i = getc(f);
117517350Seric 			if (i != EOF)
117623105Seric 				(void) ungetc(i, f);
117717350Seric 			if (i != ' ' && i != '\t')
117852647Seric 				break;
117917350Seric 		}
11807786Seric 	}
118157135Seric 	if (p == bp)
118252647Seric 		return (NULL);
118369697Seric 	if (p[-1] == '\n')
118469697Seric 		p--;
118569697Seric 	*p = '\0';
118657135Seric 	return (bp);
11877786Seric }
11887860Seric /*
11897886Seric **  CURTIME -- return current time.
11907886Seric **
11917886Seric **	Parameters:
11927886Seric **		none.
11937886Seric **
11947886Seric **	Returns:
11957886Seric **		the current time.
11967886Seric **
11977886Seric **	Side Effects:
11987886Seric **		none.
11997886Seric */
12007886Seric 
12017886Seric time_t
curtime()12027886Seric curtime()
12037886Seric {
12047886Seric 	auto time_t t;
12057886Seric 
12067886Seric 	(void) time(&t);
12077886Seric 	return (t);
12087886Seric }
12098264Seric /*
12108264Seric **  ATOBOOL -- convert a string representation to boolean.
12118264Seric **
12128264Seric **	Defaults to "TRUE"
12138264Seric **
12148264Seric **	Parameters:
12158264Seric **		s -- string to convert.  Takes "tTyY" as true,
12168264Seric **			others as false.
12178264Seric **
12188264Seric **	Returns:
12198264Seric **		A boolean representation of the string.
12208264Seric **
12218264Seric **	Side Effects:
12228264Seric **		none.
12238264Seric */
12248264Seric 
12258264Seric bool
atobool(s)12268264Seric atobool(s)
12278264Seric 	register char *s;
12288264Seric {
122963833Seric 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
12308264Seric 		return (TRUE);
12318264Seric 	return (FALSE);
12328264Seric }
12339048Seric /*
12349048Seric **  ATOOCT -- convert a string representation to octal.
12359048Seric **
12369048Seric **	Parameters:
12379048Seric **		s -- string to convert.
12389048Seric **
12399048Seric **	Returns:
12409048Seric **		An integer representing the string interpreted as an
12419048Seric **		octal number.
12429048Seric **
12439048Seric **	Side Effects:
12449048Seric **		none.
12459048Seric */
12469048Seric 
124769748Seric int
atooct(s)12489048Seric atooct(s)
12499048Seric 	register char *s;
12509048Seric {
12519048Seric 	register int i = 0;
12529048Seric 
12539048Seric 	while (*s >= '0' && *s <= '7')
12549048Seric 		i = (i << 3) | (*s++ - '0');
12559048Seric 	return (i);
12569048Seric }
12579376Seric /*
12589376Seric **  WAITFOR -- wait for a particular process id.
12599376Seric **
12609376Seric **	Parameters:
12619376Seric **		pid -- process id to wait for.
12629376Seric **
12639376Seric **	Returns:
12649376Seric **		status of pid.
12659376Seric **		-1 if pid never shows up.
12669376Seric **
12679376Seric **	Side Effects:
12689376Seric **		none.
12699376Seric */
12709376Seric 
127164562Seric int
waitfor(pid)12729376Seric waitfor(pid)
12739376Seric 	int pid;
12749376Seric {
127564562Seric #ifdef WAITUNION
127664562Seric 	union wait st;
127764562Seric #else
12789376Seric 	auto int st;
127964562Seric #endif
12809376Seric 	int i;
12819376Seric 
12829376Seric 	do
12839376Seric 	{
12849376Seric 		errno = 0;
12859376Seric 		i = wait(&st);
12869376Seric 	} while ((i >= 0 || errno == EINTR) && i != pid);
12879376Seric 	if (i < 0)
128864562Seric 		return -1;
128964562Seric #ifdef WAITUNION
129064562Seric 	return st.w_status;
129164562Seric #else
129264562Seric 	return st;
129364562Seric #endif
12949376Seric }
12959376Seric /*
129610685Seric **  BITINTERSECT -- tell if two bitmaps intersect
129710685Seric **
129810685Seric **	Parameters:
129910685Seric **		a, b -- the bitmaps in question
130010685Seric **
130110685Seric **	Returns:
130210685Seric **		TRUE if they have a non-null intersection
130310685Seric **		FALSE otherwise
130410685Seric **
130510685Seric **	Side Effects:
130610685Seric **		none.
130710685Seric */
130810685Seric 
130910685Seric bool
bitintersect(a,b)131010685Seric bitintersect(a, b)
131110685Seric 	BITMAP a;
131210685Seric 	BITMAP b;
131310685Seric {
131410685Seric 	int i;
131510685Seric 
131610685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
131710685Seric 		if ((a[i] & b[i]) != 0)
131810685Seric 			return (TRUE);
131910685Seric 	return (FALSE);
132010685Seric }
132110685Seric /*
132210685Seric **  BITZEROP -- tell if a bitmap is all zero
132310685Seric **
132410685Seric **	Parameters:
132510685Seric **		map -- the bit map to check
132610685Seric **
132710685Seric **	Returns:
132810685Seric **		TRUE if map is all zero.
132910685Seric **		FALSE if there are any bits set in map.
133010685Seric **
133110685Seric **	Side Effects:
133210685Seric **		none.
133310685Seric */
133410685Seric 
133510685Seric bool
bitzerop(map)133610685Seric bitzerop(map)
133710685Seric 	BITMAP map;
133810685Seric {
133910685Seric 	int i;
134010685Seric 
134110685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
134210685Seric 		if (map[i] != 0)
134310685Seric 			return (FALSE);
134410685Seric 	return (TRUE);
134510685Seric }
134658247Seric /*
134758318Seric **  STRCONTAINEDIN -- tell if one string is contained in another
134858318Seric **
134958318Seric **	Parameters:
135058318Seric **		a -- possible substring.
135158318Seric **		b -- possible superstring.
135258318Seric **
135358318Seric **	Returns:
135458318Seric **		TRUE if a is contained in b.
135558318Seric **		FALSE otherwise.
135658318Seric */
135758318Seric 
135858318Seric bool
strcontainedin(a,b)135958318Seric strcontainedin(a, b)
136058318Seric 	register char *a;
136158318Seric 	register char *b;
136258318Seric {
136365012Seric 	int la;
136465012Seric 	int lb;
136565012Seric 	int c;
136658318Seric 
136765012Seric 	la = strlen(a);
136865012Seric 	lb = strlen(b);
136965012Seric 	c = *a;
137065012Seric 	if (isascii(c) && isupper(c))
137165012Seric 		c = tolower(c);
137265012Seric 	for (; lb-- >= la; b++)
137358318Seric 	{
137465012Seric 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
137565012Seric 			continue;
137665012Seric 		if (strncasecmp(a, b, la) == 0)
137758318Seric 			return TRUE;
137858318Seric 	}
137965012Seric 	return FALSE;
138058318Seric }
138163753Seric /*
138263753Seric **  CHECKFD012 -- check low numbered file descriptors
138363753Seric **
138463753Seric **	File descriptors 0, 1, and 2 should be open at all times.
138563753Seric **	This routine verifies that, and fixes it if not true.
138663753Seric **
138763753Seric **	Parameters:
138863753Seric **		where -- a tag printed if the assertion failed
138963753Seric **
139063753Seric **	Returns:
139163753Seric **		none
139263753Seric */
139363753Seric 
139469748Seric void
checkfd012(where)139563753Seric checkfd012(where)
139663753Seric 	char *where;
139763753Seric {
139869881Seric #if XDEBUG
139963753Seric 	register int i;
140063753Seric 	struct stat stbuf;
140163753Seric 
140263753Seric 	for (i = 0; i < 3; i++)
140363753Seric 	{
140464735Seric 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
140563753Seric 		{
140663753Seric 			/* oops.... */
140763753Seric 			int fd;
140863753Seric 
140963753Seric 			syserr("%s: fd %d not open", where, i);
141063753Seric 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
141163753Seric 			if (fd != i)
141263753Seric 			{
141363753Seric 				(void) dup2(fd, i);
141463753Seric 				(void) close(fd);
141563753Seric 			}
141663753Seric 		}
141763753Seric 	}
141863937Seric #endif /* XDEBUG */
141963753Seric }
142064725Seric /*
142164725Seric **  PRINTOPENFDS -- print the open file descriptors (for debugging)
142264725Seric **
142364725Seric **	Parameters:
142464725Seric **		logit -- if set, send output to syslog; otherwise
142564725Seric **			print for debugging.
142664725Seric **
142764725Seric **	Returns:
142864725Seric **		none.
142964725Seric */
143064725Seric 
143164725Seric #include <arpa/inet.h>
143264725Seric 
143369748Seric void
printopenfds(logit)143464725Seric printopenfds(logit)
143564725Seric 	bool logit;
143664725Seric {
143764725Seric 	register int fd;
143864743Seric 	extern int DtableSize;
143964743Seric 
144064743Seric 	for (fd = 0; fd < DtableSize; fd++)
144164743Seric 		dumpfd(fd, FALSE, logit);
144264743Seric }
144364743Seric /*
144464743Seric **  DUMPFD -- dump a file descriptor
144564743Seric **
144664743Seric **	Parameters:
144764743Seric **		fd -- the file descriptor to dump.
144864743Seric **		printclosed -- if set, print a notification even if
144964743Seric **			it is closed; otherwise print nothing.
145064743Seric **		logit -- if set, send output to syslog instead of stdout.
145164743Seric */
145264743Seric 
145369748Seric void
dumpfd(fd,printclosed,logit)145464743Seric dumpfd(fd, printclosed, logit)
145564743Seric 	int fd;
145664743Seric 	bool printclosed;
145764743Seric 	bool logit;
145864743Seric {
145964725Seric 	register char *p;
146068777Seric 	char *hp;
146166747Seric 	char *fmtstr;
146268777Seric 	SOCKADDR sa;
146364743Seric 	auto int slen;
146464743Seric 	struct stat st;
146564725Seric 	char buf[200];
146668777Seric 	extern char *hostnamebyanyaddr();
146764725Seric 
146864743Seric 	p = buf;
146964743Seric 	sprintf(p, "%3d: ", fd);
147064743Seric 	p += strlen(p);
147164743Seric 
147264743Seric 	if (fstat(fd, &st) < 0)
147364725Seric 	{
147464743Seric 		if (printclosed || errno != EBADF)
147564743Seric 		{
147664743Seric 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
147764743Seric 			goto printit;
147864743Seric 		}
147964743Seric 		return;
148064743Seric 	}
148164725Seric 
148264743Seric 	slen = fcntl(fd, F_GETFL, NULL);
148364743Seric 	if (slen != -1)
148464743Seric 	{
148564743Seric 		sprintf(p, "fl=0x%x, ", slen);
148664743Seric 		p += strlen(p);
148764743Seric 	}
148864725Seric 
148964743Seric 	sprintf(p, "mode=%o: ", st.st_mode);
149064743Seric 	p += strlen(p);
149164743Seric 	switch (st.st_mode & S_IFMT)
149264743Seric 	{
149364807Seric #ifdef S_IFSOCK
149464743Seric 	  case S_IFSOCK:
149564743Seric 		sprintf(p, "SOCK ");
149664725Seric 		p += strlen(p);
149768777Seric 		slen = sizeof sa;
149868777Seric 		if (getsockname(fd, &sa.sa, &slen) < 0)
149968810Seric 			sprintf(p, "(%s)", errstring(errno));
150064743Seric 		else
150164725Seric 		{
150268777Seric 			hp = hostnamebyanyaddr(&sa);
150368777Seric 			if (sa.sa.sa_family == AF_INET)
150468777Seric 				sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
150568777Seric 			else
150668777Seric 				sprintf(p, "%s", hp);
150764743Seric 		}
150864743Seric 		p += strlen(p);
150964743Seric 		sprintf(p, "->");
151064743Seric 		p += strlen(p);
151168777Seric 		slen = sizeof sa;
151268777Seric 		if (getpeername(fd, &sa.sa, &slen) < 0)
151368810Seric 			sprintf(p, "(%s)", errstring(errno));
151464743Seric 		else
151564743Seric 		{
151668777Seric 			hp = hostnamebyanyaddr(&sa);
151768777Seric 			if (sa.sa.sa_family == AF_INET)
151868777Seric 				sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
151968777Seric 			else
152068777Seric 				sprintf(p, "%s", hp);
152164743Seric 		}
152264743Seric 		break;
152364807Seric #endif
152464725Seric 
152564743Seric 	  case S_IFCHR:
152664743Seric 		sprintf(p, "CHR: ");
152764743Seric 		p += strlen(p);
152864743Seric 		goto defprint;
152964725Seric 
153064743Seric 	  case S_IFBLK:
153164743Seric 		sprintf(p, "BLK: ");
153264743Seric 		p += strlen(p);
153364743Seric 		goto defprint;
153464725Seric 
153566252Seric #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
153665378Seric 	  case S_IFIFO:
153765378Seric 		sprintf(p, "FIFO: ");
153865378Seric 		p += strlen(p);
153965378Seric 		goto defprint;
154065378Seric #endif
154165378Seric 
154265378Seric #ifdef S_IFDIR
154365378Seric 	  case S_IFDIR:
154465378Seric 		sprintf(p, "DIR: ");
154565378Seric 		p += strlen(p);
154665378Seric 		goto defprint;
154765378Seric #endif
154865378Seric 
154965378Seric #ifdef S_IFLNK
155065378Seric 	  case S_IFLNK:
155165378Seric 		sprintf(p, "LNK: ");
155265378Seric 		p += strlen(p);
155365378Seric 		goto defprint;
155465378Seric #endif
155565378Seric 
155664743Seric 	  default:
155764725Seric defprint:
155866785Seric 		if (sizeof st.st_size > sizeof (long))
155966751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
156066747Seric 		else
156166751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
156266747Seric 		sprintf(p, fmtstr,
156364743Seric 			major(st.st_dev), minor(st.st_dev), st.st_ino,
156464743Seric 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
156564743Seric 		break;
156664743Seric 	}
156764725Seric 
156864743Seric printit:
156966748Seric #ifdef LOG
157064743Seric 	if (logit)
157165006Seric 		syslog(LOG_DEBUG, "%s", buf);
157264743Seric 	else
157366748Seric #endif
157464743Seric 		printf("%s\n", buf);
157564725Seric }
157665015Seric /*
157765015Seric **  SHORTENSTRING -- return short version of a string
157865015Seric **
157965015Seric **	If the string is already short, just return it.  If it is too
158065015Seric **	long, return the head and tail of the string.
158165015Seric **
158265015Seric **	Parameters:
158365015Seric **		s -- the string to shorten.
158465015Seric **		m -- the max length of the string.
158565015Seric **
158665015Seric **	Returns:
158765015Seric **		Either s or a short version of s.
158865015Seric */
158965015Seric 
159065015Seric #ifndef MAXSHORTSTR
159165055Seric # define MAXSHORTSTR	203
159265015Seric #endif
159365015Seric 
159465015Seric char *
shortenstring(s,m)159565015Seric shortenstring(s, m)
159669748Seric 	register const char *s;
159765015Seric 	int m;
159865015Seric {
159965015Seric 	int l;
160065015Seric 	static char buf[MAXSHORTSTR + 1];
160165015Seric 
160265015Seric 	l = strlen(s);
160365015Seric 	if (l < m)
160469748Seric 		return (char *) s;
160565015Seric 	if (m > MAXSHORTSTR)
160665015Seric 		m = MAXSHORTSTR;
160765015Seric 	else if (m < 10)
160865015Seric 	{
160965015Seric 		if (m < 5)
161065015Seric 		{
161165015Seric 			strncpy(buf, s, m);
161265015Seric 			buf[m] = '\0';
161365015Seric 			return buf;
161465015Seric 		}
161565015Seric 		strncpy(buf, s, m - 3);
161665015Seric 		strcpy(buf + m - 3, "...");
161765015Seric 		return buf;
161865015Seric 	}
161965015Seric 	m = (m - 3) / 2;
162065015Seric 	strncpy(buf, s, m);
162165015Seric 	strcpy(buf + m, "...");
162265015Seric 	strcpy(buf + m + 3, s + l - m);
162365015Seric 	return buf;
162465015Seric }
162567848Seric /*
162669401Seric **  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
162769401Seric **
162869401Seric **	Parameters:
162969401Seric **		host -- the host to shorten (stripped in place).
163069401Seric **
163169401Seric **	Returns:
163269401Seric **		none.
163369401Seric */
163469401Seric 
163569401Seric void
shorten_hostname(host)163669401Seric shorten_hostname(host)
163769401Seric 	char host[];
163869401Seric {
163969401Seric 	register char *p;
164069401Seric 	char *mydom;
164169401Seric 	int i;
164269773Seric 	bool canon = FALSE;
164369401Seric 
164469401Seric 	/* strip off final dot */
164569401Seric 	p = &host[strlen(host) - 1];
164669401Seric 	if (*p == '.')
164769773Seric 	{
164869401Seric 		*p = '\0';
164969773Seric 		canon = TRUE;
165069773Seric 	}
165169401Seric 
165269401Seric 	/* see if there is any domain at all -- if not, we are done */
165369401Seric 	p = strchr(host, '.');
165469401Seric 	if (p == NULL)
165569401Seric 		return;
165669401Seric 
165769401Seric 	/* yes, we have a domain -- see if it looks like us */
165869401Seric 	mydom = macvalue('m', CurEnv);
165969401Seric 	if (mydom == NULL)
166069401Seric 		mydom = "";
166169401Seric 	i = strlen(++p);
166269773Seric 	if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
166369401Seric 	    (mydom[i] == '.' || mydom[i] == '\0'))
166469401Seric 		*--p = '\0';
166569401Seric }
166669401Seric /*
166769453Seric **  PROG_OPEN -- open a program for reading
166869453Seric **
166969453Seric **	Parameters:
167069453Seric **		argv -- the argument list.
167169453Seric **		pfd -- pointer to a place to store the file descriptor.
167269453Seric **		e -- the current envelope.
167369453Seric **
167469453Seric **	Returns:
167569453Seric **		pid of the process -- -1 if it failed.
167669453Seric */
167769453Seric 
167869453Seric int
prog_open(argv,pfd,e)167969453Seric prog_open(argv, pfd, e)
168069453Seric 	char **argv;
168169453Seric 	int *pfd;
168269453Seric 	ENVELOPE *e;
168369453Seric {
168469453Seric 	int pid;
168569453Seric 	int i;
168669453Seric 	int saveerrno;
168769453Seric 	int fdv[2];
168869453Seric 	char *p, *q;
168969453Seric 	char buf[MAXLINE + 1];
169069453Seric 	extern int DtableSize;
169169453Seric 
169269453Seric 	if (pipe(fdv) < 0)
169369453Seric 	{
169469453Seric 		syserr("%s: cannot create pipe for stdout", argv[0]);
169569453Seric 		return -1;
169669453Seric 	}
169769453Seric 	pid = fork();
169869453Seric 	if (pid < 0)
169969453Seric 	{
170069453Seric 		syserr("%s: cannot fork", argv[0]);
170169453Seric 		close(fdv[0]);
170269453Seric 		close(fdv[1]);
170369453Seric 		return -1;
170469453Seric 	}
170569453Seric 	if (pid > 0)
170669453Seric 	{
170769453Seric 		/* parent */
170869453Seric 		close(fdv[1]);
170969453Seric 		*pfd = fdv[0];
171069453Seric 		return pid;
171169453Seric 	}
171269453Seric 
171369453Seric 	/* child -- close stdin */
171469453Seric 	close(0);
171569453Seric 
171669453Seric 	/* stdout goes back to parent */
171769453Seric 	close(fdv[0]);
171869453Seric 	if (dup2(fdv[1], 1) < 0)
171969453Seric 	{
172069453Seric 		syserr("%s: cannot dup2 for stdout", argv[0]);
172169453Seric 		_exit(EX_OSERR);
172269453Seric 	}
172369453Seric 	close(fdv[1]);
172469453Seric 
172569453Seric 	/* stderr goes to transcript if available */
172669453Seric 	if (e->e_xfp != NULL)
172769453Seric 	{
172869453Seric 		if (dup2(fileno(e->e_xfp), 2) < 0)
172969453Seric 		{
173069453Seric 			syserr("%s: cannot dup2 for stderr", argv[0]);
173169453Seric 			_exit(EX_OSERR);
173269453Seric 		}
173369453Seric 	}
173469453Seric 
173569453Seric 	/* this process has no right to the queue file */
173669453Seric 	if (e->e_lockfp != NULL)
173769453Seric 		close(fileno(e->e_lockfp));
173869453Seric 
173969453Seric 	/* run as default user */
1740*69963Seric 	endpwent();
174169453Seric 	setgid(DefGid);
174269453Seric 	setuid(DefUid);
174369453Seric 
174469453Seric 	/* run in some directory */
174569453Seric 	if (ProgMailer != NULL)
174669453Seric 		p = ProgMailer->m_execdir;
174769453Seric 	else
174869453Seric 		p = NULL;
174969453Seric 	for (; p != NULL; p = q)
175069453Seric 	{
175169453Seric 		q = strchr(p, ':');
175269453Seric 		if (q != NULL)
175369453Seric 			*q = '\0';
175469453Seric 		expand(p, buf, sizeof buf, e);
175569453Seric 		if (q != NULL)
175669453Seric 			*q++ = ':';
175769453Seric 		if (buf[0] != '\0' && chdir(buf) >= 0)
175869453Seric 			break;
175969453Seric 	}
176069453Seric 	if (p == NULL)
176169453Seric 	{
176269453Seric 		/* backup directories */
176369453Seric 		if (chdir("/tmp") < 0)
176469453Seric 			(void) chdir("/");
176569453Seric 	}
176669453Seric 
176769453Seric 	/* arrange for all the files to be closed */
176869453Seric 	for (i = 3; i < DtableSize; i++)
176969453Seric 	{
177069453Seric 		register int j;
177169453Seric 
177269453Seric 		if ((j = fcntl(i, F_GETFD, 0)) != -1)
177369453Seric 			(void) fcntl(i, F_SETFD, j | 1);
177469453Seric 	}
177569453Seric 
177669453Seric 	/* now exec the process */
177769476Seric 	execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
177869453Seric 
177969453Seric 	/* woops!  failed */
178069453Seric 	saveerrno = errno;
178169453Seric 	syserr("%s: cannot exec", argv[0]);
178269453Seric 	if (transienterror(saveerrno))
178369453Seric 		_exit(EX_OSERR);
178469453Seric 	_exit(EX_CONFIG);
178569453Seric }
178669453Seric /*
178768481Seric **  GET_COLUMN  -- look up a Column in a line buffer
178868481Seric **
178968481Seric **	Parameters:
179068481Seric **		line -- the raw text line to search.
179168481Seric **		col -- the column number to fetch.
179268481Seric **		delim -- the delimiter between columns.  If null,
179368481Seric **			use white space.
179468481Seric **		buf -- the output buffer.
179568481Seric **
179668481Seric **	Returns:
179768481Seric **		buf if successful.
179868481Seric **		NULL otherwise.
179968481Seric */
180068481Seric 
180168481Seric char *
get_column(line,col,delim,buf)180268481Seric get_column(line, col, delim, buf)
180368481Seric 	char line[];
180468481Seric 	int col;
180568481Seric 	char delim;
180668481Seric 	char buf[];
180768481Seric {
180868481Seric 	char *p;
180968481Seric 	char *begin, *end;
181068481Seric 	int i;
181168481Seric 	char delimbuf[3];
181268481Seric 
181368481Seric 	if (delim == '\0')
181469662Seric 		strcpy(delimbuf, "\n\t ");
181568481Seric 	else
181668481Seric 	{
181768481Seric 		delimbuf[0] = delim;
181868481Seric 		delimbuf[1] = '\0';
181968481Seric 	}
182068481Seric 
182168481Seric 	p = line;
182268481Seric 	if (*p == '\0')
182368481Seric 		return NULL;			/* line empty */
182468481Seric 	if (*p == delim && col == 0)
182568481Seric 		return NULL;			/* first column empty */
182668481Seric 
182768481Seric 	begin = line;
182868481Seric 
182968481Seric 	if (col == 0 && delim == '\0')
183068481Seric 	{
183168481Seric 		while (*begin && isspace(*begin))
183268481Seric 			begin++;
183368481Seric 	}
183468481Seric 
183568481Seric 	for (i = 0; i < col; i++)
183668481Seric 	{
183768481Seric 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
183868481Seric 			return NULL;		/* no such column */
183968481Seric 		begin++;
184068481Seric 		if (delim == '\0')
184168481Seric 		{
184268481Seric 			while (*begin && isspace(*begin))
184368481Seric 				begin++;
184468481Seric 		}
184568481Seric 	}
184668481Seric 
184768481Seric 	end = strpbrk(begin, delimbuf);
184868481Seric 	if (end == NULL)
184968481Seric 	{
185068481Seric 		strcpy(buf, begin);
185168481Seric 	}
185268481Seric 	else
185368481Seric 	{
185468481Seric 		strncpy(buf, begin, end - begin);
185568481Seric 		buf[end - begin] = '\0';
185668481Seric 	}
185768481Seric 	return buf;
185868481Seric }
185968481Seric /*
186068267Seric **  CLEANSTRCPY -- copy string keeping out bogus characters
186168267Seric **
186268267Seric **	Parameters:
186368267Seric **		t -- "to" string.
186468267Seric **		f -- "from" string.
186568267Seric **		l -- length of space available in "to" string.
186668267Seric **
186768267Seric **	Returns:
186868267Seric **		none.
186968267Seric */
187068267Seric 
187168267Seric void
cleanstrcpy(t,f,l)187268267Seric cleanstrcpy(t, f, l)
187368267Seric 	register char *t;
187468267Seric 	register char *f;
187568267Seric 	int l;
187668267Seric {
187768281Seric #ifdef LOG
187868281Seric 	/* check for newlines and log if necessary */
187968478Seric 	(void) denlstring(f, TRUE, TRUE);
188068281Seric #endif
188168281Seric 
188268267Seric 	l--;
188368267Seric 	while (l > 0 && *f != '\0')
188468267Seric 	{
188568267Seric 		if (isascii(*f) &&
188668267Seric 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
188768267Seric 		{
188868267Seric 			l--;
188968267Seric 			*t++ = *f;
189068267Seric 		}
189168267Seric 		f++;
189268267Seric 	}
189368267Seric 	*t = '\0';
189468267Seric }
189568267Seric /*
189668267Seric **  DENLSTRING -- convert newlines in a string to spaces
189768267Seric **
189868267Seric **	Parameters:
189968267Seric **		s -- the input string
190068478Seric **		strict -- if set, don't permit continuation lines.
190168457Seric **		logattacks -- if set, log attempted attacks.
190268267Seric **
190368267Seric **	Returns:
190468267Seric **		A pointer to a version of the string with newlines
190568267Seric **		mapped to spaces.  This should be copied.
190668267Seric */
190768267Seric 
190868267Seric char *
denlstring(s,strict,logattacks)190968478Seric denlstring(s, strict, logattacks)
191068267Seric 	char *s;
191168702Seric 	bool strict;
191268702Seric 	bool logattacks;
191368267Seric {
191468267Seric 	register char *p;
191568267Seric 	int l;
191668267Seric 	static char *bp = NULL;
191768267Seric 	static int bl = 0;
191868267Seric 
191968478Seric 	p = s;
192068478Seric 	while ((p = strchr(p, '\n')) != NULL)
192168478Seric 		if (strict || (*++p != ' ' && *p != '\t'))
192268478Seric 			break;
192368478Seric 	if (p == NULL)
192468267Seric 		return s;
192568267Seric 
192668267Seric 	l = strlen(s) + 1;
192768267Seric 	if (bl < l)
192868267Seric 	{
192968267Seric 		/* allocate more space */
193068267Seric 		if (bp != NULL)
193168267Seric 			free(bp);
193268267Seric 		bp = xalloc(l);
193368267Seric 		bl = l;
193468267Seric 	}
193568267Seric 	strcpy(bp, s);
193668267Seric 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
193768267Seric 		*p++ = ' ';
193868281Seric 
193968481Seric /*
194068281Seric #ifdef LOG
194168457Seric 	if (logattacks)
194268457Seric 	{
194368457Seric 		syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
194468457Seric 			RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
194568457Seric 			shortenstring(bp, 80));
194668457Seric 	}
194768281Seric #endif
194868481Seric */
194968281Seric 
195068267Seric 	return bp;
195168267Seric }
1952