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