138032Speter/*
2261194Sgshapiro * Copyright (c) 1998-2007, 2009 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1564562Sgshapiro
16266527SgshapiroSM_RCSID("@(#)$Id: util.c,v 8.427 2013-11-22 20:51:57 ca Exp $")
1764562Sgshapiro
18168515Sgshapiro#include <sm/sendmail.h>
1990792Sgshapiro#include <sysexits.h>
2090792Sgshapiro#include <sm/xtrap.h>
2164562Sgshapiro
2290792Sgshapiro/*
23132943Sgshapiro**  NEWSTR -- Create a copy of a C string
24132943Sgshapiro**
25132943Sgshapiro**	Parameters:
26132943Sgshapiro**		s -- the string to copy.
27132943Sgshapiro**
28132943Sgshapiro**	Returns:
29132943Sgshapiro**		pointer to newly allocated string.
30132943Sgshapiro*/
31132943Sgshapiro
32132943Sgshapirochar *
33132943Sgshapironewstr(s)
34132943Sgshapiro	const char *s;
35132943Sgshapiro{
36132943Sgshapiro	size_t l;
37132943Sgshapiro	char *n;
38132943Sgshapiro
39132943Sgshapiro	l = strlen(s);
40132943Sgshapiro	SM_ASSERT(l + 1 > l);
41132943Sgshapiro	n = xalloc(l + 1);
42132943Sgshapiro	sm_strlcpy(n, s, l + 1);
43132943Sgshapiro	return n;
44132943Sgshapiro}
45132943Sgshapiro
46132943Sgshapiro/*
4738032Speter**  ADDQUOTES -- Adds quotes & quote bits to a string.
4838032Speter**
4990792Sgshapiro**	Runs through a string and adds backslashes and quote bits.
5038032Speter**
5138032Speter**	Parameters:
5238032Speter**		s -- the string to modify.
5390792Sgshapiro**		rpool -- resource pool from which to allocate result
5438032Speter**
5538032Speter**	Returns:
5638032Speter**		pointer to quoted string.
5738032Speter*/
5838032Speter
5938032Speterchar *
6090792Sgshapiroaddquotes(s, rpool)
6138032Speter	char *s;
6290792Sgshapiro	SM_RPOOL_T *rpool;
6338032Speter{
6438032Speter	int len = 0;
6538032Speter	char c;
6638032Speter	char *p = s, *q, *r;
6738032Speter
6838032Speter	if (s == NULL)
6938032Speter		return NULL;
7038032Speter
7138032Speter	/* Find length of quoted string */
7238032Speter	while ((c = *p++) != '\0')
7338032Speter	{
7438032Speter		len++;
7538032Speter		if (c == '\\' || c == '"')
7638032Speter			len++;
7738032Speter	}
7864562Sgshapiro
7990792Sgshapiro	q = r = sm_rpool_malloc_x(rpool, len + 3);
8038032Speter	p = s;
8138032Speter
8238032Speter	/* add leading quote */
8338032Speter	*q++ = '"';
8438032Speter	while ((c = *p++) != '\0')
8538032Speter	{
8638032Speter		/* quote \ or " */
8738032Speter		if (c == '\\' || c == '"')
8838032Speter			*q++ = '\\';
8938032Speter		*q++ = c;
9038032Speter	}
9138032Speter	*q++ = '"';
9238032Speter	*q = '\0';
9338032Speter	return r;
9438032Speter}
95110560Sgshapiro
9690792Sgshapiro/*
97141858Sgshapiro**  STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
98141858Sgshapiro**	the following character is alpha-numerical.
99110560Sgshapiro**
100110560Sgshapiro**	This is done in place.
101110560Sgshapiro**
102110560Sgshapiro**	Parameters:
103110560Sgshapiro**		s -- the string to strip.
104110560Sgshapiro**
105110560Sgshapiro**	Returns:
106110560Sgshapiro**		none.
107110560Sgshapiro*/
108110560Sgshapiro
109110560Sgshapirovoid
110110560Sgshapirostripbackslash(s)
111110560Sgshapiro	char *s;
112110560Sgshapiro{
113110560Sgshapiro	char *p, *q, c;
114110560Sgshapiro
115110560Sgshapiro	if (s == NULL || *s == '\0')
116110560Sgshapiro		return;
117110560Sgshapiro	p = q = s;
118110560Sgshapiro	while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
119110560Sgshapiro		p++;
120110560Sgshapiro	do
121110560Sgshapiro	{
122110560Sgshapiro		c = *q++ = *p++;
123110560Sgshapiro	} while (c != '\0');
124110560Sgshapiro}
125110560Sgshapiro
126110560Sgshapiro/*
12738032Speter**  RFC822_STRING -- Checks string for proper RFC822 string quoting.
12838032Speter**
12938032Speter**	Runs through a string and verifies RFC822 special characters
13038032Speter**	are only found inside comments, quoted strings, or backslash
13138032Speter**	escaped.  Also verified balanced quotes and parenthesis.
13238032Speter**
13338032Speter**	Parameters:
13438032Speter**		s -- the string to modify.
13538032Speter**
13638032Speter**	Returns:
13790792Sgshapiro**		true iff the string is RFC822 compliant, false otherwise.
13838032Speter*/
13938032Speter
14038032Speterbool
14138032Speterrfc822_string(s)
14238032Speter	char *s;
14338032Speter{
14490792Sgshapiro	bool quoted = false;
14538032Speter	int commentlev = 0;
14638032Speter	char *c = s;
14738032Speter
14838032Speter	if (s == NULL)
14990792Sgshapiro		return false;
15038032Speter
15138032Speter	while (*c != '\0')
15238032Speter	{
15338032Speter		/* escaped character */
15438032Speter		if (*c == '\\')
15538032Speter		{
15638032Speter			c++;
15738032Speter			if (*c == '\0')
15890792Sgshapiro				return false;
15938032Speter		}
16038032Speter		else if (commentlev == 0 && *c == '"')
16138032Speter			quoted = !quoted;
16238032Speter		else if (!quoted)
16338032Speter		{
16438032Speter			if (*c == ')')
16538032Speter			{
16638032Speter				/* unbalanced ')' */
16738032Speter				if (commentlev == 0)
16890792Sgshapiro					return false;
16938032Speter				else
17038032Speter					commentlev--;
17138032Speter			}
17238032Speter			else if (*c == '(')
17338032Speter				commentlev++;
17438032Speter			else if (commentlev == 0 &&
17538032Speter				 strchr(MustQuoteChars, *c) != NULL)
17690792Sgshapiro				return false;
17738032Speter		}
17838032Speter		c++;
17938032Speter	}
18090792Sgshapiro
18138032Speter	/* unbalanced '"' or '(' */
18290792Sgshapiro	return !quoted && commentlev == 0;
18338032Speter}
184168515Sgshapiro
18590792Sgshapiro/*
18642575Speter**  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
18742575Speter**
18864562Sgshapiro**	Arbitrarily shorten (in place) an RFC822 string and rebalance
18942575Speter**	comments and quotes.
19042575Speter**
19142575Speter**	Parameters:
19242575Speter**		string -- the string to shorten
19342575Speter**		length -- the maximum size, 0 if no maximum
19442575Speter**
19542575Speter**	Returns:
19690792Sgshapiro**		true if string is changed, false otherwise
19742575Speter**
19842575Speter**	Side Effects:
19942575Speter**		Changes string in place, possibly resulting
20042575Speter**		in a shorter string.
20142575Speter*/
20242575Speter
20342575Speterbool
20442575Spetershorten_rfc822_string(string, length)
20542575Speter	char *string;
20642575Speter	size_t length;
20742575Speter{
20890792Sgshapiro	bool backslash = false;
20990792Sgshapiro	bool modified = false;
21090792Sgshapiro	bool quoted = false;
21142575Speter	size_t slen;
21242575Speter	int parencount = 0;
21342575Speter	char *ptr = string;
21464562Sgshapiro
21542575Speter	/*
21642575Speter	**  If have to rebalance an already short enough string,
21742575Speter	**  need to do it within allocated space.
21842575Speter	*/
21971345Sgshapiro
22042575Speter	slen = strlen(string);
22142575Speter	if (length == 0 || slen < length)
22242575Speter		length = slen;
22342575Speter
22442575Speter	while (*ptr != '\0')
22542575Speter	{
22642575Speter		if (backslash)
22742575Speter		{
22890792Sgshapiro			backslash = false;
22942575Speter			goto increment;
23042575Speter		}
23142575Speter
23242575Speter		if (*ptr == '\\')
23390792Sgshapiro			backslash = true;
23442575Speter		else if (*ptr == '(')
23542575Speter		{
23642575Speter			if (!quoted)
23742575Speter				parencount++;
23842575Speter		}
23942575Speter		else if (*ptr == ')')
24042575Speter		{
24142575Speter			if (--parencount < 0)
24242575Speter				parencount = 0;
24342575Speter		}
24464562Sgshapiro
24542575Speter		/* Inside a comment, quotes don't matter */
24642575Speter		if (parencount <= 0 && *ptr == '"')
24742575Speter			quoted = !quoted;
24842575Speter
24942575Speterincrement:
25042575Speter		/* Check for sufficient space for next character */
25164562Sgshapiro		if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
25242575Speter						parencount +
25342575Speter						(quoted ? 1 : 0)))
25442575Speter		{
25542575Speter			/* Not enough, backtrack */
25642575Speter			if (*ptr == '\\')
25790792Sgshapiro				backslash = false;
25842575Speter			else if (*ptr == '(' && !quoted)
25942575Speter				parencount--;
26042575Speter			else if (*ptr == '"' && parencount == 0)
26190792Sgshapiro				quoted = false;
26242575Speter			break;
26342575Speter		}
26442575Speter		ptr++;
26542575Speter	}
26642575Speter
26742575Speter	/* Rebalance */
26842575Speter	while (parencount-- > 0)
26942575Speter	{
27042575Speter		if (*ptr != ')')
27142575Speter		{
27290792Sgshapiro			modified = true;
27342575Speter			*ptr = ')';
27442575Speter		}
27542575Speter		ptr++;
27642575Speter	}
27742575Speter	if (quoted)
27842575Speter	{
27942575Speter		if (*ptr != '"')
28042575Speter		{
28190792Sgshapiro			modified = true;
28242575Speter			*ptr = '"';
28342575Speter		}
28442575Speter		ptr++;
28542575Speter	}
28642575Speter	if (*ptr != '\0')
28742575Speter	{
28890792Sgshapiro		modified = true;
28942575Speter		*ptr = '\0';
29042575Speter	}
29142575Speter	return modified;
29242575Speter}
293168515Sgshapiro
29490792Sgshapiro/*
29542575Speter**  FIND_CHARACTER -- find an unquoted character in an RFC822 string
29642575Speter**
29742575Speter**	Find an unquoted, non-commented character in an RFC822
29842575Speter**	string and return a pointer to its location in the
29942575Speter**	string.
30042575Speter**
30142575Speter**	Parameters:
30242575Speter**		string -- the string to search
30342575Speter**		character -- the character to find
30442575Speter**
30542575Speter**	Returns:
30642575Speter**		pointer to the character, or
30742575Speter**		a pointer to the end of the line if character is not found
30842575Speter*/
30942575Speter
31042575Speterchar *
31142575Speterfind_character(string, character)
31242575Speter	char *string;
31364562Sgshapiro	int character;
31442575Speter{
31590792Sgshapiro	bool backslash = false;
31690792Sgshapiro	bool quoted = false;
31742575Speter	int parencount = 0;
31864562Sgshapiro
31942575Speter	while (string != NULL && *string != '\0')
32042575Speter	{
32142575Speter		if (backslash)
32242575Speter		{
32390792Sgshapiro			backslash = false;
32442575Speter			if (!quoted && character == '\\' && *string == '\\')
32542575Speter				break;
32642575Speter			string++;
32742575Speter			continue;
32842575Speter		}
32942575Speter		switch (*string)
33042575Speter		{
33142575Speter		  case '\\':
33290792Sgshapiro			backslash = true;
33342575Speter			break;
33464562Sgshapiro
33542575Speter		  case '(':
33642575Speter			if (!quoted)
33742575Speter				parencount++;
33842575Speter			break;
33964562Sgshapiro
34042575Speter		  case ')':
34142575Speter			if (--parencount < 0)
34242575Speter				parencount = 0;
34342575Speter			break;
34442575Speter		}
34564562Sgshapiro
34642575Speter		/* Inside a comment, nothing matters */
34742575Speter		if (parencount > 0)
34842575Speter		{
34942575Speter			string++;
35042575Speter			continue;
35142575Speter		}
35264562Sgshapiro
35342575Speter		if (*string == '"')
35442575Speter			quoted = !quoted;
35542575Speter		else if (*string == character && !quoted)
35642575Speter			break;
35742575Speter		string++;
35842575Speter	}
35942575Speter
36042575Speter	/* Return pointer to the character */
36142575Speter	return string;
36242575Speter}
36390792Sgshapiro
36490792Sgshapiro/*
36590792Sgshapiro**  CHECK_BODYTYPE -- check bodytype parameter
36638032Speter**
36738032Speter**	Parameters:
36890792Sgshapiro**		bodytype -- bodytype parameter
36938032Speter**
37038032Speter**	Returns:
37190792Sgshapiro**		BODYTYPE_* according to parameter
37238032Speter**
37338032Speter*/
37438032Speter
37590792Sgshapiroint
37690792Sgshapirocheck_bodytype(bodytype)
37790792Sgshapiro	char *bodytype;
37838032Speter{
37990792Sgshapiro	/* check body type for legality */
38090792Sgshapiro	if (bodytype == NULL)
38190792Sgshapiro		return BODYTYPE_NONE;
38290792Sgshapiro	if (sm_strcasecmp(bodytype, "7BIT") == 0)
38390792Sgshapiro		return BODYTYPE_7BIT;
38490792Sgshapiro	if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
38590792Sgshapiro		return BODYTYPE_8BITMIME;
38690792Sgshapiro	return BODYTYPE_ILLEGAL;
38790792Sgshapiro}
38838032Speter
38990792Sgshapiro/*
39090792Sgshapiro**  TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
39177349Sgshapiro**
39277349Sgshapiro**	Parameters:
39390792Sgshapiro**		str -- string to truncate
39490792Sgshapiro**		len -- maximum length (including '\0') (0 for unlimited)
39590792Sgshapiro**		delim -- delimiter character
39677349Sgshapiro**
39777349Sgshapiro**	Returns:
39890792Sgshapiro**		None.
39977349Sgshapiro*/
40077349Sgshapiro
40190792Sgshapirovoid
40290792Sgshapirotruncate_at_delim(str, len, delim)
40390792Sgshapiro	char *str;
40490792Sgshapiro	size_t len;
40590792Sgshapiro	int delim;
40677349Sgshapiro{
40790792Sgshapiro	char *p;
40877349Sgshapiro
40990792Sgshapiro	if (str == NULL || len == 0 || strlen(str) < len)
41090792Sgshapiro		return;
41177349Sgshapiro
41290792Sgshapiro	*(str + len - 1) = '\0';
41390792Sgshapiro	while ((p = strrchr(str, delim)) != NULL)
41477349Sgshapiro	{
41590792Sgshapiro		*p = '\0';
41690792Sgshapiro		if (p - str + 4 < len)
41790792Sgshapiro		{
418120256Sgshapiro			*p++ = (char) delim;
41990792Sgshapiro			*p = '\0';
42090792Sgshapiro			(void) sm_strlcat(str, "...", len);
42190792Sgshapiro			return;
42290792Sgshapiro		}
42390792Sgshapiro	}
42477349Sgshapiro
42590792Sgshapiro	/* Couldn't find a place to append "..." */
42690792Sgshapiro	if (len > 3)
42790792Sgshapiro		(void) sm_strlcpy(str, "...", len);
42890792Sgshapiro	else
42990792Sgshapiro		str[0] = '\0';
43077349Sgshapiro}
431168515Sgshapiro
43290792Sgshapiro/*
43390792Sgshapiro**  XALLOC -- Allocate memory, raise an exception on error
43477349Sgshapiro**
43577349Sgshapiro**	Parameters:
43690792Sgshapiro**		sz -- size of area to allocate.
43777349Sgshapiro**
43877349Sgshapiro**	Returns:
43977349Sgshapiro**		pointer to data region.
44077349Sgshapiro**
44190792Sgshapiro**	Exceptions:
44290792Sgshapiro**		SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
44390792Sgshapiro**
44477349Sgshapiro**	Side Effects:
44577349Sgshapiro**		Memory is allocated.
44677349Sgshapiro*/
44777349Sgshapiro
44877349Sgshapirochar *
44990792Sgshapiro#if SM_HEAP_CHECK
45090792Sgshapiroxalloc_tagged(sz, file, line)
45190792Sgshapiro	register int sz;
45290792Sgshapiro	char *file;
45390792Sgshapiro	int line;
45490792Sgshapiro#else /* SM_HEAP_CHECK */
45590792Sgshapiroxalloc(sz)
45690792Sgshapiro	register int sz;
45790792Sgshapiro#endif /* SM_HEAP_CHECK */
45877349Sgshapiro{
45977349Sgshapiro	register char *p;
46077349Sgshapiro
461157001Sgshapiro	SM_REQUIRE(sz >= 0);
462157001Sgshapiro
46377349Sgshapiro	/* some systems can't handle size zero mallocs */
46477349Sgshapiro	if (sz <= 0)
46577349Sgshapiro		sz = 1;
46677349Sgshapiro
46790792Sgshapiro	/* scaffolding for testing error handling code */
46890792Sgshapiro	sm_xtrap_raise_x(&SmHeapOutOfMemory);
46990792Sgshapiro
47090792Sgshapiro	p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
47177349Sgshapiro	if (p == NULL)
47277349Sgshapiro	{
47390792Sgshapiro		sm_exc_raise_x(&SmHeapOutOfMemory);
47477349Sgshapiro	}
47577349Sgshapiro	return p;
47677349Sgshapiro}
477168515Sgshapiro
47890792Sgshapiro/*
47938032Speter**  COPYPLIST -- copy list of pointers.
48038032Speter**
48190792Sgshapiro**	This routine is the equivalent of strdup for lists of
48238032Speter**	pointers.
48338032Speter**
48438032Speter**	Parameters:
48538032Speter**		list -- list of pointers to copy.
48638032Speter**			Must be NULL terminated.
48790792Sgshapiro**		copycont -- if true, copy the contents of the vector
48838032Speter**			(which must be a string) also.
48990792Sgshapiro**		rpool -- resource pool from which to allocate storage,
49090792Sgshapiro**			or NULL
49138032Speter**
49238032Speter**	Returns:
49338032Speter**		a copy of 'list'.
49438032Speter*/
49538032Speter
49638032Speterchar **
49790792Sgshapirocopyplist(list, copycont, rpool)
49838032Speter	char **list;
49938032Speter	bool copycont;
50090792Sgshapiro	SM_RPOOL_T *rpool;
50138032Speter{
50238032Speter	register char **vp;
50338032Speter	register char **newvp;
50438032Speter
50538032Speter	for (vp = list; *vp != NULL; vp++)
50638032Speter		continue;
50738032Speter
50838032Speter	vp++;
50938032Speter
510168515Sgshapiro	newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof(*vp));
511168515Sgshapiro	memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp));
51238032Speter
51338032Speter	if (copycont)
51438032Speter	{
51538032Speter		for (vp = newvp; *vp != NULL; vp++)
51690792Sgshapiro			*vp = sm_rpool_strdup_x(rpool, *vp);
51738032Speter	}
51838032Speter
51964562Sgshapiro	return newvp;
52038032Speter}
521168515Sgshapiro
52290792Sgshapiro/*
52338032Speter**  COPYQUEUE -- copy address queue.
52438032Speter**
52590792Sgshapiro**	This routine is the equivalent of strdup for address queues;
52664562Sgshapiro**	addresses marked as QS_IS_DEAD() aren't copied
52738032Speter**
52838032Speter**	Parameters:
52938032Speter**		addr -- list of address structures to copy.
53090792Sgshapiro**		rpool -- resource pool from which to allocate storage
53138032Speter**
53238032Speter**	Returns:
53338032Speter**		a copy of 'addr'.
53438032Speter*/
53538032Speter
53638032SpeterADDRESS *
53790792Sgshapirocopyqueue(addr, rpool)
53838032Speter	ADDRESS *addr;
53990792Sgshapiro	SM_RPOOL_T *rpool;
54038032Speter{
54138032Speter	register ADDRESS *newaddr;
54238032Speter	ADDRESS *ret;
54338032Speter	register ADDRESS **tail = &ret;
54438032Speter
54538032Speter	while (addr != NULL)
54638032Speter	{
54764562Sgshapiro		if (!QS_IS_DEAD(addr->q_state))
54838032Speter		{
54990792Sgshapiro			newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
550168515Sgshapiro							sizeof(*newaddr));
55138032Speter			STRUCTCOPY(*addr, *newaddr);
55238032Speter			*tail = newaddr;
55338032Speter			tail = &newaddr->q_next;
55438032Speter		}
55538032Speter		addr = addr->q_next;
55638032Speter	}
55738032Speter	*tail = NULL;
55864562Sgshapiro
55938032Speter	return ret;
56038032Speter}
561168515Sgshapiro
56290792Sgshapiro/*
56364562Sgshapiro**  LOG_SENDMAIL_PID -- record sendmail pid and command line.
56464562Sgshapiro**
56564562Sgshapiro**	Parameters:
56664562Sgshapiro**		e -- the current envelope.
56764562Sgshapiro**
56864562Sgshapiro**	Returns:
56964562Sgshapiro**		none.
57064562Sgshapiro**
57164562Sgshapiro**	Side Effects:
57290792Sgshapiro**		writes pidfile, logs command line.
573132943Sgshapiro**		keeps file open and locked to prevent overwrite of active file
57464562Sgshapiro*/
57564562Sgshapiro
576132943Sgshapirostatic SM_FILE_T	*Pidf = NULL;
577132943Sgshapiro
57864562Sgshapirovoid
57964562Sgshapirolog_sendmail_pid(e)
58064562Sgshapiro	ENVELOPE *e;
58164562Sgshapiro{
58264562Sgshapiro	long sff;
58398121Sgshapiro	char pidpath[MAXPATHLEN];
58490792Sgshapiro	extern char *CommandLineArgs;
58564562Sgshapiro
58664562Sgshapiro	/* write the pid to the log file for posterity */
587132943Sgshapiro	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK;
58864562Sgshapiro	if (TrustedUid != 0 && RealUid == TrustedUid)
58964562Sgshapiro		sff |= SFF_OPENASROOT;
590168515Sgshapiro	expand(PidFile, pidpath, sizeof(pidpath), e);
591132943Sgshapiro	Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
592132943Sgshapiro	if (Pidf == NULL)
59364562Sgshapiro	{
594132943Sgshapiro		if (errno == EWOULDBLOCK)
595132943Sgshapiro			sm_syslog(LOG_ERR, NOQID,
596132943Sgshapiro				  "unable to write pid to %s: file in use by another process",
597132943Sgshapiro				  pidpath);
598132943Sgshapiro		else
599132943Sgshapiro			sm_syslog(LOG_ERR, NOQID,
600132943Sgshapiro				  "unable to write pid to %s: %s",
601132943Sgshapiro				  pidpath, sm_errstring(errno));
60264562Sgshapiro	}
60364562Sgshapiro	else
60464562Sgshapiro	{
605132943Sgshapiro		PidFilePid = getpid();
60664562Sgshapiro
60764562Sgshapiro		/* write the process id on line 1 */
608132943Sgshapiro		(void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n",
609132943Sgshapiro				     (long) PidFilePid);
61064562Sgshapiro
61164562Sgshapiro		/* line 2 contains all command line flags */
612132943Sgshapiro		(void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n",
61390792Sgshapiro				     CommandLineArgs);
61464562Sgshapiro
615132943Sgshapiro		/* flush */
616132943Sgshapiro		(void) sm_io_flush(Pidf, SM_TIME_DEFAULT);
617132943Sgshapiro
618132943Sgshapiro		/*
619132943Sgshapiro		**  Leave pid file open until process ends
620132943Sgshapiro		**  so it's not overwritten by another
621132943Sgshapiro		**  process.
622132943Sgshapiro		*/
62364562Sgshapiro	}
62490792Sgshapiro	if (LogLevel > 9)
62590792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
62664562Sgshapiro}
627132943Sgshapiro
62890792Sgshapiro/*
629132943Sgshapiro**  CLOSE_SENDMAIL_PID -- close sendmail pid file
630132943Sgshapiro**
631132943Sgshapiro**	Parameters:
632132943Sgshapiro**		none.
633132943Sgshapiro**
634132943Sgshapiro**	Returns:
635132943Sgshapiro**		none.
636132943Sgshapiro*/
637132943Sgshapiro
638132943Sgshapirovoid
639132943Sgshapiroclose_sendmail_pid()
640132943Sgshapiro{
641132943Sgshapiro	if (Pidf == NULL)
642132943Sgshapiro		return;
643132943Sgshapiro
644132943Sgshapiro	(void) sm_io_close(Pidf, SM_TIME_DEFAULT);
645132943Sgshapiro	Pidf = NULL;
646132943Sgshapiro}
647132943Sgshapiro
648132943Sgshapiro/*
64964562Sgshapiro**  SET_DELIVERY_MODE -- set and record the delivery mode
65064562Sgshapiro**
65164562Sgshapiro**	Parameters:
65264562Sgshapiro**		mode -- delivery mode
65364562Sgshapiro**		e -- the current envelope.
65464562Sgshapiro**
65564562Sgshapiro**	Returns:
65664562Sgshapiro**		none.
65764562Sgshapiro**
65864562Sgshapiro**	Side Effects:
65990792Sgshapiro**		sets {deliveryMode} macro
66064562Sgshapiro*/
66164562Sgshapiro
66264562Sgshapirovoid
66364562Sgshapiroset_delivery_mode(mode, e)
66464562Sgshapiro	int mode;
66564562Sgshapiro	ENVELOPE *e;
66664562Sgshapiro{
66764562Sgshapiro	char buf[2];
66864562Sgshapiro
66990792Sgshapiro	e->e_sendmode = (char) mode;
67090792Sgshapiro	buf[0] = (char) mode;
67164562Sgshapiro	buf[1] = '\0';
67290792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
67364562Sgshapiro}
674168515Sgshapiro
67590792Sgshapiro/*
67690792Sgshapiro**  SET_OP_MODE -- set and record the op mode
67790792Sgshapiro**
67890792Sgshapiro**	Parameters:
67990792Sgshapiro**		mode -- op mode
68090792Sgshapiro**		e -- the current envelope.
68190792Sgshapiro**
68290792Sgshapiro**	Returns:
68390792Sgshapiro**		none.
68490792Sgshapiro**
68590792Sgshapiro**	Side Effects:
68690792Sgshapiro**		sets {opMode} macro
68790792Sgshapiro*/
68890792Sgshapiro
68990792Sgshapirovoid
69090792Sgshapiroset_op_mode(mode)
69190792Sgshapiro	int mode;
69290792Sgshapiro{
69390792Sgshapiro	char buf[2];
69490792Sgshapiro	extern ENVELOPE BlankEnvelope;
69590792Sgshapiro
69690792Sgshapiro	OpMode = (char) mode;
69790792Sgshapiro	buf[0] = (char) mode;
69890792Sgshapiro	buf[1] = '\0';
69990792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
70090792Sgshapiro}
701168515Sgshapiro
70290792Sgshapiro/*
70338032Speter**  PRINTAV -- print argument vector.
70438032Speter**
70538032Speter**	Parameters:
706132943Sgshapiro**		fp -- output file pointer.
70738032Speter**		av -- argument vector.
70838032Speter**
70938032Speter**	Returns:
71038032Speter**		none.
71138032Speter**
71238032Speter**	Side Effects:
71338032Speter**		prints av.
71438032Speter*/
71538032Speter
71638032Spetervoid
717132943Sgshapiroprintav(fp, av)
718132943Sgshapiro	SM_FILE_T *fp;
719168515Sgshapiro	char **av;
72038032Speter{
72138032Speter	while (*av != NULL)
72238032Speter	{
72338032Speter		if (tTd(0, 44))
72490792Sgshapiro			sm_dprintf("\n\t%08lx=", (unsigned long) *av);
72538032Speter		else
726132943Sgshapiro			(void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
727168515Sgshapiro		if (tTd(0, 99))
728168515Sgshapiro			sm_dprintf("%s", str2prt(*av++));
729168515Sgshapiro		else
730168515Sgshapiro			xputs(fp, *av++);
73138032Speter	}
732132943Sgshapiro	(void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
73338032Speter}
734168515Sgshapiro
73590792Sgshapiro/*
73638032Speter**  XPUTS -- put string doing control escapes.
73738032Speter**
73838032Speter**	Parameters:
739132943Sgshapiro**		fp -- output file pointer.
74038032Speter**		s -- string to put.
74138032Speter**
74238032Speter**	Returns:
74338032Speter**		none.
74438032Speter**
74538032Speter**	Side Effects:
74638032Speter**		output to stdout
74738032Speter*/
74838032Speter
74938032Spetervoid
750132943Sgshapiroxputs(fp, s)
751132943Sgshapiro	SM_FILE_T *fp;
752168515Sgshapiro	const char *s;
75338032Speter{
754168515Sgshapiro	int c;
755168515Sgshapiro	struct metamac *mp;
75690792Sgshapiro	bool shiftout = false;
75738032Speter	extern struct metamac MetaMacros[];
75890792Sgshapiro	static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
75990792Sgshapiro		"@(#)$Debug: ANSI - enable reverse video in debug output $");
76038032Speter
76190792Sgshapiro	/*
76290792Sgshapiro	**  TermEscape is set here, rather than in main(),
76390792Sgshapiro	**  because ANSI mode can be turned on or off at any time
76490792Sgshapiro	**  if we are in -bt rule testing mode.
76590792Sgshapiro	*/
76690792Sgshapiro
76790792Sgshapiro	if (sm_debug_unknown(&DebugANSI))
76890792Sgshapiro	{
76990792Sgshapiro		if (sm_debug_active(&DebugANSI, 1))
77090792Sgshapiro		{
77190792Sgshapiro			TermEscape.te_rv_on = "\033[7m";
772168515Sgshapiro			TermEscape.te_normal = "\033[0m";
77390792Sgshapiro		}
77490792Sgshapiro		else
77590792Sgshapiro		{
77690792Sgshapiro			TermEscape.te_rv_on = "";
777168515Sgshapiro			TermEscape.te_normal = "";
77890792Sgshapiro		}
77990792Sgshapiro	}
78090792Sgshapiro
78138032Speter	if (s == NULL)
78238032Speter	{
783132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s",
784168515Sgshapiro				     TermEscape.te_rv_on, TermEscape.te_normal);
78538032Speter		return;
78638032Speter	}
78738032Speter	while ((c = (*s++ & 0377)) != '\0')
78838032Speter	{
78938032Speter		if (shiftout)
79038032Speter		{
791132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
792168515Sgshapiro					     TermEscape.te_normal);
79390792Sgshapiro			shiftout = false;
79438032Speter		}
795168515Sgshapiro		if (!isascii(c) && !tTd(84, 1))
79638032Speter		{
79738032Speter			if (c == MATCHREPL)
79838032Speter			{
799132943Sgshapiro				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
80090792Sgshapiro						     "%s$",
80190792Sgshapiro						     TermEscape.te_rv_on);
80290792Sgshapiro				shiftout = true;
80338032Speter				if (*s == '\0')
80438032Speter					continue;
80538032Speter				c = *s++ & 0377;
80638032Speter				goto printchar;
80738032Speter			}
80838032Speter			if (c == MACROEXPAND || c == MACRODEXPAND)
80938032Speter			{
810132943Sgshapiro				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
81190792Sgshapiro						     "%s$",
81290792Sgshapiro						     TermEscape.te_rv_on);
81338032Speter				if (c == MACRODEXPAND)
814132943Sgshapiro					(void) sm_io_putc(fp,
81590792Sgshapiro							  SM_TIME_DEFAULT, '&');
81690792Sgshapiro				shiftout = true;
81738032Speter				if (*s == '\0')
81838032Speter					continue;
81938032Speter				if (strchr("=~&?", *s) != NULL)
820132943Sgshapiro					(void) sm_io_putc(fp,
82190792Sgshapiro							  SM_TIME_DEFAULT,
82290792Sgshapiro							  *s++);
82338032Speter				if (bitset(0200, *s))
824132943Sgshapiro					(void) sm_io_fprintf(fp,
82590792Sgshapiro							     SM_TIME_DEFAULT,
82690792Sgshapiro							     "{%s}",
82790792Sgshapiro							     macname(bitidx(*s++)));
82838032Speter				else
829132943Sgshapiro					(void) sm_io_fprintf(fp,
83090792Sgshapiro							     SM_TIME_DEFAULT,
83190792Sgshapiro							     "%c",
83290792Sgshapiro							     *s++);
83338032Speter				continue;
83438032Speter			}
83538032Speter			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
83638032Speter			{
83790792Sgshapiro				if (bitidx(mp->metaval) == c)
83838032Speter				{
839132943Sgshapiro					(void) sm_io_fprintf(fp,
84090792Sgshapiro							     SM_TIME_DEFAULT,
84190792Sgshapiro							     "%s$%c",
84290792Sgshapiro							     TermEscape.te_rv_on,
84390792Sgshapiro							     mp->metaname);
84490792Sgshapiro					shiftout = true;
84538032Speter					break;
84638032Speter				}
84738032Speter			}
84838032Speter			if (c == MATCHCLASS || c == MATCHNCLASS)
84938032Speter			{
85038032Speter				if (bitset(0200, *s))
851132943Sgshapiro					(void) sm_io_fprintf(fp,
85290792Sgshapiro							     SM_TIME_DEFAULT,
85390792Sgshapiro							     "{%s}",
85490792Sgshapiro							     macname(bitidx(*s++)));
85538032Speter				else if (*s != '\0')
856132943Sgshapiro					(void) sm_io_fprintf(fp,
85790792Sgshapiro							     SM_TIME_DEFAULT,
85890792Sgshapiro							     "%c",
85990792Sgshapiro							     *s++);
86038032Speter			}
86138032Speter			if (mp->metaname != '\0')
86238032Speter				continue;
86338032Speter
86438032Speter			/* unrecognized meta character */
865132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-",
86690792Sgshapiro					     TermEscape.te_rv_on);
86790792Sgshapiro			shiftout = true;
86838032Speter			c &= 0177;
86938032Speter		}
87038032Speter  printchar:
871203004Sgshapiro		if (isascii(c) && isprint(c))
87238032Speter		{
873132943Sgshapiro			(void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
87438032Speter			continue;
87538032Speter		}
87638032Speter
87738032Speter		/* wasn't a meta-macro -- find another way to print it */
87838032Speter		switch (c)
87938032Speter		{
88038032Speter		  case '\n':
88138032Speter			c = 'n';
88238032Speter			break;
88338032Speter
88438032Speter		  case '\r':
88538032Speter			c = 'r';
88638032Speter			break;
88738032Speter
88838032Speter		  case '\t':
88938032Speter			c = 't';
89038032Speter			break;
89138032Speter		}
89238032Speter		if (!shiftout)
89338032Speter		{
894132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
89590792Sgshapiro					     TermEscape.te_rv_on);
89690792Sgshapiro			shiftout = true;
89738032Speter		}
898203004Sgshapiro		if (isascii(c) && isprint(c))
89938032Speter		{
900132943Sgshapiro			(void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\');
901132943Sgshapiro			(void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
90238032Speter		}
903168515Sgshapiro		else if (tTd(84, 2))
904168515Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c);
905168515Sgshapiro		else if (tTd(84, 1))
906168515Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c);
907168515Sgshapiro		else if (!isascii(c) && !tTd(84, 1))
90838032Speter		{
909132943Sgshapiro			(void) sm_io_putc(fp, SM_TIME_DEFAULT, '^');
910132943Sgshapiro			(void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100);
91138032Speter		}
91238032Speter	}
91338032Speter	if (shiftout)
914132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
915168515Sgshapiro				     TermEscape.te_normal);
916132943Sgshapiro	(void) sm_io_flush(fp, SM_TIME_DEFAULT);
91738032Speter}
918168515Sgshapiro
91990792Sgshapiro/*
92038032Speter**  MAKELOWER -- Translate a line into lower case
92138032Speter**
92238032Speter**	Parameters:
92338032Speter**		p -- the string to translate.  If NULL, return is
92438032Speter**			immediate.
92538032Speter**
92638032Speter**	Returns:
92738032Speter**		none.
92838032Speter**
92938032Speter**	Side Effects:
93038032Speter**		String pointed to by p is translated to lower case.
93138032Speter*/
93238032Speter
93338032Spetervoid
93438032Spetermakelower(p)
93538032Speter	register char *p;
93638032Speter{
93738032Speter	register char c;
93838032Speter
93938032Speter	if (p == NULL)
94038032Speter		return;
94138032Speter	for (; (c = *p) != '\0'; p++)
94238032Speter		if (isascii(c) && isupper(c))
94338032Speter			*p = tolower(c);
94438032Speter}
945168515Sgshapiro
94690792Sgshapiro/*
94738032Speter**  FIXCRLF -- fix <CR><LF> in line.
94838032Speter**
94938032Speter**	Looks for the <CR><LF> combination and turns it into the
95038032Speter**	UNIX canonical <NL> character.  It only takes one line,
95138032Speter**	i.e., it is assumed that the first <NL> found is the end
95238032Speter**	of the line.
95338032Speter**
95438032Speter**	Parameters:
95538032Speter**		line -- the line to fix.
95638032Speter**		stripnl -- if true, strip the newline also.
95738032Speter**
95838032Speter**	Returns:
95938032Speter**		none.
96038032Speter**
96138032Speter**	Side Effects:
96238032Speter**		line is changed in place.
96338032Speter*/
96438032Speter
96538032Spetervoid
96638032Speterfixcrlf(line, stripnl)
96738032Speter	char *line;
96838032Speter	bool stripnl;
96938032Speter{
97038032Speter	register char *p;
97138032Speter
97238032Speter	p = strchr(line, '\n');
97338032Speter	if (p == NULL)
97438032Speter		return;
97538032Speter	if (p > line && p[-1] == '\r')
97638032Speter		p--;
97738032Speter	if (!stripnl)
97838032Speter		*p++ = '\n';
97938032Speter	*p = '\0';
98038032Speter}
981168515Sgshapiro
98290792Sgshapiro/*
98338032Speter**  PUTLINE -- put a line like fputs obeying SMTP conventions
98438032Speter**
98538032Speter**	This routine always guarantees outputing a newline (or CRLF,
98638032Speter**	as appropriate) at the end of the string.
98738032Speter**
98838032Speter**	Parameters:
98938032Speter**		l -- line to put.
99038032Speter**		mci -- the mailer connection information.
99138032Speter**
99238032Speter**	Returns:
993157001Sgshapiro**		true iff line was written successfully
99438032Speter**
99538032Speter**	Side Effects:
99690792Sgshapiro**		output of l to mci->mci_out.
99738032Speter*/
99838032Speter
999157001Sgshapirobool
100038032Speterputline(l, mci)
100138032Speter	register char *l;
100238032Speter	register MCI *mci;
100338032Speter{
1004157001Sgshapiro	return putxline(l, strlen(l), mci, PXLF_MAPFROM);
100538032Speter}
1006168515Sgshapiro
100790792Sgshapiro/*
100838032Speter**  PUTXLINE -- putline with flags bits.
100938032Speter**
101038032Speter**	This routine always guarantees outputing a newline (or CRLF,
101138032Speter**	as appropriate) at the end of the string.
101238032Speter**
101338032Speter**	Parameters:
101438032Speter**		l -- line to put.
101538032Speter**		len -- the length of the line.
101638032Speter**		mci -- the mailer connection information.
101738032Speter**		pxflags -- flag bits:
101838032Speter**		    PXLF_MAPFROM -- map From_ to >From_.
101938032Speter**		    PXLF_STRIP8BIT -- strip 8th bit.
102038032Speter**		    PXLF_HEADER -- map bare newline in header to newline space.
102194334Sgshapiro**		    PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
1022168515Sgshapiro**		    PXLF_STRIPMQUOTE -- strip METAQUOTE bytes.
102338032Speter**
102438032Speter**	Returns:
1025157001Sgshapiro**		true iff line was written successfully
102638032Speter**
102738032Speter**	Side Effects:
102890792Sgshapiro**		output of l to mci->mci_out.
102938032Speter*/
103038032Speter
1031168515Sgshapiro
1032168515Sgshapiro#define PUTX(limit)							\
1033168515Sgshapiro	do								\
1034168515Sgshapiro	{								\
1035168515Sgshapiro		quotenext = false;					\
1036168515Sgshapiro		while (l < limit)					\
1037168515Sgshapiro		{							\
1038168515Sgshapiro			unsigned char c = (unsigned char) *l++;		\
1039168515Sgshapiro									\
1040168515Sgshapiro			if (bitset(PXLF_STRIPMQUOTE, pxflags) &&	\
1041168515Sgshapiro			    !quotenext && c == METAQUOTE)		\
1042168515Sgshapiro			{						\
1043168515Sgshapiro				quotenext = true;			\
1044168515Sgshapiro				continue;				\
1045168515Sgshapiro			}						\
1046168515Sgshapiro			quotenext = false;				\
1047168515Sgshapiro			if (strip8bit)					\
1048168515Sgshapiro				c &= 0177;				\
1049168515Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,	\
1050168515Sgshapiro				       c) == SM_IO_EOF)			\
1051168515Sgshapiro			{						\
1052168515Sgshapiro				dead = true;				\
1053168515Sgshapiro				break;					\
1054168515Sgshapiro			}						\
1055168515Sgshapiro			if (TrafficLogFile != NULL)			\
1056168515Sgshapiro				(void) sm_io_putc(TrafficLogFile,	\
1057168515Sgshapiro						  SM_TIME_DEFAULT,	\
1058168515Sgshapiro						  c);			\
1059168515Sgshapiro		}							\
1060168515Sgshapiro	} while (0)
1061168515Sgshapiro
1062157001Sgshapirobool
106338032Speterputxline(l, len, mci, pxflags)
106438032Speter	register char *l;
106538032Speter	size_t len;
106638032Speter	register MCI *mci;
106738032Speter	int pxflags;
106838032Speter{
106938032Speter	register char *p, *end;
1070168515Sgshapiro	int slop;
1071168515Sgshapiro	bool dead, quotenext, strip8bit;
107238032Speter
107338032Speter	/* strip out 0200 bits -- these can look like TELNET protocol */
1074168515Sgshapiro	strip8bit = bitset(MCIF_7BIT, mci->mci_flags) ||
1075168515Sgshapiro		    bitset(PXLF_STRIP8BIT, pxflags);
1076168515Sgshapiro	dead = false;
1077168515Sgshapiro	slop = 0;
107838032Speter
107938032Speter	end = l + len;
108038032Speter	do
108138032Speter	{
108294334Sgshapiro		bool noeol = false;
108394334Sgshapiro
108438032Speter		/* find the end of the line */
108538032Speter		p = memchr(l, '\n', end - l);
108638032Speter		if (p == NULL)
108794334Sgshapiro		{
108838032Speter			p = end;
108994334Sgshapiro			noeol = true;
109094334Sgshapiro		}
109138032Speter
109238032Speter		if (TrafficLogFile != NULL)
109390792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
109490792Sgshapiro					     "%05d >>> ", (int) CurrentPid);
109538032Speter
109638032Speter		/* check for line overflow */
109738032Speter		while (mci->mci_mailer->m_linelimit > 0 &&
109838032Speter		       (p - l + slop) > mci->mci_mailer->m_linelimit)
109938032Speter		{
110038032Speter			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
110138032Speter
110238032Speter			if (l[0] == '.' && slop == 0 &&
110338032Speter			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
110438032Speter			{
110590792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
110690792Sgshapiro					       '.') == SM_IO_EOF)
110790792Sgshapiro					dead = true;
110838032Speter				if (TrafficLogFile != NULL)
110990792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
111090792Sgshapiro							  SM_TIME_DEFAULT, '.');
111138032Speter			}
111238032Speter			else if (l[0] == 'F' && slop == 0 &&
111338032Speter				 bitset(PXLF_MAPFROM, pxflags) &&
111438032Speter				 strncmp(l, "From ", 5) == 0 &&
111538032Speter				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
111638032Speter			{
111790792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
111890792Sgshapiro					       '>') == SM_IO_EOF)
111990792Sgshapiro					dead = true;
112038032Speter				if (TrafficLogFile != NULL)
112190792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
112290792Sgshapiro							  SM_TIME_DEFAULT,
112390792Sgshapiro							  '>');
112438032Speter			}
112564562Sgshapiro			if (dead)
112664562Sgshapiro				break;
112764562Sgshapiro
1128168515Sgshapiro			PUTX(q);
112964562Sgshapiro			if (dead)
113064562Sgshapiro				break;
113164562Sgshapiro
1132168515Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1133168515Sgshapiro					'!') == SM_IO_EOF ||
113490792Sgshapiro			    sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1135168515Sgshapiro					mci->mci_mailer->m_eol) == SM_IO_EOF ||
1136168515Sgshapiro			    sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1137168515Sgshapiro					' ') == SM_IO_EOF)
113864562Sgshapiro			{
113990792Sgshapiro				dead = true;
114064562Sgshapiro				break;
114164562Sgshapiro			}
114238032Speter			if (TrafficLogFile != NULL)
114338032Speter			{
114490792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
114590792Sgshapiro						     SM_TIME_DEFAULT,
114690792Sgshapiro						     "!\n%05d >>>  ",
114790792Sgshapiro						     (int) CurrentPid);
114838032Speter			}
114938032Speter			slop = 1;
115038032Speter		}
115138032Speter
115264562Sgshapiro		if (dead)
115364562Sgshapiro			break;
115464562Sgshapiro
115538032Speter		/* output last part */
115638032Speter		if (l[0] == '.' && slop == 0 &&
1157173340Sgshapiro		    bitnset(M_XDOT, mci->mci_mailer->m_flags) &&
1158173340Sgshapiro		    !bitset(MCIF_INLONGLINE, mci->mci_flags))
115938032Speter		{
116090792Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
116190792Sgshapiro			    SM_IO_EOF)
1162157001Sgshapiro			{
1163157001Sgshapiro				dead = true;
116464562Sgshapiro				break;
116571345Sgshapiro			}
116638032Speter			if (TrafficLogFile != NULL)
116790792Sgshapiro				(void) sm_io_putc(TrafficLogFile,
116890792Sgshapiro						  SM_TIME_DEFAULT, '.');
116938032Speter		}
117038032Speter		else if (l[0] == 'F' && slop == 0 &&
117138032Speter			 bitset(PXLF_MAPFROM, pxflags) &&
117238032Speter			 strncmp(l, "From ", 5) == 0 &&
1173173340Sgshapiro			 bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
1174173340Sgshapiro			 !bitset(MCIF_INLONGLINE, mci->mci_flags))
117538032Speter		{
117690792Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
117790792Sgshapiro			    SM_IO_EOF)
1178157001Sgshapiro			{
1179157001Sgshapiro				dead = true;
118064562Sgshapiro				break;
118171345Sgshapiro			}
118238032Speter			if (TrafficLogFile != NULL)
118390792Sgshapiro				(void) sm_io_putc(TrafficLogFile,
118490792Sgshapiro						  SM_TIME_DEFAULT, '>');
118538032Speter		}
1186168515Sgshapiro		PUTX(p);
118764562Sgshapiro		if (dead)
118864562Sgshapiro			break;
118964562Sgshapiro
119038032Speter		if (TrafficLogFile != NULL)
119190792Sgshapiro			(void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
119290792Sgshapiro					  '\n');
1193173340Sgshapiro		if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol))
1194157001Sgshapiro		{
1195173340Sgshapiro			mci->mci_flags &= ~MCIF_INLONGLINE;
1196173340Sgshapiro			if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1197173340Sgshapiro					mci->mci_mailer->m_eol) == SM_IO_EOF)
1198173340Sgshapiro			{
1199173340Sgshapiro				dead = true;
1200173340Sgshapiro				break;
1201173340Sgshapiro			}
120271345Sgshapiro		}
1203173340Sgshapiro		else
1204173340Sgshapiro			mci->mci_flags |= MCIF_INLONGLINE;
1205173340Sgshapiro
120638032Speter		if (l < end && *l == '\n')
120738032Speter		{
120838032Speter			if (*++l != ' ' && *l != '\t' && *l != '\0' &&
120938032Speter			    bitset(PXLF_HEADER, pxflags))
121038032Speter			{
121190792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
121290792Sgshapiro					       ' ') == SM_IO_EOF)
1213157001Sgshapiro				{
1214157001Sgshapiro					dead = true;
121564562Sgshapiro					break;
121671345Sgshapiro				}
121790792Sgshapiro
121838032Speter				if (TrafficLogFile != NULL)
121990792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
122090792Sgshapiro							  SM_TIME_DEFAULT, ' ');
122138032Speter			}
122238032Speter		}
122390792Sgshapiro
122438032Speter	} while (l < end);
1225157001Sgshapiro	return !dead;
122638032Speter}
1227157001Sgshapiro
122890792Sgshapiro/*
122938032Speter**  XUNLINK -- unlink a file, doing logging as appropriate.
123038032Speter**
123138032Speter**	Parameters:
123238032Speter**		f -- name of file to unlink.
123338032Speter**
123438032Speter**	Returns:
123590792Sgshapiro**		return value of unlink()
123638032Speter**
123738032Speter**	Side Effects:
123838032Speter**		f is unlinked.
123938032Speter*/
124038032Speter
124190792Sgshapiroint
124238032Speterxunlink(f)
124338032Speter	char *f;
124438032Speter{
124538032Speter	register int i;
124690792Sgshapiro	int save_errno;
124738032Speter
124838032Speter	if (LogLevel > 98)
124990792Sgshapiro		sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
125038032Speter
125138032Speter	i = unlink(f);
125290792Sgshapiro	save_errno = errno;
125338032Speter	if (i < 0 && LogLevel > 97)
125490792Sgshapiro		sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
125564562Sgshapiro			  f, errno);
125690792Sgshapiro	if (i >= 0)
125790792Sgshapiro		SYNC_DIR(f, false);
125890792Sgshapiro	errno = save_errno;
125990792Sgshapiro	return i;
126038032Speter}
1261168515Sgshapiro
126290792Sgshapiro/*
126338032Speter**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
126438032Speter**
126538032Speter**	Parameters:
126638032Speter**		buf -- place to put the input line.
126738032Speter**		siz -- size of buf.
126838032Speter**		fp -- file to read from.
126938032Speter**		timeout -- the timeout before error occurs.
127038032Speter**		during -- what we are trying to read (for error messages).
127138032Speter**
127238032Speter**	Returns:
127390792Sgshapiro**		NULL on error (including timeout).  This may also leave
127438032Speter**			buf containing a null string.
127538032Speter**		buf otherwise.
127638032Speter*/
127738032Speter
127864562Sgshapiro
127938032Speterchar *
128038032Spetersfgets(buf, siz, fp, timeout, during)
128138032Speter	char *buf;
128238032Speter	int siz;
128390792Sgshapiro	SM_FILE_T *fp;
128438032Speter	time_t timeout;
128538032Speter	char *during;
128638032Speter{
128738032Speter	register char *p;
1288249729Sgshapiro	int save_errno, io_timeout, l;
128938032Speter
129090792Sgshapiro	SM_REQUIRE(siz > 0);
129190792Sgshapiro	SM_REQUIRE(buf != NULL);
129290792Sgshapiro
129338032Speter	if (fp == NULL)
129438032Speter	{
129538032Speter		buf[0] = '\0';
129690792Sgshapiro		errno = EBADF;
129738032Speter		return NULL;
129838032Speter	}
129938032Speter
130090792Sgshapiro	/* try to read */
1301249729Sgshapiro	l = -1;
130290792Sgshapiro	errno = 0;
130390792Sgshapiro
130490792Sgshapiro	/* convert the timeout to sm_io notation */
130590792Sgshapiro	io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
130690792Sgshapiro	while (!sm_io_eof(fp) && !sm_io_error(fp))
130738032Speter	{
130890792Sgshapiro		errno = 0;
1309249729Sgshapiro		l = sm_io_fgets(fp, io_timeout, buf, siz);
1310249729Sgshapiro		if (l < 0 && errno == EAGAIN)
131138032Speter		{
131290792Sgshapiro			/* The sm_io_fgets() call timedout */
131338032Speter			if (LogLevel > 1)
131438032Speter				sm_syslog(LOG_NOTICE, CurEnv->e_id,
131564562Sgshapiro					  "timeout waiting for input from %.100s during %s",
131690792Sgshapiro					  CURHOSTNAME,
131764562Sgshapiro					  during);
131838032Speter			buf[0] = '\0';
131938032Speter#if XDEBUG
132038032Speter			checkfd012(during);
1321363466Sgshapiro#endif
132238032Speter			if (TrafficLogFile != NULL)
132390792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
132490792Sgshapiro						     SM_TIME_DEFAULT,
132590792Sgshapiro						     "%05d <<< [TIMEOUT]\n",
132690792Sgshapiro						     (int) CurrentPid);
132790792Sgshapiro			errno = ETIMEDOUT;
132864562Sgshapiro			return NULL;
132938032Speter		}
1330249729Sgshapiro		if (l >= 0 || errno != EINTR)
133138032Speter			break;
133290792Sgshapiro		(void) sm_io_clearerr(fp);
133338032Speter	}
133443730Speter	save_errno = errno;
133538032Speter
133638032Speter	/* clean up the books and exit */
133738032Speter	LineNumber++;
1338249729Sgshapiro	if (l < 0)
133938032Speter	{
134038032Speter		buf[0] = '\0';
134138032Speter		if (TrafficLogFile != NULL)
134290792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
134390792Sgshapiro					     "%05d <<< [EOF]\n",
134490792Sgshapiro					     (int) CurrentPid);
134543730Speter		errno = save_errno;
134664562Sgshapiro		return NULL;
134738032Speter	}
134838032Speter	if (TrafficLogFile != NULL)
134990792Sgshapiro		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
135090792Sgshapiro				     "%05d <<< %s", (int) CurrentPid, buf);
135138032Speter	if (SevenBitInput)
135238032Speter	{
135338032Speter		for (p = buf; *p != '\0'; p++)
135438032Speter			*p &= ~0200;
135538032Speter	}
135638032Speter	else if (!HasEightBits)
135738032Speter	{
135838032Speter		for (p = buf; *p != '\0'; p++)
135938032Speter		{
136038032Speter			if (bitset(0200, *p))
136138032Speter			{
136290792Sgshapiro				HasEightBits = true;
136338032Speter				break;
136438032Speter			}
136538032Speter		}
136638032Speter	}
136764562Sgshapiro	return buf;
136838032Speter}
1369168515Sgshapiro
137090792Sgshapiro/*
137190792Sgshapiro**  FGETFOLDED -- like fgets, but knows about folded lines.
137238032Speter**
137338032Speter**	Parameters:
137438032Speter**		buf -- place to put result.
1375168515Sgshapiro**		np -- pointer to bytes available; will be updated with
1376168515Sgshapiro**			the actual buffer size (not number of bytes filled)
1377168515Sgshapiro**			on return.
137838032Speter**		f -- file to read from.
137938032Speter**
138038032Speter**	Returns:
138190792Sgshapiro**		input line(s) on success, NULL on error or SM_IO_EOF.
138238032Speter**		This will normally be buf -- unless the line is too
138390792Sgshapiro**			long, when it will be sm_malloc_x()ed.
138438032Speter**
138538032Speter**	Side Effects:
138638032Speter**		buf gets lines from f, with continuation lines (lines
138738032Speter**		with leading white space) appended.  CRLF's are mapped
138838032Speter**		into single newlines.  Any trailing NL is stripped.
1389363466Sgshapiro**		Increases LineNumber for each line.
139038032Speter*/
139138032Speter
139238032Speterchar *
1393168515Sgshapirofgetfolded(buf, np, f)
139438032Speter	char *buf;
1395168515Sgshapiro	int *np;
139690792Sgshapiro	SM_FILE_T *f;
139738032Speter{
139838032Speter	register char *p = buf;
139938032Speter	char *bp = buf;
140038032Speter	register int i;
1401168515Sgshapiro	int n;
140238032Speter
1403168515Sgshapiro	SM_REQUIRE(np != NULL);
1404168515Sgshapiro	n = *np;
140590792Sgshapiro	SM_REQUIRE(n > 0);
140690792Sgshapiro	SM_REQUIRE(buf != NULL);
140790792Sgshapiro	if (f == NULL)
140890792Sgshapiro	{
140990792Sgshapiro		buf[0] = '\0';
141090792Sgshapiro		errno = EBADF;
141190792Sgshapiro		return NULL;
141290792Sgshapiro	}
141390792Sgshapiro
141438032Speter	n--;
141590792Sgshapiro	while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
141638032Speter	{
141738032Speter		if (i == '\r')
141838032Speter		{
141990792Sgshapiro			i = sm_io_getc(f, SM_TIME_DEFAULT);
142038032Speter			if (i != '\n')
142138032Speter			{
142290792Sgshapiro				if (i != SM_IO_EOF)
142390792Sgshapiro					(void) sm_io_ungetc(f, SM_TIME_DEFAULT,
142490792Sgshapiro							    i);
142538032Speter				i = '\r';
142638032Speter			}
142738032Speter		}
142838032Speter		if (--n <= 0)
142938032Speter		{
143038032Speter			/* allocate new space */
143138032Speter			char *nbp;
143238032Speter			int nn;
143338032Speter
143438032Speter			nn = (p - bp);
143538032Speter			if (nn < MEMCHUNKSIZE)
143638032Speter				nn *= 2;
143738032Speter			else
143838032Speter				nn += MEMCHUNKSIZE;
143990792Sgshapiro			nbp = sm_malloc_x(nn);
144064562Sgshapiro			memmove(nbp, bp, p - bp);
144138032Speter			p = &nbp[p - bp];
144238032Speter			if (bp != buf)
144377349Sgshapiro				sm_free(bp);
144438032Speter			bp = nbp;
144538032Speter			n = nn - (p - bp);
1446168515Sgshapiro			*np = nn;
144738032Speter		}
144838032Speter		*p++ = i;
144938032Speter		if (i == '\n')
145038032Speter		{
145138032Speter			LineNumber++;
145290792Sgshapiro			i = sm_io_getc(f, SM_TIME_DEFAULT);
145390792Sgshapiro			if (i != SM_IO_EOF)
145490792Sgshapiro				(void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
145538032Speter			if (i != ' ' && i != '\t')
145638032Speter				break;
145738032Speter		}
145838032Speter	}
145938032Speter	if (p == bp)
146064562Sgshapiro		return NULL;
146138032Speter	if (p[-1] == '\n')
146238032Speter		p--;
146338032Speter	*p = '\0';
146464562Sgshapiro	return bp;
146538032Speter}
1466168515Sgshapiro
146790792Sgshapiro/*
146838032Speter**  CURTIME -- return current time.
146938032Speter**
147038032Speter**	Parameters:
147138032Speter**		none.
147238032Speter**
147338032Speter**	Returns:
147438032Speter**		the current time.
147538032Speter*/
147638032Speter
147738032Spetertime_t
147838032Spetercurtime()
147938032Speter{
148038032Speter	auto time_t t;
148138032Speter
148238032Speter	(void) time(&t);
148364562Sgshapiro	return t;
148438032Speter}
1485168515Sgshapiro
148690792Sgshapiro/*
148738032Speter**  ATOBOOL -- convert a string representation to boolean.
148838032Speter**
148990792Sgshapiro**	Defaults to false
149038032Speter**
149138032Speter**	Parameters:
149290792Sgshapiro**		s -- string to convert.  Takes "tTyY", empty, and NULL as true,
149338032Speter**			others as false.
149438032Speter**
149538032Speter**	Returns:
149638032Speter**		A boolean representation of the string.
149738032Speter*/
149838032Speter
149938032Speterbool
150038032Speteratobool(s)
150138032Speter	register char *s;
150238032Speter{
150338032Speter	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
150490792Sgshapiro		return true;
150590792Sgshapiro	return false;
150638032Speter}
1507168515Sgshapiro
150890792Sgshapiro/*
150938032Speter**  ATOOCT -- convert a string representation to octal.
151038032Speter**
151138032Speter**	Parameters:
151238032Speter**		s -- string to convert.
151338032Speter**
151438032Speter**	Returns:
151538032Speter**		An integer representing the string interpreted as an
151638032Speter**		octal number.
151738032Speter*/
151838032Speter
151938032Speterint
152038032Speteratooct(s)
152138032Speter	register char *s;
152238032Speter{
152338032Speter	register int i = 0;
152438032Speter
152538032Speter	while (*s >= '0' && *s <= '7')
152638032Speter		i = (i << 3) | (*s++ - '0');
152764562Sgshapiro	return i;
152838032Speter}
1529168515Sgshapiro
153090792Sgshapiro/*
153138032Speter**  BITINTERSECT -- tell if two bitmaps intersect
153238032Speter**
153338032Speter**	Parameters:
153438032Speter**		a, b -- the bitmaps in question
153538032Speter**
153638032Speter**	Returns:
153790792Sgshapiro**		true if they have a non-null intersection
153890792Sgshapiro**		false otherwise
153938032Speter*/
154038032Speter
154138032Speterbool
154238032Speterbitintersect(a, b)
154364562Sgshapiro	BITMAP256 a;
154464562Sgshapiro	BITMAP256 b;
154538032Speter{
154638032Speter	int i;
154738032Speter
1548168515Sgshapiro	for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
154971345Sgshapiro	{
155038032Speter		if ((a[i] & b[i]) != 0)
155190792Sgshapiro			return true;
155271345Sgshapiro	}
155390792Sgshapiro	return false;
155438032Speter}
1555168515Sgshapiro
155690792Sgshapiro/*
155738032Speter**  BITZEROP -- tell if a bitmap is all zero
155838032Speter**
155938032Speter**	Parameters:
156038032Speter**		map -- the bit map to check
156138032Speter**
156238032Speter**	Returns:
156390792Sgshapiro**		true if map is all zero.
156490792Sgshapiro**		false if there are any bits set in map.
156538032Speter*/
156638032Speter
156738032Speterbool
156838032Speterbitzerop(map)
156964562Sgshapiro	BITMAP256 map;
157038032Speter{
157138032Speter	int i;
157238032Speter
1573168515Sgshapiro	for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
157471345Sgshapiro	{
157538032Speter		if (map[i] != 0)
157690792Sgshapiro			return false;
157771345Sgshapiro	}
157890792Sgshapiro	return true;
157938032Speter}
1580168515Sgshapiro
158190792Sgshapiro/*
158238032Speter**  STRCONTAINEDIN -- tell if one string is contained in another
158338032Speter**
158438032Speter**	Parameters:
158590792Sgshapiro**		icase -- ignore case?
158638032Speter**		a -- possible substring.
158738032Speter**		b -- possible superstring.
158838032Speter**
158938032Speter**	Returns:
159090792Sgshapiro**		true if a is contained in b (case insensitive).
159190792Sgshapiro**		false otherwise.
159238032Speter*/
159338032Speter
159438032Speterbool
159590792Sgshapirostrcontainedin(icase, a, b)
159690792Sgshapiro	bool icase;
159738032Speter	register char *a;
159838032Speter	register char *b;
159938032Speter{
160038032Speter	int la;
160138032Speter	int lb;
160238032Speter	int c;
160338032Speter
160438032Speter	la = strlen(a);
160538032Speter	lb = strlen(b);
160638032Speter	c = *a;
160790792Sgshapiro	if (icase && isascii(c) && isupper(c))
160838032Speter		c = tolower(c);
160938032Speter	for (; lb-- >= la; b++)
161038032Speter	{
161190792Sgshapiro		if (icase)
161290792Sgshapiro		{
161390792Sgshapiro			if (*b != c &&
161490792Sgshapiro			    isascii(*b) && isupper(*b) && tolower(*b) != c)
161590792Sgshapiro				continue;
161690792Sgshapiro			if (sm_strncasecmp(a, b, la) == 0)
161790792Sgshapiro				return true;
161890792Sgshapiro		}
161990792Sgshapiro		else
162090792Sgshapiro		{
162190792Sgshapiro			if (*b != c)
162290792Sgshapiro				continue;
162390792Sgshapiro			if (strncmp(a, b, la) == 0)
162490792Sgshapiro				return true;
162590792Sgshapiro		}
162638032Speter	}
162790792Sgshapiro	return false;
162838032Speter}
1629168515Sgshapiro
163090792Sgshapiro/*
163138032Speter**  CHECKFD012 -- check low numbered file descriptors
163238032Speter**
163338032Speter**	File descriptors 0, 1, and 2 should be open at all times.
163438032Speter**	This routine verifies that, and fixes it if not true.
163538032Speter**
163638032Speter**	Parameters:
163738032Speter**		where -- a tag printed if the assertion failed
163838032Speter**
163938032Speter**	Returns:
164038032Speter**		none
164138032Speter*/
164238032Speter
164338032Spetervoid
164438032Spetercheckfd012(where)
164538032Speter	char *where;
164638032Speter{
164738032Speter#if XDEBUG
164838032Speter	register int i;
164938032Speter
165038032Speter	for (i = 0; i < 3; i++)
165138032Speter		fill_fd(i, where);
165238032Speter#endif /* XDEBUG */
165338032Speter}
1654168515Sgshapiro
165590792Sgshapiro/*
165638032Speter**  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
165738032Speter**
165838032Speter**	Parameters:
165938032Speter**		fd -- file descriptor to check.
166038032Speter**		where -- tag to print on failure.
166138032Speter**
166238032Speter**	Returns:
166338032Speter**		none.
166438032Speter*/
166538032Speter
166638032Spetervoid
166738032Spetercheckfdopen(fd, where)
166838032Speter	int fd;
166938032Speter	char *where;
167038032Speter{
167138032Speter#if XDEBUG
167238032Speter	struct stat st;
167338032Speter
167438032Speter	if (fstat(fd, &st) < 0 && errno == EBADF)
167538032Speter	{
167638032Speter		syserr("checkfdopen(%d): %s not open as expected!", fd, where);
167790792Sgshapiro		printopenfds(true);
167838032Speter	}
167964562Sgshapiro#endif /* XDEBUG */
168038032Speter}
1681168515Sgshapiro
168290792Sgshapiro/*
168338032Speter**  CHECKFDS -- check for new or missing file descriptors
168438032Speter**
168538032Speter**	Parameters:
168638032Speter**		where -- tag for printing.  If null, take a base line.
168738032Speter**
168838032Speter**	Returns:
168938032Speter**		none
169038032Speter**
169138032Speter**	Side Effects:
169238032Speter**		If where is set, shows changes since the last call.
169338032Speter*/
169438032Speter
169538032Spetervoid
169638032Spetercheckfds(where)
169738032Speter	char *where;
169838032Speter{
169938032Speter	int maxfd;
170038032Speter	register int fd;
170190792Sgshapiro	bool printhdr = true;
170238032Speter	int save_errno = errno;
170364562Sgshapiro	static BITMAP256 baseline;
170438032Speter	extern int DtableSize;
170538032Speter
170671345Sgshapiro	if (DtableSize > BITMAPBITS)
170771345Sgshapiro		maxfd = BITMAPBITS;
170838032Speter	else
170938032Speter		maxfd = DtableSize;
171038032Speter	if (where == NULL)
171138032Speter		clrbitmap(baseline);
171238032Speter
171338032Speter	for (fd = 0; fd < maxfd; fd++)
171438032Speter	{
171538032Speter		struct stat stbuf;
171638032Speter
171738032Speter		if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
171838032Speter		{
171938032Speter			if (!bitnset(fd, baseline))
172038032Speter				continue;
172138032Speter			clrbitn(fd, baseline);
172238032Speter		}
172338032Speter		else if (!bitnset(fd, baseline))
172438032Speter			setbitn(fd, baseline);
172538032Speter		else
172638032Speter			continue;
172738032Speter
172838032Speter		/* file state has changed */
172938032Speter		if (where == NULL)
173038032Speter			continue;
173138032Speter		if (printhdr)
173238032Speter		{
173338032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
173464562Sgshapiro				  "%s: changed fds:",
173564562Sgshapiro				  where);
173690792Sgshapiro			printhdr = false;
173738032Speter		}
173890792Sgshapiro		dumpfd(fd, true, true);
173938032Speter	}
174038032Speter	errno = save_errno;
174138032Speter}
1742168515Sgshapiro
174390792Sgshapiro/*
174438032Speter**  PRINTOPENFDS -- print the open file descriptors (for debugging)
174538032Speter**
174638032Speter**	Parameters:
174738032Speter**		logit -- if set, send output to syslog; otherwise
174838032Speter**			print for debugging.
174938032Speter**
175038032Speter**	Returns:
175138032Speter**		none.
175238032Speter*/
175338032Speter
175464562Sgshapiro#if NETINET || NETINET6
175564562Sgshapiro# include <arpa/inet.h>
1756363466Sgshapiro#endif
175738032Speter
175838032Spetervoid
175938032Speterprintopenfds(logit)
176038032Speter	bool logit;
176138032Speter{
176238032Speter	register int fd;
176338032Speter	extern int DtableSize;
176438032Speter
176538032Speter	for (fd = 0; fd < DtableSize; fd++)
176690792Sgshapiro		dumpfd(fd, false, logit);
176738032Speter}
1768168515Sgshapiro
176990792Sgshapiro/*
177038032Speter**  DUMPFD -- dump a file descriptor
177138032Speter**
177238032Speter**	Parameters:
177338032Speter**		fd -- the file descriptor to dump.
177438032Speter**		printclosed -- if set, print a notification even if
177538032Speter**			it is closed; otherwise print nothing.
1776132943Sgshapiro**		logit -- if set, use sm_syslog instead of sm_dprintf()
177790792Sgshapiro**
177890792Sgshapiro**	Returns:
177990792Sgshapiro**		none.
178038032Speter*/
178138032Speter
178238032Spetervoid
178338032Speterdumpfd(fd, printclosed, logit)
178438032Speter	int fd;
178538032Speter	bool printclosed;
178638032Speter	bool logit;
178738032Speter{
178838032Speter	register char *p;
178938032Speter	char *hp;
179038032Speter#ifdef S_IFSOCK
179138032Speter	SOCKADDR sa;
1792363466Sgshapiro#endif
179338032Speter	auto SOCKADDR_LEN_T slen;
179438032Speter	int i;
179538032Speter#if STAT64 > 0
179638032Speter	struct stat64 st;
1797363466Sgshapiro#else
179838032Speter	struct stat st;
1799363466Sgshapiro#endif
180038032Speter	char buf[200];
180138032Speter
180238032Speter	p = buf;
180390792Sgshapiro	(void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
180438032Speter	p += strlen(p);
180538032Speter
180638032Speter	if (
180738032Speter#if STAT64 > 0
180838032Speter	    fstat64(fd, &st)
1809363466Sgshapiro#else
181038032Speter	    fstat(fd, &st)
1811363466Sgshapiro#endif
181238032Speter	    < 0)
181338032Speter	{
181438032Speter		if (errno != EBADF)
181538032Speter		{
181690792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p),
181790792Sgshapiro				"CANNOT STAT (%s)",
181890792Sgshapiro				sm_errstring(errno));
181938032Speter			goto printit;
182038032Speter		}
182138032Speter		else if (printclosed)
182238032Speter		{
182390792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
182438032Speter			goto printit;
182538032Speter		}
182638032Speter		return;
182738032Speter	}
182838032Speter
182994334Sgshapiro	i = fcntl(fd, F_GETFL, 0);
183038032Speter	if (i != -1)
183138032Speter	{
183290792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
183338032Speter		p += strlen(p);
183438032Speter	}
183538032Speter
183690792Sgshapiro	(void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
1837285229Sgshapiro			(unsigned int) st.st_mode);
183838032Speter	p += strlen(p);
183938032Speter	switch (st.st_mode & S_IFMT)
184038032Speter	{
184138032Speter#ifdef S_IFSOCK
184238032Speter	  case S_IFSOCK:
184390792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
184438032Speter		p += strlen(p);
1845168515Sgshapiro		memset(&sa, '\0', sizeof(sa));
1846168515Sgshapiro		slen = sizeof(sa);
184738032Speter		if (getsockname(fd, &sa.sa, &slen) < 0)
184890792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
184990792Sgshapiro				 sm_errstring(errno));
185038032Speter		else
185138032Speter		{
185238032Speter			hp = hostnamebyanyaddr(&sa);
185364562Sgshapiro			if (hp == NULL)
185464562Sgshapiro			{
185564562Sgshapiro				/* EMPTY */
185664562Sgshapiro				/* do nothing */
185764562Sgshapiro			}
185864562Sgshapiro# if NETINET
185964562Sgshapiro			else if (sa.sa.sa_family == AF_INET)
186090792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
186190792Sgshapiro					"%s/%d", hp, ntohs(sa.sin.sin_port));
1862363466Sgshapiro# endif
186364562Sgshapiro# if NETINET6
186464562Sgshapiro			else if (sa.sa.sa_family == AF_INET6)
186590792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
186690792Sgshapiro					"%s/%d", hp, ntohs(sa.sin6.sin6_port));
1867363466Sgshapiro# endif
186838032Speter			else
186990792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
187090792Sgshapiro					"%s", hp);
187138032Speter		}
187238032Speter		p += strlen(p);
187390792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "->");
187438032Speter		p += strlen(p);
1875168515Sgshapiro		slen = sizeof(sa);
187638032Speter		if (getpeername(fd, &sa.sa, &slen) < 0)
187790792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
187890792Sgshapiro					sm_errstring(errno));
187938032Speter		else
188038032Speter		{
188138032Speter			hp = hostnamebyanyaddr(&sa);
188264562Sgshapiro			if (hp == NULL)
188364562Sgshapiro			{
188464562Sgshapiro				/* EMPTY */
188564562Sgshapiro				/* do nothing */
188664562Sgshapiro			}
188764562Sgshapiro# if NETINET
188864562Sgshapiro			else if (sa.sa.sa_family == AF_INET)
188990792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
189090792Sgshapiro					"%s/%d", hp, ntohs(sa.sin.sin_port));
1891363466Sgshapiro# endif
189264562Sgshapiro# if NETINET6
189364562Sgshapiro			else if (sa.sa.sa_family == AF_INET6)
189490792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
189590792Sgshapiro					"%s/%d", hp, ntohs(sa.sin6.sin6_port));
1896363466Sgshapiro# endif
189738032Speter			else
189890792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
189990792Sgshapiro					"%s", hp);
190038032Speter		}
190138032Speter		break;
190264562Sgshapiro#endif /* S_IFSOCK */
190338032Speter
190438032Speter	  case S_IFCHR:
190590792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
190638032Speter		p += strlen(p);
190738032Speter		goto defprint;
190838032Speter
190990792Sgshapiro#ifdef S_IFBLK
191038032Speter	  case S_IFBLK:
191190792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
191238032Speter		p += strlen(p);
191338032Speter		goto defprint;
1914363466Sgshapiro#endif
191538032Speter
191638032Speter#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
191738032Speter	  case S_IFIFO:
191890792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
191938032Speter		p += strlen(p);
192038032Speter		goto defprint;
1921363466Sgshapiro#endif
192238032Speter
192338032Speter#ifdef S_IFDIR
192438032Speter	  case S_IFDIR:
192590792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
192638032Speter		p += strlen(p);
192738032Speter		goto defprint;
1928363466Sgshapiro#endif
192938032Speter
193038032Speter#ifdef S_IFLNK
193138032Speter	  case S_IFLNK:
193290792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
193338032Speter		p += strlen(p);
193438032Speter		goto defprint;
1935363466Sgshapiro#endif
193638032Speter
193738032Speter	  default:
193838032Speterdefprint:
193990792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p),
1940285229Sgshapiro			 "dev=%ld/%ld, ino=%llu, nlink=%d, u/gid=%ld/%ld, ",
1941285229Sgshapiro			 (long) major(st.st_dev), (long) minor(st.st_dev),
194290792Sgshapiro			 (ULONGLONG_T) st.st_ino,
1943285229Sgshapiro			 (int) st.st_nlink, (long) st.st_uid,
1944285229Sgshapiro			 (long) st.st_gid);
194590792Sgshapiro		p += strlen(p);
194690792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
194790792Sgshapiro			 (ULONGLONG_T) st.st_size);
194838032Speter		break;
194938032Speter	}
195038032Speter
195138032Speterprintit:
195238032Speter	if (logit)
195338032Speter		sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
195464562Sgshapiro			  "%.800s", buf);
195538032Speter	else
1956132943Sgshapiro		sm_dprintf("%s\n", buf);
195738032Speter}
1958168515Sgshapiro
195990792Sgshapiro/*
196038032Speter**  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
196138032Speter**
196238032Speter**	Parameters:
196338032Speter**		host -- the host to shorten (stripped in place).
196438032Speter**
196538032Speter**	Returns:
196690792Sgshapiro**		place where string was truncated, NULL if not truncated.
196738032Speter*/
196838032Speter
196973188Sgshapirochar *
197038032Spetershorten_hostname(host)
197138032Speter	char host[];
197238032Speter{
197338032Speter	register char *p;
197438032Speter	char *mydom;
197538032Speter	int i;
197690792Sgshapiro	bool canon = false;
197738032Speter
197838032Speter	/* strip off final dot */
197990792Sgshapiro	i = strlen(host);
198090792Sgshapiro	p = &host[(i == 0) ? 0 : i - 1];
198138032Speter	if (*p == '.')
198238032Speter	{
198338032Speter		*p = '\0';
198490792Sgshapiro		canon = true;
198538032Speter	}
198638032Speter
198738032Speter	/* see if there is any domain at all -- if not, we are done */
198838032Speter	p = strchr(host, '.');
198938032Speter	if (p == NULL)
199073188Sgshapiro		return NULL;
199138032Speter
199238032Speter	/* yes, we have a domain -- see if it looks like us */
199338032Speter	mydom = macvalue('m', CurEnv);
199438032Speter	if (mydom == NULL)
199538032Speter		mydom = "";
199638032Speter	i = strlen(++p);
199790792Sgshapiro	if ((canon ? sm_strcasecmp(p, mydom)
199890792Sgshapiro		   : sm_strncasecmp(p, mydom, i)) == 0 &&
199990792Sgshapiro			(mydom[i] == '.' || mydom[i] == '\0'))
200073188Sgshapiro	{
200138032Speter		*--p = '\0';
200273188Sgshapiro		return p;
200373188Sgshapiro	}
200473188Sgshapiro	return NULL;
200538032Speter}
2006168515Sgshapiro
200790792Sgshapiro/*
200838032Speter**  PROG_OPEN -- open a program for reading
200938032Speter**
201038032Speter**	Parameters:
201138032Speter**		argv -- the argument list.
201238032Speter**		pfd -- pointer to a place to store the file descriptor.
201338032Speter**		e -- the current envelope.
201438032Speter**
201538032Speter**	Returns:
201638032Speter**		pid of the process -- -1 if it failed.
201738032Speter*/
201838032Speter
201977349Sgshapiropid_t
202038032Speterprog_open(argv, pfd, e)
202138032Speter	char **argv;
202238032Speter	int *pfd;
202338032Speter	ENVELOPE *e;
202438032Speter{
202577349Sgshapiro	pid_t pid;
202664562Sgshapiro	int save_errno;
202790792Sgshapiro	int sff;
202890792Sgshapiro	int ret;
202938032Speter	int fdv[2];
203038032Speter	char *p, *q;
203198121Sgshapiro	char buf[MAXPATHLEN];
203238032Speter	extern int DtableSize;
203338032Speter
203438032Speter	if (pipe(fdv) < 0)
203538032Speter	{
203638032Speter		syserr("%s: cannot create pipe for stdout", argv[0]);
203738032Speter		return -1;
203838032Speter	}
203938032Speter	pid = fork();
204038032Speter	if (pid < 0)
204138032Speter	{
204238032Speter		syserr("%s: cannot fork", argv[0]);
204364562Sgshapiro		(void) close(fdv[0]);
204464562Sgshapiro		(void) close(fdv[1]);
204538032Speter		return -1;
204638032Speter	}
204738032Speter	if (pid > 0)
204838032Speter	{
204938032Speter		/* parent */
205064562Sgshapiro		(void) close(fdv[1]);
205138032Speter		*pfd = fdv[0];
205238032Speter		return pid;
205338032Speter	}
205438032Speter
205577349Sgshapiro	/* Reset global flags */
205677349Sgshapiro	RestartRequest = NULL;
205790792Sgshapiro	RestartWorkGroup = false;
205877349Sgshapiro	ShutdownRequest = NULL;
205977349Sgshapiro	PendingSignal = 0;
206090792Sgshapiro	CurrentPid = getpid();
206177349Sgshapiro
206290792Sgshapiro	/*
206390792Sgshapiro	**  Initialize exception stack and default exception
206490792Sgshapiro	**  handler for child process.
206590792Sgshapiro	*/
206690792Sgshapiro
206790792Sgshapiro	sm_exc_newthread(fatal_error);
206890792Sgshapiro
206990792Sgshapiro	/* child -- close stdin */
207090792Sgshapiro	(void) close(0);
207190792Sgshapiro
207238032Speter	/* stdout goes back to parent */
207364562Sgshapiro	(void) close(fdv[0]);
207438032Speter	if (dup2(fdv[1], 1) < 0)
207538032Speter	{
207638032Speter		syserr("%s: cannot dup2 for stdout", argv[0]);
207738032Speter		_exit(EX_OSERR);
207838032Speter	}
207964562Sgshapiro	(void) close(fdv[1]);
208038032Speter
208138032Speter	/* stderr goes to transcript if available */
208238032Speter	if (e->e_xfp != NULL)
208338032Speter	{
208464562Sgshapiro		int xfd;
208564562Sgshapiro
208690792Sgshapiro		xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
208764562Sgshapiro		if (xfd >= 0 && dup2(xfd, 2) < 0)
208838032Speter		{
208938032Speter			syserr("%s: cannot dup2 for stderr", argv[0]);
209038032Speter			_exit(EX_OSERR);
209138032Speter		}
209238032Speter	}
209338032Speter
209438032Speter	/* this process has no right to the queue file */
209538032Speter	if (e->e_lockfp != NULL)
2096159609Sgshapiro	{
2097159609Sgshapiro		int fd;
209838032Speter
2099159609Sgshapiro		fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
2100159609Sgshapiro		if (fd >= 0)
2101159609Sgshapiro			(void) close(fd);
2102159609Sgshapiro		else
2103159609Sgshapiro			syserr("%s: lockfp does not have a fd", argv[0]);
2104159609Sgshapiro	}
2105159609Sgshapiro
210664562Sgshapiro	/* chroot to the program mailer directory, if defined */
210764562Sgshapiro	if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
210864562Sgshapiro	{
2109168515Sgshapiro		expand(ProgMailer->m_rootdir, buf, sizeof(buf), e);
211064562Sgshapiro		if (chroot(buf) < 0)
211164562Sgshapiro		{
211264562Sgshapiro			syserr("prog_open: cannot chroot(%s)", buf);
211364562Sgshapiro			exit(EX_TEMPFAIL);
211464562Sgshapiro		}
211564562Sgshapiro		if (chdir("/") < 0)
211664562Sgshapiro		{
211764562Sgshapiro			syserr("prog_open: cannot chdir(/)");
211864562Sgshapiro			exit(EX_TEMPFAIL);
211964562Sgshapiro		}
212064562Sgshapiro	}
212164562Sgshapiro
212238032Speter	/* run as default user */
212338032Speter	endpwent();
212490792Sgshapiro	sm_mbdb_terminate();
2125157001Sgshapiro#if _FFR_MEMSTAT
2126157001Sgshapiro	(void) sm_memstat_close();
2127363466Sgshapiro#endif
212838032Speter	if (setgid(DefGid) < 0 && geteuid() == 0)
212964562Sgshapiro	{
213038032Speter		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
213164562Sgshapiro		exit(EX_TEMPFAIL);
213264562Sgshapiro	}
213338032Speter	if (setuid(DefUid) < 0 && geteuid() == 0)
213464562Sgshapiro	{
213538032Speter		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
213664562Sgshapiro		exit(EX_TEMPFAIL);
213764562Sgshapiro	}
213838032Speter
213938032Speter	/* run in some directory */
214038032Speter	if (ProgMailer != NULL)
214138032Speter		p = ProgMailer->m_execdir;
214238032Speter	else
214338032Speter		p = NULL;
214438032Speter	for (; p != NULL; p = q)
214538032Speter	{
214638032Speter		q = strchr(p, ':');
214738032Speter		if (q != NULL)
214838032Speter			*q = '\0';
2149168515Sgshapiro		expand(p, buf, sizeof(buf), e);
215038032Speter		if (q != NULL)
215138032Speter			*q++ = ':';
215238032Speter		if (buf[0] != '\0' && chdir(buf) >= 0)
215338032Speter			break;
215438032Speter	}
215538032Speter	if (p == NULL)
215638032Speter	{
215738032Speter		/* backup directories */
215838032Speter		if (chdir("/tmp") < 0)
215938032Speter			(void) chdir("/");
216038032Speter	}
216138032Speter
216290792Sgshapiro	/* Check safety of program to be run */
216390792Sgshapiro	sff = SFF_ROOTOK|SFF_EXECOK;
216490792Sgshapiro	if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
216590792Sgshapiro		sff |= SFF_NOGWFILES|SFF_NOWWFILES;
216690792Sgshapiro	if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
216790792Sgshapiro		sff |= SFF_NOPATHCHECK;
216890792Sgshapiro	else
216990792Sgshapiro		sff |= SFF_SAFEDIRPATH;
217090792Sgshapiro	ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
217190792Sgshapiro	if (ret != 0)
217290792Sgshapiro		sm_syslog(LOG_INFO, e->e_id,
217390792Sgshapiro			  "Warning: prog_open: program %s unsafe: %s",
217490792Sgshapiro			  argv[0], sm_errstring(ret));
217590792Sgshapiro
217638032Speter	/* arrange for all the files to be closed */
2177132943Sgshapiro	sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
217838032Speter
217938032Speter	/* now exec the process */
218064562Sgshapiro	(void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
218138032Speter
218238032Speter	/* woops!  failed */
218364562Sgshapiro	save_errno = errno;
218438032Speter	syserr("%s: cannot exec", argv[0]);
218564562Sgshapiro	if (transienterror(save_errno))
218638032Speter		_exit(EX_OSERR);
218738032Speter	_exit(EX_CONFIG);
218838032Speter	return -1;	/* avoid compiler warning on IRIX */
218938032Speter}
2190168515Sgshapiro
219190792Sgshapiro/*
219264562Sgshapiro**  GET_COLUMN -- look up a Column in a line buffer
219338032Speter**
219438032Speter**	Parameters:
219538032Speter**		line -- the raw text line to search.
219638032Speter**		col -- the column number to fetch.
219738032Speter**		delim -- the delimiter between columns.  If null,
219838032Speter**			use white space.
219938032Speter**		buf -- the output buffer.
220038032Speter**		buflen -- the length of buf.
220138032Speter**
220238032Speter**	Returns:
220338032Speter**		buf if successful.
220438032Speter**		NULL otherwise.
220538032Speter*/
220638032Speter
220738032Speterchar *
220838032Speterget_column(line, col, delim, buf, buflen)
220938032Speter	char line[];
221038032Speter	int col;
221164562Sgshapiro	int delim;
221238032Speter	char buf[];
221338032Speter	int buflen;
221438032Speter{
221538032Speter	char *p;
221638032Speter	char *begin, *end;
221738032Speter	int i;
221838032Speter	char delimbuf[4];
221964562Sgshapiro
222090792Sgshapiro	if ((char) delim == '\0')
2221168515Sgshapiro		(void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf));
222238032Speter	else
222338032Speter	{
222490792Sgshapiro		delimbuf[0] = (char) delim;
222538032Speter		delimbuf[1] = '\0';
222638032Speter	}
222738032Speter
222838032Speter	p = line;
222938032Speter	if (*p == '\0')
223038032Speter		return NULL;			/* line empty */
223190792Sgshapiro	if (*p == (char) delim && col == 0)
223238032Speter		return NULL;			/* first column empty */
223338032Speter
223438032Speter	begin = line;
223538032Speter
223690792Sgshapiro	if (col == 0 && (char) delim == '\0')
223738032Speter	{
2238363466Sgshapiro		while (*begin != '\0' && SM_ISSPACE(*begin))
223938032Speter			begin++;
224038032Speter	}
224138032Speter
224238032Speter	for (i = 0; i < col; i++)
224338032Speter	{
224438032Speter		if ((begin = strpbrk(begin, delimbuf)) == NULL)
224538032Speter			return NULL;		/* no such column */
224638032Speter		begin++;
224790792Sgshapiro		if ((char) delim == '\0')
224838032Speter		{
2249363466Sgshapiro			while (*begin != '\0' && SM_ISSPACE(*begin))
225038032Speter				begin++;
225138032Speter		}
225238032Speter	}
225364562Sgshapiro
225438032Speter	end = strpbrk(begin, delimbuf);
225538032Speter	if (end == NULL)
225638032Speter		i = strlen(begin);
225738032Speter	else
225838032Speter		i = end - begin;
225938032Speter	if (i >= buflen)
226038032Speter		i = buflen - 1;
226190792Sgshapiro	(void) sm_strlcpy(buf, begin, i + 1);
226238032Speter	return buf;
226338032Speter}
2264168515Sgshapiro
226590792Sgshapiro/*
226638032Speter**  CLEANSTRCPY -- copy string keeping out bogus characters
226738032Speter**
226838032Speter**	Parameters:
226938032Speter**		t -- "to" string.
227038032Speter**		f -- "from" string.
227138032Speter**		l -- length of space available in "to" string.
227238032Speter**
227338032Speter**	Returns:
227438032Speter**		none.
227538032Speter*/
227638032Speter
227738032Spetervoid
227838032Spetercleanstrcpy(t, f, l)
227938032Speter	register char *t;
228038032Speter	register char *f;
228138032Speter	int l;
228238032Speter{
228338032Speter	/* check for newlines and log if necessary */
228490792Sgshapiro	(void) denlstring(f, true, true);
228538032Speter
228664562Sgshapiro	if (l <= 0)
228764562Sgshapiro		syserr("!cleanstrcpy: length == 0");
228864562Sgshapiro
228938032Speter	l--;
229038032Speter	while (l > 0 && *f != '\0')
229138032Speter	{
229238032Speter		if (isascii(*f) &&
229338032Speter		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
229438032Speter		{
229538032Speter			l--;
229638032Speter			*t++ = *f;
229738032Speter		}
229838032Speter		f++;
229938032Speter	}
230038032Speter	*t = '\0';
230138032Speter}
2302168515Sgshapiro
230390792Sgshapiro/*
230438032Speter**  DENLSTRING -- convert newlines in a string to spaces
230538032Speter**
230638032Speter**	Parameters:
230738032Speter**		s -- the input string
230838032Speter**		strict -- if set, don't permit continuation lines.
230938032Speter**		logattacks -- if set, log attempted attacks.
231038032Speter**
231138032Speter**	Returns:
231238032Speter**		A pointer to a version of the string with newlines
231338032Speter**		mapped to spaces.  This should be copied.
231438032Speter*/
231538032Speter
231638032Speterchar *
231738032Speterdenlstring(s, strict, logattacks)
231838032Speter	char *s;
231938032Speter	bool strict;
232038032Speter	bool logattacks;
232138032Speter{
232238032Speter	register char *p;
232338032Speter	int l;
232438032Speter	static char *bp = NULL;
232538032Speter	static int bl = 0;
232638032Speter
232738032Speter	p = s;
232838032Speter	while ((p = strchr(p, '\n')) != NULL)
232938032Speter		if (strict || (*++p != ' ' && *p != '\t'))
233038032Speter			break;
233138032Speter	if (p == NULL)
233238032Speter		return s;
233338032Speter
233438032Speter	l = strlen(s) + 1;
233538032Speter	if (bl < l)
233638032Speter	{
233738032Speter		/* allocate more space */
233890792Sgshapiro		char *nbp = sm_pmalloc_x(l);
233990792Sgshapiro
234038032Speter		if (bp != NULL)
234177349Sgshapiro			sm_free(bp);
234290792Sgshapiro		bp = nbp;
234338032Speter		bl = l;
234438032Speter	}
234590792Sgshapiro	(void) sm_strlcpy(bp, s, l);
234638032Speter	for (p = bp; (p = strchr(p, '\n')) != NULL; )
234738032Speter		*p++ = ' ';
234838032Speter
234938032Speter	if (logattacks)
235038032Speter	{
2351168515Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL,
235264562Sgshapiro			  "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
235364562Sgshapiro			  RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
235464562Sgshapiro			  shortenstring(bp, MAXSHORTSTR));
235538032Speter	}
235638032Speter
235738032Speter	return bp;
235838032Speter}
235998841Sgshapiro
236090792Sgshapiro/*
236198841Sgshapiro**  STRREPLNONPRT -- replace "unprintable" characters in a string with subst
236298841Sgshapiro**
236398841Sgshapiro**	Parameters:
236498841Sgshapiro**		s -- string to manipulate (in place)
236598841Sgshapiro**		subst -- character to use as replacement
236698841Sgshapiro**
236798841Sgshapiro**	Returns:
236898841Sgshapiro**		true iff string did not contain "unprintable" characters
236998841Sgshapiro*/
237098841Sgshapiro
237198841Sgshapirobool
237298841Sgshapirostrreplnonprt(s, c)
237398841Sgshapiro	char *s;
237498841Sgshapiro	int c;
237598841Sgshapiro{
237698841Sgshapiro	bool ok;
237798841Sgshapiro
237898841Sgshapiro	ok = true;
237998841Sgshapiro	if (s == NULL)
238098841Sgshapiro		return ok;
238198841Sgshapiro	while (*s != '\0')
238298841Sgshapiro	{
238398841Sgshapiro		if (!(isascii(*s) && isprint(*s)))
238498841Sgshapiro		{
238598841Sgshapiro			*s = c;
238698841Sgshapiro			ok = false;
238798841Sgshapiro		}
238898841Sgshapiro		++s;
238998841Sgshapiro	}
239098841Sgshapiro	return ok;
239198841Sgshapiro}
239298841Sgshapiro
239398841Sgshapiro/*
239438032Speter**  PATH_IS_DIR -- check to see if file exists and is a directory.
239538032Speter**
239638032Speter**	There are some additional checks for security violations in
239738032Speter**	here.  This routine is intended to be used for the host status
239838032Speter**	support.
239938032Speter**
240038032Speter**	Parameters:
240138032Speter**		pathname -- pathname to check for directory-ness.
240238032Speter**		createflag -- if set, create directory if needed.
240338032Speter**
240438032Speter**	Returns:
240590792Sgshapiro**		true -- if the indicated pathname is a directory
240690792Sgshapiro**		false -- otherwise
240738032Speter*/
240838032Speter
2409120256Sgshapirobool
241038032Speterpath_is_dir(pathname, createflag)
241138032Speter	char *pathname;
241238032Speter	bool createflag;
241338032Speter{
241438032Speter	struct stat statbuf;
241538032Speter
241638032Speter#if HASLSTAT
241738032Speter	if (lstat(pathname, &statbuf) < 0)
2418363466Sgshapiro#else
241938032Speter	if (stat(pathname, &statbuf) < 0)
2420363466Sgshapiro#endif
242138032Speter	{
242238032Speter		if (errno != ENOENT || !createflag)
242390792Sgshapiro			return false;
242438032Speter		if (mkdir(pathname, 0755) < 0)
242590792Sgshapiro			return false;
242690792Sgshapiro		return true;
242738032Speter	}
242838032Speter	if (!S_ISDIR(statbuf.st_mode))
242938032Speter	{
243038032Speter		errno = ENOTDIR;
243190792Sgshapiro		return false;
243238032Speter	}
243338032Speter
243438032Speter	/* security: don't allow writable directories */
243538032Speter	if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
243638032Speter	{
243738032Speter		errno = EACCES;
243890792Sgshapiro		return false;
243938032Speter	}
244090792Sgshapiro	return true;
244138032Speter}
2442168515Sgshapiro
244390792Sgshapiro/*
244438032Speter**  PROC_LIST_ADD -- add process id to list of our children
244538032Speter**
244638032Speter**	Parameters:
244738032Speter**		pid -- pid to add to list.
244864562Sgshapiro**		task -- task of pid.
244964562Sgshapiro**		type -- type of process.
245090792Sgshapiro**		count -- number of processes.
245190792Sgshapiro**		other -- other information for this type.
245238032Speter**
245338032Speter**	Returns:
245438032Speter**		none
245590792Sgshapiro**
245690792Sgshapiro**	Side Effects:
245790792Sgshapiro**		May increase CurChildren. May grow ProcList.
245838032Speter*/
245938032Speter
246090792Sgshapirotypedef struct procs	PROCS_T;
246142575Speter
246290792Sgshapirostruct procs
246390792Sgshapiro{
2464132943Sgshapiro	pid_t		proc_pid;
2465132943Sgshapiro	char		*proc_task;
2466132943Sgshapiro	int		proc_type;
2467132943Sgshapiro	int		proc_count;
2468132943Sgshapiro	int		proc_other;
2469132943Sgshapiro	SOCKADDR	proc_hostaddr;
247090792Sgshapiro};
247190792Sgshapiro
247290792Sgshapirostatic PROCS_T	*volatile ProcListVec = NULL;
247390792Sgshapirostatic int	ProcListSize = 0;
247490792Sgshapiro
247538032Spetervoid
2476132943Sgshapiroproc_list_add(pid, task, type, count, other, hostaddr)
247738032Speter	pid_t pid;
247842575Speter	char *task;
247964562Sgshapiro	int type;
248090792Sgshapiro	int count;
248190792Sgshapiro	int other;
2482132943Sgshapiro	SOCKADDR *hostaddr;
248338032Speter{
248438032Speter	int i;
248538032Speter
248638032Speter	for (i = 0; i < ProcListSize; i++)
248738032Speter	{
248842575Speter		if (ProcListVec[i].proc_pid == NO_PID)
248938032Speter			break;
249038032Speter	}
249138032Speter	if (i >= ProcListSize)
249238032Speter	{
249338032Speter		/* probe the existing vector to avoid growing infinitely */
249438032Speter		proc_list_probe();
249538032Speter
249638032Speter		/* now scan again */
249738032Speter		for (i = 0; i < ProcListSize; i++)
249838032Speter		{
249942575Speter			if (ProcListVec[i].proc_pid == NO_PID)
250038032Speter				break;
250138032Speter		}
250238032Speter	}
250338032Speter	if (i >= ProcListSize)
250438032Speter	{
250538032Speter		/* grow process list */
2506168515Sgshapiro		int chldwasblocked;
250790792Sgshapiro		PROCS_T *npv;
250838032Speter
250990792Sgshapiro		SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
2510168515Sgshapiro		npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) *
251190792Sgshapiro					       (ProcListSize + PROC_LIST_SEG));
2512168515Sgshapiro
2513168515Sgshapiro		/* Block SIGCHLD so reapchild() doesn't mess with us */
2514168515Sgshapiro		chldwasblocked = sm_blocksignal(SIGCHLD);
251538032Speter		if (ProcListSize > 0)
251638032Speter		{
251764562Sgshapiro			memmove(npv, ProcListVec,
2518168515Sgshapiro				ProcListSize * sizeof(PROCS_T));
251977349Sgshapiro			sm_free(ProcListVec);
252038032Speter		}
252190792Sgshapiro
252290792Sgshapiro		/* XXX just use memset() to initialize this part? */
252338032Speter		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
252442575Speter		{
252542575Speter			npv[i].proc_pid = NO_PID;
252642575Speter			npv[i].proc_task = NULL;
252764562Sgshapiro			npv[i].proc_type = PROC_NONE;
252842575Speter		}
252938032Speter		i = ProcListSize;
253038032Speter		ProcListSize += PROC_LIST_SEG;
253138032Speter		ProcListVec = npv;
2532168515Sgshapiro		if (chldwasblocked == 0)
2533168515Sgshapiro			(void) sm_releasesignal(SIGCHLD);
253438032Speter	}
253542575Speter	ProcListVec[i].proc_pid = pid;
253690792Sgshapiro	PSTRSET(ProcListVec[i].proc_task, task);
253764562Sgshapiro	ProcListVec[i].proc_type = type;
253890792Sgshapiro	ProcListVec[i].proc_count = count;
253990792Sgshapiro	ProcListVec[i].proc_other = other;
2540132943Sgshapiro	if (hostaddr != NULL)
2541132943Sgshapiro		ProcListVec[i].proc_hostaddr = *hostaddr;
2542132943Sgshapiro	else
2543132943Sgshapiro		memset(&ProcListVec[i].proc_hostaddr, 0,
2544132943Sgshapiro			sizeof(ProcListVec[i].proc_hostaddr));
254542575Speter
254642575Speter	/* if process adding itself, it's not a child */
254790792Sgshapiro	if (pid != CurrentPid)
254890792Sgshapiro	{
254990792Sgshapiro		SM_ASSERT(CurChildren < INT_MAX);
255042575Speter		CurChildren++;
255190792Sgshapiro	}
255238032Speter}
2553168515Sgshapiro
255490792Sgshapiro/*
255542575Speter**  PROC_LIST_SET -- set pid task in process list
255642575Speter**
255742575Speter**	Parameters:
255842575Speter**		pid -- pid to set
255942575Speter**		task -- task of pid
256042575Speter**
256142575Speter**	Returns:
256242575Speter**		none.
256342575Speter*/
256442575Speter
256542575Spetervoid
256642575Speterproc_list_set(pid, task)
256742575Speter	pid_t pid;
256842575Speter	char *task;
256942575Speter{
257042575Speter	int i;
257142575Speter
257242575Speter	for (i = 0; i < ProcListSize; i++)
257342575Speter	{
257442575Speter		if (ProcListVec[i].proc_pid == pid)
257542575Speter		{
257690792Sgshapiro			PSTRSET(ProcListVec[i].proc_task, task);
257742575Speter			break;
257842575Speter		}
257942575Speter	}
258042575Speter}
2581168515Sgshapiro
258290792Sgshapiro/*
258338032Speter**  PROC_LIST_DROP -- drop pid from process list
258438032Speter**
258538032Speter**	Parameters:
258638032Speter**		pid -- pid to drop
258790792Sgshapiro**		st -- process status
258890792Sgshapiro**		other -- storage for proc_other (return).
258938032Speter**
259038032Speter**	Returns:
259190792Sgshapiro**		none.
259277349Sgshapiro**
259390792Sgshapiro**	Side Effects:
259490792Sgshapiro**		May decrease CurChildren, CurRunners, or
259590792Sgshapiro**		set RestartRequest or ShutdownRequest.
259690792Sgshapiro**
259777349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
259877349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
259977349Sgshapiro**		DOING.
260038032Speter*/
260138032Speter
260290792Sgshapirovoid
260390792Sgshapiroproc_list_drop(pid, st, other)
260438032Speter	pid_t pid;
260590792Sgshapiro	int st;
260690792Sgshapiro	int *other;
260738032Speter{
260838032Speter	int i;
260964562Sgshapiro	int type = PROC_NONE;
261038032Speter
261138032Speter	for (i = 0; i < ProcListSize; i++)
261238032Speter	{
261342575Speter		if (ProcListVec[i].proc_pid == pid)
261438032Speter		{
261542575Speter			ProcListVec[i].proc_pid = NO_PID;
261664562Sgshapiro			type = ProcListVec[i].proc_type;
261790792Sgshapiro			if (other != NULL)
261890792Sgshapiro				*other = ProcListVec[i].proc_other;
2619157001Sgshapiro			if (CurChildren > 0)
2620157001Sgshapiro				CurChildren--;
262138032Speter			break;
262238032Speter		}
262338032Speter	}
262464562Sgshapiro
262564562Sgshapiro
262690792Sgshapiro	if (type == PROC_CONTROL && WIFEXITED(st))
262790792Sgshapiro	{
262890792Sgshapiro		/* if so, see if we need to restart or shutdown */
262990792Sgshapiro		if (WEXITSTATUS(st) == EX_RESTART)
263090792Sgshapiro			RestartRequest = "control socket";
263190792Sgshapiro		else if (WEXITSTATUS(st) == EX_SHUTDOWN)
263290792Sgshapiro			ShutdownRequest = "control socket";
263390792Sgshapiro	}
263490792Sgshapiro	else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
263590792Sgshapiro		 ProcListVec[i].proc_other > -1)
263690792Sgshapiro	{
263790792Sgshapiro		/* restart this persistent runner */
263890792Sgshapiro		mark_work_group_restart(ProcListVec[i].proc_other, st);
263990792Sgshapiro	}
264090792Sgshapiro	else if (type == PROC_QUEUE)
2641244833Sgshapiro	{
264290792Sgshapiro		CurRunners -= ProcListVec[i].proc_count;
2643244833Sgshapiro
2644244833Sgshapiro		/* CHK_CUR_RUNNERS() can't be used here: uses syslog() */
2645244833Sgshapiro		if (CurRunners < 0)
2646244833Sgshapiro			CurRunners = 0;
2647244833Sgshapiro	}
264838032Speter}
2649168515Sgshapiro
265090792Sgshapiro/*
265138032Speter**  PROC_LIST_CLEAR -- clear the process list
265238032Speter**
265338032Speter**	Parameters:
265438032Speter**		none.
265538032Speter**
265638032Speter**	Returns:
265738032Speter**		none.
265890792Sgshapiro**
265990792Sgshapiro**	Side Effects:
266090792Sgshapiro**		Sets CurChildren to zero.
266138032Speter*/
266238032Speter
266338032Spetervoid
266438032Speterproc_list_clear()
266538032Speter{
266638032Speter	int i;
266738032Speter
266842575Speter	/* start from 1 since 0 is the daemon itself */
266942575Speter	for (i = 1; i < ProcListSize; i++)
267042575Speter		ProcListVec[i].proc_pid = NO_PID;
267138032Speter	CurChildren = 0;
267238032Speter}
2673168515Sgshapiro
267490792Sgshapiro/*
267538032Speter**  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
267638032Speter**
267738032Speter**	Parameters:
267838032Speter**		none
267938032Speter**
268038032Speter**	Returns:
268138032Speter**		none
268290792Sgshapiro**
268390792Sgshapiro**	Side Effects:
268490792Sgshapiro**		May decrease CurChildren.
268538032Speter*/
268638032Speter
268738032Spetervoid
268838032Speterproc_list_probe()
268938032Speter{
2690157001Sgshapiro	int i, children;
2691157001Sgshapiro	int chldwasblocked;
2692157001Sgshapiro	pid_t pid;
269338032Speter
2694157001Sgshapiro	children = 0;
2695157001Sgshapiro	chldwasblocked = sm_blocksignal(SIGCHLD);
2696157001Sgshapiro
269742575Speter	/* start from 1 since 0 is the daemon itself */
269842575Speter	for (i = 1; i < ProcListSize; i++)
269938032Speter	{
2700157001Sgshapiro		pid = ProcListVec[i].proc_pid;
2701157001Sgshapiro		if (pid == NO_PID || pid == CurrentPid)
270238032Speter			continue;
2703157001Sgshapiro		if (kill(pid, 0) < 0)
270438032Speter		{
270538032Speter			if (LogLevel > 3)
270638032Speter				sm_syslog(LOG_DEBUG, CurEnv->e_id,
270764562Sgshapiro					  "proc_list_probe: lost pid %d",
270864562Sgshapiro					  (int) ProcListVec[i].proc_pid);
270942575Speter			ProcListVec[i].proc_pid = NO_PID;
2710363466Sgshapiro			SM_FREE(ProcListVec[i].proc_task);
2711244833Sgshapiro
2712244833Sgshapiro			if (ProcListVec[i].proc_type == PROC_QUEUE)
2713244833Sgshapiro			{
2714244833Sgshapiro				CurRunners -= ProcListVec[i].proc_count;
2715244833Sgshapiro				CHK_CUR_RUNNERS("proc_list_probe", i,
2716244833Sgshapiro						ProcListVec[i].proc_count);
2717244833Sgshapiro			}
2718244833Sgshapiro
271938032Speter			CurChildren--;
272038032Speter		}
2721157001Sgshapiro		else
2722157001Sgshapiro		{
2723157001Sgshapiro			++children;
2724157001Sgshapiro		}
272538032Speter	}
272638032Speter	if (CurChildren < 0)
272738032Speter		CurChildren = 0;
2728157001Sgshapiro	if (chldwasblocked == 0)
2729157001Sgshapiro		(void) sm_releasesignal(SIGCHLD);
2730159609Sgshapiro	if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid)
2731157001Sgshapiro	{
2732157001Sgshapiro		sm_syslog(LOG_ERR, NOQID,
2733157001Sgshapiro			  "proc_list_probe: found %d children, expected %d",
2734157001Sgshapiro			  children, CurChildren);
2735157001Sgshapiro	}
273638032Speter}
273790792Sgshapiro
273890792Sgshapiro/*
273942575Speter**  PROC_LIST_DISPLAY -- display the process list
274042575Speter**
274142575Speter**	Parameters:
274242575Speter**		out -- output file pointer
274390792Sgshapiro**		prefix -- string to output in front of each line.
274442575Speter**
274542575Speter**	Returns:
274642575Speter**		none.
274742575Speter*/
274842575Speter
274942575Spetervoid
275090792Sgshapiroproc_list_display(out, prefix)
275190792Sgshapiro	SM_FILE_T *out;
275290792Sgshapiro	char *prefix;
275342575Speter{
275442575Speter	int i;
275542575Speter
275642575Speter	for (i = 0; i < ProcListSize; i++)
275742575Speter	{
275842575Speter		if (ProcListVec[i].proc_pid == NO_PID)
275942575Speter			continue;
276042575Speter
276190792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
276290792Sgshapiro				     prefix,
276390792Sgshapiro				     (int) ProcListVec[i].proc_pid,
276490792Sgshapiro				     ProcListVec[i].proc_task != NULL ?
276590792Sgshapiro				     ProcListVec[i].proc_task : "(unknown)",
276690792Sgshapiro				     (OpMode == MD_SMTP ||
276790792Sgshapiro				      OpMode == MD_DAEMON ||
276890792Sgshapiro				      OpMode == MD_ARPAFTP) ? "\r" : "");
276942575Speter	}
277042575Speter}
277190792Sgshapiro
277290792Sgshapiro/*
277390792Sgshapiro**  PROC_LIST_SIGNAL -- send a signal to a type of process in the list
277480785Sgshapiro**
277580785Sgshapiro**	Parameters:
277690792Sgshapiro**		type -- type of process to signal
277790792Sgshapiro**		signal -- the type of signal to send
277880785Sgshapiro**
277990792Sgshapiro**	Results:
278090792Sgshapiro**		none.
278190792Sgshapiro**
278290792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
278390792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
278490792Sgshapiro**		DOING.
278580785Sgshapiro*/
278680785Sgshapiro
278790792Sgshapirovoid
278890792Sgshapiroproc_list_signal(type, signal)
278990792Sgshapiro	int type;
279090792Sgshapiro	int signal;
279180785Sgshapiro{
279290792Sgshapiro	int chldwasblocked;
279390792Sgshapiro	int alrmwasblocked;
279490792Sgshapiro	int i;
279590792Sgshapiro	pid_t mypid = getpid();
279680785Sgshapiro
279790792Sgshapiro	/* block these signals so that we may signal cleanly */
279890792Sgshapiro	chldwasblocked = sm_blocksignal(SIGCHLD);
279990792Sgshapiro	alrmwasblocked = sm_blocksignal(SIGALRM);
280080785Sgshapiro
280190792Sgshapiro	/* Find all processes of type and send signal */
280290792Sgshapiro	for (i = 0; i < ProcListSize; i++)
280380785Sgshapiro	{
280490792Sgshapiro		if (ProcListVec[i].proc_pid == NO_PID ||
280590792Sgshapiro		    ProcListVec[i].proc_pid == mypid)
280690792Sgshapiro			continue;
280790792Sgshapiro		if (ProcListVec[i].proc_type != type)
280890792Sgshapiro			continue;
280990792Sgshapiro		(void) kill(ProcListVec[i].proc_pid, signal);
281080785Sgshapiro	}
281180785Sgshapiro
281290792Sgshapiro	/* restore the signals */
281390792Sgshapiro	if (alrmwasblocked == 0)
281490792Sgshapiro		(void) sm_releasesignal(SIGALRM);
281590792Sgshapiro	if (chldwasblocked == 0)
281690792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
281780785Sgshapiro}
2818132943Sgshapiro
2819132943Sgshapiro/*
2820132943Sgshapiro**  COUNT_OPEN_CONNECTIONS
2821132943Sgshapiro**
2822132943Sgshapiro**	Parameters:
2823132943Sgshapiro**		hostaddr - ClientAddress
2824132943Sgshapiro**
2825132943Sgshapiro**	Returns:
2826132943Sgshapiro**		the number of open connections for this client
2827132943Sgshapiro**
2828132943Sgshapiro*/
2829132943Sgshapiro
2830132943Sgshapiroint
2831132943Sgshapirocount_open_connections(hostaddr)
2832132943Sgshapiro	SOCKADDR *hostaddr;
2833132943Sgshapiro{
2834132943Sgshapiro	int i, n;
2835132943Sgshapiro
2836132943Sgshapiro	if (hostaddr == NULL)
2837132943Sgshapiro		return 0;
2838173340Sgshapiro
2839173340Sgshapiro	/*
2840182352Sgshapiro	**  This code gets called before proc_list_add() gets called,
2841182352Sgshapiro	**  so we (the daemon child for this connection) have not yet
2842182352Sgshapiro	**  counted ourselves.  Hence initialize the counter to 1
2843182352Sgshapiro	**  instead of 0 to compensate.
2844173340Sgshapiro	*/
2845173340Sgshapiro
2846173340Sgshapiro	n = 1;
2847132943Sgshapiro	for (i = 0; i < ProcListSize; i++)
2848132943Sgshapiro	{
2849132943Sgshapiro		if (ProcListVec[i].proc_pid == NO_PID)
2850132943Sgshapiro			continue;
2851132943Sgshapiro		if (hostaddr->sa.sa_family !=
2852132943Sgshapiro		    ProcListVec[i].proc_hostaddr.sa.sa_family)
2853132943Sgshapiro			continue;
2854132943Sgshapiro#if NETINET
2855132943Sgshapiro		if (hostaddr->sa.sa_family == AF_INET &&
2856132943Sgshapiro		    (hostaddr->sin.sin_addr.s_addr ==
2857132943Sgshapiro		     ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr))
2858132943Sgshapiro			n++;
2859363466Sgshapiro#endif
2860132943Sgshapiro#if NETINET6
2861132943Sgshapiro		if (hostaddr->sa.sa_family == AF_INET6 &&
2862132943Sgshapiro		    IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr),
2863132943Sgshapiro				       &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr)))
2864132943Sgshapiro			n++;
2865363466Sgshapiro#endif
2866132943Sgshapiro	}
2867132943Sgshapiro	return n;
2868132943Sgshapiro}
2869244833Sgshapiro
2870285229Sgshapiro#if _FFR_XCNCT
2871285229Sgshapiro/*
2872285229Sgshapiro**  XCONNECT -- get X-CONNECT info
2873285229Sgshapiro**
2874285229Sgshapiro**	Parameters:
2875285229Sgshapiro**		inchannel -- FILE to check
2876285229Sgshapiro**
2877285229Sgshapiro**	Returns:
2878285229Sgshapiro**		-1 on error
2879285229Sgshapiro**		0 if X-CONNECT was not given
2880285229Sgshapiro**		>0 if X-CONNECT was used successfully (D_XCNCT*)
2881285229Sgshapiro*/
2882285229Sgshapiro
2883285229Sgshapiroint
2884285229Sgshapiroxconnect(inchannel)
2885285229Sgshapiro	SM_FILE_T *inchannel;
2886285229Sgshapiro{
2887285229Sgshapiro	int r, i;
2888285229Sgshapiro	char *p, *b, delim, inp[MAXINPLINE];
2889285229Sgshapiro	SOCKADDR addr;
2890285229Sgshapiro	char **pvp;
2891285229Sgshapiro	char pvpbuf[PSBUFSIZE];
2892285229Sgshapiro	char *peerhostname;	/* name of SMTP peer or "localhost" */
2893285229Sgshapiro	extern ENVELOPE BlankEnvelope;
2894285229Sgshapiro
2895285229Sgshapiro#define XCONNECT "X-CONNECT "
2896285229Sgshapiro#define XCNNCTLEN (sizeof(XCONNECT) - 1)
2897285229Sgshapiro
2898285229Sgshapiro	/* Ask the ruleset whether to use x-connect */
2899285229Sgshapiro	pvp = NULL;
2900285229Sgshapiro	peerhostname = RealHostName;
2901285229Sgshapiro	if (peerhostname == NULL)
2902285229Sgshapiro		peerhostname = "localhost";
2903285229Sgshapiro	r = rscap("x_connect", peerhostname,
2904285229Sgshapiro		  anynet_ntoa(&RealHostAddr), &BlankEnvelope,
2905285229Sgshapiro		  &pvp, pvpbuf, sizeof(pvpbuf));
2906285229Sgshapiro	if (tTd(75, 8))
2907285229Sgshapiro		sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r);
2908285229Sgshapiro	if (r == EX_UNAVAILABLE)
2909285229Sgshapiro		return 0;
2910285229Sgshapiro	if (r != EX_OK)
2911285229Sgshapiro	{
2912285229Sgshapiro		/* ruleset error */
2913285229Sgshapiro		sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r);
2914285229Sgshapiro		return 0;
2915285229Sgshapiro	}
2916285229Sgshapiro	if (pvp != NULL && pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
2917285229Sgshapiro	{
2918285229Sgshapiro		/* $#: no x-connect */
2919285229Sgshapiro		if (tTd(75, 7))
2920285229Sgshapiro			sm_syslog(LOG_INFO, NOQID, "x-connect: nope");
2921285229Sgshapiro		return 0;
2922285229Sgshapiro	}
2923285229Sgshapiro
2924363466Sgshapiro# if _FFR_XCNCT > 1
2925363466Sgshapiro	if (pvp != NULL && pvp[0] != NULL &&
2926363466Sgshapiro	    pvp[0][0] == '2' && pvp[0][1] == '2' && pvp[0][2] == '0')
2927363466Sgshapiro	{
2928363466Sgshapiro		char *hostname;			/* my hostname ($j) */
2929363466Sgshapiro
2930363466Sgshapiro		hostname = macvalue('j', &BlankEnvelope);
2931363466Sgshapiro		if (tTd(75, 7))
2932363466Sgshapiro			sm_syslog(LOG_INFO, NOQID, "x-connect=%s", pvp[0]);
2933363466Sgshapiro		message("220-%s %s", hostname != NULL ? hostname : "xconnect",
2934363466Sgshapiro			pvp[1] != NULL ? pvp[1] : "waiting for xconnect");
2935363466Sgshapiro		sm_io_flush(OutChannel, SM_TIME_DEFAULT);
2936363466Sgshapiro	}
2937363466Sgshapiro# endif
2938363466Sgshapiro
2939285229Sgshapiro	p = sfgets(inp, sizeof(inp), InChannel, TimeOuts.to_nextcommand, "pre");
2940285229Sgshapiro	if (tTd(75, 6))
2941285229Sgshapiro		sm_syslog(LOG_INFO, NOQID, "x-connect: input=%s", p);
2942285229Sgshapiro	if (p == NULL || strncasecmp(p, XCONNECT, XCNNCTLEN) != 0)
2943285229Sgshapiro		return -1;
2944285229Sgshapiro	p += XCNNCTLEN;
2945363466Sgshapiro	while (SM_ISSPACE(*p))
2946285229Sgshapiro		p++;
2947285229Sgshapiro
2948285229Sgshapiro	/* parameters: IPAddress [Hostname[ M]] */
2949285229Sgshapiro	b = p;
2950285229Sgshapiro	while (*p != '\0' && isascii(*p) &&
2951285229Sgshapiro	       (isalnum(*p) || *p == '.' || *p== ':'))
2952285229Sgshapiro		p++;
2953285229Sgshapiro	delim = *p;
2954285229Sgshapiro	*p = '\0';
2955285229Sgshapiro
2956285229Sgshapiro	memset(&addr, '\0', sizeof(addr));
2957285229Sgshapiro	addr.sin.sin_addr.s_addr = inet_addr(b);
2958285229Sgshapiro	if (addr.sin.sin_addr.s_addr != INADDR_NONE)
2959285229Sgshapiro	{
2960285229Sgshapiro		addr.sa.sa_family = AF_INET;
2961285229Sgshapiro		memcpy(&RealHostAddr, &addr, sizeof(addr));
2962285229Sgshapiro		if (tTd(75, 2))
2963285229Sgshapiro			sm_syslog(LOG_INFO, NOQID, "x-connect: addr=%s",
2964285229Sgshapiro				anynet_ntoa(&RealHostAddr));
2965285229Sgshapiro	}
2966285229Sgshapiro# if NETINET6
2967285229Sgshapiro	else if ((r = inet_pton(AF_INET6, b, &addr.sin6.sin6_addr)) == 1)
2968285229Sgshapiro	{
2969285229Sgshapiro		addr.sa.sa_family = AF_INET6;
2970285229Sgshapiro		memcpy(&RealHostAddr, &addr, sizeof(addr));
2971285229Sgshapiro	}
2972363466Sgshapiro# endif
2973285229Sgshapiro	else
2974285229Sgshapiro		return -1;
2975285229Sgshapiro
2976285229Sgshapiro	/* more parameters? */
2977285229Sgshapiro	if (delim != ' ')
2978285229Sgshapiro		return D_XCNCT;
2979285229Sgshapiro
2980285229Sgshapiro	for (b = ++p, i = 0;
2981285229Sgshapiro	     *p != '\0' && isascii(*p) && (isalnum(*p) || *p == '.' || *p == '-');
2982285229Sgshapiro	     p++, i++)
2983285229Sgshapiro		;
2984285229Sgshapiro	if (i == 0)
2985285229Sgshapiro		return D_XCNCT;
2986285229Sgshapiro	delim = *p;
2987285229Sgshapiro	if (i > MAXNAME)
2988285229Sgshapiro		b[MAXNAME] = '\0';
2989285229Sgshapiro	else
2990285229Sgshapiro		b[i] = '\0';
2991363466Sgshapiro	SM_FREE(RealHostName);
2992285229Sgshapiro	RealHostName = newstr(b);
2993285229Sgshapiro	if (tTd(75, 2))
2994285229Sgshapiro		sm_syslog(LOG_INFO, NOQID, "x-connect: host=%s", b);
2995285229Sgshapiro	*p = delim;
2996285229Sgshapiro
2997285229Sgshapiro	b = p;
2998285229Sgshapiro	if (*p != ' ')
2999285229Sgshapiro		return D_XCNCT;
3000285229Sgshapiro
3001363466Sgshapiro	while (*p != '\0' && SM_ISSPACE(*p))
3002285229Sgshapiro		p++;
3003285229Sgshapiro
3004285229Sgshapiro	if (tTd(75, 4))
3005285229Sgshapiro	{
3006285229Sgshapiro		char *e;
3007285229Sgshapiro
3008285229Sgshapiro		e = strpbrk(p, "\r\n");
3009285229Sgshapiro		if (e != NULL)
3010285229Sgshapiro			*e = '\0';
3011285229Sgshapiro		sm_syslog(LOG_INFO, NOQID, "x-connect: rest=%s", p);
3012285229Sgshapiro	}
3013285229Sgshapiro	if (*p == 'M')
3014285229Sgshapiro		return D_XCNCT_M;
3015285229Sgshapiro
3016285229Sgshapiro	return D_XCNCT;
3017285229Sgshapiro}
3018285229Sgshapiro#endif /* _FFR_XCNCT */
3019