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