util.c revision 132943
192282Sobrien/*
279968Sobrien * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
379968Sobrien *	All rights reserved.
479968Sobrien * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
579968Sobrien * Copyright (c) 1988, 1993
679968Sobrien *	The Regents of the University of California.  All rights reserved.
779968Sobrien *
879968Sobrien * By using this file, you agree to the terms and conditions set
979968Sobrien * forth in the LICENSE file which can be found at the top level of
1079968Sobrien * the sendmail distribution.
1179968Sobrien *
1279968Sobrien */
1379968Sobrien
1479968Sobrien#include <sendmail.h>
1579968Sobrien
1679968SobrienSM_RCSID("@(#)$Id: util.c,v 8.382 2004/03/26 19:01:10 ca Exp $")
1779968Sobrien
1879968Sobrien#include <sysexits.h>
1979968Sobrien#include <sm/xtrap.h>
2079968Sobrien
2179968Sobrien/*
2279968Sobrien**  NEWSTR -- Create a copy of a C string
2379968Sobrien**
2479968Sobrien**	Parameters:
2579968Sobrien**		s -- the string to copy.
2679968Sobrien**
2779968Sobrien**	Returns:
2879968Sobrien**		pointer to newly allocated string.
2979968Sobrien*/
3079968Sobrien
3179968Sobrienchar *
3279968Sobriennewstr(s)
3379968Sobrien	const char *s;
3479968Sobrien{
3579968Sobrien	size_t l;
3679968Sobrien	char *n;
3779968Sobrien
3879968Sobrien	l = strlen(s);
3979968Sobrien	SM_ASSERT(l + 1 > l);
4079968Sobrien	n = xalloc(l + 1);
4179968Sobrien	sm_strlcpy(n, s, l + 1);
4279968Sobrien	return n;
4379968Sobrien}
4479968Sobrien
4579968Sobrien/*
4679968Sobrien**  ADDQUOTES -- Adds quotes & quote bits to a string.
4779968Sobrien**
4879968Sobrien**	Runs through a string and adds backslashes and quote bits.
4979968Sobrien**
5079968Sobrien**	Parameters:
5179968Sobrien**		s -- the string to modify.
5279968Sobrien**		rpool -- resource pool from which to allocate result
5379968Sobrien**
5479968Sobrien**	Returns:
5579968Sobrien**		pointer to quoted string.
5679968Sobrien*/
5779968Sobrien
5879968Sobrienchar *
5979968Sobrienaddquotes(s, rpool)
6079968Sobrien	char *s;
6179968Sobrien	SM_RPOOL_T *rpool;
6279968Sobrien{
6379968Sobrien	int len = 0;
6479968Sobrien	char c;
6579968Sobrien	char *p = s, *q, *r;
6679968Sobrien
6779968Sobrien	if (s == NULL)
6879968Sobrien		return NULL;
6979968Sobrien
7079968Sobrien	/* Find length of quoted string */
7179968Sobrien	while ((c = *p++) != '\0')
7279968Sobrien	{
7379968Sobrien		len++;
7479968Sobrien		if (c == '\\' || c == '"')
7579968Sobrien			len++;
7679968Sobrien	}
7779968Sobrien
7879968Sobrien	q = r = sm_rpool_malloc_x(rpool, len + 3);
7979968Sobrien	p = s;
8079968Sobrien
8179968Sobrien	/* add leading quote */
8279968Sobrien	*q++ = '"';
8379968Sobrien	while ((c = *p++) != '\0')
8479968Sobrien	{
8579968Sobrien		/* quote \ or " */
8679968Sobrien		if (c == '\\' || c == '"')
8779968Sobrien			*q++ = '\\';
8879968Sobrien		*q++ = c;
8979968Sobrien	}
9079968Sobrien	*q++ = '"';
9179968Sobrien	*q = '\0';
9279968Sobrien	return r;
9379968Sobrien}
9479968Sobrien
9579968Sobrien/*
9679968Sobrien**  STRIPBACKSLASH -- Strip leading backslash from a string.
9779968Sobrien**
9879968Sobrien**	This is done in place.
9979968Sobrien**
10079968Sobrien**	Parameters:
10179968Sobrien**		s -- the string to strip.
10279968Sobrien**
10379968Sobrien**	Returns:
10479968Sobrien**		none.
10592282Sobrien*/
10692282Sobrien
10792282Sobrienvoid
10892282Sobrienstripbackslash(s)
10992282Sobrien	char *s;
11079968Sobrien{
11179968Sobrien	char *p, *q, c;
11279968Sobrien
11379968Sobrien	if (s == NULL || *s == '\0')
11479968Sobrien		return;
11592282Sobrien	p = q = s;
11679968Sobrien	while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
11779968Sobrien		p++;
11879968Sobrien	do
11979968Sobrien	{
12079968Sobrien		c = *q++ = *p++;
12179968Sobrien	} while (c != '\0');
12279968Sobrien}
12379968Sobrien
12479968Sobrien/*
12579968Sobrien**  RFC822_STRING -- Checks string for proper RFC822 string quoting.
12679968Sobrien**
12779968Sobrien**	Runs through a string and verifies RFC822 special characters
12879968Sobrien**	are only found inside comments, quoted strings, or backslash
12979968Sobrien**	escaped.  Also verified balanced quotes and parenthesis.
13079968Sobrien**
13179968Sobrien**	Parameters:
13279968Sobrien**		s -- the string to modify.
13379968Sobrien**
13479968Sobrien**	Returns:
13579968Sobrien**		true iff the string is RFC822 compliant, false otherwise.
13679968Sobrien*/
13779968Sobrien
13879968Sobrienbool
13979968Sobrienrfc822_string(s)
14079968Sobrien	char *s;
14179968Sobrien{
14279968Sobrien	bool quoted = false;
14379968Sobrien	int commentlev = 0;
14479968Sobrien	char *c = s;
14579968Sobrien
14679968Sobrien	if (s == NULL)
14779968Sobrien		return false;
14879968Sobrien
14979968Sobrien	while (*c != '\0')
15079968Sobrien	{
15179968Sobrien		/* escaped character */
15279968Sobrien		if (*c == '\\')
15379968Sobrien		{
15479968Sobrien			c++;
15579968Sobrien			if (*c == '\0')
15679968Sobrien				return false;
15779968Sobrien		}
15879968Sobrien		else if (commentlev == 0 && *c == '"')
15979968Sobrien			quoted = !quoted;
16079968Sobrien		else if (!quoted)
16179968Sobrien		{
16279968Sobrien			if (*c == ')')
16379968Sobrien			{
16479968Sobrien				/* unbalanced ')' */
16579968Sobrien				if (commentlev == 0)
16679968Sobrien					return false;
16779968Sobrien				else
16879968Sobrien					commentlev--;
16979968Sobrien			}
17079968Sobrien			else if (*c == '(')
17179968Sobrien				commentlev++;
17279968Sobrien			else if (commentlev == 0 &&
17379968Sobrien				 strchr(MustQuoteChars, *c) != NULL)
17479968Sobrien				return false;
17579968Sobrien		}
17679968Sobrien		c++;
17779968Sobrien	}
17879968Sobrien
17979968Sobrien	/* unbalanced '"' or '(' */
18079968Sobrien	return !quoted && commentlev == 0;
18179968Sobrien}
18279968Sobrien/*
18379968Sobrien**  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
18479968Sobrien**
18579968Sobrien**	Arbitrarily shorten (in place) an RFC822 string and rebalance
18679968Sobrien**	comments and quotes.
18779968Sobrien**
18879968Sobrien**	Parameters:
18979968Sobrien**		string -- the string to shorten
19079968Sobrien**		length -- the maximum size, 0 if no maximum
19179968Sobrien**
19279968Sobrien**	Returns:
19379968Sobrien**		true if string is changed, false otherwise
19479968Sobrien**
19579968Sobrien**	Side Effects:
19679968Sobrien**		Changes string in place, possibly resulting
19779968Sobrien**		in a shorter string.
19879968Sobrien*/
19979968Sobrien
20079968Sobrienbool
20179968Sobrienshorten_rfc822_string(string, length)
20279968Sobrien	char *string;
20379968Sobrien	size_t length;
20479968Sobrien{
20579968Sobrien	bool backslash = false;
20679968Sobrien	bool modified = false;
20779968Sobrien	bool quoted = false;
20879968Sobrien	size_t slen;
20979968Sobrien	int parencount = 0;
21079968Sobrien	char *ptr = string;
21179968Sobrien
21279968Sobrien	/*
21379968Sobrien	**  If have to rebalance an already short enough string,
21479968Sobrien	**  need to do it within allocated space.
21579968Sobrien	*/
21679968Sobrien
21779968Sobrien	slen = strlen(string);
21879968Sobrien	if (length == 0 || slen < length)
21979968Sobrien		length = slen;
22079968Sobrien
22179968Sobrien	while (*ptr != '\0')
22279968Sobrien	{
22379968Sobrien		if (backslash)
22479968Sobrien		{
22579968Sobrien			backslash = false;
22679968Sobrien			goto increment;
22779968Sobrien		}
22879968Sobrien
22979968Sobrien		if (*ptr == '\\')
23079968Sobrien			backslash = true;
23179968Sobrien		else if (*ptr == '(')
23292282Sobrien		{
23392282Sobrien			if (!quoted)
23492282Sobrien				parencount++;
23579968Sobrien		}
23679968Sobrien		else if (*ptr == ')')
23779968Sobrien		{
23879968Sobrien			if (--parencount < 0)
23992282Sobrien				parencount = 0;
24079968Sobrien		}
24192282Sobrien
24279968Sobrien		/* Inside a comment, quotes don't matter */
24379968Sobrien		if (parencount <= 0 && *ptr == '"')
24479968Sobrien			quoted = !quoted;
24579968Sobrien
24679968Sobrienincrement:
24779968Sobrien		/* Check for sufficient space for next character */
24879968Sobrien		if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
24992282Sobrien						parencount +
25079968Sobrien						(quoted ? 1 : 0)))
25179968Sobrien		{
25279968Sobrien			/* Not enough, backtrack */
25379968Sobrien			if (*ptr == '\\')
25479968Sobrien				backslash = false;
25579968Sobrien			else if (*ptr == '(' && !quoted)
25679968Sobrien				parencount--;
25779968Sobrien			else if (*ptr == '"' && parencount == 0)
25892282Sobrien				quoted = false;
25979968Sobrien			break;
26079968Sobrien		}
26179968Sobrien		ptr++;
26279968Sobrien	}
26379968Sobrien
26479968Sobrien	/* Rebalance */
26579968Sobrien	while (parencount-- > 0)
26679968Sobrien	{
26779968Sobrien		if (*ptr != ')')
26879968Sobrien		{
26979968Sobrien			modified = true;
27079968Sobrien			*ptr = ')';
27179968Sobrien		}
27279968Sobrien		ptr++;
27379968Sobrien	}
27479968Sobrien	if (quoted)
27579968Sobrien	{
27679968Sobrien		if (*ptr != '"')
27779968Sobrien		{
27879968Sobrien			modified = true;
27979968Sobrien			*ptr = '"';
28079968Sobrien		}
28179968Sobrien		ptr++;
28279968Sobrien	}
28379968Sobrien	if (*ptr != '\0')
28479968Sobrien	{
28579968Sobrien		modified = true;
28679968Sobrien		*ptr = '\0';
28779968Sobrien	}
28879968Sobrien	return modified;
28992282Sobrien}
29079968Sobrien/*
29179968Sobrien**  FIND_CHARACTER -- find an unquoted character in an RFC822 string
29279968Sobrien**
29379968Sobrien**	Find an unquoted, non-commented character in an RFC822
29479968Sobrien**	string and return a pointer to its location in the
29579968Sobrien**	string.
29679968Sobrien**
29779968Sobrien**	Parameters:
29879968Sobrien**		string -- the string to search
29979968Sobrien**		character -- the character to find
30079968Sobrien**
30179968Sobrien**	Returns:
30279968Sobrien**		pointer to the character, or
30379968Sobrien**		a pointer to the end of the line if character is not found
30479968Sobrien*/
30579968Sobrien
30679968Sobrienchar *
30779968Sobrienfind_character(string, character)
30879968Sobrien	char *string;
30979968Sobrien	int character;
31079968Sobrien{
31179968Sobrien	bool backslash = false;
31279968Sobrien	bool quoted = false;
31379968Sobrien	int parencount = 0;
31479968Sobrien
31579968Sobrien	while (string != NULL && *string != '\0')
31679968Sobrien	{
31779968Sobrien		if (backslash)
31879968Sobrien		{
31979968Sobrien			backslash = false;
32079968Sobrien			if (!quoted && character == '\\' && *string == '\\')
32179968Sobrien				break;
32279968Sobrien			string++;
32379968Sobrien			continue;
32479968Sobrien		}
32579968Sobrien		switch (*string)
32679968Sobrien		{
32779968Sobrien		  case '\\':
32879968Sobrien			backslash = true;
32979968Sobrien			break;
33079968Sobrien
33179968Sobrien		  case '(':
33279968Sobrien			if (!quoted)
33379968Sobrien				parencount++;
33479968Sobrien			break;
33579968Sobrien
33679968Sobrien		  case ')':
33779968Sobrien			if (--parencount < 0)
33879968Sobrien				parencount = 0;
33979968Sobrien			break;
34079968Sobrien		}
34179968Sobrien
34279968Sobrien		/* Inside a comment, nothing matters */
34379968Sobrien		if (parencount > 0)
34479968Sobrien		{
34579968Sobrien			string++;
34679968Sobrien			continue;
34779968Sobrien		}
34879968Sobrien
34979968Sobrien		if (*string == '"')
35079968Sobrien			quoted = !quoted;
35179968Sobrien		else if (*string == character && !quoted)
35279968Sobrien			break;
35379968Sobrien		string++;
35479968Sobrien	}
35579968Sobrien
35679968Sobrien	/* Return pointer to the character */
35779968Sobrien	return string;
35879968Sobrien}
35979968Sobrien
36079968Sobrien/*
36179968Sobrien**  CHECK_BODYTYPE -- check bodytype parameter
36279968Sobrien**
36379968Sobrien**	Parameters:
36479968Sobrien**		bodytype -- bodytype parameter
36579968Sobrien**
36679968Sobrien**	Returns:
36779968Sobrien**		BODYTYPE_* according to parameter
36879968Sobrien**
36979968Sobrien*/
37079968Sobrien
37179968Sobrienint
37279968Sobriencheck_bodytype(bodytype)
37379968Sobrien	char *bodytype;
37479968Sobrien{
37579968Sobrien	/* check body type for legality */
37679968Sobrien	if (bodytype == NULL)
37779968Sobrien		return BODYTYPE_NONE;
37879968Sobrien	if (sm_strcasecmp(bodytype, "7BIT") == 0)
37979968Sobrien		return BODYTYPE_7BIT;
38079968Sobrien	if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
38179968Sobrien		return BODYTYPE_8BITMIME;
38279968Sobrien	return BODYTYPE_ILLEGAL;
38379968Sobrien}
38479968Sobrien
38579968Sobrien#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
38679968Sobrien/*
38779968Sobrien**  TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
38879968Sobrien**
38979968Sobrien**	Parameters:
39079968Sobrien**		str -- string to truncate
39179968Sobrien**		len -- maximum length (including '\0') (0 for unlimited)
39279968Sobrien**		delim -- delimiter character
39379968Sobrien**
39479968Sobrien**	Returns:
39579968Sobrien**		None.
39679968Sobrien*/
39779968Sobrien
39879968Sobrienvoid
39979968Sobrientruncate_at_delim(str, len, delim)
40079968Sobrien	char *str;
40179968Sobrien	size_t len;
40279968Sobrien	int delim;
40379968Sobrien{
40479968Sobrien	char *p;
40579968Sobrien
40679968Sobrien	if (str == NULL || len == 0 || strlen(str) < len)
40779968Sobrien		return;
40879968Sobrien
40979968Sobrien	*(str + len - 1) = '\0';
41079968Sobrien	while ((p = strrchr(str, delim)) != NULL)
41179968Sobrien	{
41279968Sobrien		*p = '\0';
41379968Sobrien		if (p - str + 4 < len)
41479968Sobrien		{
41579968Sobrien			*p++ = (char) delim;
41679968Sobrien			*p = '\0';
41779968Sobrien			(void) sm_strlcat(str, "...", len);
41879968Sobrien			return;
41979968Sobrien		}
42079968Sobrien	}
42179968Sobrien
42279968Sobrien	/* Couldn't find a place to append "..." */
42379968Sobrien	if (len > 3)
42479968Sobrien		(void) sm_strlcpy(str, "...", len);
42579968Sobrien	else
42679968Sobrien		str[0] = '\0';
42779968Sobrien}
42879968Sobrien#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */
42979968Sobrien/*
43079968Sobrien**  XALLOC -- Allocate memory, raise an exception on error
43179968Sobrien**
43279968Sobrien**	Parameters:
43379968Sobrien**		sz -- size of area to allocate.
43479968Sobrien**
43579968Sobrien**	Returns:
43679968Sobrien**		pointer to data region.
43779968Sobrien**
43879968Sobrien**	Exceptions:
43979968Sobrien**		SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
44079968Sobrien**
44179968Sobrien**	Side Effects:
44279968Sobrien**		Memory is allocated.
44379968Sobrien*/
44479968Sobrien
44579968Sobrienchar *
44679968Sobrien#if SM_HEAP_CHECK
44779968Sobrienxalloc_tagged(sz, file, line)
44879968Sobrien	register int sz;
44979968Sobrien	char *file;
45079968Sobrien	int line;
45179968Sobrien#else /* SM_HEAP_CHECK */
45279968Sobrienxalloc(sz)
45379968Sobrien	register int sz;
45479968Sobrien#endif /* SM_HEAP_CHECK */
45579968Sobrien{
45679968Sobrien	register char *p;
45779968Sobrien
45879968Sobrien	/* some systems can't handle size zero mallocs */
45979968Sobrien	if (sz <= 0)
46092282Sobrien		sz = 1;
46192282Sobrien
46292282Sobrien	/* scaffolding for testing error handling code */
46392282Sobrien	sm_xtrap_raise_x(&SmHeapOutOfMemory);
46492282Sobrien
46579968Sobrien	p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
46679968Sobrien	if (p == NULL)
46792282Sobrien	{
46879968Sobrien		sm_exc_raise_x(&SmHeapOutOfMemory);
46979968Sobrien	}
47079968Sobrien	return p;
47179968Sobrien}
47279968Sobrien/*
47379968Sobrien**  COPYPLIST -- copy list of pointers.
47479968Sobrien**
47579968Sobrien**	This routine is the equivalent of strdup for lists of
47679968Sobrien**	pointers.
47779968Sobrien**
47879968Sobrien**	Parameters:
47979968Sobrien**		list -- list of pointers to copy.
48079968Sobrien**			Must be NULL terminated.
48179968Sobrien**		copycont -- if true, copy the contents of the vector
48279968Sobrien**			(which must be a string) also.
48379968Sobrien**		rpool -- resource pool from which to allocate storage,
48479968Sobrien**			or NULL
48579968Sobrien**
48679968Sobrien**	Returns:
48779968Sobrien**		a copy of 'list'.
48879968Sobrien*/
48979968Sobrien
49079968Sobrienchar **
49179968Sobriencopyplist(list, copycont, rpool)
49279968Sobrien	char **list;
49379968Sobrien	bool copycont;
49479968Sobrien	SM_RPOOL_T *rpool;
49579968Sobrien{
49679968Sobrien	register char **vp;
49779968Sobrien	register char **newvp;
49879968Sobrien
49979968Sobrien	for (vp = list; *vp != NULL; vp++)
50079968Sobrien		continue;
50179968Sobrien
50279968Sobrien	vp++;
50379968Sobrien
50479968Sobrien	newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp);
50579968Sobrien	memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
50679968Sobrien
50779968Sobrien	if (copycont)
50879968Sobrien	{
50979968Sobrien		for (vp = newvp; *vp != NULL; vp++)
51079968Sobrien			*vp = sm_rpool_strdup_x(rpool, *vp);
51179968Sobrien	}
51279968Sobrien
51379968Sobrien	return newvp;
51479968Sobrien}
51579968Sobrien/*
51679968Sobrien**  COPYQUEUE -- copy address queue.
51779968Sobrien**
51879968Sobrien**	This routine is the equivalent of strdup for address queues;
51979968Sobrien**	addresses marked as QS_IS_DEAD() aren't copied
52079968Sobrien**
52179968Sobrien**	Parameters:
52279968Sobrien**		addr -- list of address structures to copy.
52379968Sobrien**		rpool -- resource pool from which to allocate storage
52479968Sobrien**
52579968Sobrien**	Returns:
52679968Sobrien**		a copy of 'addr'.
52779968Sobrien*/
52879968Sobrien
52979968SobrienADDRESS *
53079968Sobriencopyqueue(addr, rpool)
53179968Sobrien	ADDRESS *addr;
53279968Sobrien	SM_RPOOL_T *rpool;
53379968Sobrien{
53479968Sobrien	register ADDRESS *newaddr;
53579968Sobrien	ADDRESS *ret;
53679968Sobrien	register ADDRESS **tail = &ret;
53779968Sobrien
53879968Sobrien	while (addr != NULL)
53979968Sobrien	{
54079968Sobrien		if (!QS_IS_DEAD(addr->q_state))
54179968Sobrien		{
54279968Sobrien			newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
54379968Sobrien							sizeof *newaddr);
54479968Sobrien			STRUCTCOPY(*addr, *newaddr);
54579968Sobrien			*tail = newaddr;
54679968Sobrien			tail = &newaddr->q_next;
54779968Sobrien		}
54879968Sobrien		addr = addr->q_next;
54979968Sobrien	}
55079968Sobrien	*tail = NULL;
55179968Sobrien
55279968Sobrien	return ret;
55379968Sobrien}
55479968Sobrien/*
55579968Sobrien**  LOG_SENDMAIL_PID -- record sendmail pid and command line.
55679968Sobrien**
55779968Sobrien**	Parameters:
55879968Sobrien**		e -- the current envelope.
55979968Sobrien**
56079968Sobrien**	Returns:
56179968Sobrien**		none.
56279968Sobrien**
56379968Sobrien**	Side Effects:
56479968Sobrien**		writes pidfile, logs command line.
56579968Sobrien**		keeps file open and locked to prevent overwrite of active file
56679968Sobrien*/
56779968Sobrien
56879968Sobrienstatic SM_FILE_T	*Pidf = NULL;
56979968Sobrien
57079968Sobrienvoid
57179968Sobrienlog_sendmail_pid(e)
57279968Sobrien	ENVELOPE *e;
57379968Sobrien{
57479968Sobrien	long sff;
57579968Sobrien	char pidpath[MAXPATHLEN];
57679968Sobrien	extern char *CommandLineArgs;
57779968Sobrien
57879968Sobrien	/* write the pid to the log file for posterity */
57979968Sobrien	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK;
58079968Sobrien	if (TrustedUid != 0 && RealUid == TrustedUid)
58179968Sobrien		sff |= SFF_OPENASROOT;
58279968Sobrien	expand(PidFile, pidpath, sizeof pidpath, e);
58379968Sobrien	Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
58479968Sobrien	if (Pidf == NULL)
58579968Sobrien	{
58679968Sobrien		if (errno == EWOULDBLOCK)
58779968Sobrien			sm_syslog(LOG_ERR, NOQID,
58879968Sobrien				  "unable to write pid to %s: file in use by another process",
58979968Sobrien				  pidpath);
59079968Sobrien		else
59179968Sobrien			sm_syslog(LOG_ERR, NOQID,
59279968Sobrien				  "unable to write pid to %s: %s",
59379968Sobrien				  pidpath, sm_errstring(errno));
59479968Sobrien	}
59579968Sobrien	else
59679968Sobrien	{
59779968Sobrien		PidFilePid = getpid();
59879968Sobrien
59979968Sobrien		/* write the process id on line 1 */
60079968Sobrien		(void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n",
60179968Sobrien				     (long) PidFilePid);
60279968Sobrien
60379968Sobrien		/* line 2 contains all command line flags */
60479968Sobrien		(void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n",
60579968Sobrien				     CommandLineArgs);
60679968Sobrien
60779968Sobrien		/* flush */
60879968Sobrien		(void) sm_io_flush(Pidf, SM_TIME_DEFAULT);
60979968Sobrien
61079968Sobrien		/*
61179968Sobrien		**  Leave pid file open until process ends
61279968Sobrien		**  so it's not overwritten by another
61379968Sobrien		**  process.
61479968Sobrien		*/
61579968Sobrien	}
61679968Sobrien	if (LogLevel > 9)
61779968Sobrien		sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
61879968Sobrien}
61979968Sobrien
62079968Sobrien/*
62179968Sobrien**  CLOSE_SENDMAIL_PID -- close sendmail pid file
62279968Sobrien**
62379968Sobrien**	Parameters:
62479968Sobrien**		none.
62579968Sobrien**
62679968Sobrien**	Returns:
62779968Sobrien**		none.
62879968Sobrien*/
62979968Sobrien
63079968Sobrienvoid
63179968Sobrienclose_sendmail_pid()
63279968Sobrien{
63379968Sobrien	if (Pidf == NULL)
63479968Sobrien		return;
63579968Sobrien
63679968Sobrien	(void) sm_io_close(Pidf, SM_TIME_DEFAULT);
63779968Sobrien	Pidf = NULL;
63879968Sobrien}
63979968Sobrien
64079968Sobrien/*
64179968Sobrien**  SET_DELIVERY_MODE -- set and record the delivery mode
64279968Sobrien**
64379968Sobrien**	Parameters:
64479968Sobrien**		mode -- delivery mode
64579968Sobrien**		e -- the current envelope.
64679968Sobrien**
64779968Sobrien**	Returns:
64879968Sobrien**		none.
64979968Sobrien**
65079968Sobrien**	Side Effects:
65179968Sobrien**		sets {deliveryMode} macro
65279968Sobrien*/
65379968Sobrien
65479968Sobrienvoid
65579968Sobrienset_delivery_mode(mode, e)
65679968Sobrien	int mode;
65779968Sobrien	ENVELOPE *e;
65879968Sobrien{
65979968Sobrien	char buf[2];
66079968Sobrien
66179968Sobrien	e->e_sendmode = (char) mode;
66279968Sobrien	buf[0] = (char) mode;
66379968Sobrien	buf[1] = '\0';
66479968Sobrien	macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
66579968Sobrien}
66679968Sobrien/*
66779968Sobrien**  SET_OP_MODE -- set and record the op mode
66879968Sobrien**
66979968Sobrien**	Parameters:
67079968Sobrien**		mode -- op mode
67179968Sobrien**		e -- the current envelope.
67279968Sobrien**
67379968Sobrien**	Returns:
67479968Sobrien**		none.
67579968Sobrien**
67679968Sobrien**	Side Effects:
67779968Sobrien**		sets {opMode} macro
67879968Sobrien*/
67979968Sobrien
68079968Sobrienvoid
68179968Sobrienset_op_mode(mode)
68279968Sobrien	int mode;
68379968Sobrien{
68479968Sobrien	char buf[2];
68579968Sobrien	extern ENVELOPE BlankEnvelope;
68679968Sobrien
68779968Sobrien	OpMode = (char) mode;
68879968Sobrien	buf[0] = (char) mode;
68979968Sobrien	buf[1] = '\0';
69079968Sobrien	macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
69179968Sobrien}
69279968Sobrien/*
69379968Sobrien**  PRINTAV -- print argument vector.
69479968Sobrien**
69579968Sobrien**	Parameters:
69679968Sobrien**		fp -- output file pointer.
69779968Sobrien**		av -- argument vector.
69879968Sobrien**
69979968Sobrien**	Returns:
70079968Sobrien**		none.
70179968Sobrien**
70279968Sobrien**	Side Effects:
70379968Sobrien**		prints av.
70479968Sobrien*/
70579968Sobrien
70679968Sobrienvoid
70779968Sobrienprintav(fp, av)
70879968Sobrien	SM_FILE_T *fp;
70979968Sobrien	register char **av;
71079968Sobrien{
71179968Sobrien	while (*av != NULL)
71279968Sobrien	{
71392282Sobrien		if (tTd(0, 44))
71492282Sobrien			sm_dprintf("\n\t%08lx=", (unsigned long) *av);
71592282Sobrien		else
71692282Sobrien			(void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
71792282Sobrien		xputs(fp, *av++);
71892282Sobrien	}
71992282Sobrien	(void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
72092282Sobrien}
72179968Sobrien/*
72292282Sobrien**  XPUTS -- put string doing control escapes.
72379968Sobrien**
72479968Sobrien**	Parameters:
72579968Sobrien**		fp -- output file pointer.
72679968Sobrien**		s -- string to put.
72779968Sobrien**
72879968Sobrien**	Returns:
72979968Sobrien**		none.
73079968Sobrien**
73179968Sobrien**	Side Effects:
73279968Sobrien**		output to stdout
73379968Sobrien*/
73479968Sobrien
73579968Sobrienvoid
73679968Sobrienxputs(fp, s)
73779968Sobrien	SM_FILE_T *fp;
73879968Sobrien	register const char *s;
73979968Sobrien{
74079968Sobrien	register int c;
74179968Sobrien	register struct metamac *mp;
74279968Sobrien	bool shiftout = false;
74379968Sobrien	extern struct metamac MetaMacros[];
74479968Sobrien	static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
74579968Sobrien		"@(#)$Debug: ANSI - enable reverse video in debug output $");
74679968Sobrien
74779968Sobrien	/*
74879968Sobrien	**  TermEscape is set here, rather than in main(),
74979968Sobrien	**  because ANSI mode can be turned on or off at any time
75079968Sobrien	**  if we are in -bt rule testing mode.
75179968Sobrien	*/
75279968Sobrien
75379968Sobrien	if (sm_debug_unknown(&DebugANSI))
75479968Sobrien	{
75579968Sobrien		if (sm_debug_active(&DebugANSI, 1))
75679968Sobrien		{
75779968Sobrien			TermEscape.te_rv_on = "\033[7m";
75879968Sobrien			TermEscape.te_rv_off = "\033[0m";
75979968Sobrien		}
76079968Sobrien		else
76179968Sobrien		{
76279968Sobrien			TermEscape.te_rv_on = "";
76379968Sobrien			TermEscape.te_rv_off = "";
76479968Sobrien		}
76579968Sobrien	}
76679968Sobrien
76779968Sobrien	if (s == NULL)
76879968Sobrien	{
76979968Sobrien		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s",
77079968Sobrien				     TermEscape.te_rv_on, TermEscape.te_rv_off);
77179968Sobrien		return;
77279968Sobrien	}
77379968Sobrien	while ((c = (*s++ & 0377)) != '\0')
77479968Sobrien	{
77579968Sobrien		if (shiftout)
77679968Sobrien		{
77792282Sobrien			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
77892282Sobrien					     TermEscape.te_rv_off);
77979968Sobrien			shiftout = false;
78079968Sobrien		}
78179968Sobrien		if (!isascii(c))
78279968Sobrien		{
78379968Sobrien			if (c == MATCHREPL)
78492282Sobrien			{
78592282Sobrien				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
78692282Sobrien						     "%s$",
78792282Sobrien						     TermEscape.te_rv_on);
78892282Sobrien				shiftout = true;
78992282Sobrien				if (*s == '\0')
79092282Sobrien					continue;
79192282Sobrien				c = *s++ & 0377;
79292282Sobrien				goto printchar;
79392282Sobrien			}
79479968Sobrien			if (c == MACROEXPAND || c == MACRODEXPAND)
79579968Sobrien			{
79679968Sobrien				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
79779968Sobrien						     "%s$",
79879968Sobrien						     TermEscape.te_rv_on);
79979968Sobrien				if (c == MACRODEXPAND)
80079968Sobrien					(void) sm_io_putc(fp,
80179968Sobrien							  SM_TIME_DEFAULT, '&');
80279968Sobrien				shiftout = true;
80379968Sobrien				if (*s == '\0')
80479968Sobrien					continue;
80579968Sobrien				if (strchr("=~&?", *s) != NULL)
80679968Sobrien					(void) sm_io_putc(fp,
80779968Sobrien							  SM_TIME_DEFAULT,
80879968Sobrien							  *s++);
80979968Sobrien				if (bitset(0200, *s))
81079968Sobrien					(void) sm_io_fprintf(fp,
81179968Sobrien							     SM_TIME_DEFAULT,
81279968Sobrien							     "{%s}",
81379968Sobrien							     macname(bitidx(*s++)));
81479968Sobrien				else
81579968Sobrien					(void) sm_io_fprintf(fp,
81679968Sobrien							     SM_TIME_DEFAULT,
81779968Sobrien							     "%c",
81879968Sobrien							     *s++);
819				continue;
820			}
821			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
822			{
823				if (bitidx(mp->metaval) == c)
824				{
825					(void) sm_io_fprintf(fp,
826							     SM_TIME_DEFAULT,
827							     "%s$%c",
828							     TermEscape.te_rv_on,
829							     mp->metaname);
830					shiftout = true;
831					break;
832				}
833			}
834			if (c == MATCHCLASS || c == MATCHNCLASS)
835			{
836				if (bitset(0200, *s))
837					(void) sm_io_fprintf(fp,
838							     SM_TIME_DEFAULT,
839							     "{%s}",
840							     macname(bitidx(*s++)));
841				else if (*s != '\0')
842					(void) sm_io_fprintf(fp,
843							     SM_TIME_DEFAULT,
844							     "%c",
845							     *s++);
846			}
847			if (mp->metaname != '\0')
848				continue;
849
850			/* unrecognized meta character */
851			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-",
852					     TermEscape.te_rv_on);
853			shiftout = true;
854			c &= 0177;
855		}
856  printchar:
857		if (isprint(c))
858		{
859			(void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
860			continue;
861		}
862
863		/* wasn't a meta-macro -- find another way to print it */
864		switch (c)
865		{
866		  case '\n':
867			c = 'n';
868			break;
869
870		  case '\r':
871			c = 'r';
872			break;
873
874		  case '\t':
875			c = 't';
876			break;
877		}
878		if (!shiftout)
879		{
880			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
881					     TermEscape.te_rv_on);
882			shiftout = true;
883		}
884		if (isprint(c))
885		{
886			(void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\');
887			(void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
888		}
889		else
890		{
891			(void) sm_io_putc(fp, SM_TIME_DEFAULT, '^');
892			(void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100);
893		}
894	}
895	if (shiftout)
896		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
897				     TermEscape.te_rv_off);
898	(void) sm_io_flush(fp, SM_TIME_DEFAULT);
899}
900/*
901**  MAKELOWER -- Translate a line into lower case
902**
903**	Parameters:
904**		p -- the string to translate.  If NULL, return is
905**			immediate.
906**
907**	Returns:
908**		none.
909**
910**	Side Effects:
911**		String pointed to by p is translated to lower case.
912*/
913
914void
915makelower(p)
916	register char *p;
917{
918	register char c;
919
920	if (p == NULL)
921		return;
922	for (; (c = *p) != '\0'; p++)
923		if (isascii(c) && isupper(c))
924			*p = tolower(c);
925}
926/*
927**  FIXCRLF -- fix <CR><LF> in line.
928**
929**	Looks for the <CR><LF> combination and turns it into the
930**	UNIX canonical <NL> character.  It only takes one line,
931**	i.e., it is assumed that the first <NL> found is the end
932**	of the line.
933**
934**	Parameters:
935**		line -- the line to fix.
936**		stripnl -- if true, strip the newline also.
937**
938**	Returns:
939**		none.
940**
941**	Side Effects:
942**		line is changed in place.
943*/
944
945void
946fixcrlf(line, stripnl)
947	char *line;
948	bool stripnl;
949{
950	register char *p;
951
952	p = strchr(line, '\n');
953	if (p == NULL)
954		return;
955	if (p > line && p[-1] == '\r')
956		p--;
957	if (!stripnl)
958		*p++ = '\n';
959	*p = '\0';
960}
961/*
962**  PUTLINE -- put a line like fputs obeying SMTP conventions
963**
964**	This routine always guarantees outputing a newline (or CRLF,
965**	as appropriate) at the end of the string.
966**
967**	Parameters:
968**		l -- line to put.
969**		mci -- the mailer connection information.
970**
971**	Returns:
972**		none
973**
974**	Side Effects:
975**		output of l to mci->mci_out.
976*/
977
978void
979putline(l, mci)
980	register char *l;
981	register MCI *mci;
982{
983	putxline(l, strlen(l), mci, PXLF_MAPFROM);
984}
985/*
986**  PUTXLINE -- putline with flags bits.
987**
988**	This routine always guarantees outputing a newline (or CRLF,
989**	as appropriate) at the end of the string.
990**
991**	Parameters:
992**		l -- line to put.
993**		len -- the length of the line.
994**		mci -- the mailer connection information.
995**		pxflags -- flag bits:
996**		    PXLF_MAPFROM -- map From_ to >From_.
997**		    PXLF_STRIP8BIT -- strip 8th bit.
998**		    PXLF_HEADER -- map bare newline in header to newline space.
999**		    PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
1000**
1001**	Returns:
1002**		none
1003**
1004**	Side Effects:
1005**		output of l to mci->mci_out.
1006*/
1007
1008void
1009putxline(l, len, mci, pxflags)
1010	register char *l;
1011	size_t len;
1012	register MCI *mci;
1013	int pxflags;
1014{
1015	bool dead = false;
1016	register char *p, *end;
1017	int slop = 0;
1018
1019	/* strip out 0200 bits -- these can look like TELNET protocol */
1020	if (bitset(MCIF_7BIT, mci->mci_flags) ||
1021	    bitset(PXLF_STRIP8BIT, pxflags))
1022	{
1023		register char svchar;
1024
1025		for (p = l; (svchar = *p) != '\0'; ++p)
1026			if (bitset(0200, svchar))
1027				*p = svchar &~ 0200;
1028	}
1029
1030	end = l + len;
1031	do
1032	{
1033		bool noeol = false;
1034
1035		/* find the end of the line */
1036		p = memchr(l, '\n', end - l);
1037		if (p == NULL)
1038		{
1039			p = end;
1040			noeol = true;
1041		}
1042
1043		if (TrafficLogFile != NULL)
1044			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1045					     "%05d >>> ", (int) CurrentPid);
1046
1047		/* check for line overflow */
1048		while (mci->mci_mailer->m_linelimit > 0 &&
1049		       (p - l + slop) > mci->mci_mailer->m_linelimit)
1050		{
1051			char *l_base = l;
1052			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
1053
1054			if (l[0] == '.' && slop == 0 &&
1055			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
1056			{
1057				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1058					       '.') == SM_IO_EOF)
1059					dead = true;
1060				else
1061				{
1062					/* record progress for DATA timeout */
1063					DataProgress = true;
1064				}
1065				if (TrafficLogFile != NULL)
1066					(void) sm_io_putc(TrafficLogFile,
1067							  SM_TIME_DEFAULT, '.');
1068			}
1069			else if (l[0] == 'F' && slop == 0 &&
1070				 bitset(PXLF_MAPFROM, pxflags) &&
1071				 strncmp(l, "From ", 5) == 0 &&
1072				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1073			{
1074				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1075					       '>') == SM_IO_EOF)
1076					dead = true;
1077				else
1078				{
1079					/* record progress for DATA timeout */
1080					DataProgress = true;
1081				}
1082				if (TrafficLogFile != NULL)
1083					(void) sm_io_putc(TrafficLogFile,
1084							  SM_TIME_DEFAULT,
1085							  '>');
1086			}
1087			if (dead)
1088				break;
1089
1090			while (l < q)
1091			{
1092				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1093					       (unsigned char) *l++) == SM_IO_EOF)
1094				{
1095					dead = true;
1096					break;
1097				}
1098				else
1099				{
1100					/* record progress for DATA timeout */
1101					DataProgress = true;
1102				}
1103			}
1104			if (dead)
1105				break;
1106
1107			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') ==
1108			    SM_IO_EOF ||
1109			    sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1110					mci->mci_mailer->m_eol) ==
1111			    SM_IO_EOF ||
1112			    sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') ==
1113			    SM_IO_EOF)
1114			{
1115				dead = true;
1116				break;
1117			}
1118			else
1119			{
1120				/* record progress for DATA timeout */
1121				DataProgress = true;
1122			}
1123			if (TrafficLogFile != NULL)
1124			{
1125				for (l = l_base; l < q; l++)
1126					(void) sm_io_putc(TrafficLogFile,
1127							  SM_TIME_DEFAULT,
1128							  (unsigned char)*l);
1129				(void) sm_io_fprintf(TrafficLogFile,
1130						     SM_TIME_DEFAULT,
1131						     "!\n%05d >>>  ",
1132						     (int) CurrentPid);
1133			}
1134			slop = 1;
1135		}
1136
1137		if (dead)
1138			break;
1139
1140		/* output last part */
1141		if (l[0] == '.' && slop == 0 &&
1142		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
1143		{
1144			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
1145			    SM_IO_EOF)
1146				break;
1147			else
1148			{
1149				/* record progress for DATA timeout */
1150				DataProgress = true;
1151			}
1152			if (TrafficLogFile != NULL)
1153				(void) sm_io_putc(TrafficLogFile,
1154						  SM_TIME_DEFAULT, '.');
1155		}
1156		else if (l[0] == 'F' && slop == 0 &&
1157			 bitset(PXLF_MAPFROM, pxflags) &&
1158			 strncmp(l, "From ", 5) == 0 &&
1159			 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1160		{
1161			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
1162			    SM_IO_EOF)
1163				break;
1164			else
1165			{
1166				/* record progress for DATA timeout */
1167				DataProgress = true;
1168			}
1169			if (TrafficLogFile != NULL)
1170				(void) sm_io_putc(TrafficLogFile,
1171						  SM_TIME_DEFAULT, '>');
1172		}
1173		for ( ; l < p; ++l)
1174		{
1175			if (TrafficLogFile != NULL)
1176				(void) sm_io_putc(TrafficLogFile,
1177						  SM_TIME_DEFAULT,
1178						  (unsigned char)*l);
1179			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1180				       (unsigned char) *l) == SM_IO_EOF)
1181			{
1182				dead = true;
1183				break;
1184			}
1185			else
1186			{
1187				/* record progress for DATA timeout */
1188				DataProgress = true;
1189			}
1190		}
1191		if (dead)
1192			break;
1193
1194		if (TrafficLogFile != NULL)
1195			(void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
1196					  '\n');
1197		if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) &&
1198		    sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1199				mci->mci_mailer->m_eol) == SM_IO_EOF)
1200			break;
1201		else
1202		{
1203			/* record progress for DATA timeout */
1204			DataProgress = true;
1205		}
1206		if (l < end && *l == '\n')
1207		{
1208			if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1209			    bitset(PXLF_HEADER, pxflags))
1210			{
1211				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1212					       ' ') == SM_IO_EOF)
1213					break;
1214				else
1215				{
1216					/* record progress for DATA timeout */
1217					DataProgress = true;
1218				}
1219
1220				if (TrafficLogFile != NULL)
1221					(void) sm_io_putc(TrafficLogFile,
1222							  SM_TIME_DEFAULT, ' ');
1223			}
1224		}
1225
1226		/* record progress for DATA timeout */
1227		DataProgress = true;
1228	} while (l < end);
1229}
1230/*
1231**  XUNLINK -- unlink a file, doing logging as appropriate.
1232**
1233**	Parameters:
1234**		f -- name of file to unlink.
1235**
1236**	Returns:
1237**		return value of unlink()
1238**
1239**	Side Effects:
1240**		f is unlinked.
1241*/
1242
1243int
1244xunlink(f)
1245	char *f;
1246{
1247	register int i;
1248	int save_errno;
1249
1250	if (LogLevel > 98)
1251		sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
1252
1253	i = unlink(f);
1254	save_errno = errno;
1255	if (i < 0 && LogLevel > 97)
1256		sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
1257			  f, errno);
1258	if (i >= 0)
1259		SYNC_DIR(f, false);
1260	errno = save_errno;
1261	return i;
1262}
1263/*
1264**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1265**
1266**	Parameters:
1267**		buf -- place to put the input line.
1268**		siz -- size of buf.
1269**		fp -- file to read from.
1270**		timeout -- the timeout before error occurs.
1271**		during -- what we are trying to read (for error messages).
1272**
1273**	Returns:
1274**		NULL on error (including timeout).  This may also leave
1275**			buf containing a null string.
1276**		buf otherwise.
1277*/
1278
1279
1280char *
1281sfgets(buf, siz, fp, timeout, during)
1282	char *buf;
1283	int siz;
1284	SM_FILE_T *fp;
1285	time_t timeout;
1286	char *during;
1287{
1288	register char *p;
1289	int save_errno;
1290	int io_timeout;
1291
1292	SM_REQUIRE(siz > 0);
1293	SM_REQUIRE(buf != NULL);
1294
1295	if (fp == NULL)
1296	{
1297		buf[0] = '\0';
1298		errno = EBADF;
1299		return NULL;
1300	}
1301
1302	/* try to read */
1303	p = NULL;
1304	errno = 0;
1305
1306	/* convert the timeout to sm_io notation */
1307	io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
1308	while (!sm_io_eof(fp) && !sm_io_error(fp))
1309	{
1310		errno = 0;
1311		p = sm_io_fgets(fp, io_timeout, buf, siz);
1312		if (p == NULL && errno == EAGAIN)
1313		{
1314			/* The sm_io_fgets() call timedout */
1315			if (LogLevel > 1)
1316				sm_syslog(LOG_NOTICE, CurEnv->e_id,
1317					  "timeout waiting for input from %.100s during %s",
1318					  CURHOSTNAME,
1319					  during);
1320			buf[0] = '\0';
1321#if XDEBUG
1322			checkfd012(during);
1323#endif /* XDEBUG */
1324			if (TrafficLogFile != NULL)
1325				(void) sm_io_fprintf(TrafficLogFile,
1326						     SM_TIME_DEFAULT,
1327						     "%05d <<< [TIMEOUT]\n",
1328						     (int) CurrentPid);
1329			errno = ETIMEDOUT;
1330			return NULL;
1331		}
1332		if (p != NULL || errno != EINTR)
1333			break;
1334		(void) sm_io_clearerr(fp);
1335	}
1336	save_errno = errno;
1337
1338	/* clean up the books and exit */
1339	LineNumber++;
1340	if (p == NULL)
1341	{
1342		buf[0] = '\0';
1343		if (TrafficLogFile != NULL)
1344			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1345					     "%05d <<< [EOF]\n",
1346					     (int) CurrentPid);
1347		errno = save_errno;
1348		return NULL;
1349	}
1350	if (TrafficLogFile != NULL)
1351		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1352				     "%05d <<< %s", (int) CurrentPid, buf);
1353	if (SevenBitInput)
1354	{
1355		for (p = buf; *p != '\0'; p++)
1356			*p &= ~0200;
1357	}
1358	else if (!HasEightBits)
1359	{
1360		for (p = buf; *p != '\0'; p++)
1361		{
1362			if (bitset(0200, *p))
1363			{
1364				HasEightBits = true;
1365				break;
1366			}
1367		}
1368	}
1369	return buf;
1370}
1371/*
1372**  FGETFOLDED -- like fgets, but knows about folded lines.
1373**
1374**	Parameters:
1375**		buf -- place to put result.
1376**		n -- bytes available.
1377**		f -- file to read from.
1378**
1379**	Returns:
1380**		input line(s) on success, NULL on error or SM_IO_EOF.
1381**		This will normally be buf -- unless the line is too
1382**			long, when it will be sm_malloc_x()ed.
1383**
1384**	Side Effects:
1385**		buf gets lines from f, with continuation lines (lines
1386**		with leading white space) appended.  CRLF's are mapped
1387**		into single newlines.  Any trailing NL is stripped.
1388*/
1389
1390char *
1391fgetfolded(buf, n, f)
1392	char *buf;
1393	register int n;
1394	SM_FILE_T *f;
1395{
1396	register char *p = buf;
1397	char *bp = buf;
1398	register int i;
1399
1400	SM_REQUIRE(n > 0);
1401	SM_REQUIRE(buf != NULL);
1402	if (f == NULL)
1403	{
1404		buf[0] = '\0';
1405		errno = EBADF;
1406		return NULL;
1407	}
1408
1409	n--;
1410	while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
1411	{
1412		if (i == '\r')
1413		{
1414			i = sm_io_getc(f, SM_TIME_DEFAULT);
1415			if (i != '\n')
1416			{
1417				if (i != SM_IO_EOF)
1418					(void) sm_io_ungetc(f, SM_TIME_DEFAULT,
1419							    i);
1420				i = '\r';
1421			}
1422		}
1423		if (--n <= 0)
1424		{
1425			/* allocate new space */
1426			char *nbp;
1427			int nn;
1428
1429			nn = (p - bp);
1430			if (nn < MEMCHUNKSIZE)
1431				nn *= 2;
1432			else
1433				nn += MEMCHUNKSIZE;
1434			nbp = sm_malloc_x(nn);
1435			memmove(nbp, bp, p - bp);
1436			p = &nbp[p - bp];
1437			if (bp != buf)
1438				sm_free(bp);
1439			bp = nbp;
1440			n = nn - (p - bp);
1441		}
1442		*p++ = i;
1443		if (i == '\n')
1444		{
1445			LineNumber++;
1446			i = sm_io_getc(f, SM_TIME_DEFAULT);
1447			if (i != SM_IO_EOF)
1448				(void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
1449			if (i != ' ' && i != '\t')
1450				break;
1451		}
1452	}
1453	if (p == bp)
1454		return NULL;
1455	if (p[-1] == '\n')
1456		p--;
1457	*p = '\0';
1458	return bp;
1459}
1460/*
1461**  CURTIME -- return current time.
1462**
1463**	Parameters:
1464**		none.
1465**
1466**	Returns:
1467**		the current time.
1468*/
1469
1470time_t
1471curtime()
1472{
1473	auto time_t t;
1474
1475	(void) time(&t);
1476	return t;
1477}
1478/*
1479**  ATOBOOL -- convert a string representation to boolean.
1480**
1481**	Defaults to false
1482**
1483**	Parameters:
1484**		s -- string to convert.  Takes "tTyY", empty, and NULL as true,
1485**			others as false.
1486**
1487**	Returns:
1488**		A boolean representation of the string.
1489*/
1490
1491bool
1492atobool(s)
1493	register char *s;
1494{
1495	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1496		return true;
1497	return false;
1498}
1499/*
1500**  ATOOCT -- convert a string representation to octal.
1501**
1502**	Parameters:
1503**		s -- string to convert.
1504**
1505**	Returns:
1506**		An integer representing the string interpreted as an
1507**		octal number.
1508*/
1509
1510int
1511atooct(s)
1512	register char *s;
1513{
1514	register int i = 0;
1515
1516	while (*s >= '0' && *s <= '7')
1517		i = (i << 3) | (*s++ - '0');
1518	return i;
1519}
1520/*
1521**  BITINTERSECT -- tell if two bitmaps intersect
1522**
1523**	Parameters:
1524**		a, b -- the bitmaps in question
1525**
1526**	Returns:
1527**		true if they have a non-null intersection
1528**		false otherwise
1529*/
1530
1531bool
1532bitintersect(a, b)
1533	BITMAP256 a;
1534	BITMAP256 b;
1535{
1536	int i;
1537
1538	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1539	{
1540		if ((a[i] & b[i]) != 0)
1541			return true;
1542	}
1543	return false;
1544}
1545/*
1546**  BITZEROP -- tell if a bitmap is all zero
1547**
1548**	Parameters:
1549**		map -- the bit map to check
1550**
1551**	Returns:
1552**		true if map is all zero.
1553**		false if there are any bits set in map.
1554*/
1555
1556bool
1557bitzerop(map)
1558	BITMAP256 map;
1559{
1560	int i;
1561
1562	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1563	{
1564		if (map[i] != 0)
1565			return false;
1566	}
1567	return true;
1568}
1569/*
1570**  STRCONTAINEDIN -- tell if one string is contained in another
1571**
1572**	Parameters:
1573**		icase -- ignore case?
1574**		a -- possible substring.
1575**		b -- possible superstring.
1576**
1577**	Returns:
1578**		true if a is contained in b (case insensitive).
1579**		false otherwise.
1580*/
1581
1582bool
1583strcontainedin(icase, a, b)
1584	bool icase;
1585	register char *a;
1586	register char *b;
1587{
1588	int la;
1589	int lb;
1590	int c;
1591
1592	la = strlen(a);
1593	lb = strlen(b);
1594	c = *a;
1595	if (icase && isascii(c) && isupper(c))
1596		c = tolower(c);
1597	for (; lb-- >= la; b++)
1598	{
1599		if (icase)
1600		{
1601			if (*b != c &&
1602			    isascii(*b) && isupper(*b) && tolower(*b) != c)
1603				continue;
1604			if (sm_strncasecmp(a, b, la) == 0)
1605				return true;
1606		}
1607		else
1608		{
1609			if (*b != c)
1610				continue;
1611			if (strncmp(a, b, la) == 0)
1612				return true;
1613		}
1614	}
1615	return false;
1616}
1617/*
1618**  CHECKFD012 -- check low numbered file descriptors
1619**
1620**	File descriptors 0, 1, and 2 should be open at all times.
1621**	This routine verifies that, and fixes it if not true.
1622**
1623**	Parameters:
1624**		where -- a tag printed if the assertion failed
1625**
1626**	Returns:
1627**		none
1628*/
1629
1630void
1631checkfd012(where)
1632	char *where;
1633{
1634#if XDEBUG
1635	register int i;
1636
1637	for (i = 0; i < 3; i++)
1638		fill_fd(i, where);
1639#endif /* XDEBUG */
1640}
1641/*
1642**  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1643**
1644**	Parameters:
1645**		fd -- file descriptor to check.
1646**		where -- tag to print on failure.
1647**
1648**	Returns:
1649**		none.
1650*/
1651
1652void
1653checkfdopen(fd, where)
1654	int fd;
1655	char *where;
1656{
1657#if XDEBUG
1658	struct stat st;
1659
1660	if (fstat(fd, &st) < 0 && errno == EBADF)
1661	{
1662		syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1663		printopenfds(true);
1664	}
1665#endif /* XDEBUG */
1666}
1667/*
1668**  CHECKFDS -- check for new or missing file descriptors
1669**
1670**	Parameters:
1671**		where -- tag for printing.  If null, take a base line.
1672**
1673**	Returns:
1674**		none
1675**
1676**	Side Effects:
1677**		If where is set, shows changes since the last call.
1678*/
1679
1680void
1681checkfds(where)
1682	char *where;
1683{
1684	int maxfd;
1685	register int fd;
1686	bool printhdr = true;
1687	int save_errno = errno;
1688	static BITMAP256 baseline;
1689	extern int DtableSize;
1690
1691	if (DtableSize > BITMAPBITS)
1692		maxfd = BITMAPBITS;
1693	else
1694		maxfd = DtableSize;
1695	if (where == NULL)
1696		clrbitmap(baseline);
1697
1698	for (fd = 0; fd < maxfd; fd++)
1699	{
1700		struct stat stbuf;
1701
1702		if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1703		{
1704			if (!bitnset(fd, baseline))
1705				continue;
1706			clrbitn(fd, baseline);
1707		}
1708		else if (!bitnset(fd, baseline))
1709			setbitn(fd, baseline);
1710		else
1711			continue;
1712
1713		/* file state has changed */
1714		if (where == NULL)
1715			continue;
1716		if (printhdr)
1717		{
1718			sm_syslog(LOG_DEBUG, CurEnv->e_id,
1719				  "%s: changed fds:",
1720				  where);
1721			printhdr = false;
1722		}
1723		dumpfd(fd, true, true);
1724	}
1725	errno = save_errno;
1726}
1727/*
1728**  PRINTOPENFDS -- print the open file descriptors (for debugging)
1729**
1730**	Parameters:
1731**		logit -- if set, send output to syslog; otherwise
1732**			print for debugging.
1733**
1734**	Returns:
1735**		none.
1736*/
1737
1738#if NETINET || NETINET6
1739# include <arpa/inet.h>
1740#endif /* NETINET || NETINET6 */
1741
1742void
1743printopenfds(logit)
1744	bool logit;
1745{
1746	register int fd;
1747	extern int DtableSize;
1748
1749	for (fd = 0; fd < DtableSize; fd++)
1750		dumpfd(fd, false, logit);
1751}
1752/*
1753**  DUMPFD -- dump a file descriptor
1754**
1755**	Parameters:
1756**		fd -- the file descriptor to dump.
1757**		printclosed -- if set, print a notification even if
1758**			it is closed; otherwise print nothing.
1759**		logit -- if set, use sm_syslog instead of sm_dprintf()
1760**
1761**	Returns:
1762**		none.
1763*/
1764
1765void
1766dumpfd(fd, printclosed, logit)
1767	int fd;
1768	bool printclosed;
1769	bool logit;
1770{
1771	register char *p;
1772	char *hp;
1773#ifdef S_IFSOCK
1774	SOCKADDR sa;
1775#endif /* S_IFSOCK */
1776	auto SOCKADDR_LEN_T slen;
1777	int i;
1778#if STAT64 > 0
1779	struct stat64 st;
1780#else /* STAT64 > 0 */
1781	struct stat st;
1782#endif /* STAT64 > 0 */
1783	char buf[200];
1784
1785	p = buf;
1786	(void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1787	p += strlen(p);
1788
1789	if (
1790#if STAT64 > 0
1791	    fstat64(fd, &st)
1792#else /* STAT64 > 0 */
1793	    fstat(fd, &st)
1794#endif /* STAT64 > 0 */
1795	    < 0)
1796	{
1797		if (errno != EBADF)
1798		{
1799			(void) sm_snprintf(p, SPACELEFT(buf, p),
1800				"CANNOT STAT (%s)",
1801				sm_errstring(errno));
1802			goto printit;
1803		}
1804		else if (printclosed)
1805		{
1806			(void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
1807			goto printit;
1808		}
1809		return;
1810	}
1811
1812	i = fcntl(fd, F_GETFL, 0);
1813	if (i != -1)
1814	{
1815		(void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1816		p += strlen(p);
1817	}
1818
1819	(void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
1820			(int) st.st_mode);
1821	p += strlen(p);
1822	switch (st.st_mode & S_IFMT)
1823	{
1824#ifdef S_IFSOCK
1825	  case S_IFSOCK:
1826		(void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
1827		p += strlen(p);
1828		memset(&sa, '\0', sizeof sa);
1829		slen = sizeof sa;
1830		if (getsockname(fd, &sa.sa, &slen) < 0)
1831			(void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1832				 sm_errstring(errno));
1833		else
1834		{
1835			hp = hostnamebyanyaddr(&sa);
1836			if (hp == NULL)
1837			{
1838				/* EMPTY */
1839				/* do nothing */
1840			}
1841# if NETINET
1842			else if (sa.sa.sa_family == AF_INET)
1843				(void) sm_snprintf(p, SPACELEFT(buf, p),
1844					"%s/%d", hp, ntohs(sa.sin.sin_port));
1845# endif /* NETINET */
1846# if NETINET6
1847			else if (sa.sa.sa_family == AF_INET6)
1848				(void) sm_snprintf(p, SPACELEFT(buf, p),
1849					"%s/%d", hp, ntohs(sa.sin6.sin6_port));
1850# endif /* NETINET6 */
1851			else
1852				(void) sm_snprintf(p, SPACELEFT(buf, p),
1853					"%s", hp);
1854		}
1855		p += strlen(p);
1856		(void) sm_snprintf(p, SPACELEFT(buf, p), "->");
1857		p += strlen(p);
1858		slen = sizeof sa;
1859		if (getpeername(fd, &sa.sa, &slen) < 0)
1860			(void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1861					sm_errstring(errno));
1862		else
1863		{
1864			hp = hostnamebyanyaddr(&sa);
1865			if (hp == NULL)
1866			{
1867				/* EMPTY */
1868				/* do nothing */
1869			}
1870# if NETINET
1871			else if (sa.sa.sa_family == AF_INET)
1872				(void) sm_snprintf(p, SPACELEFT(buf, p),
1873					"%s/%d", hp, ntohs(sa.sin.sin_port));
1874# endif /* NETINET */
1875# if NETINET6
1876			else if (sa.sa.sa_family == AF_INET6)
1877				(void) sm_snprintf(p, SPACELEFT(buf, p),
1878					"%s/%d", hp, ntohs(sa.sin6.sin6_port));
1879# endif /* NETINET6 */
1880			else
1881				(void) sm_snprintf(p, SPACELEFT(buf, p),
1882					"%s", hp);
1883		}
1884		break;
1885#endif /* S_IFSOCK */
1886
1887	  case S_IFCHR:
1888		(void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
1889		p += strlen(p);
1890		goto defprint;
1891
1892#ifdef S_IFBLK
1893	  case S_IFBLK:
1894		(void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
1895		p += strlen(p);
1896		goto defprint;
1897#endif /* S_IFBLK */
1898
1899#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1900	  case S_IFIFO:
1901		(void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1902		p += strlen(p);
1903		goto defprint;
1904#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1905
1906#ifdef S_IFDIR
1907	  case S_IFDIR:
1908		(void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
1909		p += strlen(p);
1910		goto defprint;
1911#endif /* S_IFDIR */
1912
1913#ifdef S_IFLNK
1914	  case S_IFLNK:
1915		(void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
1916		p += strlen(p);
1917		goto defprint;
1918#endif /* S_IFLNK */
1919
1920	  default:
1921defprint:
1922		(void) sm_snprintf(p, SPACELEFT(buf, p),
1923			 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
1924			 major(st.st_dev), minor(st.st_dev),
1925			 (ULONGLONG_T) st.st_ino,
1926			 (int) st.st_nlink, (int) st.st_uid,
1927			 (int) st.st_gid);
1928		p += strlen(p);
1929		(void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
1930			 (ULONGLONG_T) st.st_size);
1931		break;
1932	}
1933
1934printit:
1935	if (logit)
1936		sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1937			  "%.800s", buf);
1938	else
1939		sm_dprintf("%s\n", buf);
1940}
1941/*
1942**  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1943**
1944**	Parameters:
1945**		host -- the host to shorten (stripped in place).
1946**
1947**	Returns:
1948**		place where string was truncated, NULL if not truncated.
1949*/
1950
1951char *
1952shorten_hostname(host)
1953	char host[];
1954{
1955	register char *p;
1956	char *mydom;
1957	int i;
1958	bool canon = false;
1959
1960	/* strip off final dot */
1961	i = strlen(host);
1962	p = &host[(i == 0) ? 0 : i - 1];
1963	if (*p == '.')
1964	{
1965		*p = '\0';
1966		canon = true;
1967	}
1968
1969	/* see if there is any domain at all -- if not, we are done */
1970	p = strchr(host, '.');
1971	if (p == NULL)
1972		return NULL;
1973
1974	/* yes, we have a domain -- see if it looks like us */
1975	mydom = macvalue('m', CurEnv);
1976	if (mydom == NULL)
1977		mydom = "";
1978	i = strlen(++p);
1979	if ((canon ? sm_strcasecmp(p, mydom)
1980		   : sm_strncasecmp(p, mydom, i)) == 0 &&
1981			(mydom[i] == '.' || mydom[i] == '\0'))
1982	{
1983		*--p = '\0';
1984		return p;
1985	}
1986	return NULL;
1987}
1988/*
1989**  PROG_OPEN -- open a program for reading
1990**
1991**	Parameters:
1992**		argv -- the argument list.
1993**		pfd -- pointer to a place to store the file descriptor.
1994**		e -- the current envelope.
1995**
1996**	Returns:
1997**		pid of the process -- -1 if it failed.
1998*/
1999
2000pid_t
2001prog_open(argv, pfd, e)
2002	char **argv;
2003	int *pfd;
2004	ENVELOPE *e;
2005{
2006	pid_t pid;
2007	int save_errno;
2008	int sff;
2009	int ret;
2010	int fdv[2];
2011	char *p, *q;
2012	char buf[MAXPATHLEN];
2013	extern int DtableSize;
2014
2015	if (pipe(fdv) < 0)
2016	{
2017		syserr("%s: cannot create pipe for stdout", argv[0]);
2018		return -1;
2019	}
2020	pid = fork();
2021	if (pid < 0)
2022	{
2023		syserr("%s: cannot fork", argv[0]);
2024		(void) close(fdv[0]);
2025		(void) close(fdv[1]);
2026		return -1;
2027	}
2028	if (pid > 0)
2029	{
2030		/* parent */
2031		(void) close(fdv[1]);
2032		*pfd = fdv[0];
2033		return pid;
2034	}
2035
2036	/* Reset global flags */
2037	RestartRequest = NULL;
2038	RestartWorkGroup = false;
2039	ShutdownRequest = NULL;
2040	PendingSignal = 0;
2041	CurrentPid = getpid();
2042
2043	/*
2044	**  Initialize exception stack and default exception
2045	**  handler for child process.
2046	*/
2047
2048	sm_exc_newthread(fatal_error);
2049
2050	/* child -- close stdin */
2051	(void) close(0);
2052
2053	/* stdout goes back to parent */
2054	(void) close(fdv[0]);
2055	if (dup2(fdv[1], 1) < 0)
2056	{
2057		syserr("%s: cannot dup2 for stdout", argv[0]);
2058		_exit(EX_OSERR);
2059	}
2060	(void) close(fdv[1]);
2061
2062	/* stderr goes to transcript if available */
2063	if (e->e_xfp != NULL)
2064	{
2065		int xfd;
2066
2067		xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
2068		if (xfd >= 0 && dup2(xfd, 2) < 0)
2069		{
2070			syserr("%s: cannot dup2 for stderr", argv[0]);
2071			_exit(EX_OSERR);
2072		}
2073	}
2074
2075	/* this process has no right to the queue file */
2076	if (e->e_lockfp != NULL)
2077		(void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL));
2078
2079	/* chroot to the program mailer directory, if defined */
2080	if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
2081	{
2082		expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
2083		if (chroot(buf) < 0)
2084		{
2085			syserr("prog_open: cannot chroot(%s)", buf);
2086			exit(EX_TEMPFAIL);
2087		}
2088		if (chdir("/") < 0)
2089		{
2090			syserr("prog_open: cannot chdir(/)");
2091			exit(EX_TEMPFAIL);
2092		}
2093	}
2094
2095	/* run as default user */
2096	endpwent();
2097	sm_mbdb_terminate();
2098	if (setgid(DefGid) < 0 && geteuid() == 0)
2099	{
2100		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2101		exit(EX_TEMPFAIL);
2102	}
2103	if (setuid(DefUid) < 0 && geteuid() == 0)
2104	{
2105		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2106		exit(EX_TEMPFAIL);
2107	}
2108
2109	/* run in some directory */
2110	if (ProgMailer != NULL)
2111		p = ProgMailer->m_execdir;
2112	else
2113		p = NULL;
2114	for (; p != NULL; p = q)
2115	{
2116		q = strchr(p, ':');
2117		if (q != NULL)
2118			*q = '\0';
2119		expand(p, buf, sizeof buf, e);
2120		if (q != NULL)
2121			*q++ = ':';
2122		if (buf[0] != '\0' && chdir(buf) >= 0)
2123			break;
2124	}
2125	if (p == NULL)
2126	{
2127		/* backup directories */
2128		if (chdir("/tmp") < 0)
2129			(void) chdir("/");
2130	}
2131
2132	/* Check safety of program to be run */
2133	sff = SFF_ROOTOK|SFF_EXECOK;
2134	if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
2135		sff |= SFF_NOGWFILES|SFF_NOWWFILES;
2136	if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
2137		sff |= SFF_NOPATHCHECK;
2138	else
2139		sff |= SFF_SAFEDIRPATH;
2140	ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
2141	if (ret != 0)
2142		sm_syslog(LOG_INFO, e->e_id,
2143			  "Warning: prog_open: program %s unsafe: %s",
2144			  argv[0], sm_errstring(ret));
2145
2146	/* arrange for all the files to be closed */
2147	sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
2148
2149	/* now exec the process */
2150	(void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2151
2152	/* woops!  failed */
2153	save_errno = errno;
2154	syserr("%s: cannot exec", argv[0]);
2155	if (transienterror(save_errno))
2156		_exit(EX_OSERR);
2157	_exit(EX_CONFIG);
2158	return -1;	/* avoid compiler warning on IRIX */
2159}
2160/*
2161**  GET_COLUMN -- look up a Column in a line buffer
2162**
2163**	Parameters:
2164**		line -- the raw text line to search.
2165**		col -- the column number to fetch.
2166**		delim -- the delimiter between columns.  If null,
2167**			use white space.
2168**		buf -- the output buffer.
2169**		buflen -- the length of buf.
2170**
2171**	Returns:
2172**		buf if successful.
2173**		NULL otherwise.
2174*/
2175
2176char *
2177get_column(line, col, delim, buf, buflen)
2178	char line[];
2179	int col;
2180	int delim;
2181	char buf[];
2182	int buflen;
2183{
2184	char *p;
2185	char *begin, *end;
2186	int i;
2187	char delimbuf[4];
2188
2189	if ((char) delim == '\0')
2190		(void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
2191	else
2192	{
2193		delimbuf[0] = (char) delim;
2194		delimbuf[1] = '\0';
2195	}
2196
2197	p = line;
2198	if (*p == '\0')
2199		return NULL;			/* line empty */
2200	if (*p == (char) delim && col == 0)
2201		return NULL;			/* first column empty */
2202
2203	begin = line;
2204
2205	if (col == 0 && (char) delim == '\0')
2206	{
2207		while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2208			begin++;
2209	}
2210
2211	for (i = 0; i < col; i++)
2212	{
2213		if ((begin = strpbrk(begin, delimbuf)) == NULL)
2214			return NULL;		/* no such column */
2215		begin++;
2216		if ((char) delim == '\0')
2217		{
2218			while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2219				begin++;
2220		}
2221	}
2222
2223	end = strpbrk(begin, delimbuf);
2224	if (end == NULL)
2225		i = strlen(begin);
2226	else
2227		i = end - begin;
2228	if (i >= buflen)
2229		i = buflen - 1;
2230	(void) sm_strlcpy(buf, begin, i + 1);
2231	return buf;
2232}
2233/*
2234**  CLEANSTRCPY -- copy string keeping out bogus characters
2235**
2236**	Parameters:
2237**		t -- "to" string.
2238**		f -- "from" string.
2239**		l -- length of space available in "to" string.
2240**
2241**	Returns:
2242**		none.
2243*/
2244
2245void
2246cleanstrcpy(t, f, l)
2247	register char *t;
2248	register char *f;
2249	int l;
2250{
2251	/* check for newlines and log if necessary */
2252	(void) denlstring(f, true, true);
2253
2254	if (l <= 0)
2255		syserr("!cleanstrcpy: length == 0");
2256
2257	l--;
2258	while (l > 0 && *f != '\0')
2259	{
2260		if (isascii(*f) &&
2261		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2262		{
2263			l--;
2264			*t++ = *f;
2265		}
2266		f++;
2267	}
2268	*t = '\0';
2269}
2270/*
2271**  DENLSTRING -- convert newlines in a string to spaces
2272**
2273**	Parameters:
2274**		s -- the input string
2275**		strict -- if set, don't permit continuation lines.
2276**		logattacks -- if set, log attempted attacks.
2277**
2278**	Returns:
2279**		A pointer to a version of the string with newlines
2280**		mapped to spaces.  This should be copied.
2281*/
2282
2283char *
2284denlstring(s, strict, logattacks)
2285	char *s;
2286	bool strict;
2287	bool logattacks;
2288{
2289	register char *p;
2290	int l;
2291	static char *bp = NULL;
2292	static int bl = 0;
2293
2294	p = s;
2295	while ((p = strchr(p, '\n')) != NULL)
2296		if (strict || (*++p != ' ' && *p != '\t'))
2297			break;
2298	if (p == NULL)
2299		return s;
2300
2301	l = strlen(s) + 1;
2302	if (bl < l)
2303	{
2304		/* allocate more space */
2305		char *nbp = sm_pmalloc_x(l);
2306
2307		if (bp != NULL)
2308			sm_free(bp);
2309		bp = nbp;
2310		bl = l;
2311	}
2312	(void) sm_strlcpy(bp, s, l);
2313	for (p = bp; (p = strchr(p, '\n')) != NULL; )
2314		*p++ = ' ';
2315
2316	if (logattacks)
2317	{
2318		sm_syslog(LOG_NOTICE, CurEnv->e_id,
2319			  "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2320			  RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2321			  shortenstring(bp, MAXSHORTSTR));
2322	}
2323
2324	return bp;
2325}
2326
2327/*
2328**  STRREPLNONPRT -- replace "unprintable" characters in a string with subst
2329**
2330**	Parameters:
2331**		s -- string to manipulate (in place)
2332**		subst -- character to use as replacement
2333**
2334**	Returns:
2335**		true iff string did not contain "unprintable" characters
2336*/
2337
2338bool
2339strreplnonprt(s, c)
2340	char *s;
2341	int c;
2342{
2343	bool ok;
2344
2345	ok = true;
2346	if (s == NULL)
2347		return ok;
2348	while (*s != '\0')
2349	{
2350		if (!(isascii(*s) && isprint(*s)))
2351		{
2352			*s = c;
2353			ok = false;
2354		}
2355		++s;
2356	}
2357	return ok;
2358}
2359
2360/*
2361**  STR2PRT -- convert "unprintable" characters in a string to \oct
2362**
2363**	Parameters:
2364**		s -- string to convert
2365**
2366**	Returns:
2367**		converted string.
2368**		This is a static local buffer, string must be copied
2369**		before this function is called again!
2370*/
2371
2372char *
2373str2prt(s)
2374	char *s;
2375{
2376	int l;
2377	char c, *h;
2378	bool ok;
2379	static int len = 0;
2380	static char *buf = NULL;
2381
2382	if (s == NULL)
2383		return NULL;
2384	ok = true;
2385	for (h = s, l = 1; *h != '\0'; h++, l++)
2386	{
2387		if (*h == '\\')
2388		{
2389			++l;
2390			ok = false;
2391		}
2392		else if (!(isascii(*h) && isprint(*h)))
2393		{
2394			l += 3;
2395			ok = false;
2396		}
2397	}
2398	if (ok)
2399		return s;
2400	if (l > len)
2401	{
2402		char *nbuf = sm_pmalloc_x(l);
2403
2404		if (buf != NULL)
2405			sm_free(buf);
2406		len = l;
2407		buf = nbuf;
2408	}
2409	for (h = buf; *s != '\0' && l > 0; s++, l--)
2410	{
2411		c = *s;
2412		if (isascii(c) && isprint(c) && c != '\\')
2413		{
2414			*h++ = c;
2415		}
2416		else
2417		{
2418			*h++ = '\\';
2419			--l;
2420			switch (c)
2421			{
2422			  case '\\':
2423				*h++ = '\\';
2424				break;
2425			  case '\t':
2426				*h++ = 't';
2427				break;
2428			  case '\n':
2429				*h++ = 'n';
2430				break;
2431			  case '\r':
2432				*h++ = 'r';
2433				break;
2434			  default:
2435				(void) sm_snprintf(h, l, "%03o",
2436					(unsigned int)((unsigned char) c));
2437
2438				/*
2439				**  XXX since l is unsigned this may
2440				**  wrap around if the calculation is screwed
2441				**  up...
2442				*/
2443
2444				l -= 2;
2445				h += 3;
2446				break;
2447			}
2448		}
2449	}
2450	*h = '\0';
2451	buf[len - 1] = '\0';
2452	return buf;
2453}
2454/*
2455**  PATH_IS_DIR -- check to see if file exists and is a directory.
2456**
2457**	There are some additional checks for security violations in
2458**	here.  This routine is intended to be used for the host status
2459**	support.
2460**
2461**	Parameters:
2462**		pathname -- pathname to check for directory-ness.
2463**		createflag -- if set, create directory if needed.
2464**
2465**	Returns:
2466**		true -- if the indicated pathname is a directory
2467**		false -- otherwise
2468*/
2469
2470bool
2471path_is_dir(pathname, createflag)
2472	char *pathname;
2473	bool createflag;
2474{
2475	struct stat statbuf;
2476
2477#if HASLSTAT
2478	if (lstat(pathname, &statbuf) < 0)
2479#else /* HASLSTAT */
2480	if (stat(pathname, &statbuf) < 0)
2481#endif /* HASLSTAT */
2482	{
2483		if (errno != ENOENT || !createflag)
2484			return false;
2485		if (mkdir(pathname, 0755) < 0)
2486			return false;
2487		return true;
2488	}
2489	if (!S_ISDIR(statbuf.st_mode))
2490	{
2491		errno = ENOTDIR;
2492		return false;
2493	}
2494
2495	/* security: don't allow writable directories */
2496	if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2497	{
2498		errno = EACCES;
2499		return false;
2500	}
2501	return true;
2502}
2503/*
2504**  PROC_LIST_ADD -- add process id to list of our children
2505**
2506**	Parameters:
2507**		pid -- pid to add to list.
2508**		task -- task of pid.
2509**		type -- type of process.
2510**		count -- number of processes.
2511**		other -- other information for this type.
2512**
2513**	Returns:
2514**		none
2515**
2516**	Side Effects:
2517**		May increase CurChildren. May grow ProcList.
2518*/
2519
2520typedef struct procs	PROCS_T;
2521
2522struct procs
2523{
2524	pid_t		proc_pid;
2525	char		*proc_task;
2526	int		proc_type;
2527	int		proc_count;
2528	int		proc_other;
2529	SOCKADDR	proc_hostaddr;
2530};
2531
2532static PROCS_T	*volatile ProcListVec = NULL;
2533static int	ProcListSize = 0;
2534
2535void
2536proc_list_add(pid, task, type, count, other, hostaddr)
2537	pid_t pid;
2538	char *task;
2539	int type;
2540	int count;
2541	int other;
2542	SOCKADDR *hostaddr;
2543{
2544	int i;
2545
2546	for (i = 0; i < ProcListSize; i++)
2547	{
2548		if (ProcListVec[i].proc_pid == NO_PID)
2549			break;
2550	}
2551	if (i >= ProcListSize)
2552	{
2553		/* probe the existing vector to avoid growing infinitely */
2554		proc_list_probe();
2555
2556		/* now scan again */
2557		for (i = 0; i < ProcListSize; i++)
2558		{
2559			if (ProcListVec[i].proc_pid == NO_PID)
2560				break;
2561		}
2562	}
2563	if (i >= ProcListSize)
2564	{
2565		/* grow process list */
2566		PROCS_T *npv;
2567
2568		SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
2569		npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) *
2570					       (ProcListSize + PROC_LIST_SEG));
2571		if (ProcListSize > 0)
2572		{
2573			memmove(npv, ProcListVec,
2574				ProcListSize * sizeof (PROCS_T));
2575			sm_free(ProcListVec);
2576		}
2577
2578		/* XXX just use memset() to initialize this part? */
2579		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2580		{
2581			npv[i].proc_pid = NO_PID;
2582			npv[i].proc_task = NULL;
2583			npv[i].proc_type = PROC_NONE;
2584		}
2585		i = ProcListSize;
2586		ProcListSize += PROC_LIST_SEG;
2587		ProcListVec = npv;
2588	}
2589	ProcListVec[i].proc_pid = pid;
2590	PSTRSET(ProcListVec[i].proc_task, task);
2591	ProcListVec[i].proc_type = type;
2592	ProcListVec[i].proc_count = count;
2593	ProcListVec[i].proc_other = other;
2594	if (hostaddr != NULL)
2595		ProcListVec[i].proc_hostaddr = *hostaddr;
2596	else
2597		memset(&ProcListVec[i].proc_hostaddr, 0,
2598			sizeof(ProcListVec[i].proc_hostaddr));
2599
2600	/* if process adding itself, it's not a child */
2601	if (pid != CurrentPid)
2602	{
2603		SM_ASSERT(CurChildren < INT_MAX);
2604		CurChildren++;
2605	}
2606}
2607/*
2608**  PROC_LIST_SET -- set pid task in process list
2609**
2610**	Parameters:
2611**		pid -- pid to set
2612**		task -- task of pid
2613**
2614**	Returns:
2615**		none.
2616*/
2617
2618void
2619proc_list_set(pid, task)
2620	pid_t pid;
2621	char *task;
2622{
2623	int i;
2624
2625	for (i = 0; i < ProcListSize; i++)
2626	{
2627		if (ProcListVec[i].proc_pid == pid)
2628		{
2629			PSTRSET(ProcListVec[i].proc_task, task);
2630			break;
2631		}
2632	}
2633}
2634/*
2635**  PROC_LIST_DROP -- drop pid from process list
2636**
2637**	Parameters:
2638**		pid -- pid to drop
2639**		st -- process status
2640**		other -- storage for proc_other (return).
2641**
2642**	Returns:
2643**		none.
2644**
2645**	Side Effects:
2646**		May decrease CurChildren, CurRunners, or
2647**		set RestartRequest or ShutdownRequest.
2648**
2649**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2650**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2651**		DOING.
2652*/
2653
2654void
2655proc_list_drop(pid, st, other)
2656	pid_t pid;
2657	int st;
2658	int *other;
2659{
2660	int i;
2661	int type = PROC_NONE;
2662
2663	for (i = 0; i < ProcListSize; i++)
2664	{
2665		if (ProcListVec[i].proc_pid == pid)
2666		{
2667			ProcListVec[i].proc_pid = NO_PID;
2668			type = ProcListVec[i].proc_type;
2669			if (other != NULL)
2670				*other = ProcListVec[i].proc_other;
2671			break;
2672		}
2673	}
2674	if (CurChildren > 0)
2675		CurChildren--;
2676
2677
2678	if (type == PROC_CONTROL && WIFEXITED(st))
2679	{
2680		/* if so, see if we need to restart or shutdown */
2681		if (WEXITSTATUS(st) == EX_RESTART)
2682			RestartRequest = "control socket";
2683		else if (WEXITSTATUS(st) == EX_SHUTDOWN)
2684			ShutdownRequest = "control socket";
2685	}
2686	else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
2687		 ProcListVec[i].proc_other > -1)
2688	{
2689		/* restart this persistent runner */
2690		mark_work_group_restart(ProcListVec[i].proc_other, st);
2691	}
2692	else if (type == PROC_QUEUE)
2693		CurRunners -= ProcListVec[i].proc_count;
2694}
2695/*
2696**  PROC_LIST_CLEAR -- clear the process list
2697**
2698**	Parameters:
2699**		none.
2700**
2701**	Returns:
2702**		none.
2703**
2704**	Side Effects:
2705**		Sets CurChildren to zero.
2706*/
2707
2708void
2709proc_list_clear()
2710{
2711	int i;
2712
2713	/* start from 1 since 0 is the daemon itself */
2714	for (i = 1; i < ProcListSize; i++)
2715		ProcListVec[i].proc_pid = NO_PID;
2716	CurChildren = 0;
2717}
2718/*
2719**  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2720**
2721**	Parameters:
2722**		none
2723**
2724**	Returns:
2725**		none
2726**
2727**	Side Effects:
2728**		May decrease CurChildren.
2729*/
2730
2731void
2732proc_list_probe()
2733{
2734	int i;
2735
2736	/* start from 1 since 0 is the daemon itself */
2737	for (i = 1; i < ProcListSize; i++)
2738	{
2739		if (ProcListVec[i].proc_pid == NO_PID)
2740			continue;
2741		if (kill(ProcListVec[i].proc_pid, 0) < 0)
2742		{
2743			if (LogLevel > 3)
2744				sm_syslog(LOG_DEBUG, CurEnv->e_id,
2745					  "proc_list_probe: lost pid %d",
2746					  (int) ProcListVec[i].proc_pid);
2747			ProcListVec[i].proc_pid = NO_PID;
2748			SM_FREE_CLR(ProcListVec[i].proc_task);
2749			CurChildren--;
2750		}
2751	}
2752	if (CurChildren < 0)
2753		CurChildren = 0;
2754}
2755
2756/*
2757**  PROC_LIST_DISPLAY -- display the process list
2758**
2759**	Parameters:
2760**		out -- output file pointer
2761**		prefix -- string to output in front of each line.
2762**
2763**	Returns:
2764**		none.
2765*/
2766
2767void
2768proc_list_display(out, prefix)
2769	SM_FILE_T *out;
2770	char *prefix;
2771{
2772	int i;
2773
2774	for (i = 0; i < ProcListSize; i++)
2775	{
2776		if (ProcListVec[i].proc_pid == NO_PID)
2777			continue;
2778
2779		(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
2780				     prefix,
2781				     (int) ProcListVec[i].proc_pid,
2782				     ProcListVec[i].proc_task != NULL ?
2783				     ProcListVec[i].proc_task : "(unknown)",
2784				     (OpMode == MD_SMTP ||
2785				      OpMode == MD_DAEMON ||
2786				      OpMode == MD_ARPAFTP) ? "\r" : "");
2787	}
2788}
2789
2790/*
2791**  PROC_LIST_SIGNAL -- send a signal to a type of process in the list
2792**
2793**	Parameters:
2794**		type -- type of process to signal
2795**		signal -- the type of signal to send
2796**
2797**	Results:
2798**		none.
2799**
2800**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2801**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2802**		DOING.
2803*/
2804
2805void
2806proc_list_signal(type, signal)
2807	int type;
2808	int signal;
2809{
2810	int chldwasblocked;
2811	int alrmwasblocked;
2812	int i;
2813	pid_t mypid = getpid();
2814
2815	/* block these signals so that we may signal cleanly */
2816	chldwasblocked = sm_blocksignal(SIGCHLD);
2817	alrmwasblocked = sm_blocksignal(SIGALRM);
2818
2819	/* Find all processes of type and send signal */
2820	for (i = 0; i < ProcListSize; i++)
2821	{
2822		if (ProcListVec[i].proc_pid == NO_PID ||
2823		    ProcListVec[i].proc_pid == mypid)
2824			continue;
2825		if (ProcListVec[i].proc_type != type)
2826			continue;
2827		(void) kill(ProcListVec[i].proc_pid, signal);
2828	}
2829
2830	/* restore the signals */
2831	if (alrmwasblocked == 0)
2832		(void) sm_releasesignal(SIGALRM);
2833	if (chldwasblocked == 0)
2834		(void) sm_releasesignal(SIGCHLD);
2835}
2836
2837/*
2838**  COUNT_OPEN_CONNECTIONS
2839**
2840**	Parameters:
2841**		hostaddr - ClientAddress
2842**
2843**	Returns:
2844**		the number of open connections for this client
2845**
2846*/
2847
2848int
2849count_open_connections(hostaddr)
2850	SOCKADDR *hostaddr;
2851{
2852	int i, n;
2853
2854	if (hostaddr == NULL)
2855		return 0;
2856	n = 0;
2857	for (i = 0; i < ProcListSize; i++)
2858	{
2859		if (ProcListVec[i].proc_pid == NO_PID)
2860			continue;
2861
2862		if (hostaddr->sa.sa_family !=
2863		    ProcListVec[i].proc_hostaddr.sa.sa_family)
2864			continue;
2865#if NETINET
2866		if (hostaddr->sa.sa_family == AF_INET &&
2867		    (hostaddr->sin.sin_addr.s_addr ==
2868		     ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr))
2869			n++;
2870#endif /* NETINET */
2871#if NETINET6
2872		if (hostaddr->sa.sa_family == AF_INET6 &&
2873		    IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr),
2874				       &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr)))
2875			n++;
2876#endif /* NETINET6 */
2877	}
2878	return n;
2879}
2880