util.c revision 98841
138032Speter/*
294334Sgshapiro * Copyright (c) 1998-2002 Sendmail, 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
1698841SgshapiroSM_RCSID("@(#)$Id: util.c,v 8.363.2.1 2002/06/21 20:25:25 ca Exp $")
1764562Sgshapiro
1890792Sgshapiro#include <sysexits.h>
1990792Sgshapiro#include <sm/xtrap.h>
2064562Sgshapiro
2190792Sgshapiro/*
2238032Speter**  ADDQUOTES -- Adds quotes & quote bits to a string.
2338032Speter**
2490792Sgshapiro**	Runs through a string and adds backslashes and quote bits.
2538032Speter**
2638032Speter**	Parameters:
2738032Speter**		s -- the string to modify.
2890792Sgshapiro**		rpool -- resource pool from which to allocate result
2938032Speter**
3038032Speter**	Returns:
3138032Speter**		pointer to quoted string.
3238032Speter*/
3338032Speter
3438032Speterchar *
3590792Sgshapiroaddquotes(s, rpool)
3638032Speter	char *s;
3790792Sgshapiro	SM_RPOOL_T *rpool;
3838032Speter{
3938032Speter	int len = 0;
4038032Speter	char c;
4138032Speter	char *p = s, *q, *r;
4238032Speter
4338032Speter	if (s == NULL)
4438032Speter		return NULL;
4538032Speter
4638032Speter	/* Find length of quoted string */
4738032Speter	while ((c = *p++) != '\0')
4838032Speter	{
4938032Speter		len++;
5038032Speter		if (c == '\\' || c == '"')
5138032Speter			len++;
5238032Speter	}
5364562Sgshapiro
5490792Sgshapiro	q = r = sm_rpool_malloc_x(rpool, len + 3);
5538032Speter	p = s;
5638032Speter
5738032Speter	/* add leading quote */
5838032Speter	*q++ = '"';
5938032Speter	while ((c = *p++) != '\0')
6038032Speter	{
6138032Speter		/* quote \ or " */
6238032Speter		if (c == '\\' || c == '"')
6338032Speter			*q++ = '\\';
6438032Speter		*q++ = c;
6538032Speter	}
6638032Speter	*q++ = '"';
6738032Speter	*q = '\0';
6838032Speter	return r;
6938032Speter}
7090792Sgshapiro/*
7138032Speter**  RFC822_STRING -- Checks string for proper RFC822 string quoting.
7238032Speter**
7338032Speter**	Runs through a string and verifies RFC822 special characters
7438032Speter**	are only found inside comments, quoted strings, or backslash
7538032Speter**	escaped.  Also verified balanced quotes and parenthesis.
7638032Speter**
7738032Speter**	Parameters:
7838032Speter**		s -- the string to modify.
7938032Speter**
8038032Speter**	Returns:
8190792Sgshapiro**		true iff the string is RFC822 compliant, false otherwise.
8238032Speter*/
8338032Speter
8438032Speterbool
8538032Speterrfc822_string(s)
8638032Speter	char *s;
8738032Speter{
8890792Sgshapiro	bool quoted = false;
8938032Speter	int commentlev = 0;
9038032Speter	char *c = s;
9138032Speter
9238032Speter	if (s == NULL)
9390792Sgshapiro		return false;
9438032Speter
9538032Speter	while (*c != '\0')
9638032Speter	{
9738032Speter		/* escaped character */
9838032Speter		if (*c == '\\')
9938032Speter		{
10038032Speter			c++;
10138032Speter			if (*c == '\0')
10290792Sgshapiro				return false;
10338032Speter		}
10438032Speter		else if (commentlev == 0 && *c == '"')
10538032Speter			quoted = !quoted;
10638032Speter		else if (!quoted)
10738032Speter		{
10838032Speter			if (*c == ')')
10938032Speter			{
11038032Speter				/* unbalanced ')' */
11138032Speter				if (commentlev == 0)
11290792Sgshapiro					return false;
11338032Speter				else
11438032Speter					commentlev--;
11538032Speter			}
11638032Speter			else if (*c == '(')
11738032Speter				commentlev++;
11838032Speter			else if (commentlev == 0 &&
11938032Speter				 strchr(MustQuoteChars, *c) != NULL)
12090792Sgshapiro				return false;
12138032Speter		}
12238032Speter		c++;
12338032Speter	}
12490792Sgshapiro
12538032Speter	/* unbalanced '"' or '(' */
12690792Sgshapiro	return !quoted && commentlev == 0;
12738032Speter}
12890792Sgshapiro/*
12942575Speter**  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
13042575Speter**
13164562Sgshapiro**	Arbitrarily shorten (in place) an RFC822 string and rebalance
13242575Speter**	comments and quotes.
13342575Speter**
13442575Speter**	Parameters:
13542575Speter**		string -- the string to shorten
13642575Speter**		length -- the maximum size, 0 if no maximum
13742575Speter**
13842575Speter**	Returns:
13990792Sgshapiro**		true if string is changed, false otherwise
14042575Speter**
14142575Speter**	Side Effects:
14242575Speter**		Changes string in place, possibly resulting
14342575Speter**		in a shorter string.
14442575Speter*/
14542575Speter
14642575Speterbool
14742575Spetershorten_rfc822_string(string, length)
14842575Speter	char *string;
14942575Speter	size_t length;
15042575Speter{
15190792Sgshapiro	bool backslash = false;
15290792Sgshapiro	bool modified = false;
15390792Sgshapiro	bool quoted = false;
15442575Speter	size_t slen;
15542575Speter	int parencount = 0;
15642575Speter	char *ptr = string;
15764562Sgshapiro
15842575Speter	/*
15942575Speter	**  If have to rebalance an already short enough string,
16042575Speter	**  need to do it within allocated space.
16142575Speter	*/
16271345Sgshapiro
16342575Speter	slen = strlen(string);
16442575Speter	if (length == 0 || slen < length)
16542575Speter		length = slen;
16642575Speter
16742575Speter	while (*ptr != '\0')
16842575Speter	{
16942575Speter		if (backslash)
17042575Speter		{
17190792Sgshapiro			backslash = false;
17242575Speter			goto increment;
17342575Speter		}
17442575Speter
17542575Speter		if (*ptr == '\\')
17690792Sgshapiro			backslash = true;
17742575Speter		else if (*ptr == '(')
17842575Speter		{
17942575Speter			if (!quoted)
18042575Speter				parencount++;
18142575Speter		}
18242575Speter		else if (*ptr == ')')
18342575Speter		{
18442575Speter			if (--parencount < 0)
18542575Speter				parencount = 0;
18642575Speter		}
18764562Sgshapiro
18842575Speter		/* Inside a comment, quotes don't matter */
18942575Speter		if (parencount <= 0 && *ptr == '"')
19042575Speter			quoted = !quoted;
19142575Speter
19242575Speterincrement:
19342575Speter		/* Check for sufficient space for next character */
19464562Sgshapiro		if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
19542575Speter						parencount +
19642575Speter						(quoted ? 1 : 0)))
19742575Speter		{
19842575Speter			/* Not enough, backtrack */
19942575Speter			if (*ptr == '\\')
20090792Sgshapiro				backslash = false;
20142575Speter			else if (*ptr == '(' && !quoted)
20242575Speter				parencount--;
20342575Speter			else if (*ptr == '"' && parencount == 0)
20490792Sgshapiro				quoted = false;
20542575Speter			break;
20642575Speter		}
20742575Speter		ptr++;
20842575Speter	}
20942575Speter
21042575Speter	/* Rebalance */
21142575Speter	while (parencount-- > 0)
21242575Speter	{
21342575Speter		if (*ptr != ')')
21442575Speter		{
21590792Sgshapiro			modified = true;
21642575Speter			*ptr = ')';
21742575Speter		}
21842575Speter		ptr++;
21942575Speter	}
22042575Speter	if (quoted)
22142575Speter	{
22242575Speter		if (*ptr != '"')
22342575Speter		{
22490792Sgshapiro			modified = true;
22542575Speter			*ptr = '"';
22642575Speter		}
22742575Speter		ptr++;
22842575Speter	}
22942575Speter	if (*ptr != '\0')
23042575Speter	{
23190792Sgshapiro		modified = true;
23242575Speter		*ptr = '\0';
23342575Speter	}
23442575Speter	return modified;
23542575Speter}
23690792Sgshapiro/*
23742575Speter**  FIND_CHARACTER -- find an unquoted character in an RFC822 string
23842575Speter**
23942575Speter**	Find an unquoted, non-commented character in an RFC822
24042575Speter**	string and return a pointer to its location in the
24142575Speter**	string.
24242575Speter**
24342575Speter**	Parameters:
24442575Speter**		string -- the string to search
24542575Speter**		character -- the character to find
24642575Speter**
24742575Speter**	Returns:
24842575Speter**		pointer to the character, or
24942575Speter**		a pointer to the end of the line if character is not found
25042575Speter*/
25142575Speter
25242575Speterchar *
25342575Speterfind_character(string, character)
25442575Speter	char *string;
25564562Sgshapiro	int character;
25642575Speter{
25790792Sgshapiro	bool backslash = false;
25890792Sgshapiro	bool quoted = false;
25942575Speter	int parencount = 0;
26064562Sgshapiro
26142575Speter	while (string != NULL && *string != '\0')
26242575Speter	{
26342575Speter		if (backslash)
26442575Speter		{
26590792Sgshapiro			backslash = false;
26642575Speter			if (!quoted && character == '\\' && *string == '\\')
26742575Speter				break;
26842575Speter			string++;
26942575Speter			continue;
27042575Speter		}
27142575Speter		switch (*string)
27242575Speter		{
27342575Speter		  case '\\':
27490792Sgshapiro			backslash = true;
27542575Speter			break;
27664562Sgshapiro
27742575Speter		  case '(':
27842575Speter			if (!quoted)
27942575Speter				parencount++;
28042575Speter			break;
28164562Sgshapiro
28242575Speter		  case ')':
28342575Speter			if (--parencount < 0)
28442575Speter				parencount = 0;
28542575Speter			break;
28642575Speter		}
28764562Sgshapiro
28842575Speter		/* Inside a comment, nothing matters */
28942575Speter		if (parencount > 0)
29042575Speter		{
29142575Speter			string++;
29242575Speter			continue;
29342575Speter		}
29464562Sgshapiro
29542575Speter		if (*string == '"')
29642575Speter			quoted = !quoted;
29742575Speter		else if (*string == character && !quoted)
29842575Speter			break;
29942575Speter		string++;
30042575Speter	}
30142575Speter
30242575Speter	/* Return pointer to the character */
30342575Speter	return string;
30442575Speter}
30590792Sgshapiro
30690792Sgshapiro/*
30790792Sgshapiro**  CHECK_BODYTYPE -- check bodytype parameter
30838032Speter**
30938032Speter**	Parameters:
31090792Sgshapiro**		bodytype -- bodytype parameter
31138032Speter**
31238032Speter**	Returns:
31390792Sgshapiro**		BODYTYPE_* according to parameter
31438032Speter**
31538032Speter*/
31638032Speter
31790792Sgshapiroint
31890792Sgshapirocheck_bodytype(bodytype)
31990792Sgshapiro	char *bodytype;
32038032Speter{
32190792Sgshapiro	/* check body type for legality */
32290792Sgshapiro	if (bodytype == NULL)
32390792Sgshapiro		return BODYTYPE_NONE;
32490792Sgshapiro	if (sm_strcasecmp(bodytype, "7BIT") == 0)
32590792Sgshapiro		return BODYTYPE_7BIT;
32690792Sgshapiro	if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
32790792Sgshapiro		return BODYTYPE_8BITMIME;
32890792Sgshapiro	return BODYTYPE_ILLEGAL;
32990792Sgshapiro}
33038032Speter
33190792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
33290792Sgshapiro/*
33390792Sgshapiro**  TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
33477349Sgshapiro**
33577349Sgshapiro**	Parameters:
33690792Sgshapiro**		str -- string to truncate
33790792Sgshapiro**		len -- maximum length (including '\0') (0 for unlimited)
33890792Sgshapiro**		delim -- delimiter character
33977349Sgshapiro**
34077349Sgshapiro**	Returns:
34190792Sgshapiro**		None.
34277349Sgshapiro*/
34377349Sgshapiro
34490792Sgshapirovoid
34590792Sgshapirotruncate_at_delim(str, len, delim)
34690792Sgshapiro	char *str;
34790792Sgshapiro	size_t len;
34890792Sgshapiro	int delim;
34977349Sgshapiro{
35090792Sgshapiro	char *p;
35177349Sgshapiro
35290792Sgshapiro	if (str == NULL || len == 0 || strlen(str) < len)
35390792Sgshapiro		return;
35477349Sgshapiro
35590792Sgshapiro	*(str + len - 1) = '\0';
35690792Sgshapiro	while ((p = strrchr(str, delim)) != NULL)
35777349Sgshapiro	{
35890792Sgshapiro		*p = '\0';
35990792Sgshapiro		if (p - str + 4 < len)
36090792Sgshapiro		{
36190792Sgshapiro			*p++ = ':';
36290792Sgshapiro			*p = '\0';
36390792Sgshapiro			(void) sm_strlcat(str, "...", len);
36490792Sgshapiro			return;
36590792Sgshapiro		}
36690792Sgshapiro	}
36777349Sgshapiro
36890792Sgshapiro	/* Couldn't find a place to append "..." */
36990792Sgshapiro	if (len > 3)
37090792Sgshapiro		(void) sm_strlcpy(str, "...", len);
37190792Sgshapiro	else
37290792Sgshapiro		str[0] = '\0';
37377349Sgshapiro}
37490792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */
37590792Sgshapiro/*
37690792Sgshapiro**  XALLOC -- Allocate memory, raise an exception on error
37777349Sgshapiro**
37877349Sgshapiro**	Parameters:
37990792Sgshapiro**		sz -- size of area to allocate.
38077349Sgshapiro**
38177349Sgshapiro**	Returns:
38277349Sgshapiro**		pointer to data region.
38377349Sgshapiro**
38490792Sgshapiro**	Exceptions:
38590792Sgshapiro**		SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
38690792Sgshapiro**
38777349Sgshapiro**	Side Effects:
38877349Sgshapiro**		Memory is allocated.
38977349Sgshapiro*/
39077349Sgshapiro
39177349Sgshapirochar *
39290792Sgshapiro#if SM_HEAP_CHECK
39390792Sgshapiroxalloc_tagged(sz, file, line)
39490792Sgshapiro	register int sz;
39590792Sgshapiro	char *file;
39690792Sgshapiro	int line;
39790792Sgshapiro#else /* SM_HEAP_CHECK */
39890792Sgshapiroxalloc(sz)
39990792Sgshapiro	register int sz;
40090792Sgshapiro#endif /* SM_HEAP_CHECK */
40177349Sgshapiro{
40277349Sgshapiro	register char *p;
40377349Sgshapiro
40477349Sgshapiro	/* some systems can't handle size zero mallocs */
40577349Sgshapiro	if (sz <= 0)
40677349Sgshapiro		sz = 1;
40777349Sgshapiro
40890792Sgshapiro	/* scaffolding for testing error handling code */
40990792Sgshapiro	sm_xtrap_raise_x(&SmHeapOutOfMemory);
41090792Sgshapiro
41190792Sgshapiro	p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
41277349Sgshapiro	if (p == NULL)
41377349Sgshapiro	{
41490792Sgshapiro		sm_exc_raise_x(&SmHeapOutOfMemory);
41577349Sgshapiro	}
41677349Sgshapiro	return p;
41777349Sgshapiro}
41890792Sgshapiro/*
41938032Speter**  COPYPLIST -- copy list of pointers.
42038032Speter**
42190792Sgshapiro**	This routine is the equivalent of strdup for lists of
42238032Speter**	pointers.
42338032Speter**
42438032Speter**	Parameters:
42538032Speter**		list -- list of pointers to copy.
42638032Speter**			Must be NULL terminated.
42790792Sgshapiro**		copycont -- if true, copy the contents of the vector
42838032Speter**			(which must be a string) also.
42990792Sgshapiro**		rpool -- resource pool from which to allocate storage,
43090792Sgshapiro**			or NULL
43138032Speter**
43238032Speter**	Returns:
43338032Speter**		a copy of 'list'.
43438032Speter*/
43538032Speter
43638032Speterchar **
43790792Sgshapirocopyplist(list, copycont, rpool)
43838032Speter	char **list;
43938032Speter	bool copycont;
44090792Sgshapiro	SM_RPOOL_T *rpool;
44138032Speter{
44238032Speter	register char **vp;
44338032Speter	register char **newvp;
44438032Speter
44538032Speter	for (vp = list; *vp != NULL; vp++)
44638032Speter		continue;
44738032Speter
44838032Speter	vp++;
44938032Speter
45090792Sgshapiro	newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp);
45164562Sgshapiro	memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
45238032Speter
45338032Speter	if (copycont)
45438032Speter	{
45538032Speter		for (vp = newvp; *vp != NULL; vp++)
45690792Sgshapiro			*vp = sm_rpool_strdup_x(rpool, *vp);
45738032Speter	}
45838032Speter
45964562Sgshapiro	return newvp;
46038032Speter}
46190792Sgshapiro/*
46238032Speter**  COPYQUEUE -- copy address queue.
46338032Speter**
46490792Sgshapiro**	This routine is the equivalent of strdup for address queues;
46564562Sgshapiro**	addresses marked as QS_IS_DEAD() aren't copied
46638032Speter**
46738032Speter**	Parameters:
46838032Speter**		addr -- list of address structures to copy.
46990792Sgshapiro**		rpool -- resource pool from which to allocate storage
47038032Speter**
47138032Speter**	Returns:
47238032Speter**		a copy of 'addr'.
47338032Speter*/
47438032Speter
47538032SpeterADDRESS *
47690792Sgshapirocopyqueue(addr, rpool)
47738032Speter	ADDRESS *addr;
47890792Sgshapiro	SM_RPOOL_T *rpool;
47938032Speter{
48038032Speter	register ADDRESS *newaddr;
48138032Speter	ADDRESS *ret;
48238032Speter	register ADDRESS **tail = &ret;
48338032Speter
48438032Speter	while (addr != NULL)
48538032Speter	{
48664562Sgshapiro		if (!QS_IS_DEAD(addr->q_state))
48738032Speter		{
48890792Sgshapiro			newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
48990792Sgshapiro							sizeof *newaddr);
49038032Speter			STRUCTCOPY(*addr, *newaddr);
49138032Speter			*tail = newaddr;
49238032Speter			tail = &newaddr->q_next;
49338032Speter		}
49438032Speter		addr = addr->q_next;
49538032Speter	}
49638032Speter	*tail = NULL;
49764562Sgshapiro
49838032Speter	return ret;
49938032Speter}
50090792Sgshapiro/*
50164562Sgshapiro**  LOG_SENDMAIL_PID -- record sendmail pid and command line.
50264562Sgshapiro**
50364562Sgshapiro**	Parameters:
50464562Sgshapiro**		e -- the current envelope.
50564562Sgshapiro**
50664562Sgshapiro**	Returns:
50764562Sgshapiro**		none.
50864562Sgshapiro**
50964562Sgshapiro**	Side Effects:
51090792Sgshapiro**		writes pidfile, logs command line.
51164562Sgshapiro*/
51264562Sgshapiro
51364562Sgshapirovoid
51464562Sgshapirolog_sendmail_pid(e)
51564562Sgshapiro	ENVELOPE *e;
51664562Sgshapiro{
51764562Sgshapiro	long sff;
51890792Sgshapiro	SM_FILE_T *pidf;
51998121Sgshapiro	char pidpath[MAXPATHLEN];
52090792Sgshapiro	extern char *CommandLineArgs;
52164562Sgshapiro
52264562Sgshapiro	/* write the pid to the log file for posterity */
52364562Sgshapiro	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
52464562Sgshapiro	if (TrustedUid != 0 && RealUid == TrustedUid)
52564562Sgshapiro		sff |= SFF_OPENASROOT;
52664562Sgshapiro	expand(PidFile, pidpath, sizeof pidpath, e);
52798121Sgshapiro	pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
52864562Sgshapiro	if (pidf == NULL)
52964562Sgshapiro	{
53073188Sgshapiro		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
53190792Sgshapiro			  pidpath, sm_errstring(errno));
53264562Sgshapiro	}
53364562Sgshapiro	else
53464562Sgshapiro	{
53577349Sgshapiro		pid_t pid;
53664562Sgshapiro
53777349Sgshapiro		pid = getpid();
53871345Sgshapiro
53964562Sgshapiro		/* write the process id on line 1 */
54090792Sgshapiro		(void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%ld\n",
54190792Sgshapiro				     (long) pid);
54264562Sgshapiro
54364562Sgshapiro		/* line 2 contains all command line flags */
54490792Sgshapiro		(void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%s\n",
54590792Sgshapiro				     CommandLineArgs);
54664562Sgshapiro
54764562Sgshapiro		/* flush and close */
54890792Sgshapiro		(void) sm_io_close(pidf, SM_TIME_DEFAULT);
54964562Sgshapiro	}
55090792Sgshapiro	if (LogLevel > 9)
55190792Sgshapiro		sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
55264562Sgshapiro}
55390792Sgshapiro/*
55464562Sgshapiro**  SET_DELIVERY_MODE -- set and record the delivery mode
55564562Sgshapiro**
55664562Sgshapiro**	Parameters:
55764562Sgshapiro**		mode -- delivery mode
55864562Sgshapiro**		e -- the current envelope.
55964562Sgshapiro**
56064562Sgshapiro**	Returns:
56164562Sgshapiro**		none.
56264562Sgshapiro**
56364562Sgshapiro**	Side Effects:
56490792Sgshapiro**		sets {deliveryMode} macro
56564562Sgshapiro*/
56664562Sgshapiro
56764562Sgshapirovoid
56864562Sgshapiroset_delivery_mode(mode, e)
56964562Sgshapiro	int mode;
57064562Sgshapiro	ENVELOPE *e;
57164562Sgshapiro{
57264562Sgshapiro	char buf[2];
57364562Sgshapiro
57490792Sgshapiro	e->e_sendmode = (char) mode;
57590792Sgshapiro	buf[0] = (char) mode;
57664562Sgshapiro	buf[1] = '\0';
57790792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
57864562Sgshapiro}
57990792Sgshapiro/*
58090792Sgshapiro**  SET_OP_MODE -- set and record the op mode
58190792Sgshapiro**
58290792Sgshapiro**	Parameters:
58390792Sgshapiro**		mode -- op mode
58490792Sgshapiro**		e -- the current envelope.
58590792Sgshapiro**
58690792Sgshapiro**	Returns:
58790792Sgshapiro**		none.
58890792Sgshapiro**
58990792Sgshapiro**	Side Effects:
59090792Sgshapiro**		sets {opMode} macro
59190792Sgshapiro*/
59290792Sgshapiro
59390792Sgshapirovoid
59490792Sgshapiroset_op_mode(mode)
59590792Sgshapiro	int mode;
59690792Sgshapiro{
59790792Sgshapiro	char buf[2];
59890792Sgshapiro	extern ENVELOPE BlankEnvelope;
59990792Sgshapiro
60090792Sgshapiro	OpMode = (char) mode;
60190792Sgshapiro	buf[0] = (char) mode;
60290792Sgshapiro	buf[1] = '\0';
60390792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
60490792Sgshapiro}
60590792Sgshapiro/*
60638032Speter**  PRINTAV -- print argument vector.
60738032Speter**
60838032Speter**	Parameters:
60938032Speter**		av -- argument vector.
61038032Speter**
61138032Speter**	Returns:
61238032Speter**		none.
61338032Speter**
61438032Speter**	Side Effects:
61538032Speter**		prints av.
61638032Speter*/
61738032Speter
61838032Spetervoid
61938032Speterprintav(av)
62038032Speter	register char **av;
62138032Speter{
62238032Speter	while (*av != NULL)
62338032Speter	{
62438032Speter		if (tTd(0, 44))
62590792Sgshapiro			sm_dprintf("\n\t%08lx=", (unsigned long) *av);
62638032Speter		else
62790792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, ' ');
62838032Speter		xputs(*av++);
62938032Speter	}
63090792Sgshapiro	(void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\n');
63138032Speter}
63290792Sgshapiro/*
63338032Speter**  XPUTS -- put string doing control escapes.
63438032Speter**
63538032Speter**	Parameters:
63638032Speter**		s -- string to put.
63738032Speter**
63838032Speter**	Returns:
63938032Speter**		none.
64038032Speter**
64138032Speter**	Side Effects:
64238032Speter**		output to stdout
64338032Speter*/
64438032Speter
64538032Spetervoid
64638032Speterxputs(s)
64738032Speter	register const char *s;
64838032Speter{
64938032Speter	register int c;
65038032Speter	register struct metamac *mp;
65190792Sgshapiro	bool shiftout = false;
65238032Speter	extern struct metamac MetaMacros[];
65390792Sgshapiro	static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
65490792Sgshapiro		"@(#)$Debug: ANSI - enable reverse video in debug output $");
65538032Speter
65690792Sgshapiro	/*
65790792Sgshapiro	**  TermEscape is set here, rather than in main(),
65890792Sgshapiro	**  because ANSI mode can be turned on or off at any time
65990792Sgshapiro	**  if we are in -bt rule testing mode.
66090792Sgshapiro	*/
66190792Sgshapiro
66290792Sgshapiro	if (sm_debug_unknown(&DebugANSI))
66390792Sgshapiro	{
66490792Sgshapiro		if (sm_debug_active(&DebugANSI, 1))
66590792Sgshapiro		{
66690792Sgshapiro			TermEscape.te_rv_on = "\033[7m";
66790792Sgshapiro			TermEscape.te_rv_off = "\033[0m";
66890792Sgshapiro		}
66990792Sgshapiro		else
67090792Sgshapiro		{
67190792Sgshapiro			TermEscape.te_rv_on = "";
67290792Sgshapiro			TermEscape.te_rv_off = "";
67390792Sgshapiro		}
67490792Sgshapiro	}
67590792Sgshapiro
67638032Speter	if (s == NULL)
67738032Speter	{
67890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s<null>%s",
67990792Sgshapiro				     TermEscape.te_rv_on, TermEscape.te_rv_off);
68038032Speter		return;
68138032Speter	}
68238032Speter	while ((c = (*s++ & 0377)) != '\0')
68338032Speter	{
68438032Speter		if (shiftout)
68538032Speter		{
68690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
68790792Sgshapiro					     TermEscape.te_rv_off);
68890792Sgshapiro			shiftout = false;
68938032Speter		}
69038032Speter		if (!isascii(c))
69138032Speter		{
69238032Speter			if (c == MATCHREPL)
69338032Speter			{
69490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
69590792Sgshapiro						     "%s$",
69690792Sgshapiro						     TermEscape.te_rv_on);
69790792Sgshapiro				shiftout = true;
69838032Speter				if (*s == '\0')
69938032Speter					continue;
70038032Speter				c = *s++ & 0377;
70138032Speter				goto printchar;
70238032Speter			}
70338032Speter			if (c == MACROEXPAND || c == MACRODEXPAND)
70438032Speter			{
70590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
70690792Sgshapiro						     "%s$",
70790792Sgshapiro						     TermEscape.te_rv_on);
70838032Speter				if (c == MACRODEXPAND)
70990792Sgshapiro					(void) sm_io_putc(smioout,
71090792Sgshapiro							  SM_TIME_DEFAULT, '&');
71190792Sgshapiro				shiftout = true;
71238032Speter				if (*s == '\0')
71338032Speter					continue;
71438032Speter				if (strchr("=~&?", *s) != NULL)
71590792Sgshapiro					(void) sm_io_putc(smioout,
71690792Sgshapiro							  SM_TIME_DEFAULT,
71790792Sgshapiro							  *s++);
71838032Speter				if (bitset(0200, *s))
71990792Sgshapiro					(void) sm_io_fprintf(smioout,
72090792Sgshapiro							     SM_TIME_DEFAULT,
72190792Sgshapiro							     "{%s}",
72290792Sgshapiro							     macname(bitidx(*s++)));
72338032Speter				else
72490792Sgshapiro					(void) sm_io_fprintf(smioout,
72590792Sgshapiro							     SM_TIME_DEFAULT,
72690792Sgshapiro							     "%c",
72790792Sgshapiro							     *s++);
72838032Speter				continue;
72938032Speter			}
73038032Speter			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
73138032Speter			{
73290792Sgshapiro				if (bitidx(mp->metaval) == c)
73338032Speter				{
73490792Sgshapiro					(void) sm_io_fprintf(smioout,
73590792Sgshapiro							     SM_TIME_DEFAULT,
73690792Sgshapiro							     "%s$%c",
73790792Sgshapiro							     TermEscape.te_rv_on,
73890792Sgshapiro							     mp->metaname);
73990792Sgshapiro					shiftout = true;
74038032Speter					break;
74138032Speter				}
74238032Speter			}
74338032Speter			if (c == MATCHCLASS || c == MATCHNCLASS)
74438032Speter			{
74538032Speter				if (bitset(0200, *s))
74690792Sgshapiro					(void) sm_io_fprintf(smioout,
74790792Sgshapiro							     SM_TIME_DEFAULT,
74890792Sgshapiro							     "{%s}",
74990792Sgshapiro							     macname(bitidx(*s++)));
75038032Speter				else if (*s != '\0')
75190792Sgshapiro					(void) sm_io_fprintf(smioout,
75290792Sgshapiro							     SM_TIME_DEFAULT,
75390792Sgshapiro							     "%c",
75490792Sgshapiro							     *s++);
75538032Speter			}
75638032Speter			if (mp->metaname != '\0')
75738032Speter				continue;
75838032Speter
75938032Speter			/* unrecognized meta character */
76090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%sM-",
76190792Sgshapiro					     TermEscape.te_rv_on);
76290792Sgshapiro			shiftout = true;
76338032Speter			c &= 0177;
76438032Speter		}
76538032Speter  printchar:
76638032Speter		if (isprint(c))
76738032Speter		{
76890792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
76938032Speter			continue;
77038032Speter		}
77138032Speter
77238032Speter		/* wasn't a meta-macro -- find another way to print it */
77338032Speter		switch (c)
77438032Speter		{
77538032Speter		  case '\n':
77638032Speter			c = 'n';
77738032Speter			break;
77838032Speter
77938032Speter		  case '\r':
78038032Speter			c = 'r';
78138032Speter			break;
78238032Speter
78338032Speter		  case '\t':
78438032Speter			c = 't';
78538032Speter			break;
78638032Speter		}
78738032Speter		if (!shiftout)
78838032Speter		{
78990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
79090792Sgshapiro					     TermEscape.te_rv_on);
79190792Sgshapiro			shiftout = true;
79238032Speter		}
79338032Speter		if (isprint(c))
79438032Speter		{
79590792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\\');
79690792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
79738032Speter		}
79838032Speter		else
79938032Speter		{
80090792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, '^');
80190792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c ^ 0100);
80238032Speter		}
80338032Speter	}
80438032Speter	if (shiftout)
80590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
80690792Sgshapiro				     TermEscape.te_rv_off);
80790792Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
80838032Speter}
80990792Sgshapiro/*
81038032Speter**  MAKELOWER -- Translate a line into lower case
81138032Speter**
81238032Speter**	Parameters:
81338032Speter**		p -- the string to translate.  If NULL, return is
81438032Speter**			immediate.
81538032Speter**
81638032Speter**	Returns:
81738032Speter**		none.
81838032Speter**
81938032Speter**	Side Effects:
82038032Speter**		String pointed to by p is translated to lower case.
82138032Speter*/
82238032Speter
82338032Spetervoid
82438032Spetermakelower(p)
82538032Speter	register char *p;
82638032Speter{
82738032Speter	register char c;
82838032Speter
82938032Speter	if (p == NULL)
83038032Speter		return;
83138032Speter	for (; (c = *p) != '\0'; p++)
83238032Speter		if (isascii(c) && isupper(c))
83338032Speter			*p = tolower(c);
83438032Speter}
83590792Sgshapiro/*
83638032Speter**  FIXCRLF -- fix <CR><LF> in line.
83738032Speter**
83838032Speter**	Looks for the <CR><LF> combination and turns it into the
83938032Speter**	UNIX canonical <NL> character.  It only takes one line,
84038032Speter**	i.e., it is assumed that the first <NL> found is the end
84138032Speter**	of the line.
84238032Speter**
84338032Speter**	Parameters:
84438032Speter**		line -- the line to fix.
84538032Speter**		stripnl -- if true, strip the newline also.
84638032Speter**
84738032Speter**	Returns:
84838032Speter**		none.
84938032Speter**
85038032Speter**	Side Effects:
85138032Speter**		line is changed in place.
85238032Speter*/
85338032Speter
85438032Spetervoid
85538032Speterfixcrlf(line, stripnl)
85638032Speter	char *line;
85738032Speter	bool stripnl;
85838032Speter{
85938032Speter	register char *p;
86038032Speter
86138032Speter	p = strchr(line, '\n');
86238032Speter	if (p == NULL)
86338032Speter		return;
86438032Speter	if (p > line && p[-1] == '\r')
86538032Speter		p--;
86638032Speter	if (!stripnl)
86738032Speter		*p++ = '\n';
86838032Speter	*p = '\0';
86938032Speter}
87090792Sgshapiro/*
87138032Speter**  PUTLINE -- put a line like fputs obeying SMTP conventions
87238032Speter**
87338032Speter**	This routine always guarantees outputing a newline (or CRLF,
87438032Speter**	as appropriate) at the end of the string.
87538032Speter**
87638032Speter**	Parameters:
87738032Speter**		l -- line to put.
87838032Speter**		mci -- the mailer connection information.
87938032Speter**
88038032Speter**	Returns:
88138032Speter**		none
88238032Speter**
88338032Speter**	Side Effects:
88490792Sgshapiro**		output of l to mci->mci_out.
88538032Speter*/
88638032Speter
88738032Spetervoid
88838032Speterputline(l, mci)
88938032Speter	register char *l;
89038032Speter	register MCI *mci;
89138032Speter{
89238032Speter	putxline(l, strlen(l), mci, PXLF_MAPFROM);
89338032Speter}
89490792Sgshapiro/*
89538032Speter**  PUTXLINE -- putline with flags bits.
89638032Speter**
89738032Speter**	This routine always guarantees outputing a newline (or CRLF,
89838032Speter**	as appropriate) at the end of the string.
89938032Speter**
90038032Speter**	Parameters:
90138032Speter**		l -- line to put.
90238032Speter**		len -- the length of the line.
90338032Speter**		mci -- the mailer connection information.
90438032Speter**		pxflags -- flag bits:
90538032Speter**		    PXLF_MAPFROM -- map From_ to >From_.
90638032Speter**		    PXLF_STRIP8BIT -- strip 8th bit.
90738032Speter**		    PXLF_HEADER -- map bare newline in header to newline space.
90894334Sgshapiro**		    PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
90938032Speter**
91038032Speter**	Returns:
91138032Speter**		none
91238032Speter**
91338032Speter**	Side Effects:
91490792Sgshapiro**		output of l to mci->mci_out.
91538032Speter*/
91638032Speter
91738032Spetervoid
91838032Speterputxline(l, len, mci, pxflags)
91938032Speter	register char *l;
92038032Speter	size_t len;
92138032Speter	register MCI *mci;
92238032Speter	int pxflags;
92338032Speter{
92490792Sgshapiro	bool dead = false;
92538032Speter	register char *p, *end;
92638032Speter	int slop = 0;
92738032Speter
92838032Speter	/* strip out 0200 bits -- these can look like TELNET protocol */
92938032Speter	if (bitset(MCIF_7BIT, mci->mci_flags) ||
93038032Speter	    bitset(PXLF_STRIP8BIT, pxflags))
93138032Speter	{
93238032Speter		register char svchar;
93338032Speter
93438032Speter		for (p = l; (svchar = *p) != '\0'; ++p)
93538032Speter			if (bitset(0200, svchar))
93638032Speter				*p = svchar &~ 0200;
93738032Speter	}
93838032Speter
93938032Speter	end = l + len;
94038032Speter	do
94138032Speter	{
94294334Sgshapiro		bool noeol = false;
94394334Sgshapiro
94438032Speter		/* find the end of the line */
94538032Speter		p = memchr(l, '\n', end - l);
94638032Speter		if (p == NULL)
94794334Sgshapiro		{
94838032Speter			p = end;
94994334Sgshapiro			noeol = true;
95094334Sgshapiro		}
95138032Speter
95238032Speter		if (TrafficLogFile != NULL)
95390792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
95490792Sgshapiro					     "%05d >>> ", (int) CurrentPid);
95538032Speter
95638032Speter		/* check for line overflow */
95738032Speter		while (mci->mci_mailer->m_linelimit > 0 &&
95838032Speter		       (p - l + slop) > mci->mci_mailer->m_linelimit)
95938032Speter		{
96038032Speter			char *l_base = l;
96138032Speter			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
96238032Speter
96338032Speter			if (l[0] == '.' && slop == 0 &&
96438032Speter			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
96538032Speter			{
96690792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
96790792Sgshapiro					       '.') == SM_IO_EOF)
96890792Sgshapiro					dead = true;
96971345Sgshapiro				else
97071345Sgshapiro				{
97171345Sgshapiro					/* record progress for DATA timeout */
97290792Sgshapiro					DataProgress = true;
97371345Sgshapiro				}
97438032Speter				if (TrafficLogFile != NULL)
97590792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
97690792Sgshapiro							  SM_TIME_DEFAULT, '.');
97738032Speter			}
97838032Speter			else if (l[0] == 'F' && slop == 0 &&
97938032Speter				 bitset(PXLF_MAPFROM, pxflags) &&
98038032Speter				 strncmp(l, "From ", 5) == 0 &&
98138032Speter				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
98238032Speter			{
98390792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
98490792Sgshapiro					       '>') == SM_IO_EOF)
98590792Sgshapiro					dead = true;
98671345Sgshapiro				else
98771345Sgshapiro				{
98871345Sgshapiro					/* record progress for DATA timeout */
98990792Sgshapiro					DataProgress = true;
99071345Sgshapiro				}
99138032Speter				if (TrafficLogFile != NULL)
99290792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
99390792Sgshapiro							  SM_TIME_DEFAULT,
99490792Sgshapiro							  '>');
99538032Speter			}
99664562Sgshapiro			if (dead)
99764562Sgshapiro				break;
99864562Sgshapiro
99938032Speter			while (l < q)
100038032Speter			{
100190792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
100290792Sgshapiro					       (unsigned char) *l++) == SM_IO_EOF)
100364562Sgshapiro				{
100490792Sgshapiro					dead = true;
100564562Sgshapiro					break;
100664562Sgshapiro				}
100771345Sgshapiro				else
100871345Sgshapiro				{
100971345Sgshapiro					/* record progress for DATA timeout */
101090792Sgshapiro					DataProgress = true;
101171345Sgshapiro				}
101238032Speter			}
101364562Sgshapiro			if (dead)
101464562Sgshapiro				break;
101564562Sgshapiro
101690792Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') ==
101790792Sgshapiro			    SM_IO_EOF ||
101890792Sgshapiro			    sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
101990792Sgshapiro					mci->mci_mailer->m_eol) ==
102090792Sgshapiro			    SM_IO_EOF ||
102190792Sgshapiro			    sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') ==
102290792Sgshapiro			    SM_IO_EOF)
102364562Sgshapiro			{
102490792Sgshapiro				dead = true;
102564562Sgshapiro				break;
102664562Sgshapiro			}
102771345Sgshapiro			else
102871345Sgshapiro			{
102971345Sgshapiro				/* record progress for DATA timeout */
103090792Sgshapiro				DataProgress = true;
103171345Sgshapiro			}
103238032Speter			if (TrafficLogFile != NULL)
103338032Speter			{
103438032Speter				for (l = l_base; l < q; l++)
103590792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
103690792Sgshapiro							  SM_TIME_DEFAULT,
103790792Sgshapiro							  (unsigned char)*l);
103890792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
103990792Sgshapiro						     SM_TIME_DEFAULT,
104090792Sgshapiro						     "!\n%05d >>>  ",
104190792Sgshapiro						     (int) CurrentPid);
104238032Speter			}
104338032Speter			slop = 1;
104438032Speter		}
104538032Speter
104664562Sgshapiro		if (dead)
104764562Sgshapiro			break;
104864562Sgshapiro
104938032Speter		/* output last part */
105038032Speter		if (l[0] == '.' && slop == 0 &&
105138032Speter		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
105238032Speter		{
105390792Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
105490792Sgshapiro			    SM_IO_EOF)
105564562Sgshapiro				break;
105671345Sgshapiro			else
105771345Sgshapiro			{
105871345Sgshapiro				/* record progress for DATA timeout */
105990792Sgshapiro				DataProgress = true;
106071345Sgshapiro			}
106138032Speter			if (TrafficLogFile != NULL)
106290792Sgshapiro				(void) sm_io_putc(TrafficLogFile,
106390792Sgshapiro						  SM_TIME_DEFAULT, '.');
106438032Speter		}
106538032Speter		else if (l[0] == 'F' && slop == 0 &&
106638032Speter			 bitset(PXLF_MAPFROM, pxflags) &&
106738032Speter			 strncmp(l, "From ", 5) == 0 &&
106838032Speter			 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
106938032Speter		{
107090792Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
107190792Sgshapiro			    SM_IO_EOF)
107264562Sgshapiro				break;
107371345Sgshapiro			else
107471345Sgshapiro			{
107571345Sgshapiro				/* record progress for DATA timeout */
107690792Sgshapiro				DataProgress = true;
107771345Sgshapiro			}
107838032Speter			if (TrafficLogFile != NULL)
107990792Sgshapiro				(void) sm_io_putc(TrafficLogFile,
108090792Sgshapiro						  SM_TIME_DEFAULT, '>');
108138032Speter		}
108238032Speter		for ( ; l < p; ++l)
108338032Speter		{
108438032Speter			if (TrafficLogFile != NULL)
108590792Sgshapiro				(void) sm_io_putc(TrafficLogFile,
108690792Sgshapiro						  SM_TIME_DEFAULT,
108790792Sgshapiro						  (unsigned char)*l);
108890792Sgshapiro			if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
108990792Sgshapiro				       (unsigned char) *l) == SM_IO_EOF)
109064562Sgshapiro			{
109190792Sgshapiro				dead = true;
109264562Sgshapiro				break;
109364562Sgshapiro			}
109471345Sgshapiro			else
109571345Sgshapiro			{
109671345Sgshapiro				/* record progress for DATA timeout */
109790792Sgshapiro				DataProgress = true;
109871345Sgshapiro			}
109938032Speter		}
110064562Sgshapiro		if (dead)
110164562Sgshapiro			break;
110264562Sgshapiro
110338032Speter		if (TrafficLogFile != NULL)
110490792Sgshapiro			(void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
110590792Sgshapiro					  '\n');
110694334Sgshapiro		if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) &&
110794334Sgshapiro		    sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
110890792Sgshapiro				mci->mci_mailer->m_eol) == SM_IO_EOF)
110964562Sgshapiro			break;
111071345Sgshapiro		else
111171345Sgshapiro		{
111271345Sgshapiro			/* record progress for DATA timeout */
111390792Sgshapiro			DataProgress = true;
111471345Sgshapiro		}
111538032Speter		if (l < end && *l == '\n')
111638032Speter		{
111738032Speter			if (*++l != ' ' && *l != '\t' && *l != '\0' &&
111838032Speter			    bitset(PXLF_HEADER, pxflags))
111938032Speter			{
112090792Sgshapiro				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
112190792Sgshapiro					       ' ') == SM_IO_EOF)
112264562Sgshapiro					break;
112371345Sgshapiro				else
112471345Sgshapiro				{
112571345Sgshapiro					/* record progress for DATA timeout */
112690792Sgshapiro					DataProgress = true;
112771345Sgshapiro				}
112890792Sgshapiro
112938032Speter				if (TrafficLogFile != NULL)
113090792Sgshapiro					(void) sm_io_putc(TrafficLogFile,
113190792Sgshapiro							  SM_TIME_DEFAULT, ' ');
113238032Speter			}
113338032Speter		}
113490792Sgshapiro
113590792Sgshapiro		/* record progress for DATA timeout */
113690792Sgshapiro		DataProgress = true;
113738032Speter	} while (l < end);
113838032Speter}
113990792Sgshapiro/*
114038032Speter**  XUNLINK -- unlink a file, doing logging as appropriate.
114138032Speter**
114238032Speter**	Parameters:
114338032Speter**		f -- name of file to unlink.
114438032Speter**
114538032Speter**	Returns:
114690792Sgshapiro**		return value of unlink()
114738032Speter**
114838032Speter**	Side Effects:
114938032Speter**		f is unlinked.
115038032Speter*/
115138032Speter
115290792Sgshapiroint
115338032Speterxunlink(f)
115438032Speter	char *f;
115538032Speter{
115638032Speter	register int i;
115790792Sgshapiro	int save_errno;
115838032Speter
115938032Speter	if (LogLevel > 98)
116090792Sgshapiro		sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
116138032Speter
116238032Speter	i = unlink(f);
116390792Sgshapiro	save_errno = errno;
116438032Speter	if (i < 0 && LogLevel > 97)
116590792Sgshapiro		sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
116664562Sgshapiro			  f, errno);
116790792Sgshapiro	if (i >= 0)
116890792Sgshapiro		SYNC_DIR(f, false);
116990792Sgshapiro	errno = save_errno;
117090792Sgshapiro	return i;
117138032Speter}
117290792Sgshapiro/*
117338032Speter**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
117438032Speter**
117538032Speter**	Parameters:
117638032Speter**		buf -- place to put the input line.
117738032Speter**		siz -- size of buf.
117838032Speter**		fp -- file to read from.
117938032Speter**		timeout -- the timeout before error occurs.
118038032Speter**		during -- what we are trying to read (for error messages).
118138032Speter**
118238032Speter**	Returns:
118390792Sgshapiro**		NULL on error (including timeout).  This may also leave
118438032Speter**			buf containing a null string.
118538032Speter**		buf otherwise.
118638032Speter*/
118738032Speter
118864562Sgshapiro
118938032Speterchar *
119038032Spetersfgets(buf, siz, fp, timeout, during)
119138032Speter	char *buf;
119238032Speter	int siz;
119390792Sgshapiro	SM_FILE_T *fp;
119438032Speter	time_t timeout;
119538032Speter	char *during;
119638032Speter{
119738032Speter	register char *p;
119843730Speter	int save_errno;
119990792Sgshapiro	int io_timeout;
120038032Speter
120190792Sgshapiro	SM_REQUIRE(siz > 0);
120290792Sgshapiro	SM_REQUIRE(buf != NULL);
120390792Sgshapiro
120438032Speter	if (fp == NULL)
120538032Speter	{
120638032Speter		buf[0] = '\0';
120790792Sgshapiro		errno = EBADF;
120838032Speter		return NULL;
120938032Speter	}
121038032Speter
121190792Sgshapiro	/* try to read */
121290792Sgshapiro	p = NULL;
121390792Sgshapiro	errno = 0;
121490792Sgshapiro
121590792Sgshapiro	/* convert the timeout to sm_io notation */
121690792Sgshapiro	io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
121790792Sgshapiro	while (!sm_io_eof(fp) && !sm_io_error(fp))
121838032Speter	{
121990792Sgshapiro		errno = 0;
122090792Sgshapiro		p = sm_io_fgets(fp, io_timeout, buf, siz);
122190792Sgshapiro		if (p == NULL && errno == EAGAIN)
122238032Speter		{
122390792Sgshapiro			/* The sm_io_fgets() call timedout */
122438032Speter			if (LogLevel > 1)
122538032Speter				sm_syslog(LOG_NOTICE, CurEnv->e_id,
122664562Sgshapiro					  "timeout waiting for input from %.100s during %s",
122790792Sgshapiro					  CURHOSTNAME,
122864562Sgshapiro					  during);
122938032Speter			buf[0] = '\0';
123038032Speter#if XDEBUG
123138032Speter			checkfd012(during);
123264562Sgshapiro#endif /* XDEBUG */
123338032Speter			if (TrafficLogFile != NULL)
123490792Sgshapiro				(void) sm_io_fprintf(TrafficLogFile,
123590792Sgshapiro						     SM_TIME_DEFAULT,
123690792Sgshapiro						     "%05d <<< [TIMEOUT]\n",
123790792Sgshapiro						     (int) CurrentPid);
123890792Sgshapiro			errno = ETIMEDOUT;
123964562Sgshapiro			return NULL;
124038032Speter		}
124138032Speter		if (p != NULL || errno != EINTR)
124238032Speter			break;
124390792Sgshapiro		(void) sm_io_clearerr(fp);
124438032Speter	}
124543730Speter	save_errno = errno;
124638032Speter
124738032Speter	/* clean up the books and exit */
124838032Speter	LineNumber++;
124938032Speter	if (p == NULL)
125038032Speter	{
125138032Speter		buf[0] = '\0';
125238032Speter		if (TrafficLogFile != NULL)
125390792Sgshapiro			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
125490792Sgshapiro					     "%05d <<< [EOF]\n",
125590792Sgshapiro					     (int) CurrentPid);
125643730Speter		errno = save_errno;
125764562Sgshapiro		return NULL;
125838032Speter	}
125938032Speter	if (TrafficLogFile != NULL)
126090792Sgshapiro		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
126190792Sgshapiro				     "%05d <<< %s", (int) CurrentPid, buf);
126238032Speter	if (SevenBitInput)
126338032Speter	{
126438032Speter		for (p = buf; *p != '\0'; p++)
126538032Speter			*p &= ~0200;
126638032Speter	}
126738032Speter	else if (!HasEightBits)
126838032Speter	{
126938032Speter		for (p = buf; *p != '\0'; p++)
127038032Speter		{
127138032Speter			if (bitset(0200, *p))
127238032Speter			{
127390792Sgshapiro				HasEightBits = true;
127438032Speter				break;
127538032Speter			}
127638032Speter		}
127738032Speter	}
127864562Sgshapiro	return buf;
127938032Speter}
128090792Sgshapiro/*
128190792Sgshapiro**  FGETFOLDED -- like fgets, but knows about folded lines.
128238032Speter**
128338032Speter**	Parameters:
128438032Speter**		buf -- place to put result.
128538032Speter**		n -- bytes available.
128638032Speter**		f -- file to read from.
128738032Speter**
128838032Speter**	Returns:
128990792Sgshapiro**		input line(s) on success, NULL on error or SM_IO_EOF.
129038032Speter**		This will normally be buf -- unless the line is too
129190792Sgshapiro**			long, when it will be sm_malloc_x()ed.
129238032Speter**
129338032Speter**	Side Effects:
129438032Speter**		buf gets lines from f, with continuation lines (lines
129538032Speter**		with leading white space) appended.  CRLF's are mapped
129638032Speter**		into single newlines.  Any trailing NL is stripped.
129738032Speter*/
129838032Speter
129938032Speterchar *
130038032Speterfgetfolded(buf, n, f)
130138032Speter	char *buf;
130238032Speter	register int n;
130390792Sgshapiro	SM_FILE_T *f;
130438032Speter{
130538032Speter	register char *p = buf;
130638032Speter	char *bp = buf;
130738032Speter	register int i;
130838032Speter
130990792Sgshapiro	SM_REQUIRE(n > 0);
131090792Sgshapiro	SM_REQUIRE(buf != NULL);
131190792Sgshapiro	if (f == NULL)
131290792Sgshapiro	{
131390792Sgshapiro		buf[0] = '\0';
131490792Sgshapiro		errno = EBADF;
131590792Sgshapiro		return NULL;
131690792Sgshapiro	}
131790792Sgshapiro
131838032Speter	n--;
131990792Sgshapiro	while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
132038032Speter	{
132138032Speter		if (i == '\r')
132238032Speter		{
132390792Sgshapiro			i = sm_io_getc(f, SM_TIME_DEFAULT);
132438032Speter			if (i != '\n')
132538032Speter			{
132690792Sgshapiro				if (i != SM_IO_EOF)
132790792Sgshapiro					(void) sm_io_ungetc(f, SM_TIME_DEFAULT,
132890792Sgshapiro							    i);
132938032Speter				i = '\r';
133038032Speter			}
133138032Speter		}
133238032Speter		if (--n <= 0)
133338032Speter		{
133438032Speter			/* allocate new space */
133538032Speter			char *nbp;
133638032Speter			int nn;
133738032Speter
133838032Speter			nn = (p - bp);
133938032Speter			if (nn < MEMCHUNKSIZE)
134038032Speter				nn *= 2;
134138032Speter			else
134238032Speter				nn += MEMCHUNKSIZE;
134390792Sgshapiro			nbp = sm_malloc_x(nn);
134464562Sgshapiro			memmove(nbp, bp, p - bp);
134538032Speter			p = &nbp[p - bp];
134638032Speter			if (bp != buf)
134777349Sgshapiro				sm_free(bp);
134838032Speter			bp = nbp;
134938032Speter			n = nn - (p - bp);
135038032Speter		}
135138032Speter		*p++ = i;
135238032Speter		if (i == '\n')
135338032Speter		{
135438032Speter			LineNumber++;
135590792Sgshapiro			i = sm_io_getc(f, SM_TIME_DEFAULT);
135690792Sgshapiro			if (i != SM_IO_EOF)
135790792Sgshapiro				(void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
135838032Speter			if (i != ' ' && i != '\t')
135938032Speter				break;
136038032Speter		}
136138032Speter	}
136238032Speter	if (p == bp)
136364562Sgshapiro		return NULL;
136438032Speter	if (p[-1] == '\n')
136538032Speter		p--;
136638032Speter	*p = '\0';
136764562Sgshapiro	return bp;
136838032Speter}
136990792Sgshapiro/*
137038032Speter**  CURTIME -- return current time.
137138032Speter**
137238032Speter**	Parameters:
137338032Speter**		none.
137438032Speter**
137538032Speter**	Returns:
137638032Speter**		the current time.
137738032Speter*/
137838032Speter
137938032Spetertime_t
138038032Spetercurtime()
138138032Speter{
138238032Speter	auto time_t t;
138338032Speter
138438032Speter	(void) time(&t);
138564562Sgshapiro	return t;
138638032Speter}
138790792Sgshapiro/*
138838032Speter**  ATOBOOL -- convert a string representation to boolean.
138938032Speter**
139090792Sgshapiro**	Defaults to false
139138032Speter**
139238032Speter**	Parameters:
139390792Sgshapiro**		s -- string to convert.  Takes "tTyY", empty, and NULL as true,
139438032Speter**			others as false.
139538032Speter**
139638032Speter**	Returns:
139738032Speter**		A boolean representation of the string.
139838032Speter*/
139938032Speter
140038032Speterbool
140138032Speteratobool(s)
140238032Speter	register char *s;
140338032Speter{
140438032Speter	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
140590792Sgshapiro		return true;
140690792Sgshapiro	return false;
140738032Speter}
140890792Sgshapiro/*
140938032Speter**  ATOOCT -- convert a string representation to octal.
141038032Speter**
141138032Speter**	Parameters:
141238032Speter**		s -- string to convert.
141338032Speter**
141438032Speter**	Returns:
141538032Speter**		An integer representing the string interpreted as an
141638032Speter**		octal number.
141738032Speter*/
141838032Speter
141938032Speterint
142038032Speteratooct(s)
142138032Speter	register char *s;
142238032Speter{
142338032Speter	register int i = 0;
142438032Speter
142538032Speter	while (*s >= '0' && *s <= '7')
142638032Speter		i = (i << 3) | (*s++ - '0');
142764562Sgshapiro	return i;
142838032Speter}
142990792Sgshapiro/*
143038032Speter**  BITINTERSECT -- tell if two bitmaps intersect
143138032Speter**
143238032Speter**	Parameters:
143338032Speter**		a, b -- the bitmaps in question
143438032Speter**
143538032Speter**	Returns:
143690792Sgshapiro**		true if they have a non-null intersection
143790792Sgshapiro**		false otherwise
143838032Speter*/
143938032Speter
144038032Speterbool
144138032Speterbitintersect(a, b)
144264562Sgshapiro	BITMAP256 a;
144364562Sgshapiro	BITMAP256 b;
144438032Speter{
144538032Speter	int i;
144638032Speter
144738032Speter	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
144871345Sgshapiro	{
144938032Speter		if ((a[i] & b[i]) != 0)
145090792Sgshapiro			return true;
145171345Sgshapiro	}
145290792Sgshapiro	return false;
145338032Speter}
145490792Sgshapiro/*
145538032Speter**  BITZEROP -- tell if a bitmap is all zero
145638032Speter**
145738032Speter**	Parameters:
145838032Speter**		map -- the bit map to check
145938032Speter**
146038032Speter**	Returns:
146190792Sgshapiro**		true if map is all zero.
146290792Sgshapiro**		false if there are any bits set in map.
146338032Speter*/
146438032Speter
146538032Speterbool
146638032Speterbitzerop(map)
146764562Sgshapiro	BITMAP256 map;
146838032Speter{
146938032Speter	int i;
147038032Speter
147138032Speter	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
147271345Sgshapiro	{
147338032Speter		if (map[i] != 0)
147490792Sgshapiro			return false;
147571345Sgshapiro	}
147690792Sgshapiro	return true;
147738032Speter}
147890792Sgshapiro/*
147938032Speter**  STRCONTAINEDIN -- tell if one string is contained in another
148038032Speter**
148138032Speter**	Parameters:
148290792Sgshapiro**		icase -- ignore case?
148338032Speter**		a -- possible substring.
148438032Speter**		b -- possible superstring.
148538032Speter**
148638032Speter**	Returns:
148790792Sgshapiro**		true if a is contained in b (case insensitive).
148890792Sgshapiro**		false otherwise.
148938032Speter*/
149038032Speter
149138032Speterbool
149290792Sgshapirostrcontainedin(icase, a, b)
149390792Sgshapiro	bool icase;
149438032Speter	register char *a;
149538032Speter	register char *b;
149638032Speter{
149738032Speter	int la;
149838032Speter	int lb;
149938032Speter	int c;
150038032Speter
150138032Speter	la = strlen(a);
150238032Speter	lb = strlen(b);
150338032Speter	c = *a;
150490792Sgshapiro	if (icase && isascii(c) && isupper(c))
150538032Speter		c = tolower(c);
150638032Speter	for (; lb-- >= la; b++)
150738032Speter	{
150890792Sgshapiro		if (icase)
150990792Sgshapiro		{
151090792Sgshapiro			if (*b != c &&
151190792Sgshapiro			    isascii(*b) && isupper(*b) && tolower(*b) != c)
151290792Sgshapiro				continue;
151390792Sgshapiro			if (sm_strncasecmp(a, b, la) == 0)
151490792Sgshapiro				return true;
151590792Sgshapiro		}
151690792Sgshapiro		else
151790792Sgshapiro		{
151890792Sgshapiro			if (*b != c)
151990792Sgshapiro				continue;
152090792Sgshapiro			if (strncmp(a, b, la) == 0)
152190792Sgshapiro				return true;
152290792Sgshapiro		}
152338032Speter	}
152490792Sgshapiro	return false;
152538032Speter}
152690792Sgshapiro/*
152738032Speter**  CHECKFD012 -- check low numbered file descriptors
152838032Speter**
152938032Speter**	File descriptors 0, 1, and 2 should be open at all times.
153038032Speter**	This routine verifies that, and fixes it if not true.
153138032Speter**
153238032Speter**	Parameters:
153338032Speter**		where -- a tag printed if the assertion failed
153438032Speter**
153538032Speter**	Returns:
153638032Speter**		none
153738032Speter*/
153838032Speter
153938032Spetervoid
154038032Spetercheckfd012(where)
154138032Speter	char *where;
154238032Speter{
154338032Speter#if XDEBUG
154438032Speter	register int i;
154538032Speter
154638032Speter	for (i = 0; i < 3; i++)
154738032Speter		fill_fd(i, where);
154838032Speter#endif /* XDEBUG */
154938032Speter}
155090792Sgshapiro/*
155138032Speter**  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
155238032Speter**
155338032Speter**	Parameters:
155438032Speter**		fd -- file descriptor to check.
155538032Speter**		where -- tag to print on failure.
155638032Speter**
155738032Speter**	Returns:
155838032Speter**		none.
155938032Speter*/
156038032Speter
156138032Spetervoid
156238032Spetercheckfdopen(fd, where)
156338032Speter	int fd;
156438032Speter	char *where;
156538032Speter{
156638032Speter#if XDEBUG
156738032Speter	struct stat st;
156838032Speter
156938032Speter	if (fstat(fd, &st) < 0 && errno == EBADF)
157038032Speter	{
157138032Speter		syserr("checkfdopen(%d): %s not open as expected!", fd, where);
157290792Sgshapiro		printopenfds(true);
157338032Speter	}
157464562Sgshapiro#endif /* XDEBUG */
157538032Speter}
157690792Sgshapiro/*
157738032Speter**  CHECKFDS -- check for new or missing file descriptors
157838032Speter**
157938032Speter**	Parameters:
158038032Speter**		where -- tag for printing.  If null, take a base line.
158138032Speter**
158238032Speter**	Returns:
158338032Speter**		none
158438032Speter**
158538032Speter**	Side Effects:
158638032Speter**		If where is set, shows changes since the last call.
158738032Speter*/
158838032Speter
158938032Spetervoid
159038032Spetercheckfds(where)
159138032Speter	char *where;
159238032Speter{
159338032Speter	int maxfd;
159438032Speter	register int fd;
159590792Sgshapiro	bool printhdr = true;
159638032Speter	int save_errno = errno;
159764562Sgshapiro	static BITMAP256 baseline;
159838032Speter	extern int DtableSize;
159938032Speter
160071345Sgshapiro	if (DtableSize > BITMAPBITS)
160171345Sgshapiro		maxfd = BITMAPBITS;
160238032Speter	else
160338032Speter		maxfd = DtableSize;
160438032Speter	if (where == NULL)
160538032Speter		clrbitmap(baseline);
160638032Speter
160738032Speter	for (fd = 0; fd < maxfd; fd++)
160838032Speter	{
160938032Speter		struct stat stbuf;
161038032Speter
161138032Speter		if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
161238032Speter		{
161338032Speter			if (!bitnset(fd, baseline))
161438032Speter				continue;
161538032Speter			clrbitn(fd, baseline);
161638032Speter		}
161738032Speter		else if (!bitnset(fd, baseline))
161838032Speter			setbitn(fd, baseline);
161938032Speter		else
162038032Speter			continue;
162138032Speter
162238032Speter		/* file state has changed */
162338032Speter		if (where == NULL)
162438032Speter			continue;
162538032Speter		if (printhdr)
162638032Speter		{
162738032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
162864562Sgshapiro				  "%s: changed fds:",
162964562Sgshapiro				  where);
163090792Sgshapiro			printhdr = false;
163138032Speter		}
163290792Sgshapiro		dumpfd(fd, true, true);
163338032Speter	}
163438032Speter	errno = save_errno;
163538032Speter}
163690792Sgshapiro/*
163738032Speter**  PRINTOPENFDS -- print the open file descriptors (for debugging)
163838032Speter**
163938032Speter**	Parameters:
164038032Speter**		logit -- if set, send output to syslog; otherwise
164138032Speter**			print for debugging.
164238032Speter**
164338032Speter**	Returns:
164438032Speter**		none.
164538032Speter*/
164638032Speter
164764562Sgshapiro#if NETINET || NETINET6
164864562Sgshapiro# include <arpa/inet.h>
164964562Sgshapiro#endif /* NETINET || NETINET6 */
165038032Speter
165138032Spetervoid
165238032Speterprintopenfds(logit)
165338032Speter	bool logit;
165438032Speter{
165538032Speter	register int fd;
165638032Speter	extern int DtableSize;
165738032Speter
165838032Speter	for (fd = 0; fd < DtableSize; fd++)
165990792Sgshapiro		dumpfd(fd, false, logit);
166038032Speter}
166190792Sgshapiro/*
166238032Speter**  DUMPFD -- dump a file descriptor
166338032Speter**
166438032Speter**	Parameters:
166538032Speter**		fd -- the file descriptor to dump.
166638032Speter**		printclosed -- if set, print a notification even if
166738032Speter**			it is closed; otherwise print nothing.
166838032Speter**		logit -- if set, send output to syslog instead of stdout.
166990792Sgshapiro**
167090792Sgshapiro**	Returns:
167190792Sgshapiro**		none.
167238032Speter*/
167338032Speter
167438032Spetervoid
167538032Speterdumpfd(fd, printclosed, logit)
167638032Speter	int fd;
167738032Speter	bool printclosed;
167838032Speter	bool logit;
167938032Speter{
168038032Speter	register char *p;
168138032Speter	char *hp;
168238032Speter#ifdef S_IFSOCK
168338032Speter	SOCKADDR sa;
168464562Sgshapiro#endif /* S_IFSOCK */
168538032Speter	auto SOCKADDR_LEN_T slen;
168638032Speter	int i;
168738032Speter#if STAT64 > 0
168838032Speter	struct stat64 st;
168964562Sgshapiro#else /* STAT64 > 0 */
169038032Speter	struct stat st;
169164562Sgshapiro#endif /* STAT64 > 0 */
169238032Speter	char buf[200];
169338032Speter
169438032Speter	p = buf;
169590792Sgshapiro	(void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
169638032Speter	p += strlen(p);
169738032Speter
169838032Speter	if (
169938032Speter#if STAT64 > 0
170038032Speter	    fstat64(fd, &st)
170164562Sgshapiro#else /* STAT64 > 0 */
170238032Speter	    fstat(fd, &st)
170364562Sgshapiro#endif /* STAT64 > 0 */
170438032Speter	    < 0)
170538032Speter	{
170638032Speter		if (errno != EBADF)
170738032Speter		{
170890792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p),
170990792Sgshapiro				"CANNOT STAT (%s)",
171090792Sgshapiro				sm_errstring(errno));
171138032Speter			goto printit;
171238032Speter		}
171338032Speter		else if (printclosed)
171438032Speter		{
171590792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
171638032Speter			goto printit;
171738032Speter		}
171838032Speter		return;
171938032Speter	}
172038032Speter
172194334Sgshapiro	i = fcntl(fd, F_GETFL, 0);
172238032Speter	if (i != -1)
172338032Speter	{
172490792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
172538032Speter		p += strlen(p);
172638032Speter	}
172738032Speter
172890792Sgshapiro	(void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
172990792Sgshapiro			(int) st.st_mode);
173038032Speter	p += strlen(p);
173138032Speter	switch (st.st_mode & S_IFMT)
173238032Speter	{
173338032Speter#ifdef S_IFSOCK
173438032Speter	  case S_IFSOCK:
173590792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
173638032Speter		p += strlen(p);
173764562Sgshapiro		memset(&sa, '\0', sizeof sa);
173838032Speter		slen = sizeof sa;
173938032Speter		if (getsockname(fd, &sa.sa, &slen) < 0)
174090792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
174190792Sgshapiro				 sm_errstring(errno));
174238032Speter		else
174338032Speter		{
174438032Speter			hp = hostnamebyanyaddr(&sa);
174564562Sgshapiro			if (hp == NULL)
174664562Sgshapiro			{
174764562Sgshapiro				/* EMPTY */
174864562Sgshapiro				/* do nothing */
174964562Sgshapiro			}
175064562Sgshapiro# if NETINET
175164562Sgshapiro			else if (sa.sa.sa_family == AF_INET)
175290792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
175390792Sgshapiro					"%s/%d", hp, ntohs(sa.sin.sin_port));
175464562Sgshapiro# endif /* NETINET */
175564562Sgshapiro# if NETINET6
175664562Sgshapiro			else if (sa.sa.sa_family == AF_INET6)
175790792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
175890792Sgshapiro					"%s/%d", hp, ntohs(sa.sin6.sin6_port));
175964562Sgshapiro# endif /* NETINET6 */
176038032Speter			else
176190792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
176290792Sgshapiro					"%s", hp);
176338032Speter		}
176438032Speter		p += strlen(p);
176590792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "->");
176638032Speter		p += strlen(p);
176738032Speter		slen = sizeof sa;
176838032Speter		if (getpeername(fd, &sa.sa, &slen) < 0)
176990792Sgshapiro			(void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
177090792Sgshapiro					sm_errstring(errno));
177138032Speter		else
177238032Speter		{
177338032Speter			hp = hostnamebyanyaddr(&sa);
177464562Sgshapiro			if (hp == NULL)
177564562Sgshapiro			{
177664562Sgshapiro				/* EMPTY */
177764562Sgshapiro				/* do nothing */
177864562Sgshapiro			}
177964562Sgshapiro# if NETINET
178064562Sgshapiro			else if (sa.sa.sa_family == AF_INET)
178190792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
178290792Sgshapiro					"%s/%d", hp, ntohs(sa.sin.sin_port));
178364562Sgshapiro# endif /* NETINET */
178464562Sgshapiro# if NETINET6
178564562Sgshapiro			else if (sa.sa.sa_family == AF_INET6)
178690792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
178790792Sgshapiro					"%s/%d", hp, ntohs(sa.sin6.sin6_port));
178864562Sgshapiro# endif /* NETINET6 */
178938032Speter			else
179090792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(buf, p),
179190792Sgshapiro					"%s", hp);
179238032Speter		}
179338032Speter		break;
179464562Sgshapiro#endif /* S_IFSOCK */
179538032Speter
179638032Speter	  case S_IFCHR:
179790792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
179838032Speter		p += strlen(p);
179938032Speter		goto defprint;
180038032Speter
180190792Sgshapiro#ifdef S_IFBLK
180238032Speter	  case S_IFBLK:
180390792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
180438032Speter		p += strlen(p);
180538032Speter		goto defprint;
180690792Sgshapiro#endif /* S_IFBLK */
180738032Speter
180838032Speter#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
180938032Speter	  case S_IFIFO:
181090792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
181138032Speter		p += strlen(p);
181238032Speter		goto defprint;
181364562Sgshapiro#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
181438032Speter
181538032Speter#ifdef S_IFDIR
181638032Speter	  case S_IFDIR:
181790792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
181838032Speter		p += strlen(p);
181938032Speter		goto defprint;
182064562Sgshapiro#endif /* S_IFDIR */
182138032Speter
182238032Speter#ifdef S_IFLNK
182338032Speter	  case S_IFLNK:
182490792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
182538032Speter		p += strlen(p);
182638032Speter		goto defprint;
182764562Sgshapiro#endif /* S_IFLNK */
182838032Speter
182938032Speter	  default:
183038032Speterdefprint:
183190792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p),
183290792Sgshapiro			 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
183390792Sgshapiro			 major(st.st_dev), minor(st.st_dev),
183490792Sgshapiro			 (ULONGLONG_T) st.st_ino,
183590792Sgshapiro			 (int) st.st_nlink, (int) st.st_uid,
183690792Sgshapiro			 (int) st.st_gid);
183790792Sgshapiro		p += strlen(p);
183890792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
183990792Sgshapiro			 (ULONGLONG_T) st.st_size);
184038032Speter		break;
184138032Speter	}
184238032Speter
184338032Speterprintit:
184438032Speter	if (logit)
184538032Speter		sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
184664562Sgshapiro			  "%.800s", buf);
184738032Speter	else
184890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf);
184938032Speter}
185090792Sgshapiro/*
185138032Speter**  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
185238032Speter**
185338032Speter**	Parameters:
185438032Speter**		host -- the host to shorten (stripped in place).
185538032Speter**
185638032Speter**	Returns:
185790792Sgshapiro**		place where string was truncated, NULL if not truncated.
185838032Speter*/
185938032Speter
186073188Sgshapirochar *
186138032Spetershorten_hostname(host)
186238032Speter	char host[];
186338032Speter{
186438032Speter	register char *p;
186538032Speter	char *mydom;
186638032Speter	int i;
186790792Sgshapiro	bool canon = false;
186838032Speter
186938032Speter	/* strip off final dot */
187090792Sgshapiro	i = strlen(host);
187190792Sgshapiro	p = &host[(i == 0) ? 0 : i - 1];
187238032Speter	if (*p == '.')
187338032Speter	{
187438032Speter		*p = '\0';
187590792Sgshapiro		canon = true;
187638032Speter	}
187738032Speter
187838032Speter	/* see if there is any domain at all -- if not, we are done */
187938032Speter	p = strchr(host, '.');
188038032Speter	if (p == NULL)
188173188Sgshapiro		return NULL;
188238032Speter
188338032Speter	/* yes, we have a domain -- see if it looks like us */
188438032Speter	mydom = macvalue('m', CurEnv);
188538032Speter	if (mydom == NULL)
188638032Speter		mydom = "";
188738032Speter	i = strlen(++p);
188890792Sgshapiro	if ((canon ? sm_strcasecmp(p, mydom)
188990792Sgshapiro		   : sm_strncasecmp(p, mydom, i)) == 0 &&
189090792Sgshapiro			(mydom[i] == '.' || mydom[i] == '\0'))
189173188Sgshapiro	{
189238032Speter		*--p = '\0';
189373188Sgshapiro		return p;
189473188Sgshapiro	}
189573188Sgshapiro	return NULL;
189638032Speter}
189790792Sgshapiro/*
189838032Speter**  PROG_OPEN -- open a program for reading
189938032Speter**
190038032Speter**	Parameters:
190138032Speter**		argv -- the argument list.
190238032Speter**		pfd -- pointer to a place to store the file descriptor.
190338032Speter**		e -- the current envelope.
190438032Speter**
190538032Speter**	Returns:
190638032Speter**		pid of the process -- -1 if it failed.
190738032Speter*/
190838032Speter
190977349Sgshapiropid_t
191038032Speterprog_open(argv, pfd, e)
191138032Speter	char **argv;
191238032Speter	int *pfd;
191338032Speter	ENVELOPE *e;
191438032Speter{
191577349Sgshapiro	pid_t pid;
191638032Speter	int i;
191764562Sgshapiro	int save_errno;
191890792Sgshapiro	int sff;
191990792Sgshapiro	int ret;
192038032Speter	int fdv[2];
192138032Speter	char *p, *q;
192298121Sgshapiro	char buf[MAXPATHLEN];
192338032Speter	extern int DtableSize;
192438032Speter
192538032Speter	if (pipe(fdv) < 0)
192638032Speter	{
192738032Speter		syserr("%s: cannot create pipe for stdout", argv[0]);
192838032Speter		return -1;
192938032Speter	}
193038032Speter	pid = fork();
193138032Speter	if (pid < 0)
193238032Speter	{
193338032Speter		syserr("%s: cannot fork", argv[0]);
193464562Sgshapiro		(void) close(fdv[0]);
193564562Sgshapiro		(void) close(fdv[1]);
193638032Speter		return -1;
193738032Speter	}
193838032Speter	if (pid > 0)
193938032Speter	{
194038032Speter		/* parent */
194164562Sgshapiro		(void) close(fdv[1]);
194238032Speter		*pfd = fdv[0];
194338032Speter		return pid;
194438032Speter	}
194538032Speter
194677349Sgshapiro	/* Reset global flags */
194777349Sgshapiro	RestartRequest = NULL;
194890792Sgshapiro	RestartWorkGroup = false;
194977349Sgshapiro	ShutdownRequest = NULL;
195077349Sgshapiro	PendingSignal = 0;
195190792Sgshapiro	CurrentPid = getpid();
195277349Sgshapiro
195390792Sgshapiro	/*
195490792Sgshapiro	**  Initialize exception stack and default exception
195590792Sgshapiro	**  handler for child process.
195690792Sgshapiro	*/
195790792Sgshapiro
195890792Sgshapiro	sm_exc_newthread(fatal_error);
195990792Sgshapiro
196090792Sgshapiro	/* child -- close stdin */
196190792Sgshapiro	(void) close(0);
196290792Sgshapiro
196338032Speter	/* stdout goes back to parent */
196464562Sgshapiro	(void) close(fdv[0]);
196538032Speter	if (dup2(fdv[1], 1) < 0)
196638032Speter	{
196738032Speter		syserr("%s: cannot dup2 for stdout", argv[0]);
196838032Speter		_exit(EX_OSERR);
196938032Speter	}
197064562Sgshapiro	(void) close(fdv[1]);
197138032Speter
197238032Speter	/* stderr goes to transcript if available */
197338032Speter	if (e->e_xfp != NULL)
197438032Speter	{
197564562Sgshapiro		int xfd;
197664562Sgshapiro
197790792Sgshapiro		xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
197864562Sgshapiro		if (xfd >= 0 && dup2(xfd, 2) < 0)
197938032Speter		{
198038032Speter			syserr("%s: cannot dup2 for stderr", argv[0]);
198138032Speter			_exit(EX_OSERR);
198238032Speter		}
198338032Speter	}
198438032Speter
198538032Speter	/* this process has no right to the queue file */
198638032Speter	if (e->e_lockfp != NULL)
198790792Sgshapiro		(void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL));
198838032Speter
198964562Sgshapiro	/* chroot to the program mailer directory, if defined */
199064562Sgshapiro	if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
199164562Sgshapiro	{
199264562Sgshapiro		expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
199364562Sgshapiro		if (chroot(buf) < 0)
199464562Sgshapiro		{
199564562Sgshapiro			syserr("prog_open: cannot chroot(%s)", buf);
199664562Sgshapiro			exit(EX_TEMPFAIL);
199764562Sgshapiro		}
199864562Sgshapiro		if (chdir("/") < 0)
199964562Sgshapiro		{
200064562Sgshapiro			syserr("prog_open: cannot chdir(/)");
200164562Sgshapiro			exit(EX_TEMPFAIL);
200264562Sgshapiro		}
200364562Sgshapiro	}
200464562Sgshapiro
200538032Speter	/* run as default user */
200638032Speter	endpwent();
200790792Sgshapiro	sm_mbdb_terminate();
200838032Speter	if (setgid(DefGid) < 0 && geteuid() == 0)
200964562Sgshapiro	{
201038032Speter		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
201164562Sgshapiro		exit(EX_TEMPFAIL);
201264562Sgshapiro	}
201338032Speter	if (setuid(DefUid) < 0 && geteuid() == 0)
201464562Sgshapiro	{
201538032Speter		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
201664562Sgshapiro		exit(EX_TEMPFAIL);
201764562Sgshapiro	}
201838032Speter
201938032Speter	/* run in some directory */
202038032Speter	if (ProgMailer != NULL)
202138032Speter		p = ProgMailer->m_execdir;
202238032Speter	else
202338032Speter		p = NULL;
202438032Speter	for (; p != NULL; p = q)
202538032Speter	{
202638032Speter		q = strchr(p, ':');
202738032Speter		if (q != NULL)
202838032Speter			*q = '\0';
202938032Speter		expand(p, buf, sizeof buf, e);
203038032Speter		if (q != NULL)
203138032Speter			*q++ = ':';
203238032Speter		if (buf[0] != '\0' && chdir(buf) >= 0)
203338032Speter			break;
203438032Speter	}
203538032Speter	if (p == NULL)
203638032Speter	{
203738032Speter		/* backup directories */
203838032Speter		if (chdir("/tmp") < 0)
203938032Speter			(void) chdir("/");
204038032Speter	}
204138032Speter
204290792Sgshapiro	/* Check safety of program to be run */
204390792Sgshapiro	sff = SFF_ROOTOK|SFF_EXECOK;
204490792Sgshapiro	if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
204590792Sgshapiro		sff |= SFF_NOGWFILES|SFF_NOWWFILES;
204690792Sgshapiro	if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
204790792Sgshapiro		sff |= SFF_NOPATHCHECK;
204890792Sgshapiro	else
204990792Sgshapiro		sff |= SFF_SAFEDIRPATH;
205090792Sgshapiro	ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
205190792Sgshapiro	if (ret != 0)
205290792Sgshapiro		sm_syslog(LOG_INFO, e->e_id,
205390792Sgshapiro			  "Warning: prog_open: program %s unsafe: %s",
205490792Sgshapiro			  argv[0], sm_errstring(ret));
205590792Sgshapiro
205638032Speter	/* arrange for all the files to be closed */
205738032Speter	for (i = 3; i < DtableSize; i++)
205838032Speter	{
205938032Speter		register int j;
206038032Speter
206138032Speter		if ((j = fcntl(i, F_GETFD, 0)) != -1)
206264562Sgshapiro			(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
206338032Speter	}
206438032Speter
206538032Speter	/* now exec the process */
206664562Sgshapiro	(void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
206738032Speter
206838032Speter	/* woops!  failed */
206964562Sgshapiro	save_errno = errno;
207038032Speter	syserr("%s: cannot exec", argv[0]);
207164562Sgshapiro	if (transienterror(save_errno))
207238032Speter		_exit(EX_OSERR);
207338032Speter	_exit(EX_CONFIG);
207438032Speter	return -1;	/* avoid compiler warning on IRIX */
207538032Speter}
207690792Sgshapiro/*
207764562Sgshapiro**  GET_COLUMN -- look up a Column in a line buffer
207838032Speter**
207938032Speter**	Parameters:
208038032Speter**		line -- the raw text line to search.
208138032Speter**		col -- the column number to fetch.
208238032Speter**		delim -- the delimiter between columns.  If null,
208338032Speter**			use white space.
208438032Speter**		buf -- the output buffer.
208538032Speter**		buflen -- the length of buf.
208638032Speter**
208738032Speter**	Returns:
208838032Speter**		buf if successful.
208938032Speter**		NULL otherwise.
209038032Speter*/
209138032Speter
209238032Speterchar *
209338032Speterget_column(line, col, delim, buf, buflen)
209438032Speter	char line[];
209538032Speter	int col;
209664562Sgshapiro	int delim;
209738032Speter	char buf[];
209838032Speter	int buflen;
209938032Speter{
210038032Speter	char *p;
210138032Speter	char *begin, *end;
210238032Speter	int i;
210338032Speter	char delimbuf[4];
210464562Sgshapiro
210590792Sgshapiro	if ((char) delim == '\0')
210690792Sgshapiro		(void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
210738032Speter	else
210838032Speter	{
210990792Sgshapiro		delimbuf[0] = (char) delim;
211038032Speter		delimbuf[1] = '\0';
211138032Speter	}
211238032Speter
211338032Speter	p = line;
211438032Speter	if (*p == '\0')
211538032Speter		return NULL;			/* line empty */
211690792Sgshapiro	if (*p == (char) delim && col == 0)
211738032Speter		return NULL;			/* first column empty */
211838032Speter
211938032Speter	begin = line;
212038032Speter
212190792Sgshapiro	if (col == 0 && (char) delim == '\0')
212238032Speter	{
212338032Speter		while (*begin != '\0' && isascii(*begin) && isspace(*begin))
212438032Speter			begin++;
212538032Speter	}
212638032Speter
212738032Speter	for (i = 0; i < col; i++)
212838032Speter	{
212938032Speter		if ((begin = strpbrk(begin, delimbuf)) == NULL)
213038032Speter			return NULL;		/* no such column */
213138032Speter		begin++;
213290792Sgshapiro		if ((char) delim == '\0')
213338032Speter		{
213438032Speter			while (*begin != '\0' && isascii(*begin) && isspace(*begin))
213538032Speter				begin++;
213638032Speter		}
213738032Speter	}
213864562Sgshapiro
213938032Speter	end = strpbrk(begin, delimbuf);
214038032Speter	if (end == NULL)
214138032Speter		i = strlen(begin);
214238032Speter	else
214338032Speter		i = end - begin;
214438032Speter	if (i >= buflen)
214538032Speter		i = buflen - 1;
214690792Sgshapiro	(void) sm_strlcpy(buf, begin, i + 1);
214738032Speter	return buf;
214838032Speter}
214990792Sgshapiro/*
215038032Speter**  CLEANSTRCPY -- copy string keeping out bogus characters
215138032Speter**
215238032Speter**	Parameters:
215338032Speter**		t -- "to" string.
215438032Speter**		f -- "from" string.
215538032Speter**		l -- length of space available in "to" string.
215638032Speter**
215738032Speter**	Returns:
215838032Speter**		none.
215938032Speter*/
216038032Speter
216138032Spetervoid
216238032Spetercleanstrcpy(t, f, l)
216338032Speter	register char *t;
216438032Speter	register char *f;
216538032Speter	int l;
216638032Speter{
216738032Speter	/* check for newlines and log if necessary */
216890792Sgshapiro	(void) denlstring(f, true, true);
216938032Speter
217064562Sgshapiro	if (l <= 0)
217164562Sgshapiro		syserr("!cleanstrcpy: length == 0");
217264562Sgshapiro
217338032Speter	l--;
217438032Speter	while (l > 0 && *f != '\0')
217538032Speter	{
217638032Speter		if (isascii(*f) &&
217738032Speter		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
217838032Speter		{
217938032Speter			l--;
218038032Speter			*t++ = *f;
218138032Speter		}
218238032Speter		f++;
218338032Speter	}
218438032Speter	*t = '\0';
218538032Speter}
218690792Sgshapiro/*
218738032Speter**  DENLSTRING -- convert newlines in a string to spaces
218838032Speter**
218938032Speter**	Parameters:
219038032Speter**		s -- the input string
219138032Speter**		strict -- if set, don't permit continuation lines.
219238032Speter**		logattacks -- if set, log attempted attacks.
219338032Speter**
219438032Speter**	Returns:
219538032Speter**		A pointer to a version of the string with newlines
219638032Speter**		mapped to spaces.  This should be copied.
219738032Speter*/
219838032Speter
219938032Speterchar *
220038032Speterdenlstring(s, strict, logattacks)
220138032Speter	char *s;
220238032Speter	bool strict;
220338032Speter	bool logattacks;
220438032Speter{
220538032Speter	register char *p;
220638032Speter	int l;
220738032Speter	static char *bp = NULL;
220838032Speter	static int bl = 0;
220938032Speter
221038032Speter	p = s;
221138032Speter	while ((p = strchr(p, '\n')) != NULL)
221238032Speter		if (strict || (*++p != ' ' && *p != '\t'))
221338032Speter			break;
221438032Speter	if (p == NULL)
221538032Speter		return s;
221638032Speter
221738032Speter	l = strlen(s) + 1;
221838032Speter	if (bl < l)
221938032Speter	{
222038032Speter		/* allocate more space */
222190792Sgshapiro		char *nbp = sm_pmalloc_x(l);
222290792Sgshapiro
222338032Speter		if (bp != NULL)
222477349Sgshapiro			sm_free(bp);
222590792Sgshapiro		bp = nbp;
222638032Speter		bl = l;
222738032Speter	}
222890792Sgshapiro	(void) sm_strlcpy(bp, s, l);
222938032Speter	for (p = bp; (p = strchr(p, '\n')) != NULL; )
223038032Speter		*p++ = ' ';
223138032Speter
223238032Speter	if (logattacks)
223338032Speter	{
223438032Speter		sm_syslog(LOG_NOTICE, CurEnv->e_id,
223564562Sgshapiro			  "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
223664562Sgshapiro			  RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
223764562Sgshapiro			  shortenstring(bp, MAXSHORTSTR));
223838032Speter	}
223938032Speter
224038032Speter	return bp;
224138032Speter}
224298841Sgshapiro
224390792Sgshapiro/*
224498841Sgshapiro**  STRREPLNONPRT -- replace "unprintable" characters in a string with subst
224598841Sgshapiro**
224698841Sgshapiro**	Parameters:
224798841Sgshapiro**		s -- string to manipulate (in place)
224898841Sgshapiro**		subst -- character to use as replacement
224998841Sgshapiro**
225098841Sgshapiro**	Returns:
225198841Sgshapiro**		true iff string did not contain "unprintable" characters
225298841Sgshapiro*/
225398841Sgshapiro
225498841Sgshapirobool
225598841Sgshapirostrreplnonprt(s, c)
225698841Sgshapiro	char *s;
225798841Sgshapiro	int c;
225898841Sgshapiro{
225998841Sgshapiro	bool ok;
226098841Sgshapiro
226198841Sgshapiro	ok = true;
226298841Sgshapiro	if (s == NULL)
226398841Sgshapiro		return ok;
226498841Sgshapiro	while (*s != '\0')
226598841Sgshapiro	{
226698841Sgshapiro		if (!(isascii(*s) && isprint(*s)))
226798841Sgshapiro		{
226898841Sgshapiro			*s = c;
226998841Sgshapiro			ok = false;
227098841Sgshapiro		}
227198841Sgshapiro		++s;
227298841Sgshapiro	}
227398841Sgshapiro	return ok;
227498841Sgshapiro}
227598841Sgshapiro
227698841Sgshapiro/*
227790792Sgshapiro**  STR2PRT -- convert "unprintable" characters in a string to \oct
227890792Sgshapiro**
227990792Sgshapiro**	Parameters:
228090792Sgshapiro**		s -- string to convert
228190792Sgshapiro**
228290792Sgshapiro**	Returns:
228390792Sgshapiro**		converted string.
228490792Sgshapiro**		This is a static local buffer, string must be copied
228590792Sgshapiro**		before this function is called again!
228690792Sgshapiro*/
228790792Sgshapiro
228890792Sgshapirochar *
228990792Sgshapirostr2prt(s)
229090792Sgshapiro	char *s;
229190792Sgshapiro{
229290792Sgshapiro	int l;
229390792Sgshapiro	char c, *h;
229490792Sgshapiro	bool ok;
229590792Sgshapiro	static int len = 0;
229690792Sgshapiro	static char *buf = NULL;
229790792Sgshapiro
229890792Sgshapiro	if (s == NULL)
229990792Sgshapiro		return NULL;
230090792Sgshapiro	ok = true;
230190792Sgshapiro	for (h = s, l = 1; *h != '\0'; h++, l++)
230290792Sgshapiro	{
230390792Sgshapiro		if (*h == '\\')
230490792Sgshapiro		{
230590792Sgshapiro			++l;
230690792Sgshapiro			ok = false;
230790792Sgshapiro		}
230890792Sgshapiro		else if (!(isascii(*h) && isprint(*h)))
230990792Sgshapiro		{
231090792Sgshapiro			l += 3;
231190792Sgshapiro			ok = false;
231290792Sgshapiro		}
231390792Sgshapiro	}
231490792Sgshapiro	if (ok)
231590792Sgshapiro		return s;
231690792Sgshapiro	if (l > len)
231790792Sgshapiro	{
231890792Sgshapiro		char *nbuf = sm_pmalloc_x(l);
231990792Sgshapiro
232090792Sgshapiro		if (buf != NULL)
232190792Sgshapiro			sm_free(buf);
232290792Sgshapiro		len = l;
232390792Sgshapiro		buf = nbuf;
232490792Sgshapiro	}
232590792Sgshapiro	for (h = buf; *s != '\0' && l > 0; s++, l--)
232690792Sgshapiro	{
232790792Sgshapiro		c = *s;
232890792Sgshapiro		if (isascii(c) && isprint(c) && c != '\\')
232990792Sgshapiro		{
233090792Sgshapiro			*h++ = c;
233190792Sgshapiro		}
233290792Sgshapiro		else
233390792Sgshapiro		{
233490792Sgshapiro			*h++ = '\\';
233590792Sgshapiro			--l;
233690792Sgshapiro			switch (c)
233790792Sgshapiro			{
233890792Sgshapiro			  case '\\':
233990792Sgshapiro				*h++ = '\\';
234090792Sgshapiro				break;
234190792Sgshapiro			  case '\t':
234290792Sgshapiro				*h++ = 't';
234390792Sgshapiro				break;
234490792Sgshapiro			  case '\n':
234590792Sgshapiro				*h++ = 'n';
234690792Sgshapiro				break;
234790792Sgshapiro			  case '\r':
234890792Sgshapiro				*h++ = 'r';
234990792Sgshapiro				break;
235090792Sgshapiro			  default:
235190792Sgshapiro				(void) sm_snprintf(h, l, "%03o", (int) c);
235290792Sgshapiro
235390792Sgshapiro				/*
235490792Sgshapiro				**  XXX since l is unsigned this may
235590792Sgshapiro				**  wrap around if the calculation is screwed
235690792Sgshapiro				**  up...
235790792Sgshapiro				*/
235890792Sgshapiro
235990792Sgshapiro				l -= 2;
236090792Sgshapiro				h += 3;
236190792Sgshapiro				break;
236290792Sgshapiro			}
236390792Sgshapiro		}
236490792Sgshapiro	}
236590792Sgshapiro	*h = '\0';
236690792Sgshapiro	buf[len - 1] = '\0';
236790792Sgshapiro	return buf;
236890792Sgshapiro}
236990792Sgshapiro/*
237038032Speter**  PATH_IS_DIR -- check to see if file exists and is a directory.
237138032Speter**
237238032Speter**	There are some additional checks for security violations in
237338032Speter**	here.  This routine is intended to be used for the host status
237438032Speter**	support.
237538032Speter**
237638032Speter**	Parameters:
237738032Speter**		pathname -- pathname to check for directory-ness.
237838032Speter**		createflag -- if set, create directory if needed.
237938032Speter**
238038032Speter**	Returns:
238190792Sgshapiro**		true -- if the indicated pathname is a directory
238290792Sgshapiro**		false -- otherwise
238338032Speter*/
238438032Speter
238538032Speterint
238638032Speterpath_is_dir(pathname, createflag)
238738032Speter	char *pathname;
238838032Speter	bool createflag;
238938032Speter{
239038032Speter	struct stat statbuf;
239138032Speter
239238032Speter#if HASLSTAT
239338032Speter	if (lstat(pathname, &statbuf) < 0)
239464562Sgshapiro#else /* HASLSTAT */
239538032Speter	if (stat(pathname, &statbuf) < 0)
239664562Sgshapiro#endif /* HASLSTAT */
239738032Speter	{
239838032Speter		if (errno != ENOENT || !createflag)
239990792Sgshapiro			return false;
240038032Speter		if (mkdir(pathname, 0755) < 0)
240190792Sgshapiro			return false;
240290792Sgshapiro		return true;
240338032Speter	}
240438032Speter	if (!S_ISDIR(statbuf.st_mode))
240538032Speter	{
240638032Speter		errno = ENOTDIR;
240790792Sgshapiro		return false;
240838032Speter	}
240938032Speter
241038032Speter	/* security: don't allow writable directories */
241138032Speter	if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
241238032Speter	{
241338032Speter		errno = EACCES;
241490792Sgshapiro		return false;
241538032Speter	}
241690792Sgshapiro	return true;
241738032Speter}
241890792Sgshapiro/*
241938032Speter**  PROC_LIST_ADD -- add process id to list of our children
242038032Speter**
242138032Speter**	Parameters:
242238032Speter**		pid -- pid to add to list.
242364562Sgshapiro**		task -- task of pid.
242464562Sgshapiro**		type -- type of process.
242590792Sgshapiro**		count -- number of processes.
242690792Sgshapiro**		other -- other information for this type.
242738032Speter**
242838032Speter**	Returns:
242938032Speter**		none
243090792Sgshapiro**
243190792Sgshapiro**	Side Effects:
243290792Sgshapiro**		May increase CurChildren. May grow ProcList.
243338032Speter*/
243438032Speter
243590792Sgshapirotypedef struct procs	PROCS_T;
243642575Speter
243790792Sgshapirostruct procs
243890792Sgshapiro{
243990792Sgshapiro	pid_t	proc_pid;
244090792Sgshapiro	char	*proc_task;
244190792Sgshapiro	int	proc_type;
244290792Sgshapiro	int	proc_count;
244390792Sgshapiro	int	proc_other;
244490792Sgshapiro};
244590792Sgshapiro
244690792Sgshapirostatic PROCS_T	*volatile ProcListVec = NULL;
244790792Sgshapirostatic int	ProcListSize = 0;
244890792Sgshapiro
244938032Spetervoid
245090792Sgshapiroproc_list_add(pid, task, type, count, other)
245138032Speter	pid_t pid;
245242575Speter	char *task;
245364562Sgshapiro	int type;
245490792Sgshapiro	int count;
245590792Sgshapiro	int other;
245638032Speter{
245738032Speter	int i;
245838032Speter
245938032Speter	for (i = 0; i < ProcListSize; i++)
246038032Speter	{
246142575Speter		if (ProcListVec[i].proc_pid == NO_PID)
246238032Speter			break;
246338032Speter	}
246438032Speter	if (i >= ProcListSize)
246538032Speter	{
246638032Speter		/* probe the existing vector to avoid growing infinitely */
246738032Speter		proc_list_probe();
246838032Speter
246938032Speter		/* now scan again */
247038032Speter		for (i = 0; i < ProcListSize; i++)
247138032Speter		{
247242575Speter			if (ProcListVec[i].proc_pid == NO_PID)
247338032Speter				break;
247438032Speter		}
247538032Speter	}
247638032Speter	if (i >= ProcListSize)
247738032Speter	{
247838032Speter		/* grow process list */
247990792Sgshapiro		PROCS_T *npv;
248038032Speter
248190792Sgshapiro		SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
248290792Sgshapiro		npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) *
248390792Sgshapiro					       (ProcListSize + PROC_LIST_SEG));
248438032Speter		if (ProcListSize > 0)
248538032Speter		{
248664562Sgshapiro			memmove(npv, ProcListVec,
248790792Sgshapiro				ProcListSize * sizeof (PROCS_T));
248877349Sgshapiro			sm_free(ProcListVec);
248938032Speter		}
249090792Sgshapiro
249190792Sgshapiro		/* XXX just use memset() to initialize this part? */
249238032Speter		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
249342575Speter		{
249442575Speter			npv[i].proc_pid = NO_PID;
249542575Speter			npv[i].proc_task = NULL;
249664562Sgshapiro			npv[i].proc_type = PROC_NONE;
249742575Speter		}
249838032Speter		i = ProcListSize;
249938032Speter		ProcListSize += PROC_LIST_SEG;
250038032Speter		ProcListVec = npv;
250138032Speter	}
250242575Speter	ProcListVec[i].proc_pid = pid;
250390792Sgshapiro	PSTRSET(ProcListVec[i].proc_task, task);
250464562Sgshapiro	ProcListVec[i].proc_type = type;
250590792Sgshapiro	ProcListVec[i].proc_count = count;
250690792Sgshapiro	ProcListVec[i].proc_other = other;
250742575Speter
250842575Speter	/* if process adding itself, it's not a child */
250990792Sgshapiro	if (pid != CurrentPid)
251090792Sgshapiro	{
251190792Sgshapiro		SM_ASSERT(CurChildren < INT_MAX);
251242575Speter		CurChildren++;
251390792Sgshapiro	}
251438032Speter}
251590792Sgshapiro/*
251642575Speter**  PROC_LIST_SET -- set pid task in process list
251742575Speter**
251842575Speter**	Parameters:
251942575Speter**		pid -- pid to set
252042575Speter**		task -- task of pid
252142575Speter**
252242575Speter**	Returns:
252342575Speter**		none.
252442575Speter*/
252542575Speter
252642575Spetervoid
252742575Speterproc_list_set(pid, task)
252842575Speter	pid_t pid;
252942575Speter	char *task;
253042575Speter{
253142575Speter	int i;
253242575Speter
253342575Speter	for (i = 0; i < ProcListSize; i++)
253442575Speter	{
253542575Speter		if (ProcListVec[i].proc_pid == pid)
253642575Speter		{
253790792Sgshapiro			PSTRSET(ProcListVec[i].proc_task, task);
253842575Speter			break;
253942575Speter		}
254042575Speter	}
254142575Speter}
254290792Sgshapiro/*
254338032Speter**  PROC_LIST_DROP -- drop pid from process list
254438032Speter**
254538032Speter**	Parameters:
254638032Speter**		pid -- pid to drop
254790792Sgshapiro**		st -- process status
254890792Sgshapiro**		other -- storage for proc_other (return).
254938032Speter**
255038032Speter**	Returns:
255190792Sgshapiro**		none.
255277349Sgshapiro**
255390792Sgshapiro**	Side Effects:
255490792Sgshapiro**		May decrease CurChildren, CurRunners, or
255590792Sgshapiro**		set RestartRequest or ShutdownRequest.
255690792Sgshapiro**
255777349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
255877349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
255977349Sgshapiro**		DOING.
256038032Speter*/
256138032Speter
256290792Sgshapirovoid
256390792Sgshapiroproc_list_drop(pid, st, other)
256438032Speter	pid_t pid;
256590792Sgshapiro	int st;
256690792Sgshapiro	int *other;
256738032Speter{
256838032Speter	int i;
256964562Sgshapiro	int type = PROC_NONE;
257038032Speter
257138032Speter	for (i = 0; i < ProcListSize; i++)
257238032Speter	{
257342575Speter		if (ProcListVec[i].proc_pid == pid)
257438032Speter		{
257542575Speter			ProcListVec[i].proc_pid = NO_PID;
257664562Sgshapiro			type = ProcListVec[i].proc_type;
257790792Sgshapiro			if (other != NULL)
257890792Sgshapiro				*other = ProcListVec[i].proc_other;
257938032Speter			break;
258038032Speter		}
258138032Speter	}
258238032Speter	if (CurChildren > 0)
258338032Speter		CurChildren--;
258464562Sgshapiro
258564562Sgshapiro
258690792Sgshapiro	if (type == PROC_CONTROL && WIFEXITED(st))
258790792Sgshapiro	{
258890792Sgshapiro		/* if so, see if we need to restart or shutdown */
258990792Sgshapiro		if (WEXITSTATUS(st) == EX_RESTART)
259090792Sgshapiro			RestartRequest = "control socket";
259190792Sgshapiro		else if (WEXITSTATUS(st) == EX_SHUTDOWN)
259290792Sgshapiro			ShutdownRequest = "control socket";
259390792Sgshapiro	}
259490792Sgshapiro	else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
259590792Sgshapiro		 ProcListVec[i].proc_other > -1)
259690792Sgshapiro	{
259790792Sgshapiro		/* restart this persistent runner */
259890792Sgshapiro		mark_work_group_restart(ProcListVec[i].proc_other, st);
259990792Sgshapiro	}
260090792Sgshapiro	else if (type == PROC_QUEUE)
260190792Sgshapiro		CurRunners -= ProcListVec[i].proc_count;
260238032Speter}
260390792Sgshapiro/*
260438032Speter**  PROC_LIST_CLEAR -- clear the process list
260538032Speter**
260638032Speter**	Parameters:
260738032Speter**		none.
260838032Speter**
260938032Speter**	Returns:
261038032Speter**		none.
261190792Sgshapiro**
261290792Sgshapiro**	Side Effects:
261390792Sgshapiro**		Sets CurChildren to zero.
261438032Speter*/
261538032Speter
261638032Spetervoid
261738032Speterproc_list_clear()
261838032Speter{
261938032Speter	int i;
262038032Speter
262142575Speter	/* start from 1 since 0 is the daemon itself */
262242575Speter	for (i = 1; i < ProcListSize; i++)
262342575Speter		ProcListVec[i].proc_pid = NO_PID;
262438032Speter	CurChildren = 0;
262538032Speter}
262690792Sgshapiro/*
262738032Speter**  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
262838032Speter**
262938032Speter**	Parameters:
263038032Speter**		none
263138032Speter**
263238032Speter**	Returns:
263338032Speter**		none
263490792Sgshapiro**
263590792Sgshapiro**	Side Effects:
263690792Sgshapiro**		May decrease CurChildren.
263738032Speter*/
263838032Speter
263938032Spetervoid
264038032Speterproc_list_probe()
264138032Speter{
264238032Speter	int i;
264338032Speter
264442575Speter	/* start from 1 since 0 is the daemon itself */
264542575Speter	for (i = 1; i < ProcListSize; i++)
264638032Speter	{
264742575Speter		if (ProcListVec[i].proc_pid == NO_PID)
264838032Speter			continue;
264942575Speter		if (kill(ProcListVec[i].proc_pid, 0) < 0)
265038032Speter		{
265138032Speter			if (LogLevel > 3)
265238032Speter				sm_syslog(LOG_DEBUG, CurEnv->e_id,
265364562Sgshapiro					  "proc_list_probe: lost pid %d",
265464562Sgshapiro					  (int) ProcListVec[i].proc_pid);
265542575Speter			ProcListVec[i].proc_pid = NO_PID;
265690792Sgshapiro			SM_FREE_CLR(ProcListVec[i].proc_task);
265738032Speter			CurChildren--;
265838032Speter		}
265938032Speter	}
266038032Speter	if (CurChildren < 0)
266138032Speter		CurChildren = 0;
266238032Speter}
266390792Sgshapiro
266490792Sgshapiro/*
266542575Speter**  PROC_LIST_DISPLAY -- display the process list
266642575Speter**
266742575Speter**	Parameters:
266842575Speter**		out -- output file pointer
266990792Sgshapiro**		prefix -- string to output in front of each line.
267042575Speter**
267142575Speter**	Returns:
267242575Speter**		none.
267342575Speter*/
267442575Speter
267542575Spetervoid
267690792Sgshapiroproc_list_display(out, prefix)
267790792Sgshapiro	SM_FILE_T *out;
267890792Sgshapiro	char *prefix;
267942575Speter{
268042575Speter	int i;
268142575Speter
268242575Speter	for (i = 0; i < ProcListSize; i++)
268342575Speter	{
268442575Speter		if (ProcListVec[i].proc_pid == NO_PID)
268542575Speter			continue;
268642575Speter
268790792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
268890792Sgshapiro				     prefix,
268990792Sgshapiro				     (int) ProcListVec[i].proc_pid,
269090792Sgshapiro				     ProcListVec[i].proc_task != NULL ?
269190792Sgshapiro				     ProcListVec[i].proc_task : "(unknown)",
269290792Sgshapiro				     (OpMode == MD_SMTP ||
269390792Sgshapiro				      OpMode == MD_DAEMON ||
269490792Sgshapiro				      OpMode == MD_ARPAFTP) ? "\r" : "");
269542575Speter	}
269642575Speter}
269790792Sgshapiro
269890792Sgshapiro/*
269990792Sgshapiro**  PROC_LIST_SIGNAL -- send a signal to a type of process in the list
270080785Sgshapiro**
270180785Sgshapiro**	Parameters:
270290792Sgshapiro**		type -- type of process to signal
270390792Sgshapiro**		signal -- the type of signal to send
270480785Sgshapiro**
270590792Sgshapiro**	Results:
270690792Sgshapiro**		none.
270790792Sgshapiro**
270890792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
270990792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
271090792Sgshapiro**		DOING.
271180785Sgshapiro*/
271280785Sgshapiro
271390792Sgshapirovoid
271490792Sgshapiroproc_list_signal(type, signal)
271590792Sgshapiro	int type;
271690792Sgshapiro	int signal;
271780785Sgshapiro{
271890792Sgshapiro	int chldwasblocked;
271990792Sgshapiro	int alrmwasblocked;
272090792Sgshapiro	int i;
272190792Sgshapiro	pid_t mypid = getpid();
272280785Sgshapiro
272390792Sgshapiro	/* block these signals so that we may signal cleanly */
272490792Sgshapiro	chldwasblocked = sm_blocksignal(SIGCHLD);
272590792Sgshapiro	alrmwasblocked = sm_blocksignal(SIGALRM);
272680785Sgshapiro
272790792Sgshapiro	/* Find all processes of type and send signal */
272890792Sgshapiro	for (i = 0; i < ProcListSize; i++)
272980785Sgshapiro	{
273090792Sgshapiro		if (ProcListVec[i].proc_pid == NO_PID ||
273190792Sgshapiro		    ProcListVec[i].proc_pid == mypid)
273290792Sgshapiro			continue;
273390792Sgshapiro		if (ProcListVec[i].proc_type != type)
273490792Sgshapiro			continue;
273590792Sgshapiro		(void) kill(ProcListVec[i].proc_pid, signal);
273680785Sgshapiro	}
273780785Sgshapiro
273890792Sgshapiro	/* restore the signals */
273990792Sgshapiro	if (alrmwasblocked == 0)
274090792Sgshapiro		(void) sm_releasesignal(SIGALRM);
274190792Sgshapiro	if (chldwasblocked == 0)
274290792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
274380785Sgshapiro}
2744