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