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