xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 58050)
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.6 (Berkeley) 02/18/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 (!(isascii(*s) && isalpha(*s)) && *s != '\0')
84 			*p++ = *s++;
85 		if (*s == '\0')
86 			break;
87 		*p++ = toupper(*s);
88 		s++;
89 		while (isascii(*s) && 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 int 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++ & 0377)) != '\0')
241 	{
242 		if (!isascii(c))
243 		{
244 			if (c == MATCHREPL || c == MACROEXPAND)
245 			{
246 				putchar('$');
247 				continue;
248 			}
249 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
250 			{
251 				if ((mp->metaval & 0377) == c)
252 				{
253 					printf("$%c", mp->metaname);
254 					break;
255 				}
256 			}
257 			if (mp->metaname != '\0')
258 				continue;
259 			(void) putchar('\\');
260 			c &= 0177;
261 		}
262 		if (isprint(c))
263 		{
264 			putchar(c);
265 			continue;
266 		}
267 
268 		/* wasn't a meta-macro -- find another way to print it */
269 		switch (c)
270 		{
271 		  case '\0':
272 			continue;
273 
274 		  case '\n':
275 			c = 'n';
276 			break;
277 
278 		  case '\r':
279 			c = 'r';
280 			break;
281 
282 		  case '\t':
283 			c = 't';
284 			break;
285 
286 		  default:
287 			(void) putchar('^');
288 			(void) putchar(c ^ 0100);
289 			continue;
290 		}
291 	}
292 	(void) fflush(stdout);
293 }
294 /*
295 **  MAKELOWER -- Translate a line into lower case
296 **
297 **	Parameters:
298 **		p -- the string to translate.  If NULL, return is
299 **			immediate.
300 **
301 **	Returns:
302 **		none.
303 **
304 **	Side Effects:
305 **		String pointed to by p is translated to lower case.
306 **
307 **	Called By:
308 **		parse
309 */
310 
311 makelower(p)
312 	register char *p;
313 {
314 	register char c;
315 
316 	if (p == NULL)
317 		return;
318 	for (; (c = *p) != '\0'; p++)
319 		if (isascii(c) && isupper(c))
320 			*p = tolower(c);
321 }
322 /*
323 **  BUILDFNAME -- build full name from gecos style entry.
324 **
325 **	This routine interprets the strange entry that would appear
326 **	in the GECOS field of the password file.
327 **
328 **	Parameters:
329 **		p -- name to build.
330 **		login -- the login name of this user (for &).
331 **		buf -- place to put the result.
332 **
333 **	Returns:
334 **		none.
335 **
336 **	Side Effects:
337 **		none.
338 */
339 
340 buildfname(gecos, login, buf)
341 	register char *gecos;
342 	char *login;
343 	char *buf;
344 {
345 	register char *p;
346 	register char *bp = buf;
347 	int l;
348 
349 	if (*gecos == '*')
350 		gecos++;
351 
352 	/* find length of final string */
353 	l = 0;
354 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
355 	{
356 		if (*p == '&')
357 			l += strlen(login);
358 		else
359 			l++;
360 	}
361 
362 	/* now fill in buf */
363 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
364 	{
365 		if (*p == '&')
366 		{
367 			(void) strcpy(bp, login);
368 			*bp = toupper(*bp);
369 			while (*bp != '\0')
370 				bp++;
371 		}
372 		else
373 			*bp++ = *p;
374 	}
375 	*bp = '\0';
376 }
377 /*
378 **  SAFEFILE -- return true if a file exists and is safe for a user.
379 **
380 **	Parameters:
381 **		fn -- filename to check.
382 **		uid -- uid to compare against.
383 **		mode -- mode bits that must match.
384 **
385 **	Returns:
386 **		TRUE if fn exists, is owned by uid, and matches mode.
387 **		FALSE otherwise.
388 **
389 **	Side Effects:
390 **		none.
391 */
392 
393 bool
394 safefile(fn, uid, mode)
395 	char *fn;
396 	uid_t uid;
397 	int mode;
398 {
399 	struct stat stbuf;
400 
401 	if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
402 	    (stbuf.st_mode & mode) == mode)
403 		return (TRUE);
404 	errno = 0;
405 	return (FALSE);
406 }
407 /*
408 **  FIXCRLF -- fix <CR><LF> in line.
409 **
410 **	Looks for the <CR><LF> combination and turns it into the
411 **	UNIX canonical <NL> character.  It only takes one line,
412 **	i.e., it is assumed that the first <NL> found is the end
413 **	of the line.
414 **
415 **	Parameters:
416 **		line -- the line to fix.
417 **		stripnl -- if true, strip the newline also.
418 **
419 **	Returns:
420 **		none.
421 **
422 **	Side Effects:
423 **		line is changed in place.
424 */
425 
426 fixcrlf(line, stripnl)
427 	char *line;
428 	bool stripnl;
429 {
430 	register char *p;
431 
432 	p = strchr(line, '\n');
433 	if (p == NULL)
434 		return;
435 	if (p > line && p[-1] == '\r')
436 		p--;
437 	if (!stripnl)
438 		*p++ = '\n';
439 	*p = '\0';
440 }
441 /*
442 **  DFOPEN -- determined file open
443 **
444 **	This routine has the semantics of fopen, except that it will
445 **	keep trying a few times to make this happen.  The idea is that
446 **	on very loaded systems, we may run out of resources (inodes,
447 **	whatever), so this tries to get around it.
448 */
449 
450 FILE *
451 dfopen(filename, mode)
452 	char *filename;
453 	char *mode;
454 {
455 	register int tries;
456 	register FILE *fp;
457 
458 	for (tries = 0; tries < 10; tries++)
459 	{
460 		sleep((unsigned) (10 * tries));
461 		errno = 0;
462 		fp = fopen(filename, mode);
463 		if (fp != NULL)
464 			break;
465 		if (errno != ENFILE && errno != EINTR)
466 			break;
467 	}
468 	if (fp != NULL)
469 	{
470 #ifdef FLOCK
471 		int locktype;
472 
473 		/* lock the file to avoid accidental conflicts */
474 		if (*mode == 'w' || *mode == 'a')
475 			locktype = LOCK_EX;
476 		else
477 			locktype = LOCK_SH;
478 		(void) flock(fileno(fp), locktype);
479 #endif
480 		errno = 0;
481 	}
482 	return (fp);
483 }
484 /*
485 **  PUTLINE -- put a line like fputs obeying SMTP conventions
486 **
487 **	This routine always guarantees outputing a newline (or CRLF,
488 **	as appropriate) at the end of the string.
489 **
490 **	Parameters:
491 **		l -- line to put.
492 **		fp -- file to put it onto.
493 **		m -- the mailer used to control output.
494 **
495 **	Returns:
496 **		none
497 **
498 **	Side Effects:
499 **		output of l to fp.
500 */
501 
502 putline(l, fp, m)
503 	register char *l;
504 	FILE *fp;
505 	MAILER *m;
506 {
507 	register char *p;
508 	register char svchar;
509 
510 	/* strip out 0200 bits -- these can look like TELNET protocol */
511 	if (bitnset(M_7BITS, m->m_flags))
512 	{
513 		for (p = l; svchar = *p; ++p)
514 			if (svchar & 0200)
515 				*p = svchar &~ 0200;
516 	}
517 
518 	do
519 	{
520 		/* find the end of the line */
521 		p = strchr(l, '\n');
522 		if (p == NULL)
523 			p = &l[strlen(l)];
524 
525 		/* check for line overflow */
526 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
527 		{
528 			register char *q = &l[m->m_linelimit - 1];
529 
530 			svchar = *q;
531 			*q = '\0';
532 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
533 				(void) putc('.', fp);
534 			fputs(l, fp);
535 			(void) putc('!', fp);
536 			fputs(m->m_eol, fp);
537 			*q = svchar;
538 			l = q;
539 		}
540 
541 		/* output last part */
542 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
543 			(void) putc('.', fp);
544 		for ( ; l < p; ++l)
545 			(void) putc(*l, fp);
546 		fputs(m->m_eol, fp);
547 		if (*l == '\n')
548 			++l;
549 	} while (l[0] != '\0');
550 }
551 /*
552 **  XUNLINK -- unlink a file, doing logging as appropriate.
553 **
554 **	Parameters:
555 **		f -- name of file to unlink.
556 **
557 **	Returns:
558 **		none.
559 **
560 **	Side Effects:
561 **		f is unlinked.
562 */
563 
564 xunlink(f)
565 	char *f;
566 {
567 	register int i;
568 
569 # ifdef LOG
570 	if (LogLevel > 98)
571 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
572 # endif /* LOG */
573 
574 	i = unlink(f);
575 # ifdef LOG
576 	if (i < 0 && LogLevel > 97)
577 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
578 # endif /* LOG */
579 }
580 /*
581 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
582 **
583 **	Parameters:
584 **		buf -- place to put the input line.
585 **		siz -- size of buf.
586 **		fp -- file to read from.
587 **		timeout -- the timeout before error occurs.
588 **
589 **	Returns:
590 **		NULL on error (including timeout).  This will also leave
591 **			buf containing a null string.
592 **		buf otherwise.
593 **
594 **	Side Effects:
595 **		none.
596 */
597 
598 static jmp_buf	CtxReadTimeout;
599 
600 char *
601 sfgets(buf, siz, fp, timeout)
602 	char *buf;
603 	int siz;
604 	FILE *fp;
605 	time_t timeout;
606 {
607 	register EVENT *ev = NULL;
608 	register char *p;
609 	static int readtimeout();
610 
611 	/* set the timeout */
612 	if (timeout != 0)
613 	{
614 		if (setjmp(CtxReadTimeout) != 0)
615 		{
616 # ifdef LOG
617 			syslog(LOG_NOTICE,
618 			    "timeout waiting for input from %s\n",
619 			    CurHostName? CurHostName: "local");
620 # endif
621 			errno = 0;
622 			usrerr("451 timeout waiting for input");
623 			buf[0] = '\0';
624 			return (NULL);
625 		}
626 		ev = setevent(timeout, readtimeout, 0);
627 	}
628 
629 	/* try to read */
630 	p = NULL;
631 	while (p == NULL && !feof(fp) && !ferror(fp))
632 	{
633 		errno = 0;
634 		p = fgets(buf, siz, fp);
635 		if (errno == EINTR)
636 			clearerr(fp);
637 	}
638 
639 	/* clear the event if it has not sprung */
640 	clrevent(ev);
641 
642 	/* clean up the books and exit */
643 	LineNumber++;
644 	if (p == NULL)
645 	{
646 		buf[0] = '\0';
647 		return (NULL);
648 	}
649 	if (!EightBit)
650 		for (p = buf; *p != '\0'; p++)
651 			*p &= ~0200;
652 	return (buf);
653 }
654 
655 static
656 readtimeout()
657 {
658 	longjmp(CtxReadTimeout, 1);
659 }
660 /*
661 **  FGETFOLDED -- like fgets, but know about folded lines.
662 **
663 **	Parameters:
664 **		buf -- place to put result.
665 **		n -- bytes available.
666 **		f -- file to read from.
667 **
668 **	Returns:
669 **		input line(s) on success, NULL on error or EOF.
670 **		This will normally be buf -- unless the line is too
671 **			long, when it will be xalloc()ed.
672 **
673 **	Side Effects:
674 **		buf gets lines from f, with continuation lines (lines
675 **		with leading white space) appended.  CRLF's are mapped
676 **		into single newlines.  Any trailing NL is stripped.
677 */
678 
679 char *
680 fgetfolded(buf, n, f)
681 	char *buf;
682 	register int n;
683 	FILE *f;
684 {
685 	register char *p = buf;
686 	char *bp = buf;
687 	register int i;
688 
689 	n--;
690 	while ((i = getc(f)) != EOF)
691 	{
692 		if (i == '\r')
693 		{
694 			i = getc(f);
695 			if (i != '\n')
696 			{
697 				if (i != EOF)
698 					(void) ungetc(i, f);
699 				i = '\r';
700 			}
701 		}
702 		if (--n <= 0)
703 		{
704 			/* allocate new space */
705 			char *nbp;
706 			int nn;
707 
708 			nn = (p - bp);
709 			if (nn < MEMCHUNKSIZE)
710 				nn *= 2;
711 			else
712 				nn += MEMCHUNKSIZE;
713 			nbp = xalloc(nn);
714 			bcopy(bp, nbp, p - bp);
715 			p = &nbp[p - bp];
716 			if (bp != buf)
717 				free(bp);
718 			bp = nbp;
719 			n = nn - (p - bp);
720 		}
721 		*p++ = i;
722 		if (i == '\n')
723 		{
724 			LineNumber++;
725 			i = getc(f);
726 			if (i != EOF)
727 				(void) ungetc(i, f);
728 			if (i != ' ' && i != '\t')
729 				break;
730 		}
731 	}
732 	if (p == bp)
733 		return (NULL);
734 	*--p = '\0';
735 	return (bp);
736 }
737 /*
738 **  CURTIME -- return current time.
739 **
740 **	Parameters:
741 **		none.
742 **
743 **	Returns:
744 **		the current time.
745 **
746 **	Side Effects:
747 **		none.
748 */
749 
750 time_t
751 curtime()
752 {
753 	auto time_t t;
754 
755 	(void) time(&t);
756 	return (t);
757 }
758 /*
759 **  ATOBOOL -- convert a string representation to boolean.
760 **
761 **	Defaults to "TRUE"
762 **
763 **	Parameters:
764 **		s -- string to convert.  Takes "tTyY" as true,
765 **			others as false.
766 **
767 **	Returns:
768 **		A boolean representation of the string.
769 **
770 **	Side Effects:
771 **		none.
772 */
773 
774 bool
775 atobool(s)
776 	register char *s;
777 {
778 	if (*s == '\0' || strchr("tTyY", *s) != NULL)
779 		return (TRUE);
780 	return (FALSE);
781 }
782 /*
783 **  ATOOCT -- convert a string representation to octal.
784 **
785 **	Parameters:
786 **		s -- string to convert.
787 **
788 **	Returns:
789 **		An integer representing the string interpreted as an
790 **		octal number.
791 **
792 **	Side Effects:
793 **		none.
794 */
795 
796 atooct(s)
797 	register char *s;
798 {
799 	register int i = 0;
800 
801 	while (*s >= '0' && *s <= '7')
802 		i = (i << 3) | (*s++ - '0');
803 	return (i);
804 }
805 /*
806 **  WAITFOR -- wait for a particular process id.
807 **
808 **	Parameters:
809 **		pid -- process id to wait for.
810 **
811 **	Returns:
812 **		status of pid.
813 **		-1 if pid never shows up.
814 **
815 **	Side Effects:
816 **		none.
817 */
818 
819 waitfor(pid)
820 	int pid;
821 {
822 	auto int st;
823 	int i;
824 
825 	do
826 	{
827 		errno = 0;
828 		i = wait(&st);
829 	} while ((i >= 0 || errno == EINTR) && i != pid);
830 	if (i < 0)
831 		st = -1;
832 	return (st);
833 }
834 /*
835 **  BITINTERSECT -- tell if two bitmaps intersect
836 **
837 **	Parameters:
838 **		a, b -- the bitmaps in question
839 **
840 **	Returns:
841 **		TRUE if they have a non-null intersection
842 **		FALSE otherwise
843 **
844 **	Side Effects:
845 **		none.
846 */
847 
848 bool
849 bitintersect(a, b)
850 	BITMAP a;
851 	BITMAP b;
852 {
853 	int i;
854 
855 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
856 		if ((a[i] & b[i]) != 0)
857 			return (TRUE);
858 	return (FALSE);
859 }
860 /*
861 **  BITZEROP -- tell if a bitmap is all zero
862 **
863 **	Parameters:
864 **		map -- the bit map to check
865 **
866 **	Returns:
867 **		TRUE if map is all zero.
868 **		FALSE if there are any bits set in map.
869 **
870 **	Side Effects:
871 **		none.
872 */
873 
874 bool
875 bitzerop(map)
876 	BITMAP map;
877 {
878 	int i;
879 
880 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
881 		if (map[i] != 0)
882 			return (FALSE);
883 	return (TRUE);
884 }
885