util.c revision 71345
1/*
2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char id[] = "@(#)$Id: util.c,v 8.225.2.1.2.15 2000/10/18 23:46:07 ca Exp $";
16#endif /* ! lint */
17
18#include <sendmail.h>
19#include <sysexits.h>
20
21
22static void	readtimeout __P((time_t));
23
24/*
25**  STRIPQUOTES -- Strip quotes & quote bits from a string.
26**
27**	Runs through a string and strips off unquoted quote
28**	characters and quote bits.  This is done in place.
29**
30**	Parameters:
31**		s -- the string to strip.
32**
33**	Returns:
34**		none.
35**
36**	Side Effects:
37**		none.
38*/
39
40void
41stripquotes(s)
42	char *s;
43{
44	register char *p;
45	register char *q;
46	register char c;
47
48	if (s == NULL)
49		return;
50
51	p = q = s;
52	do
53	{
54		c = *p++;
55		if (c == '\\')
56			c = *p++;
57		else if (c == '"')
58			continue;
59		*q++ = c;
60	} while (c != '\0');
61}
62/*
63**  ADDQUOTES -- Adds quotes & quote bits to a string.
64**
65**	Runs through a string and adds characters and quote bits.
66**
67**	Parameters:
68**		s -- the string to modify.
69**
70**	Returns:
71**		pointer to quoted string.
72**
73**	Side Effects:
74**		none.
75**
76*/
77
78char *
79addquotes(s)
80	char *s;
81{
82	int len = 0;
83	char c;
84	char *p = s, *q, *r;
85
86	if (s == NULL)
87		return NULL;
88
89	/* Find length of quoted string */
90	while ((c = *p++) != '\0')
91	{
92		len++;
93		if (c == '\\' || c == '"')
94			len++;
95	}
96
97	q = r = xalloc(len + 3);
98	p = s;
99
100	/* add leading quote */
101	*q++ = '"';
102	while ((c = *p++) != '\0')
103	{
104		/* quote \ or " */
105		if (c == '\\' || c == '"')
106			*q++ = '\\';
107		*q++ = c;
108	}
109	*q++ = '"';
110	*q = '\0';
111	return r;
112}
113/*
114**  RFC822_STRING -- Checks string for proper RFC822 string quoting.
115**
116**	Runs through a string and verifies RFC822 special characters
117**	are only found inside comments, quoted strings, or backslash
118**	escaped.  Also verified balanced quotes and parenthesis.
119**
120**	Parameters:
121**		s -- the string to modify.
122**
123**	Returns:
124**		TRUE -- if the string is RFC822 compliant.
125**		FALSE -- if the string is not RFC822 compliant.
126**
127**	Side Effects:
128**		none.
129**
130*/
131
132bool
133rfc822_string(s)
134	char *s;
135{
136	bool quoted = FALSE;
137	int commentlev = 0;
138	char *c = s;
139
140	if (s == NULL)
141		return FALSE;
142
143	while (*c != '\0')
144	{
145		/* escaped character */
146		if (*c == '\\')
147		{
148			c++;
149			if (*c == '\0')
150				return FALSE;
151		}
152		else if (commentlev == 0 && *c == '"')
153			quoted = !quoted;
154		else if (!quoted)
155		{
156			if (*c == ')')
157			{
158				/* unbalanced ')' */
159				if (commentlev == 0)
160					return FALSE;
161				else
162					commentlev--;
163			}
164			else if (*c == '(')
165				commentlev++;
166			else if (commentlev == 0 &&
167				 strchr(MustQuoteChars, *c) != NULL)
168				return FALSE;
169		}
170		c++;
171	}
172	/* unbalanced '"' or '(' */
173	if (quoted || commentlev != 0)
174		return FALSE;
175	else
176		return TRUE;
177}
178/*
179**  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
180**
181**	Arbitrarily shorten (in place) an RFC822 string and rebalance
182**	comments and quotes.
183**
184**	Parameters:
185**		string -- the string to shorten
186**		length -- the maximum size, 0 if no maximum
187**
188**	Returns:
189**		TRUE if string is changed, FALSE otherwise
190**
191**	Side Effects:
192**		Changes string in place, possibly resulting
193**		in a shorter string.
194*/
195
196bool
197shorten_rfc822_string(string, length)
198	char *string;
199	size_t length;
200{
201	bool backslash = FALSE;
202	bool modified = FALSE;
203	bool quoted = FALSE;
204	size_t slen;
205	int parencount = 0;
206	char *ptr = string;
207
208	/*
209	**  If have to rebalance an already short enough string,
210	**  need to do it within allocated space.
211	*/
212
213	slen = strlen(string);
214	if (length == 0 || slen < length)
215		length = slen;
216
217	while (*ptr != '\0')
218	{
219		if (backslash)
220		{
221			backslash = FALSE;
222			goto increment;
223		}
224
225		if (*ptr == '\\')
226			backslash = TRUE;
227		else if (*ptr == '(')
228		{
229			if (!quoted)
230				parencount++;
231		}
232		else if (*ptr == ')')
233		{
234			if (--parencount < 0)
235				parencount = 0;
236		}
237
238		/* Inside a comment, quotes don't matter */
239		if (parencount <= 0 && *ptr == '"')
240			quoted = !quoted;
241
242increment:
243		/* Check for sufficient space for next character */
244		if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
245						parencount +
246						(quoted ? 1 : 0)))
247		{
248			/* Not enough, backtrack */
249			if (*ptr == '\\')
250				backslash = FALSE;
251			else if (*ptr == '(' && !quoted)
252				parencount--;
253			else if (*ptr == '"' && parencount == 0)
254				quoted = FALSE;
255			break;
256		}
257		ptr++;
258	}
259
260	/* Rebalance */
261	while (parencount-- > 0)
262	{
263		if (*ptr != ')')
264		{
265			modified = TRUE;
266			*ptr = ')';
267		}
268		ptr++;
269	}
270	if (quoted)
271	{
272		if (*ptr != '"')
273		{
274			modified = TRUE;
275			*ptr = '"';
276		}
277		ptr++;
278	}
279	if (*ptr != '\0')
280	{
281		modified = TRUE;
282		*ptr = '\0';
283	}
284	return modified;
285}
286/*
287**  FIND_CHARACTER -- find an unquoted character in an RFC822 string
288**
289**	Find an unquoted, non-commented character in an RFC822
290**	string and return a pointer to its location in the
291**	string.
292**
293**	Parameters:
294**		string -- the string to search
295**		character -- the character to find
296**
297**	Returns:
298**		pointer to the character, or
299**		a pointer to the end of the line if character is not found
300*/
301
302char *
303find_character(string, character)
304	char *string;
305	int character;
306{
307	bool backslash = FALSE;
308	bool quoted = FALSE;
309	int parencount = 0;
310
311	while (string != NULL && *string != '\0')
312	{
313		if (backslash)
314		{
315			backslash = FALSE;
316			if (!quoted && character == '\\' && *string == '\\')
317				break;
318			string++;
319			continue;
320		}
321		switch (*string)
322		{
323		  case '\\':
324			backslash = TRUE;
325			break;
326
327		  case '(':
328			if (!quoted)
329				parencount++;
330			break;
331
332		  case ')':
333			if (--parencount < 0)
334				parencount = 0;
335			break;
336		}
337
338		/* Inside a comment, nothing matters */
339		if (parencount > 0)
340		{
341			string++;
342			continue;
343		}
344
345		if (*string == '"')
346			quoted = !quoted;
347		else if (*string == character && !quoted)
348			break;
349		string++;
350	}
351
352	/* Return pointer to the character */
353	return string;
354}
355/*
356**  XALLOC -- Allocate memory and bitch wildly on failure.
357**
358**	THIS IS A CLUDGE.  This should be made to give a proper
359**	error -- but after all, what can we do?
360**
361**	Parameters:
362**		sz -- size of area to allocate.
363**
364**	Returns:
365**		pointer to data region.
366**
367**	Side Effects:
368**		Memory is allocated.
369*/
370
371char *
372xalloc(sz)
373	register int sz;
374{
375	register char *p;
376
377	/* some systems can't handle size zero mallocs */
378	if (sz <= 0)
379		sz = 1;
380
381	p = malloc((unsigned) sz);
382	if (p == NULL)
383	{
384		syserr("!Out of memory!!");
385		/* exit(EX_UNAVAILABLE); */
386	}
387	return p;
388}
389/*
390**  COPYPLIST -- copy list of pointers.
391**
392**	This routine is the equivalent of newstr for lists of
393**	pointers.
394**
395**	Parameters:
396**		list -- list of pointers to copy.
397**			Must be NULL terminated.
398**		copycont -- if TRUE, copy the contents of the vector
399**			(which must be a string) also.
400**
401**	Returns:
402**		a copy of 'list'.
403**
404**	Side Effects:
405**		none.
406*/
407
408char **
409copyplist(list, copycont)
410	char **list;
411	bool copycont;
412{
413	register char **vp;
414	register char **newvp;
415
416	for (vp = list; *vp != NULL; vp++)
417		continue;
418
419	vp++;
420
421	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
422	memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
423
424	if (copycont)
425	{
426		for (vp = newvp; *vp != NULL; vp++)
427			*vp = newstr(*vp);
428	}
429
430	return newvp;
431}
432/*
433**  COPYQUEUE -- copy address queue.
434**
435**	This routine is the equivalent of newstr for address queues
436**	addresses marked as QS_IS_DEAD() aren't copied
437**
438**	Parameters:
439**		addr -- list of address structures to copy.
440**
441**	Returns:
442**		a copy of 'addr'.
443**
444**	Side Effects:
445**		none.
446*/
447
448ADDRESS *
449copyqueue(addr)
450	ADDRESS *addr;
451{
452	register ADDRESS *newaddr;
453	ADDRESS *ret;
454	register ADDRESS **tail = &ret;
455
456	while (addr != NULL)
457	{
458		if (!QS_IS_DEAD(addr->q_state))
459		{
460			newaddr = (ADDRESS *) xalloc(sizeof *newaddr);
461			STRUCTCOPY(*addr, *newaddr);
462			*tail = newaddr;
463			tail = &newaddr->q_next;
464		}
465		addr = addr->q_next;
466	}
467	*tail = NULL;
468
469	return ret;
470}
471/*
472**  LOG_SENDMAIL_PID -- record sendmail pid and command line.
473**
474**	Parameters:
475**		e -- the current envelope.
476**
477**	Returns:
478**		none.
479**
480**	Side Effects:
481**		writes pidfile.
482*/
483
484void
485log_sendmail_pid(e)
486	ENVELOPE *e;
487{
488	long sff;
489	FILE *pidf;
490	char pidpath[MAXPATHLEN + 1];
491
492	/* write the pid to the log file for posterity */
493	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
494	if (TrustedUid != 0 && RealUid == TrustedUid)
495		sff |= SFF_OPENASROOT;
496	expand(PidFile, pidpath, sizeof pidpath, e);
497	pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, 0644, sff);
498	if (pidf == NULL)
499	{
500		sm_syslog(LOG_ERR, NOQID, "unable to write %s", pidpath);
501	}
502	else
503	{
504		long pid;
505		extern char *CommandLineArgs;
506
507		pid = (long) getpid();
508
509		/* write the process id on line 1 */
510		fprintf(pidf, "%ld\n", pid);
511
512		/* line 2 contains all command line flags */
513		fprintf(pidf, "%s\n", CommandLineArgs);
514
515		/* flush and close */
516		(void) fclose(pidf);
517	}
518}
519/*
520**  SET_DELIVERY_MODE -- set and record the delivery mode
521**
522**	Parameters:
523**		mode -- delivery mode
524**		e -- the current envelope.
525**
526**	Returns:
527**		none.
528**
529**	Side Effects:
530**		sets $&{deliveryMode} macro
531*/
532
533void
534set_delivery_mode(mode, e)
535	int mode;
536	ENVELOPE *e;
537{
538	char buf[2];
539
540	e->e_sendmode = (char)mode;
541	buf[0] = (char)mode;
542	buf[1] = '\0';
543	define(macid("{deliveryMode}", NULL), newstr(buf), e);
544}
545/*
546**  PRINTAV -- print argument vector.
547**
548**	Parameters:
549**		av -- argument vector.
550**
551**	Returns:
552**		none.
553**
554**	Side Effects:
555**		prints av.
556*/
557
558void
559printav(av)
560	register char **av;
561{
562	while (*av != NULL)
563	{
564		if (tTd(0, 44))
565			dprintf("\n\t%08lx=", (u_long) *av);
566		else
567			(void) putchar(' ');
568		xputs(*av++);
569	}
570	(void) putchar('\n');
571}
572/*
573**  LOWER -- turn letter into lower case.
574**
575**	Parameters:
576**		c -- character to turn into lower case.
577**
578**	Returns:
579**		c, in lower case.
580**
581**	Side Effects:
582**		none.
583*/
584
585char
586lower(c)
587	register int c;
588{
589	return ((isascii(c) && isupper(c)) ? tolower(c) : c);
590}
591/*
592**  XPUTS -- put string doing control escapes.
593**
594**	Parameters:
595**		s -- string to put.
596**
597**	Returns:
598**		none.
599**
600**	Side Effects:
601**		output to stdout
602*/
603
604void
605xputs(s)
606	register const char *s;
607{
608	register int c;
609	register struct metamac *mp;
610	bool shiftout = FALSE;
611	extern struct metamac MetaMacros[];
612
613	if (s == NULL)
614	{
615		printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off);
616		return;
617	}
618	while ((c = (*s++ & 0377)) != '\0')
619	{
620		if (shiftout)
621		{
622			printf("%s", TermEscape.te_rv_off);
623			shiftout = FALSE;
624		}
625		if (!isascii(c))
626		{
627			if (c == MATCHREPL)
628			{
629				printf("%s$", TermEscape.te_rv_on);
630				shiftout = TRUE;
631				if (*s == '\0')
632					continue;
633				c = *s++ & 0377;
634				goto printchar;
635			}
636			if (c == MACROEXPAND || c == MACRODEXPAND)
637			{
638				printf("%s$", TermEscape.te_rv_on);
639				if (c == MACRODEXPAND)
640					(void) putchar('&');
641				shiftout = TRUE;
642				if (*s == '\0')
643					continue;
644				if (strchr("=~&?", *s) != NULL)
645					(void) putchar(*s++);
646				if (bitset(0200, *s))
647					printf("{%s}", macname(bitidx(*s++)));
648				else
649					printf("%c", *s++);
650				continue;
651			}
652			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
653			{
654				if ((mp->metaval & 0377) == c)
655				{
656					printf("%s$%c",
657						TermEscape.te_rv_on,
658						mp->metaname);
659					shiftout = TRUE;
660					break;
661				}
662			}
663			if (c == MATCHCLASS || c == MATCHNCLASS)
664			{
665				if (bitset(0200, *s))
666					printf("{%s}", macname(*s++ & 0377));
667				else if (*s != '\0')
668					printf("%c", *s++);
669			}
670			if (mp->metaname != '\0')
671				continue;
672
673			/* unrecognized meta character */
674			printf("%sM-", TermEscape.te_rv_on);
675			shiftout = TRUE;
676			c &= 0177;
677		}
678  printchar:
679		if (isprint(c))
680		{
681			(void) putchar(c);
682			continue;
683		}
684
685		/* wasn't a meta-macro -- find another way to print it */
686		switch (c)
687		{
688		  case '\n':
689			c = 'n';
690			break;
691
692		  case '\r':
693			c = 'r';
694			break;
695
696		  case '\t':
697			c = 't';
698			break;
699		}
700		if (!shiftout)
701		{
702			printf("%s", TermEscape.te_rv_on);
703			shiftout = TRUE;
704		}
705		if (isprint(c))
706		{
707			(void) putchar('\\');
708			(void) putchar(c);
709		}
710		else
711		{
712			(void) putchar('^');
713			(void) putchar(c ^ 0100);
714		}
715	}
716	if (shiftout)
717		printf("%s", TermEscape.te_rv_off);
718	(void) fflush(stdout);
719}
720/*
721**  MAKELOWER -- Translate a line into lower case
722**
723**	Parameters:
724**		p -- the string to translate.  If NULL, return is
725**			immediate.
726**
727**	Returns:
728**		none.
729**
730**	Side Effects:
731**		String pointed to by p is translated to lower case.
732*/
733
734void
735makelower(p)
736	register char *p;
737{
738	register char c;
739
740	if (p == NULL)
741		return;
742	for (; (c = *p) != '\0'; p++)
743		if (isascii(c) && isupper(c))
744			*p = tolower(c);
745}
746/*
747**  BUILDFNAME -- build full name from gecos style entry.
748**
749**	This routine interprets the strange entry that would appear
750**	in the GECOS field of the password file.
751**
752**	Parameters:
753**		p -- name to build.
754**		user -- the login name of this user (for &).
755**		buf -- place to put the result.
756**		buflen -- length of buf.
757**
758**	Returns:
759**		none.
760**
761**	Side Effects:
762**		none.
763*/
764
765void
766buildfname(gecos, user, buf, buflen)
767	register char *gecos;
768	char *user;
769	char *buf;
770	int buflen;
771{
772	register char *p;
773	register char *bp = buf;
774
775	if (*gecos == '*')
776		gecos++;
777
778	/* copy gecos, interpolating & to be full name */
779	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
780	{
781		if (bp >= &buf[buflen - 1])
782		{
783			/* buffer overflow -- just use login name */
784			snprintf(buf, buflen, "%s", user);
785			return;
786		}
787		if (*p == '&')
788		{
789			/* interpolate full name */
790			snprintf(bp, buflen - (bp - buf), "%s", user);
791			*bp = toupper(*bp);
792			bp += strlen(bp);
793		}
794		else
795			*bp++ = *p;
796	}
797	*bp = '\0';
798}
799/*
800**  FIXCRLF -- fix <CR><LF> in line.
801**
802**	Looks for the <CR><LF> combination and turns it into the
803**	UNIX canonical <NL> character.  It only takes one line,
804**	i.e., it is assumed that the first <NL> found is the end
805**	of the line.
806**
807**	Parameters:
808**		line -- the line to fix.
809**		stripnl -- if true, strip the newline also.
810**
811**	Returns:
812**		none.
813**
814**	Side Effects:
815**		line is changed in place.
816*/
817
818void
819fixcrlf(line, stripnl)
820	char *line;
821	bool stripnl;
822{
823	register char *p;
824
825	p = strchr(line, '\n');
826	if (p == NULL)
827		return;
828	if (p > line && p[-1] == '\r')
829		p--;
830	if (!stripnl)
831		*p++ = '\n';
832	*p = '\0';
833}
834/*
835**  PUTLINE -- put a line like fputs obeying SMTP conventions
836**
837**	This routine always guarantees outputing a newline (or CRLF,
838**	as appropriate) at the end of the string.
839**
840**	Parameters:
841**		l -- line to put.
842**		mci -- the mailer connection information.
843**
844**	Returns:
845**		none
846**
847**	Side Effects:
848**		output of l to fp.
849*/
850
851void
852putline(l, mci)
853	register char *l;
854	register MCI *mci;
855{
856	putxline(l, strlen(l), mci, PXLF_MAPFROM);
857}
858/*
859**  PUTXLINE -- putline with flags bits.
860**
861**	This routine always guarantees outputing a newline (or CRLF,
862**	as appropriate) at the end of the string.
863**
864**	Parameters:
865**		l -- line to put.
866**		len -- the length of the line.
867**		mci -- the mailer connection information.
868**		pxflags -- flag bits:
869**		    PXLF_MAPFROM -- map From_ to >From_.
870**		    PXLF_STRIP8BIT -- strip 8th bit.
871**		    PXLF_HEADER -- map bare newline in header to newline space.
872**
873**	Returns:
874**		none
875**
876**	Side Effects:
877**		output of l to fp.
878*/
879
880void
881putxline(l, len, mci, pxflags)
882	register char *l;
883	size_t len;
884	register MCI *mci;
885	int pxflags;
886{
887	bool dead = FALSE;
888	register char *p, *end;
889	int slop = 0;
890
891	/* strip out 0200 bits -- these can look like TELNET protocol */
892	if (bitset(MCIF_7BIT, mci->mci_flags) ||
893	    bitset(PXLF_STRIP8BIT, pxflags))
894	{
895		register char svchar;
896
897		for (p = l; (svchar = *p) != '\0'; ++p)
898			if (bitset(0200, svchar))
899				*p = svchar &~ 0200;
900	}
901
902	end = l + len;
903	do
904	{
905		/* find the end of the line */
906		p = memchr(l, '\n', end - l);
907		if (p == NULL)
908			p = end;
909
910		if (TrafficLogFile != NULL)
911			fprintf(TrafficLogFile, "%05d >>> ", (int) getpid());
912
913		/* check for line overflow */
914		while (mci->mci_mailer->m_linelimit > 0 &&
915		       (p - l + slop) > mci->mci_mailer->m_linelimit)
916		{
917			char *l_base = l;
918			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
919
920			if (l[0] == '.' && slop == 0 &&
921			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
922			{
923				if (putc('.', mci->mci_out) == EOF)
924					dead = TRUE;
925				else
926				{
927					/* record progress for DATA timeout */
928					DataProgress = TRUE;
929				}
930				if (TrafficLogFile != NULL)
931					(void) putc('.', TrafficLogFile);
932			}
933			else if (l[0] == 'F' && slop == 0 &&
934				 bitset(PXLF_MAPFROM, pxflags) &&
935				 strncmp(l, "From ", 5) == 0 &&
936				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
937			{
938				if (putc('>', mci->mci_out) == EOF)
939					dead = TRUE;
940				else
941				{
942					/* record progress for DATA timeout */
943					DataProgress = TRUE;
944				}
945				if (TrafficLogFile != NULL)
946					(void) putc('>', TrafficLogFile);
947			}
948			if (dead)
949				break;
950
951			while (l < q)
952			{
953				if (putc((unsigned char) *l++, mci->mci_out) ==
954				    EOF)
955				{
956					dead = TRUE;
957					break;
958				}
959				else
960				{
961					/* record progress for DATA timeout */
962					DataProgress = TRUE;
963				}
964			}
965			if (dead)
966				break;
967
968			if (putc('!', mci->mci_out) == EOF ||
969			    fputs(mci->mci_mailer->m_eol,
970				  mci->mci_out) == EOF ||
971			    putc(' ', mci->mci_out) == EOF)
972			{
973				dead = TRUE;
974				break;
975			}
976			else
977			{
978				/* record progress for DATA timeout */
979				DataProgress = TRUE;
980			}
981			if (TrafficLogFile != NULL)
982			{
983				for (l = l_base; l < q; l++)
984					(void) putc((unsigned char)*l,
985						    TrafficLogFile);
986				fprintf(TrafficLogFile, "!\n%05d >>>  ",
987					(int) getpid());
988			}
989			slop = 1;
990		}
991
992		if (dead)
993			break;
994
995		/* output last part */
996		if (l[0] == '.' && slop == 0 &&
997		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
998		{
999			if (putc('.', mci->mci_out) == EOF)
1000				break;
1001			else
1002			{
1003				/* record progress for DATA timeout */
1004				DataProgress = TRUE;
1005			}
1006			if (TrafficLogFile != NULL)
1007				(void) putc('.', TrafficLogFile);
1008		}
1009		else if (l[0] == 'F' && slop == 0 &&
1010			 bitset(PXLF_MAPFROM, pxflags) &&
1011			 strncmp(l, "From ", 5) == 0 &&
1012			 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1013		{
1014			if (putc('>', mci->mci_out) == EOF)
1015				break;
1016			else
1017			{
1018				/* record progress for DATA timeout */
1019				DataProgress = TRUE;
1020			}
1021			if (TrafficLogFile != NULL)
1022				(void) putc('>', TrafficLogFile);
1023		}
1024		for ( ; l < p; ++l)
1025		{
1026			if (TrafficLogFile != NULL)
1027				(void) putc((unsigned char)*l, TrafficLogFile);
1028			if (putc((unsigned char) *l, mci->mci_out) == EOF)
1029			{
1030				dead = TRUE;
1031				break;
1032			}
1033			else
1034			{
1035				/* record progress for DATA timeout */
1036				DataProgress = TRUE;
1037			}
1038		}
1039		if (dead)
1040			break;
1041
1042		if (TrafficLogFile != NULL)
1043			(void) putc('\n', TrafficLogFile);
1044		if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF)
1045			break;
1046		else
1047		{
1048			/* record progress for DATA timeout */
1049			DataProgress = TRUE;
1050		}
1051		if (l < end && *l == '\n')
1052		{
1053			if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1054			    bitset(PXLF_HEADER, pxflags))
1055			{
1056				if (putc(' ', mci->mci_out) == EOF)
1057					break;
1058				else
1059				{
1060					/* record progress for DATA timeout */
1061					DataProgress = TRUE;
1062				}
1063				if (TrafficLogFile != NULL)
1064					(void) putc(' ', TrafficLogFile);
1065			}
1066		}
1067	} while (l < end);
1068}
1069/*
1070**  XUNLINK -- unlink a file, doing logging as appropriate.
1071**
1072**	Parameters:
1073**		f -- name of file to unlink.
1074**
1075**	Returns:
1076**		none.
1077**
1078**	Side Effects:
1079**		f is unlinked.
1080*/
1081
1082void
1083xunlink(f)
1084	char *f;
1085{
1086	register int i;
1087
1088	if (LogLevel > 98)
1089		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1090			  "unlink %s",
1091			  f);
1092
1093	i = unlink(f);
1094	if (i < 0 && LogLevel > 97)
1095		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1096			  "%s: unlink-fail %d",
1097			  f, errno);
1098}
1099/*
1100**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1101**
1102**	Parameters:
1103**		buf -- place to put the input line.
1104**		siz -- size of buf.
1105**		fp -- file to read from.
1106**		timeout -- the timeout before error occurs.
1107**		during -- what we are trying to read (for error messages).
1108**
1109**	Returns:
1110**		NULL on error (including timeout).  This will also leave
1111**			buf containing a null string.
1112**		buf otherwise.
1113**
1114**	Side Effects:
1115**		none.
1116*/
1117
1118
1119static jmp_buf	CtxReadTimeout;
1120
1121char *
1122sfgets(buf, siz, fp, timeout, during)
1123	char *buf;
1124	int siz;
1125	FILE *fp;
1126	time_t timeout;
1127	char *during;
1128{
1129	register EVENT *ev = NULL;
1130	register char *p;
1131	int save_errno;
1132
1133	if (fp == NULL)
1134	{
1135		buf[0] = '\0';
1136		return NULL;
1137	}
1138
1139	/* set the timeout */
1140	if (timeout != 0)
1141	{
1142		if (setjmp(CtxReadTimeout) != 0)
1143		{
1144			if (LogLevel > 1)
1145				sm_syslog(LOG_NOTICE, CurEnv->e_id,
1146					  "timeout waiting for input from %.100s during %s",
1147					  CurHostName ? CurHostName : "local",
1148					  during);
1149			buf[0] = '\0';
1150#if XDEBUG
1151			checkfd012(during);
1152#endif /* XDEBUG */
1153			if (TrafficLogFile != NULL)
1154				fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
1155					(int) getpid());
1156			errno = 0;
1157			return NULL;
1158		}
1159		ev = setevent(timeout, readtimeout, 0);
1160	}
1161
1162	/* try to read */
1163	p = NULL;
1164	errno = 0;
1165	while (!feof(fp) && !ferror(fp))
1166	{
1167		errno = 0;
1168		p = fgets(buf, siz, fp);
1169		if (p != NULL || errno != EINTR)
1170			break;
1171		clearerr(fp);
1172	}
1173	save_errno = errno;
1174
1175	/* clear the event if it has not sprung */
1176	clrevent(ev);
1177
1178	/* clean up the books and exit */
1179	LineNumber++;
1180	if (p == NULL)
1181	{
1182		buf[0] = '\0';
1183		if (TrafficLogFile != NULL)
1184			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
1185		errno = save_errno;
1186		return NULL;
1187	}
1188	if (TrafficLogFile != NULL)
1189		fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
1190	if (SevenBitInput)
1191	{
1192		for (p = buf; *p != '\0'; p++)
1193			*p &= ~0200;
1194	}
1195	else if (!HasEightBits)
1196	{
1197		for (p = buf; *p != '\0'; p++)
1198		{
1199			if (bitset(0200, *p))
1200			{
1201				HasEightBits = TRUE;
1202				break;
1203			}
1204		}
1205	}
1206	return buf;
1207}
1208
1209/* ARGSUSED */
1210static void
1211readtimeout(timeout)
1212	time_t timeout;
1213{
1214	longjmp(CtxReadTimeout, 1);
1215}
1216/*
1217**  FGETFOLDED -- like fgets, but know about folded lines.
1218**
1219**	Parameters:
1220**		buf -- place to put result.
1221**		n -- bytes available.
1222**		f -- file to read from.
1223**
1224**	Returns:
1225**		input line(s) on success, NULL on error or EOF.
1226**		This will normally be buf -- unless the line is too
1227**			long, when it will be xalloc()ed.
1228**
1229**	Side Effects:
1230**		buf gets lines from f, with continuation lines (lines
1231**		with leading white space) appended.  CRLF's are mapped
1232**		into single newlines.  Any trailing NL is stripped.
1233*/
1234
1235char *
1236fgetfolded(buf, n, f)
1237	char *buf;
1238	register int n;
1239	FILE *f;
1240{
1241	register char *p = buf;
1242	char *bp = buf;
1243	register int i;
1244
1245	n--;
1246	while ((i = getc(f)) != EOF)
1247	{
1248		if (i == '\r')
1249		{
1250			i = getc(f);
1251			if (i != '\n')
1252			{
1253				if (i != EOF)
1254					(void) ungetc(i, f);
1255				i = '\r';
1256			}
1257		}
1258		if (--n <= 0)
1259		{
1260			/* allocate new space */
1261			char *nbp;
1262			int nn;
1263
1264			nn = (p - bp);
1265			if (nn < MEMCHUNKSIZE)
1266				nn *= 2;
1267			else
1268				nn += MEMCHUNKSIZE;
1269			nbp = xalloc(nn);
1270			memmove(nbp, bp, p - bp);
1271			p = &nbp[p - bp];
1272			if (bp != buf)
1273				free(bp);
1274			bp = nbp;
1275			n = nn - (p - bp);
1276		}
1277		*p++ = i;
1278		if (i == '\n')
1279		{
1280			LineNumber++;
1281			i = getc(f);
1282			if (i != EOF)
1283				(void) ungetc(i, f);
1284			if (i != ' ' && i != '\t')
1285				break;
1286		}
1287	}
1288	if (p == bp)
1289		return NULL;
1290	if (p[-1] == '\n')
1291		p--;
1292	*p = '\0';
1293	return bp;
1294}
1295/*
1296**  CURTIME -- return current time.
1297**
1298**	Parameters:
1299**		none.
1300**
1301**	Returns:
1302**		the current time.
1303**
1304**	Side Effects:
1305**		none.
1306*/
1307
1308time_t
1309curtime()
1310{
1311	auto time_t t;
1312
1313	(void) time(&t);
1314	return t;
1315}
1316/*
1317**  ATOBOOL -- convert a string representation to boolean.
1318**
1319**	Defaults to "TRUE"
1320**
1321**	Parameters:
1322**		s -- string to convert.  Takes "tTyY" as true,
1323**			others as false.
1324**
1325**	Returns:
1326**		A boolean representation of the string.
1327**
1328**	Side Effects:
1329**		none.
1330*/
1331
1332bool
1333atobool(s)
1334	register char *s;
1335{
1336	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1337		return TRUE;
1338	return FALSE;
1339}
1340/*
1341**  ATOOCT -- convert a string representation to octal.
1342**
1343**	Parameters:
1344**		s -- string to convert.
1345**
1346**	Returns:
1347**		An integer representing the string interpreted as an
1348**		octal number.
1349**
1350**	Side Effects:
1351**		none.
1352*/
1353
1354int
1355atooct(s)
1356	register char *s;
1357{
1358	register int i = 0;
1359
1360	while (*s >= '0' && *s <= '7')
1361		i = (i << 3) | (*s++ - '0');
1362	return i;
1363}
1364/*
1365**  BITINTERSECT -- tell if two bitmaps intersect
1366**
1367**	Parameters:
1368**		a, b -- the bitmaps in question
1369**
1370**	Returns:
1371**		TRUE if they have a non-null intersection
1372**		FALSE otherwise
1373**
1374**	Side Effects:
1375**		none.
1376*/
1377
1378bool
1379bitintersect(a, b)
1380	BITMAP256 a;
1381	BITMAP256 b;
1382{
1383	int i;
1384
1385	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1386	{
1387		if ((a[i] & b[i]) != 0)
1388			return TRUE;
1389	}
1390	return FALSE;
1391}
1392/*
1393**  BITZEROP -- tell if a bitmap is all zero
1394**
1395**	Parameters:
1396**		map -- the bit map to check
1397**
1398**	Returns:
1399**		TRUE if map is all zero.
1400**		FALSE if there are any bits set in map.
1401**
1402**	Side Effects:
1403**		none.
1404*/
1405
1406bool
1407bitzerop(map)
1408	BITMAP256 map;
1409{
1410	int i;
1411
1412	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1413	{
1414		if (map[i] != 0)
1415			return FALSE;
1416	}
1417	return TRUE;
1418}
1419/*
1420**  STRCONTAINEDIN -- tell if one string is contained in another
1421**
1422**	Parameters:
1423**		a -- possible substring.
1424**		b -- possible superstring.
1425**
1426**	Returns:
1427**		TRUE if a is contained in b.
1428**		FALSE otherwise.
1429*/
1430
1431bool
1432strcontainedin(a, b)
1433	register char *a;
1434	register char *b;
1435{
1436	int la;
1437	int lb;
1438	int c;
1439
1440	la = strlen(a);
1441	lb = strlen(b);
1442	c = *a;
1443	if (isascii(c) && isupper(c))
1444		c = tolower(c);
1445	for (; lb-- >= la; b++)
1446	{
1447		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1448			continue;
1449		if (strncasecmp(a, b, la) == 0)
1450			return TRUE;
1451	}
1452	return FALSE;
1453}
1454/*
1455**  CHECKFD012 -- check low numbered file descriptors
1456**
1457**	File descriptors 0, 1, and 2 should be open at all times.
1458**	This routine verifies that, and fixes it if not true.
1459**
1460**	Parameters:
1461**		where -- a tag printed if the assertion failed
1462**
1463**	Returns:
1464**		none
1465*/
1466
1467void
1468checkfd012(where)
1469	char *where;
1470{
1471#if XDEBUG
1472	register int i;
1473
1474	for (i = 0; i < 3; i++)
1475		fill_fd(i, where);
1476#endif /* XDEBUG */
1477}
1478/*
1479**  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1480**
1481**	Parameters:
1482**		fd -- file descriptor to check.
1483**		where -- tag to print on failure.
1484**
1485**	Returns:
1486**		none.
1487*/
1488
1489void
1490checkfdopen(fd, where)
1491	int fd;
1492	char *where;
1493{
1494#if XDEBUG
1495	struct stat st;
1496
1497	if (fstat(fd, &st) < 0 && errno == EBADF)
1498	{
1499		syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1500		printopenfds(TRUE);
1501	}
1502#endif /* XDEBUG */
1503}
1504/*
1505**  CHECKFDS -- check for new or missing file descriptors
1506**
1507**	Parameters:
1508**		where -- tag for printing.  If null, take a base line.
1509**
1510**	Returns:
1511**		none
1512**
1513**	Side Effects:
1514**		If where is set, shows changes since the last call.
1515*/
1516
1517void
1518checkfds(where)
1519	char *where;
1520{
1521	int maxfd;
1522	register int fd;
1523	bool printhdr = TRUE;
1524	int save_errno = errno;
1525	static BITMAP256 baseline;
1526	extern int DtableSize;
1527
1528	if (DtableSize > BITMAPBITS)
1529		maxfd = BITMAPBITS;
1530	else
1531		maxfd = DtableSize;
1532	if (where == NULL)
1533		clrbitmap(baseline);
1534
1535	for (fd = 0; fd < maxfd; fd++)
1536	{
1537		struct stat stbuf;
1538
1539		if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1540		{
1541			if (!bitnset(fd, baseline))
1542				continue;
1543			clrbitn(fd, baseline);
1544		}
1545		else if (!bitnset(fd, baseline))
1546			setbitn(fd, baseline);
1547		else
1548			continue;
1549
1550		/* file state has changed */
1551		if (where == NULL)
1552			continue;
1553		if (printhdr)
1554		{
1555			sm_syslog(LOG_DEBUG, CurEnv->e_id,
1556				  "%s: changed fds:",
1557				  where);
1558			printhdr = FALSE;
1559		}
1560		dumpfd(fd, TRUE, TRUE);
1561	}
1562	errno = save_errno;
1563}
1564/*
1565**  PRINTOPENFDS -- print the open file descriptors (for debugging)
1566**
1567**	Parameters:
1568**		logit -- if set, send output to syslog; otherwise
1569**			print for debugging.
1570**
1571**	Returns:
1572**		none.
1573*/
1574
1575#if NETINET || NETINET6
1576# include <arpa/inet.h>
1577#endif /* NETINET || NETINET6 */
1578
1579void
1580printopenfds(logit)
1581	bool logit;
1582{
1583	register int fd;
1584	extern int DtableSize;
1585
1586	for (fd = 0; fd < DtableSize; fd++)
1587		dumpfd(fd, FALSE, logit);
1588}
1589/*
1590**  DUMPFD -- dump a file descriptor
1591**
1592**	Parameters:
1593**		fd -- the file descriptor to dump.
1594**		printclosed -- if set, print a notification even if
1595**			it is closed; otherwise print nothing.
1596**		logit -- if set, send output to syslog instead of stdout.
1597*/
1598
1599void
1600dumpfd(fd, printclosed, logit)
1601	int fd;
1602	bool printclosed;
1603	bool logit;
1604{
1605	register char *p;
1606	char *hp;
1607#ifdef S_IFSOCK
1608	SOCKADDR sa;
1609#endif /* S_IFSOCK */
1610	auto SOCKADDR_LEN_T slen;
1611	int i;
1612#if STAT64 > 0
1613	struct stat64 st;
1614#else /* STAT64 > 0 */
1615	struct stat st;
1616#endif /* STAT64 > 0 */
1617	char buf[200];
1618
1619	p = buf;
1620	snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1621	p += strlen(p);
1622
1623	if (
1624#if STAT64 > 0
1625	    fstat64(fd, &st)
1626#else /* STAT64 > 0 */
1627	    fstat(fd, &st)
1628#endif /* STAT64 > 0 */
1629	    < 0)
1630	{
1631		if (errno != EBADF)
1632		{
1633			snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
1634				errstring(errno));
1635			goto printit;
1636		}
1637		else if (printclosed)
1638		{
1639			snprintf(p, SPACELEFT(buf, p), "CLOSED");
1640			goto printit;
1641		}
1642		return;
1643	}
1644
1645	i = fcntl(fd, F_GETFL, NULL);
1646	if (i != -1)
1647	{
1648		snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1649		p += strlen(p);
1650	}
1651
1652	snprintf(p, SPACELEFT(buf, p), "mode=%o: ", (int) st.st_mode);
1653	p += strlen(p);
1654	switch (st.st_mode & S_IFMT)
1655	{
1656#ifdef S_IFSOCK
1657	  case S_IFSOCK:
1658		snprintf(p, SPACELEFT(buf, p), "SOCK ");
1659		p += strlen(p);
1660		memset(&sa, '\0', sizeof sa);
1661		slen = sizeof sa;
1662		if (getsockname(fd, &sa.sa, &slen) < 0)
1663			snprintf(p, SPACELEFT(buf, p), "(%s)",
1664				 errstring(errno));
1665		else
1666		{
1667			hp = hostnamebyanyaddr(&sa);
1668			if (hp == NULL)
1669			{
1670				/* EMPTY */
1671				/* do nothing */
1672			}
1673# if NETINET
1674			else if (sa.sa.sa_family == AF_INET)
1675				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1676					 hp, ntohs(sa.sin.sin_port));
1677# endif /* NETINET */
1678# if NETINET6
1679			else if (sa.sa.sa_family == AF_INET6)
1680				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1681					 hp, ntohs(sa.sin6.sin6_port));
1682# endif /* NETINET6 */
1683			else
1684				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1685		}
1686		p += strlen(p);
1687		snprintf(p, SPACELEFT(buf, p), "->");
1688		p += strlen(p);
1689		slen = sizeof sa;
1690		if (getpeername(fd, &sa.sa, &slen) < 0)
1691			snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1692		else
1693		{
1694			hp = hostnamebyanyaddr(&sa);
1695			if (hp == NULL)
1696			{
1697				/* EMPTY */
1698				/* do nothing */
1699			}
1700# if NETINET
1701			else if (sa.sa.sa_family == AF_INET)
1702				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1703					 hp, ntohs(sa.sin.sin_port));
1704# endif /* NETINET */
1705# if NETINET6
1706			else if (sa.sa.sa_family == AF_INET6)
1707				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1708					 hp, ntohs(sa.sin6.sin6_port));
1709# endif /* NETINET6 */
1710			else
1711				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1712		}
1713		break;
1714#endif /* S_IFSOCK */
1715
1716	  case S_IFCHR:
1717		snprintf(p, SPACELEFT(buf, p), "CHR: ");
1718		p += strlen(p);
1719		goto defprint;
1720
1721	  case S_IFBLK:
1722		snprintf(p, SPACELEFT(buf, p), "BLK: ");
1723		p += strlen(p);
1724		goto defprint;
1725
1726#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1727	  case S_IFIFO:
1728		snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1729		p += strlen(p);
1730		goto defprint;
1731#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1732
1733#ifdef S_IFDIR
1734	  case S_IFDIR:
1735		snprintf(p, SPACELEFT(buf, p), "DIR: ");
1736		p += strlen(p);
1737		goto defprint;
1738#endif /* S_IFDIR */
1739
1740#ifdef S_IFLNK
1741	  case S_IFLNK:
1742		snprintf(p, SPACELEFT(buf, p), "LNK: ");
1743		p += strlen(p);
1744		goto defprint;
1745#endif /* S_IFLNK */
1746
1747	  default:
1748defprint:
1749		/*CONSTCOND*/
1750		if (sizeof st.st_ino > sizeof (long))
1751			snprintf(p, SPACELEFT(buf, p),
1752				 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
1753				 major(st.st_dev), minor(st.st_dev),
1754				 quad_to_string(st.st_ino),
1755				 (int) st.st_nlink, (int) st.st_uid,
1756				 (int) st.st_gid);
1757		else
1758			snprintf(p, SPACELEFT(buf, p),
1759				 "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
1760				 major(st.st_dev), minor(st.st_dev),
1761				 (unsigned long) st.st_ino,
1762				 (int) st.st_nlink, (int) st.st_uid,
1763				 (int) st.st_gid);
1764		/*CONSTCOND*/
1765		if (sizeof st.st_size > sizeof (long))
1766			snprintf(p, SPACELEFT(buf, p), "size=%s",
1767				 quad_to_string(st.st_size));
1768		else
1769			snprintf(p, SPACELEFT(buf, p), "size=%lu",
1770				 (unsigned long) st.st_size);
1771		break;
1772	}
1773
1774printit:
1775	if (logit)
1776		sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1777			  "%.800s", buf);
1778	else
1779		printf("%s\n", buf);
1780}
1781/*
1782**  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1783**
1784**	Parameters:
1785**		host -- the host to shorten (stripped in place).
1786**
1787**	Returns:
1788**		none.
1789*/
1790
1791void
1792shorten_hostname(host)
1793	char host[];
1794{
1795	register char *p;
1796	char *mydom;
1797	int i;
1798	bool canon = FALSE;
1799
1800	/* strip off final dot */
1801	p = &host[strlen(host) - 1];
1802	if (*p == '.')
1803	{
1804		*p = '\0';
1805		canon = TRUE;
1806	}
1807
1808	/* see if there is any domain at all -- if not, we are done */
1809	p = strchr(host, '.');
1810	if (p == NULL)
1811		return;
1812
1813	/* yes, we have a domain -- see if it looks like us */
1814	mydom = macvalue('m', CurEnv);
1815	if (mydom == NULL)
1816		mydom = "";
1817	i = strlen(++p);
1818	if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1819	    (mydom[i] == '.' || mydom[i] == '\0'))
1820		*--p = '\0';
1821}
1822/*
1823**  PROG_OPEN -- open a program for reading
1824**
1825**	Parameters:
1826**		argv -- the argument list.
1827**		pfd -- pointer to a place to store the file descriptor.
1828**		e -- the current envelope.
1829**
1830**	Returns:
1831**		pid of the process -- -1 if it failed.
1832*/
1833
1834int
1835prog_open(argv, pfd, e)
1836	char **argv;
1837	int *pfd;
1838	ENVELOPE *e;
1839{
1840	int pid;
1841	int i;
1842	int save_errno;
1843	int fdv[2];
1844	char *p, *q;
1845	char buf[MAXLINE + 1];
1846	extern int DtableSize;
1847
1848	if (pipe(fdv) < 0)
1849	{
1850		syserr("%s: cannot create pipe for stdout", argv[0]);
1851		return -1;
1852	}
1853	pid = fork();
1854	if (pid < 0)
1855	{
1856		syserr("%s: cannot fork", argv[0]);
1857		(void) close(fdv[0]);
1858		(void) close(fdv[1]);
1859		return -1;
1860	}
1861	if (pid > 0)
1862	{
1863		/* parent */
1864		(void) close(fdv[1]);
1865		*pfd = fdv[0];
1866		return pid;
1867	}
1868
1869	/* child -- close stdin */
1870	(void) close(0);
1871
1872	/* stdout goes back to parent */
1873	(void) close(fdv[0]);
1874	if (dup2(fdv[1], 1) < 0)
1875	{
1876		syserr("%s: cannot dup2 for stdout", argv[0]);
1877		_exit(EX_OSERR);
1878	}
1879	(void) close(fdv[1]);
1880
1881	/* stderr goes to transcript if available */
1882	if (e->e_xfp != NULL)
1883	{
1884		int xfd;
1885
1886		xfd = fileno(e->e_xfp);
1887		if (xfd >= 0 && dup2(xfd, 2) < 0)
1888		{
1889			syserr("%s: cannot dup2 for stderr", argv[0]);
1890			_exit(EX_OSERR);
1891		}
1892	}
1893
1894	/* this process has no right to the queue file */
1895	if (e->e_lockfp != NULL)
1896		(void) close(fileno(e->e_lockfp));
1897
1898	/* chroot to the program mailer directory, if defined */
1899	if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
1900	{
1901		expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
1902		if (chroot(buf) < 0)
1903		{
1904			syserr("prog_open: cannot chroot(%s)", buf);
1905			exit(EX_TEMPFAIL);
1906		}
1907		if (chdir("/") < 0)
1908		{
1909			syserr("prog_open: cannot chdir(/)");
1910			exit(EX_TEMPFAIL);
1911		}
1912	}
1913
1914	/* run as default user */
1915	endpwent();
1916	if (setgid(DefGid) < 0 && geteuid() == 0)
1917	{
1918		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
1919		exit(EX_TEMPFAIL);
1920	}
1921	if (setuid(DefUid) < 0 && geteuid() == 0)
1922	{
1923		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
1924		exit(EX_TEMPFAIL);
1925	}
1926
1927	/* run in some directory */
1928	if (ProgMailer != NULL)
1929		p = ProgMailer->m_execdir;
1930	else
1931		p = NULL;
1932	for (; p != NULL; p = q)
1933	{
1934		q = strchr(p, ':');
1935		if (q != NULL)
1936			*q = '\0';
1937		expand(p, buf, sizeof buf, e);
1938		if (q != NULL)
1939			*q++ = ':';
1940		if (buf[0] != '\0' && chdir(buf) >= 0)
1941			break;
1942	}
1943	if (p == NULL)
1944	{
1945		/* backup directories */
1946		if (chdir("/tmp") < 0)
1947			(void) chdir("/");
1948	}
1949
1950	/* arrange for all the files to be closed */
1951	for (i = 3; i < DtableSize; i++)
1952	{
1953		register int j;
1954
1955		if ((j = fcntl(i, F_GETFD, 0)) != -1)
1956			(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
1957	}
1958
1959	/* now exec the process */
1960	(void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
1961
1962	/* woops!  failed */
1963	save_errno = errno;
1964	syserr("%s: cannot exec", argv[0]);
1965	if (transienterror(save_errno))
1966		_exit(EX_OSERR);
1967	_exit(EX_CONFIG);
1968	return -1;	/* avoid compiler warning on IRIX */
1969}
1970/*
1971**  GET_COLUMN -- look up a Column in a line buffer
1972**
1973**	Parameters:
1974**		line -- the raw text line to search.
1975**		col -- the column number to fetch.
1976**		delim -- the delimiter between columns.  If null,
1977**			use white space.
1978**		buf -- the output buffer.
1979**		buflen -- the length of buf.
1980**
1981**	Returns:
1982**		buf if successful.
1983**		NULL otherwise.
1984*/
1985
1986char *
1987get_column(line, col, delim, buf, buflen)
1988	char line[];
1989	int col;
1990	int delim;
1991	char buf[];
1992	int buflen;
1993{
1994	char *p;
1995	char *begin, *end;
1996	int i;
1997	char delimbuf[4];
1998
1999	if ((char)delim == '\0')
2000		(void) strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
2001	else
2002	{
2003		delimbuf[0] = (char)delim;
2004		delimbuf[1] = '\0';
2005	}
2006
2007	p = line;
2008	if (*p == '\0')
2009		return NULL;			/* line empty */
2010	if (*p == (char)delim && col == 0)
2011		return NULL;			/* first column empty */
2012
2013	begin = line;
2014
2015	if (col == 0 && (char)delim == '\0')
2016	{
2017		while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2018			begin++;
2019	}
2020
2021	for (i = 0; i < col; i++)
2022	{
2023		if ((begin = strpbrk(begin, delimbuf)) == NULL)
2024			return NULL;		/* no such column */
2025		begin++;
2026		if ((char)delim == '\0')
2027		{
2028			while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2029				begin++;
2030		}
2031	}
2032
2033	end = strpbrk(begin, delimbuf);
2034	if (end == NULL)
2035		i = strlen(begin);
2036	else
2037		i = end - begin;
2038	if (i >= buflen)
2039		i = buflen - 1;
2040	(void) strlcpy(buf, begin, i + 1);
2041	return buf;
2042}
2043/*
2044**  CLEANSTRCPY -- copy string keeping out bogus characters
2045**
2046**	Parameters:
2047**		t -- "to" string.
2048**		f -- "from" string.
2049**		l -- length of space available in "to" string.
2050**
2051**	Returns:
2052**		none.
2053*/
2054
2055void
2056cleanstrcpy(t, f, l)
2057	register char *t;
2058	register char *f;
2059	int l;
2060{
2061	/* check for newlines and log if necessary */
2062	(void) denlstring(f, TRUE, TRUE);
2063
2064	if (l <= 0)
2065		syserr("!cleanstrcpy: length == 0");
2066
2067	l--;
2068	while (l > 0 && *f != '\0')
2069	{
2070		if (isascii(*f) &&
2071		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2072		{
2073			l--;
2074			*t++ = *f;
2075		}
2076		f++;
2077	}
2078	*t = '\0';
2079}
2080
2081/*
2082**  DENLSTRING -- convert newlines in a string to spaces
2083**
2084**	Parameters:
2085**		s -- the input string
2086**		strict -- if set, don't permit continuation lines.
2087**		logattacks -- if set, log attempted attacks.
2088**
2089**	Returns:
2090**		A pointer to a version of the string with newlines
2091**		mapped to spaces.  This should be copied.
2092*/
2093
2094char *
2095denlstring(s, strict, logattacks)
2096	char *s;
2097	bool strict;
2098	bool logattacks;
2099{
2100	register char *p;
2101	int l;
2102	static char *bp = NULL;
2103	static int bl = 0;
2104
2105	p = s;
2106	while ((p = strchr(p, '\n')) != NULL)
2107		if (strict || (*++p != ' ' && *p != '\t'))
2108			break;
2109	if (p == NULL)
2110		return s;
2111
2112	l = strlen(s) + 1;
2113	if (bl < l)
2114	{
2115		/* allocate more space */
2116		if (bp != NULL)
2117			free(bp);
2118		bp = xalloc(l);
2119		bl = l;
2120	}
2121	(void) strlcpy(bp, s, l);
2122	for (p = bp; (p = strchr(p, '\n')) != NULL; )
2123		*p++ = ' ';
2124
2125	if (logattacks)
2126	{
2127		sm_syslog(LOG_NOTICE, CurEnv->e_id,
2128			  "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2129			  RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2130			  shortenstring(bp, MAXSHORTSTR));
2131	}
2132
2133	return bp;
2134}
2135/*
2136**  PATH_IS_DIR -- check to see if file exists and is a directory.
2137**
2138**	There are some additional checks for security violations in
2139**	here.  This routine is intended to be used for the host status
2140**	support.
2141**
2142**	Parameters:
2143**		pathname -- pathname to check for directory-ness.
2144**		createflag -- if set, create directory if needed.
2145**
2146**	Returns:
2147**		TRUE -- if the indicated pathname is a directory
2148**		FALSE -- otherwise
2149*/
2150
2151int
2152path_is_dir(pathname, createflag)
2153	char *pathname;
2154	bool createflag;
2155{
2156	struct stat statbuf;
2157
2158#if HASLSTAT
2159	if (lstat(pathname, &statbuf) < 0)
2160#else /* HASLSTAT */
2161	if (stat(pathname, &statbuf) < 0)
2162#endif /* HASLSTAT */
2163	{
2164		if (errno != ENOENT || !createflag)
2165			return FALSE;
2166		if (mkdir(pathname, 0755) < 0)
2167			return FALSE;
2168		return TRUE;
2169	}
2170	if (!S_ISDIR(statbuf.st_mode))
2171	{
2172		errno = ENOTDIR;
2173		return FALSE;
2174	}
2175
2176	/* security: don't allow writable directories */
2177	if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2178	{
2179		errno = EACCES;
2180		return FALSE;
2181	}
2182
2183	return TRUE;
2184}
2185/*
2186**  PROC_LIST_ADD -- add process id to list of our children
2187**
2188**	Parameters:
2189**		pid -- pid to add to list.
2190**		task -- task of pid.
2191**		type -- type of process.
2192**
2193**	Returns:
2194**		none
2195*/
2196
2197static struct procs	*ProcListVec = NULL;
2198static int		ProcListSize = 0;
2199
2200void
2201proc_list_add(pid, task, type)
2202	pid_t pid;
2203	char *task;
2204	int type;
2205{
2206	int i;
2207
2208	for (i = 0; i < ProcListSize; i++)
2209	{
2210		if (ProcListVec[i].proc_pid == NO_PID)
2211			break;
2212	}
2213	if (i >= ProcListSize)
2214	{
2215		/* probe the existing vector to avoid growing infinitely */
2216		proc_list_probe();
2217
2218		/* now scan again */
2219		for (i = 0; i < ProcListSize; i++)
2220		{
2221			if (ProcListVec[i].proc_pid == NO_PID)
2222				break;
2223		}
2224	}
2225	if (i >= ProcListSize)
2226	{
2227		/* grow process list */
2228		struct procs *npv;
2229
2230		npv = (struct procs *) xalloc((sizeof *npv) *
2231					      (ProcListSize + PROC_LIST_SEG));
2232		if (ProcListSize > 0)
2233		{
2234			memmove(npv, ProcListVec,
2235				ProcListSize * sizeof (struct procs));
2236			free(ProcListVec);
2237		}
2238		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2239		{
2240			npv[i].proc_pid = NO_PID;
2241			npv[i].proc_task = NULL;
2242			npv[i].proc_type = PROC_NONE;
2243		}
2244		i = ProcListSize;
2245		ProcListSize += PROC_LIST_SEG;
2246		ProcListVec = npv;
2247	}
2248	ProcListVec[i].proc_pid = pid;
2249	if (ProcListVec[i].proc_task != NULL)
2250		free(ProcListVec[i].proc_task);
2251	ProcListVec[i].proc_task = newstr(task);
2252	ProcListVec[i].proc_type = type;
2253
2254	/* if process adding itself, it's not a child */
2255	if (pid != getpid())
2256		CurChildren++;
2257}
2258/*
2259**  PROC_LIST_SET -- set pid task in process list
2260**
2261**	Parameters:
2262**		pid -- pid to set
2263**		task -- task of pid
2264**
2265**	Returns:
2266**		none.
2267*/
2268
2269void
2270proc_list_set(pid, task)
2271	pid_t pid;
2272	char *task;
2273{
2274	int i;
2275
2276	for (i = 0; i < ProcListSize; i++)
2277	{
2278		if (ProcListVec[i].proc_pid == pid)
2279		{
2280			if (ProcListVec[i].proc_task != NULL)
2281				free(ProcListVec[i].proc_task);
2282			ProcListVec[i].proc_task = newstr(task);
2283			break;
2284		}
2285	}
2286}
2287/*
2288**  PROC_LIST_DROP -- drop pid from process list
2289**
2290**	Parameters:
2291**		pid -- pid to drop
2292**
2293**	Returns:
2294**		type of process
2295*/
2296
2297int
2298proc_list_drop(pid)
2299	pid_t pid;
2300{
2301	int i;
2302	int type = PROC_NONE;
2303
2304	for (i = 0; i < ProcListSize; i++)
2305	{
2306		if (ProcListVec[i].proc_pid == pid)
2307		{
2308			ProcListVec[i].proc_pid = NO_PID;
2309			type = ProcListVec[i].proc_type;
2310			break;
2311		}
2312	}
2313	if (CurChildren > 0)
2314		CurChildren--;
2315
2316
2317	return type;
2318}
2319/*
2320**  PROC_LIST_CLEAR -- clear the process list
2321**
2322**	Parameters:
2323**		none.
2324**
2325**	Returns:
2326**		none.
2327*/
2328
2329void
2330proc_list_clear()
2331{
2332	int i;
2333
2334	/* start from 1 since 0 is the daemon itself */
2335	for (i = 1; i < ProcListSize; i++)
2336	{
2337		ProcListVec[i].proc_pid = NO_PID;
2338	}
2339	CurChildren = 0;
2340}
2341/*
2342**  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2343**
2344**	Parameters:
2345**		none
2346**
2347**	Returns:
2348**		none
2349*/
2350
2351void
2352proc_list_probe()
2353{
2354	int i;
2355
2356	/* start from 1 since 0 is the daemon itself */
2357	for (i = 1; i < ProcListSize; i++)
2358	{
2359		if (ProcListVec[i].proc_pid == NO_PID)
2360			continue;
2361		if (kill(ProcListVec[i].proc_pid, 0) < 0)
2362		{
2363			if (LogLevel > 3)
2364				sm_syslog(LOG_DEBUG, CurEnv->e_id,
2365					  "proc_list_probe: lost pid %d",
2366					  (int) ProcListVec[i].proc_pid);
2367			ProcListVec[i].proc_pid = NO_PID;
2368			CurChildren--;
2369		}
2370	}
2371	if (CurChildren < 0)
2372		CurChildren = 0;
2373}
2374/*
2375**  PROC_LIST_DISPLAY -- display the process list
2376**
2377**	Parameters:
2378**		out -- output file pointer
2379**
2380**	Returns:
2381**		none.
2382*/
2383
2384void
2385proc_list_display(out)
2386	FILE *out;
2387{
2388	int i;
2389
2390	for (i = 0; i < ProcListSize; i++)
2391	{
2392		if (ProcListVec[i].proc_pid == NO_PID)
2393			continue;
2394
2395		fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid,
2396			ProcListVec[i].proc_task != NULL ?
2397			ProcListVec[i].proc_task : "(unknown)",
2398			(OpMode == MD_SMTP ||
2399			 OpMode == MD_DAEMON ||
2400			 OpMode == MD_ARPAFTP) ? "\r" : "");
2401	}
2402}
2403/*
2404**  SM_STRCASECMP -- 8-bit clean version of strcasecmp
2405**
2406**	Thank you, vendors, for making this all necessary.
2407*/
2408
2409/*
2410 * Copyright (c) 1987, 1993
2411 *	The Regents of the University of California.  All rights reserved.
2412 *
2413 * Redistribution and use in source and binary forms, with or without
2414 * modification, are permitted provided that the following conditions
2415 * are met:
2416 * 1. Redistributions of source code must retain the above copyright
2417 *    notice, this list of conditions and the following disclaimer.
2418 * 2. Redistributions in binary form must reproduce the above copyright
2419 *    notice, this list of conditions and the following disclaimer in the
2420 *    documentation and/or other materials provided with the distribution.
2421 * 3. All advertising materials mentioning features or use of this software
2422 *    must display the following acknowledgement:
2423 *	This product includes software developed by the University of
2424 *	California, Berkeley and its contributors.
2425 * 4. Neither the name of the University nor the names of its contributors
2426 *    may be used to endorse or promote products derived from this software
2427 *    without specific prior written permission.
2428 *
2429 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2430 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2431 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2432 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2433 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2434 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2435 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2436 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2437 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2438 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2439 * SUCH DAMAGE.
2440 */
2441
2442#if defined(LIBC_SCCS) && !defined(lint)
2443static char sccsid[] = "@(#)strcasecmp.c	8.1 (Berkeley) 6/4/93";
2444#endif /* defined(LIBC_SCCS) && !defined(lint) */
2445
2446/*
2447 * This array is designed for mapping upper and lower case letter
2448 * together for a case independent comparison.  The mappings are
2449 * based upon ascii character sequences.
2450 */
2451static const u_char charmap[] = {
2452	0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
2453	0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
2454	0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
2455	0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
2456	0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
2457	0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
2458	0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
2459	0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
2460	0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2461	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2462	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2463	0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
2464	0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2465	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2466	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2467	0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
2468	0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
2469	0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
2470	0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
2471	0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
2472	0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
2473	0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
2474	0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
2475	0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
2476	0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
2477	0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
2478	0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
2479	0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
2480	0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
2481	0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
2482	0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
2483	0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
2484};
2485
2486int
2487sm_strcasecmp(s1, s2)
2488	const char *s1, *s2;
2489{
2490	register const u_char *cm = charmap,
2491			*us1 = (const u_char *)s1,
2492			*us2 = (const u_char *)s2;
2493
2494	while (cm[*us1] == cm[*us2++])
2495		if (*us1++ == '\0')
2496			return 0;
2497	return (cm[*us1] - cm[*--us2]);
2498}
2499
2500int
2501sm_strncasecmp(s1, s2, n)
2502	const char *s1, *s2;
2503	register size_t n;
2504{
2505	if (n != 0) {
2506		register const u_char *cm = charmap,
2507				*us1 = (const u_char *)s1,
2508				*us2 = (const u_char *)s2;
2509
2510		do {
2511			if (cm[*us1] != cm[*us2++])
2512				return (cm[*us1] - cm[*--us2]);
2513			if (*us1++ == '\0')
2514				break;
2515		} while (--n != 0);
2516	}
2517	return 0;
2518}
2519