xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision 8a5f61af)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	6.4 (Berkeley) 01/21/93";
11 #endif /* not lint */
12 
13 # include <stdio.h>
14 # include <sys/types.h>
15 # include <sys/stat.h>
16 # include <sysexits.h>
17 # include <errno.h>
18 # include "sendmail.h"
19 /*
20 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
21 **
22 **	Runs through a string and strips off unquoted quote
23 **	characters and quote bits.  This is done in place.
24 **
25 **	Parameters:
26 **		s -- the string to strip.
27 **
28 **	Returns:
29 **		none.
30 **
31 **	Side Effects:
32 **		none.
33 **
34 **	Called By:
35 **		deliver
36 */
37 
38 stripquotes(s)
39 	char *s;
40 {
41 	register char *p;
42 	register char *q;
43 	register char c;
44 
45 	if (s == NULL)
46 		return;
47 
48 	p = q = s;
49 	do
50 	{
51 		c = *p++;
52 		if (c == '\\')
53 			c = *p++;
54 		else if (c == '"')
55 			continue;
56 		*q++ = c;
57 	} while (c != '\0');
58 }
59 /*
60 **  CAPITALIZE -- return a copy of a string, properly capitalized.
61 **
62 **	Parameters:
63 **		s -- the string to capitalize.
64 **
65 **	Returns:
66 **		a pointer to a properly capitalized string.
67 **
68 **	Side Effects:
69 **		none.
70 */
71 
72 char *
73 capitalize(s)
74 	register char *s;
75 {
76 	static char buf[50];
77 	register char *p;
78 
79 	p = buf;
80 
81 	for (;;)
82 	{
83 		while (!isalpha(*s) && *s != '\0')
84 			*p++ = *s++;
85 		if (*s == '\0')
86 			break;
87 		*p++ = toupper(*s);
88 		s++;
89 		while (isalpha(*s))
90 			*p++ = *s++;
91 	}
92 
93 	*p = '\0';
94 	return (buf);
95 }
96 /*
97 **  XALLOC -- Allocate memory and bitch wildly on failure.
98 **
99 **	THIS IS A CLUDGE.  This should be made to give a proper
100 **	error -- but after all, what can we do?
101 **
102 **	Parameters:
103 **		sz -- size of area to allocate.
104 **
105 **	Returns:
106 **		pointer to data region.
107 **
108 **	Side Effects:
109 **		Memory is allocated.
110 */
111 
112 char *
113 xalloc(sz)
114 	register int sz;
115 {
116 	register char *p;
117 
118 	p = malloc((unsigned) sz);
119 	if (p == NULL)
120 	{
121 		syserr("Out of memory!!");
122 		abort();
123 		/* exit(EX_UNAVAILABLE); */
124 	}
125 	return (p);
126 }
127 /*
128 **  COPYPLIST -- copy list of pointers.
129 **
130 **	This routine is the equivalent of newstr for lists of
131 **	pointers.
132 **
133 **	Parameters:
134 **		list -- list of pointers to copy.
135 **			Must be NULL terminated.
136 **		copycont -- if TRUE, copy the contents of the vector
137 **			(which must be a string) also.
138 **
139 **	Returns:
140 **		a copy of 'list'.
141 **
142 **	Side Effects:
143 **		none.
144 */
145 
146 char **
147 copyplist(list, copycont)
148 	char **list;
149 	bool copycont;
150 {
151 	register char **vp;
152 	register char **newvp;
153 
154 	for (vp = list; *vp != NULL; vp++)
155 		continue;
156 
157 	vp++;
158 
159 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
160 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
161 
162 	if (copycont)
163 	{
164 		for (vp = newvp; *vp != NULL; vp++)
165 			*vp = newstr(*vp);
166 	}
167 
168 	return (newvp);
169 }
170 /*
171 **  PRINTAV -- print argument vector.
172 **
173 **	Parameters:
174 **		av -- argument vector.
175 **
176 **	Returns:
177 **		none.
178 **
179 **	Side Effects:
180 **		prints av.
181 */
182 
183 printav(av)
184 	register char **av;
185 {
186 	while (*av != NULL)
187 	{
188 		if (tTd(0, 44))
189 			printf("\n\t%08x=", *av);
190 		else
191 			(void) putchar(' ');
192 		xputs(*av++);
193 	}
194 	(void) putchar('\n');
195 }
196 /*
197 **  LOWER -- turn letter into lower case.
198 **
199 **	Parameters:
200 **		c -- character to turn into lower case.
201 **
202 **	Returns:
203 **		c, in lower case.
204 **
205 **	Side Effects:
206 **		none.
207 */
208 
209 char
210 lower(c)
211 	register char c;
212 {
213 	return(isascii(c) && isupper(c) ? tolower(c) : c);
214 }
215 /*
216 **  XPUTS -- put string doing control escapes.
217 **
218 **	Parameters:
219 **		s -- string to put.
220 **
221 **	Returns:
222 **		none.
223 **
224 **	Side Effects:
225 **		output to stdout
226 */
227 
228 xputs(s)
229 	register char *s;
230 {
231 	register char c;
232 	register struct metamac *mp;
233 	extern struct metamac MetaMacros[];
234 
235 	if (s == NULL)
236 	{
237 		printf("<null>");
238 		return;
239 	}
240 	while ((c = *s++) != '\0')
241 	{
242 		if (!isascii(c))
243 		{
244 			(void) putchar('\\');
245 			c &= 0177;
246 		}
247 		if (isprint(c))
248 		{
249 			putchar(c);
250 			continue;
251 		}
252 		if (c == MATCHREPL || c == '\001')
253 		{
254 			putchar('$');
255 			continue;
256 		}
257 		for (mp = MetaMacros; mp->metaname != '\0'; mp++)
258 		{
259 			if (mp->metaval == c)
260 			{
261 				printf("$%c", mp->metaname);
262 				c = '\0';
263 				break;
264 			}
265 		}
266 
267 		/* wasn't a meta-macro -- find another way to print it */
268 		switch (c)
269 		{
270 		  case '\0':
271 			continue;
272 
273 		  case '\n':
274 			c = 'n';
275 			break;
276 
277 		  case '\r':
278 			c = 'r';
279 			break;
280 
281 		  case '\t':
282 			c = 't';
283 			break;
284 
285 		  default:
286 			(void) putchar('^');
287 			(void) putchar(c ^ 0100);
288 			continue;
289 		}
290 	}
291 	(void) fflush(stdout);
292 }
293 /*
294 **  MAKELOWER -- Translate a line into lower case
295 **
296 **	Parameters:
297 **		p -- the string to translate.  If NULL, return is
298 **			immediate.
299 **
300 **	Returns:
301 **		none.
302 **
303 **	Side Effects:
304 **		String pointed to by p is translated to lower case.
305 **
306 **	Called By:
307 **		parse
308 */
309 
310 makelower(p)
311 	register char *p;
312 {
313 	register char c;
314 
315 	if (p == NULL)
316 		return;
317 	for (; (c = *p) != '\0'; p++)
318 		if (isascii(c) && isupper(c))
319 			*p = tolower(c);
320 }
321 /*
322 **  BUILDFNAME -- build full name from gecos style entry.
323 **
324 **	This routine interprets the strange entry that would appear
325 **	in the GECOS field of the password file.
326 **
327 **	Parameters:
328 **		p -- name to build.
329 **		login -- the login name of this user (for &).
330 **		buf -- place to put the result.
331 **
332 **	Returns:
333 **		none.
334 **
335 **	Side Effects:
336 **		none.
337 */
338 
339 buildfname(gecos, login, buf)
340 	register char *gecos;
341 	char *login;
342 	char *buf;
343 {
344 	register char *p;
345 	register char *bp = buf;
346 	int l;
347 
348 	if (*gecos == '*')
349 		gecos++;
350 
351 	/* find length of final string */
352 	l = 0;
353 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
354 	{
355 		if (*p == '&')
356 			l += strlen(login);
357 		else
358 			l++;
359 	}
360 
361 	/* now fill in buf */
362 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
363 	{
364 		if (*p == '&')
365 		{
366 			(void) strcpy(bp, login);
367 			*bp = toupper(*bp);
368 			while (*bp != '\0')
369 				bp++;
370 		}
371 		else
372 			*bp++ = *p;
373 	}
374 	*bp = '\0';
375 }
376 /*
377 **  SAFEFILE -- return true if a file exists and is safe for a user.
378 **
379 **	Parameters:
380 **		fn -- filename to check.
381 **		uid -- uid to compare against.
382 **		mode -- mode bits that must match.
383 **
384 **	Returns:
385 **		TRUE if fn exists, is owned by uid, and matches mode.
386 **		FALSE otherwise.
387 **
388 **	Side Effects:
389 **		none.
390 */
391 
392 bool
393 safefile(fn, uid, mode)
394 	char *fn;
395 	uid_t uid;
396 	int mode;
397 {
398 	struct stat stbuf;
399 
400 	if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
401 	    (stbuf.st_mode & mode) == mode)
402 		return (TRUE);
403 	errno = 0;
404 	return (FALSE);
405 }
406 /*
407 **  FIXCRLF -- fix <CR><LF> in line.
408 **
409 **	Looks for the <CR><LF> combination and turns it into the
410 **	UNIX canonical <NL> character.  It only takes one line,
411 **	i.e., it is assumed that the first <NL> found is the end
412 **	of the line.
413 **
414 **	Parameters:
415 **		line -- the line to fix.
416 **		stripnl -- if true, strip the newline also.
417 **
418 **	Returns:
419 **		none.
420 **
421 **	Side Effects:
422 **		line is changed in place.
423 */
424 
425 fixcrlf(line, stripnl)
426 	char *line;
427 	bool stripnl;
428 {
429 	register char *p;
430 
431 	p = strchr(line, '\n');
432 	if (p == NULL)
433 		return;
434 	if (p > line && p[-1] == '\r')
435 		p--;
436 	if (!stripnl)
437 		*p++ = '\n';
438 	*p = '\0';
439 }
440 /*
441 **  DFOPEN -- determined file open
442 **
443 **	This routine has the semantics of fopen, except that it will
444 **	keep trying a few times to make this happen.  The idea is that
445 **	on very loaded systems, we may run out of resources (inodes,
446 **	whatever), so this tries to get around it.
447 */
448 
449 FILE *
450 dfopen(filename, mode)
451 	char *filename;
452 	char *mode;
453 {
454 	register int tries;
455 	register FILE *fp;
456 
457 	for (tries = 0; tries < 10; tries++)
458 	{
459 		sleep((unsigned) (10 * tries));
460 		errno = 0;
461 		fp = fopen(filename, mode);
462 		if (fp != NULL)
463 			break;
464 		if (errno != ENFILE && errno != EINTR)
465 			break;
466 	}
467 	if (fp != NULL)
468 	{
469 #ifdef FLOCK
470 		int locktype;
471 
472 		/* lock the file to avoid accidental conflicts */
473 		if (*mode == 'w' || *mode == 'a')
474 			locktype = LOCK_EX;
475 		else
476 			locktype = LOCK_SH;
477 		(void) flock(fileno(fp), locktype);
478 #endif
479 		errno = 0;
480 	}
481 	return (fp);
482 }
483 /*
484 **  PUTLINE -- put a line like fputs obeying SMTP conventions
485 **
486 **	This routine always guarantees outputing a newline (or CRLF,
487 **	as appropriate) at the end of the string.
488 **
489 **	Parameters:
490 **		l -- line to put.
491 **		fp -- file to put it onto.
492 **		m -- the mailer used to control output.
493 **
494 **	Returns:
495 **		none
496 **
497 **	Side Effects:
498 **		output of l to fp.
499 */
500 
501 putline(l, fp, m)
502 	register char *l;
503 	FILE *fp;
504 	MAILER *m;
505 {
506 	register char *p;
507 	register char svchar;
508 
509 	/* strip out 0200 bits -- these can look like TELNET protocol */
510 	if (bitnset(M_7BITS, m->m_flags))
511 	{
512 		for (p = l; svchar = *p; ++p)
513 			if (svchar & 0200)
514 				*p = svchar &~ 0200;
515 	}
516 
517 	do
518 	{
519 		/* find the end of the line */
520 		p = strchr(l, '\n');
521 		if (p == NULL)
522 			p = &l[strlen(l)];
523 
524 		/* check for line overflow */
525 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
526 		{
527 			register char *q = &l[m->m_linelimit - 1];
528 
529 			svchar = *q;
530 			*q = '\0';
531 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
532 				(void) putc('.', fp);
533 			fputs(l, fp);
534 			(void) putc('!', fp);
535 			fputs(m->m_eol, fp);
536 			*q = svchar;
537 			l = q;
538 		}
539 
540 		/* output last part */
541 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
542 			(void) putc('.', fp);
543 		for ( ; l < p; ++l)
544 			(void) putc(*l, fp);
545 		fputs(m->m_eol, fp);
546 		if (*l == '\n')
547 			++l;
548 	} while (l[0] != '\0');
549 }
550 /*
551 **  XUNLINK -- unlink a file, doing logging as appropriate.
552 **
553 **	Parameters:
554 **		f -- name of file to unlink.
555 **
556 **	Returns:
557 **		none.
558 **
559 **	Side Effects:
560 **		f is unlinked.
561 */
562 
563 xunlink(f)
564 	char *f;
565 {
566 	register int i;
567 
568 # ifdef LOG
569 	if (LogLevel > 20)
570 		syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
571 # endif /* LOG */
572 
573 	i = unlink(f);
574 # ifdef LOG
575 	if (i < 0 && LogLevel > 21)
576 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
577 # endif /* LOG */
578 }
579 /*
580 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
581 **
582 **	Parameters:
583 **		buf -- place to put the input line.
584 **		siz -- size of buf.
585 **		fp -- file to read from.
586 **		timeout -- the timeout before error occurs.
587 **
588 **	Returns:
589 **		NULL on error (including timeout).  This will also leave
590 **			buf containing a null string.
591 **		buf otherwise.
592 **
593 **	Side Effects:
594 **		none.
595 */
596 
597 static jmp_buf	CtxReadTimeout;
598 
599 char *
600 sfgets(buf, siz, fp, timeout)
601 	char *buf;
602 	int siz;
603 	FILE *fp;
604 	time_t timeout;
605 {
606 	register EVENT *ev = NULL;
607 	register char *p;
608 	static int readtimeout();
609 
610 	/* set the timeout */
611 	if (timeout != 0)
612 	{
613 		if (setjmp(CtxReadTimeout) != 0)
614 		{
615 # ifdef LOG
616 			syslog(LOG_NOTICE,
617 			    "timeout waiting for input from %s\n",
618 			    CurHostName? CurHostName: "local");
619 # endif
620 			errno = 0;
621 			usrerr("451 timeout waiting for input");
622 			buf[0] = '\0';
623 			return (NULL);
624 		}
625 		ev = setevent(timeout, readtimeout, 0);
626 	}
627 
628 	/* try to read */
629 	p = NULL;
630 	while (p == NULL && !feof(fp) && !ferror(fp))
631 	{
632 		errno = 0;
633 		p = fgets(buf, siz, fp);
634 		if (errno == EINTR)
635 			clearerr(fp);
636 	}
637 
638 	/* clear the event if it has not sprung */
639 	clrevent(ev);
640 
641 	/* clean up the books and exit */
642 	LineNumber++;
643 	if (p == NULL)
644 	{
645 		buf[0] = '\0';
646 		return (NULL);
647 	}
648 	if (!EightBit)
649 		for (p = buf; *p != '\0'; p++)
650 			*p &= ~0200;
651 	return (buf);
652 }
653 
654 static
655 readtimeout()
656 {
657 	longjmp(CtxReadTimeout, 1);
658 }
659 /*
660 **  FGETFOLDED -- like fgets, but know about folded lines.
661 **
662 **	Parameters:
663 **		buf -- place to put result.
664 **		n -- bytes available.
665 **		f -- file to read from.
666 **
667 **	Returns:
668 **		input line(s) on success, NULL on error or EOF.
669 **		This will normally be buf -- unless the line is too
670 **			long, when it will be xalloc()ed.
671 **
672 **	Side Effects:
673 **		buf gets lines from f, with continuation lines (lines
674 **		with leading white space) appended.  CRLF's are mapped
675 **		into single newlines.  Any trailing NL is stripped.
676 */
677 
678 char *
679 fgetfolded(buf, n, f)
680 	char *buf;
681 	register int n;
682 	FILE *f;
683 {
684 	register char *p = buf;
685 	char *bp = buf;
686 	register int i;
687 
688 	n--;
689 	while ((i = getc(f)) != EOF)
690 	{
691 		if (i == '\r')
692 		{
693 			i = getc(f);
694 			if (i != '\n')
695 			{
696 				if (i != EOF)
697 					(void) ungetc(i, f);
698 				i = '\r';
699 			}
700 		}
701 		if (--n <= 0)
702 		{
703 			/* allocate new space */
704 			char *nbp;
705 			int nn;
706 
707 			nn = (p - bp);
708 			if (nn < MEMCHUNKSIZE)
709 				nn *= 2;
710 			else
711 				nn += MEMCHUNKSIZE;
712 			nbp = xalloc(nn);
713 			bcopy(bp, nbp, p - bp);
714 			p = &nbp[p - bp];
715 			if (bp != buf)
716 				free(bp);
717 			bp = nbp;
718 			n = nn - (p - bp);
719 		}
720 		*p++ = i;
721 		if (i == '\n')
722 		{
723 			LineNumber++;
724 			i = getc(f);
725 			if (i != EOF)
726 				(void) ungetc(i, f);
727 			if (i != ' ' && i != '\t')
728 				break;
729 		}
730 	}
731 	if (p == bp)
732 		return (NULL);
733 	*--p = '\0';
734 	return (bp);
735 }
736 /*
737 **  CURTIME -- return current time.
738 **
739 **	Parameters:
740 **		none.
741 **
742 **	Returns:
743 **		the current time.
744 **
745 **	Side Effects:
746 **		none.
747 */
748 
749 time_t
750 curtime()
751 {
752 	auto time_t t;
753 
754 	(void) time(&t);
755 	return (t);
756 }
757 /*
758 **  ATOBOOL -- convert a string representation to boolean.
759 **
760 **	Defaults to "TRUE"
761 **
762 **	Parameters:
763 **		s -- string to convert.  Takes "tTyY" as true,
764 **			others as false.
765 **
766 **	Returns:
767 **		A boolean representation of the string.
768 **
769 **	Side Effects:
770 **		none.
771 */
772 
773 bool
774 atobool(s)
775 	register char *s;
776 {
777 	if (*s == '\0' || strchr("tTyY", *s) != NULL)
778 		return (TRUE);
779 	return (FALSE);
780 }
781 /*
782 **  ATOOCT -- convert a string representation to octal.
783 **
784 **	Parameters:
785 **		s -- string to convert.
786 **
787 **	Returns:
788 **		An integer representing the string interpreted as an
789 **		octal number.
790 **
791 **	Side Effects:
792 **		none.
793 */
794 
795 atooct(s)
796 	register char *s;
797 {
798 	register int i = 0;
799 
800 	while (*s >= '0' && *s <= '7')
801 		i = (i << 3) | (*s++ - '0');
802 	return (i);
803 }
804 /*
805 **  WAITFOR -- wait for a particular process id.
806 **
807 **	Parameters:
808 **		pid -- process id to wait for.
809 **
810 **	Returns:
811 **		status of pid.
812 **		-1 if pid never shows up.
813 **
814 **	Side Effects:
815 **		none.
816 */
817 
818 waitfor(pid)
819 	int pid;
820 {
821 	auto int st;
822 	int i;
823 
824 	do
825 	{
826 		errno = 0;
827 		i = wait(&st);
828 	} while ((i >= 0 || errno == EINTR) && i != pid);
829 	if (i < 0)
830 		st = -1;
831 	return (st);
832 }
833 /*
834 **  BITINTERSECT -- tell if two bitmaps intersect
835 **
836 **	Parameters:
837 **		a, b -- the bitmaps in question
838 **
839 **	Returns:
840 **		TRUE if they have a non-null intersection
841 **		FALSE otherwise
842 **
843 **	Side Effects:
844 **		none.
845 */
846 
847 bool
848 bitintersect(a, b)
849 	BITMAP a;
850 	BITMAP b;
851 {
852 	int i;
853 
854 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
855 		if ((a[i] & b[i]) != 0)
856 			return (TRUE);
857 	return (FALSE);
858 }
859 /*
860 **  BITZEROP -- tell if a bitmap is all zero
861 **
862 **	Parameters:
863 **		map -- the bit map to check
864 **
865 **	Returns:
866 **		TRUE if map is all zero.
867 **		FALSE if there are any bits set in map.
868 **
869 **	Side Effects:
870 **		none.
871 */
872 
873 bool
874 bitzerop(map)
875 	BITMAP map;
876 {
877 	int i;
878 
879 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
880 		if (map[i] != 0)
881 			return (FALSE);
882 	return (TRUE);
883 }
884