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