allow limited 8-bit support; allow [TCP] as an alias for [IPC], even
[unix-history] / usr / src / usr.sbin / sendmail / src / util.c
index 0fc5563..3f046f3 100644 (file)
@@ -1,11 +1,24 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c     5.23 (Berkeley) %G%";
+#endif /* not lint */
+
 # include <stdio.h>
 # include <stdio.h>
+# include <pwd.h>
+# include <sys/types.h>
+# include <sys/stat.h>
 # include <sysexits.h>
 # include <sysexits.h>
-# include "useful.h"
-# include <ctype.h>
+# include <errno.h>
+# include "sendmail.h"
 # include "conf.h"
 
 # include "conf.h"
 
-static char    SccsId[] = "@(#)util.c  3.8.1.1 %G%";
-
 /*
 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
 **
 /*
 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
 **
@@ -46,6 +59,33 @@ stripquotes(s, qf)
        *q = '\0';
 }
 \f/*
        *q = '\0';
 }
 \f/*
+**  QSTRLEN -- give me the string length assuming 0200 bits add a char
+**
+**     Parameters:
+**             s -- the string to measure.
+**
+**     Reurns:
+**             The length of s, including space for backslash escapes.
+**
+**     Side Effects:
+**             none.
+*/
+
+qstrlen(s)
+       register char *s;
+{
+       register int l = 0;
+       register char c;
+
+       while ((c = *s++) != '\0')
+       {
+               if (bitset(0200, c))
+                       l++;
+               l++;
+       }
+       return (l);
+}
+\f/*
 **  CAPITALIZE -- return a copy of a string, properly capitalized.
 **
 **     Parameters:
 **  CAPITALIZE -- return a copy of a string, properly capitalized.
 **
 **     Parameters:
@@ -73,7 +113,8 @@ capitalize(s)
                        *p++ = *s++;
                if (*s == '\0')
                        break;
                        *p++ = *s++;
                if (*s == '\0')
                        break;
-               *p++ = toupper(*s++);
+               *p++ = toupper(*s);
+               s++;
                while (isalpha(*s))
                        *p++ = *s++;
        }
                while (isalpha(*s))
                        *p++ = *s++;
        }
@@ -99,44 +140,21 @@ capitalize(s)
 
 char *
 xalloc(sz)
 
 char *
 xalloc(sz)
-       register unsigned int sz;
+       register int sz;
 {
        register char *p;
 {
        register char *p;
+       extern char *malloc();
 
 
-       p = malloc(sz);
+       p = malloc((unsigned) sz);
        if (p == NULL)
        {
                syserr("Out of memory!!");
        if (p == NULL)
        {
                syserr("Out of memory!!");
-               exit(EX_UNAVAILABLE);
+               abort();
+               /* exit(EX_UNAVAILABLE); */
        }
        return (p);
 }
 \f/*
        }
        return (p);
 }
 \f/*
-**  NEWSTR -- make copy of string.
-**
-**     Space is allocated for it using xalloc.
-**
-**     Parameters:
-**             string to copy.
-**
-**     Returns:
-**             pointer to new string.
-**
-**     Side Effects:
-**             none.
-*/
-
-char *
-newstr(s)
-       register char *s;
-{
-       register char *p;
-
-       p = xalloc((unsigned) (strlen(s) + 1));
-       strcpy(p, s);
-       return (p);
-}
-\f/*
 **  COPYPLIST -- copy list of pointers.
 **
 **     This routine is the equivalent of newstr for lists of
 **  COPYPLIST -- copy list of pointers.
 **
 **     This routine is the equivalent of newstr for lists of
@@ -168,8 +186,8 @@ copyplist(list, copycont)
 
        vp++;
 
 
        vp++;
 
-       newvp = (char **) xalloc((unsigned) (vp - list) * sizeof *vp);
-       bmove((char *) list, (char *) newvp, (vp - list) * sizeof *vp);
+       newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
+       bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
 
        if (copycont)
        {
 
        if (copycont)
        {
@@ -192,18 +210,19 @@ copyplist(list, copycont)
 **             prints av.
 */
 
 **             prints av.
 */
 
-# ifdef DEBUG
 printav(av)
        register char **av;
 {
        while (*av != NULL)
        {
 printav(av)
        register char **av;
 {
        while (*av != NULL)
        {
-               printf("\t%08x=", *av);
+               if (tTd(0, 44))
+                       printf("\n\t%08x=", *av);
+               else
+                       (void) putchar(' ');
                xputs(*av++);
                xputs(*av++);
-               putchar('\n');
        }
        }
+       (void) putchar('\n');
 }
 }
-# endif DEBUG
 \f/*
 **  LOWER -- turn letter into lower case.
 **
 \f/*
 **  LOWER -- turn letter into lower case.
 **
@@ -221,9 +240,7 @@ char
 lower(c)
        register char c;
 {
 lower(c)
        register char c;
 {
-       if (isascii(c) && isupper(c))
-               c = c - 'A' + 'a';
-       return (c);
+       return(isascii(c) && isupper(c) ? tolower(c) : c);
 }
 \f/*
 **  XPUTS -- put string doing control escapes.
 }
 \f/*
 **  XPUTS -- put string doing control escapes.
@@ -238,29 +255,68 @@ lower(c)
 **             output to stdout
 */
 
 **             output to stdout
 */
 
-# ifdef DEBUG
 xputs(s)
        register char *s;
 {
        register char c;
 xputs(s)
        register char *s;
 {
        register char c;
+       register struct metamac *mp;
+       extern struct metamac MetaMacros[];
 
 
+       if (s == NULL)
+       {
+               printf("<null>");
+               return;
+       }
+       c = *s;
+       if (c == MATCHREPL && isdigit(s[1]) && s[2] == '\0')
+       {
+               printf("$%c", s[1]);
+               return;
+       }
+       for (mp = MetaMacros; mp->metaname != NULL; mp++)
+       {
+               if (mp->metaval == c)
+               {
+                       printf("$%c%s", mp->metaname, ++s);
+                       return;
+               }
+       }
+       (void) putchar('"');
        while ((c = *s++) != '\0')
        {
                if (!isascii(c))
                {
        while ((c = *s++) != '\0')
        {
                if (!isascii(c))
                {
-                       putchar('\\');
+                       (void) putchar('\\');
                        c &= 0177;
                }
                        c &= 0177;
                }
-               if (iscntrl(c))
+               if (c < 040 || c >= 0177)
                {
                {
-                       putchar('^');
-                       c |= 0100;
+                       switch (c)
+                       {
+                         case '\n':
+                               c = 'n';
+                               break;
+
+                         case '\r':
+                               c = 'r';
+                               break;
+
+                         case '\t':
+                               c = 't';
+                               break;
+
+                         default:
+                               (void) putchar('^');
+                               (void) putchar(c ^ 0100);
+                               continue;
+                       }
+                       (void) putchar('\\');
                }
                }
-               putchar(c);
+               (void) putchar(c);
        }
        }
+       (void) putchar('"');
        (void) fflush(stdout);
 }
        (void) fflush(stdout);
 }
-# endif DEBUG
 \f/*
 **  MAKELOWER -- Translate a line into lower case
 **
 \f/*
 **  MAKELOWER -- Translate a line into lower case
 **
@@ -287,18 +343,56 @@ makelower(p)
                return;
        for (; (c = *p) != '\0'; p++)
                if (isascii(c) && isupper(c))
                return;
        for (; (c = *p) != '\0'; p++)
                if (isascii(c) && isupper(c))
-                       *p = c - 'A' + 'a';
+                       *p = tolower(c);
 }
 \f/*
 }
 \f/*
-**  SAMEWORD -- return TRUE if the words are the same
+**  FULLNAME -- extract full name from a passwd file entry.
+**
+**     Parameters:
+**             pw -- password entry to start from.
+**             buf -- buffer to store result in.
+**
+**     Returns:
+**             none.
 **
 **
-**     Ignores case.
+**     Side Effects:
+**             none.
+*/
+
+fullname(pw, buf)
+       register struct passwd *pw;
+       char *buf;
+{
+       register char *bp = buf;
+       register char *p = pw->pw_gecos;
+
+       if (*p == '*')
+               p++;
+       while (*p != '\0' && *p != ',' && *p != ';' && *p != '%')
+       {
+               if (*p == '&')
+               {
+                       (void) strcpy(bp, pw->pw_name);
+                       *bp = toupper(*bp);
+                       while (*bp != '\0')
+                               bp++;
+                       p++;
+               }
+               else
+                       *bp++ = *p++;
+       }
+       *bp = '\0';
+}
+\f/*
+**  SAFEFILE -- return true if a file exists and is safe for a user.
 **
 **     Parameters:
 **
 **     Parameters:
-**             a, b -- the words to compare.
+**             fn -- filename to check.
+**             uid -- uid to compare against.
+**             mode -- mode bits that must match.
 **
 **     Returns:
 **
 **     Returns:
-**             TRUE if a & b match exactly (modulo case)
+**             TRUE if fn exists, is owned by uid, and matches mode.
 **             FALSE otherwise.
 **
 **     Side Effects:
 **             FALSE otherwise.
 **
 **     Side Effects:
@@ -306,95 +400,465 @@ makelower(p)
 */
 
 bool
 */
 
 bool
-sameword(a, b)
-       register char *a, *b;
+safefile(fn, uid, mode)
+       char *fn;
+       int uid;
+       int mode;
 {
 {
-       while (lower(*a) == lower(*b))
-       {
-               if (*a == '\0')
-                       return (TRUE);
-               a++;
-               b++;
-       }
+       struct stat stbuf;
+
+       if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
+           (stbuf.st_mode & mode) == mode)
+               return (TRUE);
+       errno = 0;
        return (FALSE);
 }
 \f/*
        return (FALSE);
 }
 \f/*
-**  CLEAR -- clear a block of memory
+**  FIXCRLF -- fix <CR><LF> in line.
+**
+**     Looks for the <CR><LF> combination and turns it into the
+**     UNIX canonical <NL> character.  It only takes one line,
+**     i.e., it is assumed that the first <NL> found is the end
+**     of the line.
 **
 **     Parameters:
 **
 **     Parameters:
-**             p -- location to clear.
-**             l -- number of bytes to clear.
+**             line -- the line to fix.
+**             stripnl -- if true, strip the newline also.
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
-**             none.
+**             line is changed in place.
 */
 
 */
 
-clear(p, l)
+fixcrlf(line, stripnl)
+       char *line;
+       bool stripnl;
+{
        register char *p;
        register char *p;
-       register int l;
+
+       p = index(line, '\n');
+       if (p == NULL)
+               return;
+       if (p > line && p[-1] == '\r')
+               p--;
+       if (!stripnl)
+               *p++ = '\n';
+       *p = '\0';
+}
+\f/*
+**  DFOPEN -- determined file open
+**
+**     This routine has the semantics of fopen, except that it will
+**     keep trying a few times to make this happen.  The idea is that
+**     on very loaded systems, we may run out of resources (inodes,
+**     whatever), so this tries to get around it.
+*/
+
+FILE *
+dfopen(filename, mode)
+       char *filename;
+       char *mode;
 {
 {
-       while (l-- > 0)
-               *p++ = 0;
+       register int tries;
+       register FILE *fp;
+
+       for (tries = 0; tries < 10; tries++)
+       {
+               sleep((unsigned) (10 * tries));
+               errno = 0;
+               fp = fopen(filename, mode);
+               if (fp != NULL)
+                       break;
+               if (errno != ENFILE && errno != EINTR)
+                       break;
+       }
+       errno = 0;
+       return (fp);
 }
 \f/*
 }
 \f/*
-**  BUILDFNAME -- build full name from gecos style entry.
+**  PUTLINE -- put a line like fputs obeying SMTP conventions
 **
 **
-**     This routine interprets the strange entry that would appear
-**     in the GECOS field of the password file.
+**     This routine always guarantees outputing a newline (or CRLF,
+**     as appropriate) at the end of the string.
 **
 **     Parameters:
 **
 **     Parameters:
-**             p -- name to build.
-**             login -- the login name of this user (for &).
-**             buf -- place to put the result.
+**             l -- line to put.
+**             fp -- file to put it onto.
+**             m -- the mailer used to control output.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             output of l to fp.
+*/
+
+putline(l, fp, m)
+       register char *l;
+       FILE *fp;
+       MAILER *m;
+{
+       register char *p;
+       register char svchar;
+
+       /* strip out 0200 bits -- these can look like TELNET protocol */
+       if (bitnset(M_7BITS, m->m_flags))
+       {
+               for (p = l; svchar = *p; ++p)
+                       if (svchar & 0200)
+                               *p = svchar &~ 0200;
+       }
+
+       do
+       {
+               /* find the end of the line */
+               p = index(l, '\n');
+               if (p == NULL)
+                       p = &l[strlen(l)];
+
+               /* check for line overflow */
+               while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
+               {
+                       register char *q = &l[m->m_linelimit - 1];
+
+                       svchar = *q;
+                       *q = '\0';
+                       if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                               (void) putc('.', fp);
+                       fputs(l, fp);
+                       (void) putc('!', fp);
+                       fputs(m->m_eol, fp);
+                       *q = svchar;
+                       l = q;
+               }
+
+               /* output last part */
+               if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                       (void) putc('.', fp);
+               for ( ; l < p; ++l)
+                       (void) putc(*l, fp);
+               fputs(m->m_eol, fp);
+               if (*l == '\n')
+                       ++l;
+       } while (l[0] != '\0');
+}
+\f/*
+**  XUNLINK -- unlink a file, doing logging as appropriate.
+**
+**     Parameters:
+**             f -- name of file to unlink.
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
+**             f is unlinked.
+*/
+
+xunlink(f)
+       char *f;
+{
+       register int i;
+
+# ifdef LOG
+       if (LogLevel > 20)
+               syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
+# endif LOG
+
+       i = unlink(f);
+# ifdef LOG
+       if (i < 0 && LogLevel > 21)
+               syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
+# endif LOG
+}
+\f/*
+**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
+**
+**     Parameters:
+**             buf -- place to put the input line.
+**             siz -- size of buf.
+**             fp -- file to read from.
+**
+**     Returns:
+**             NULL on error (including timeout).  This will also leave
+**                     buf containing a null string.
+**             buf otherwise.
+**
+**     Side Effects:
 **             none.
 */
 
 **             none.
 */
 
-buildfname(p, login, buf)
+static jmp_buf CtxReadTimeout;
+
+char *
+sfgets(buf, siz, fp)
+       char *buf;
+       int siz;
+       FILE *fp;
+{
+       register EVENT *ev = NULL;
        register char *p;
        register char *p;
-       char *login;
+       static int readtimeout();
+
+       /* set the timeout */
+       if (ReadTimeout != 0)
+       {
+               if (setjmp(CtxReadTimeout) != 0)
+               {
+# ifdef LOG
+                       syslog(LOG_NOTICE,
+                           "timeout waiting for input from %s\n",
+                           RealHostName? RealHostName: "local");
+# endif
+                       errno = 0;
+                       usrerr("451 timeout waiting for input");
+                       buf[0] = '\0';
+                       return (NULL);
+               }
+               ev = setevent((time_t) ReadTimeout, readtimeout, 0);
+       }
+
+       /* try to read */
+       p = NULL;
+       while (p == NULL && !feof(fp) && !ferror(fp))
+       {
+               errno = 0;
+               p = fgets(buf, siz, fp);
+               if (errno == EINTR)
+                       clearerr(fp);
+       }
+
+       /* clear the event if it has not sprung */
+       clrevent(ev);
+
+       /* clean up the books and exit */
+       LineNumber++;
+       if (p == NULL)
+       {
+               buf[0] = '\0';
+               return (NULL);
+       }
+       if (!EightBit)
+               for (p = buf; *p != '\0'; p++)
+                       *p &= ~0200;
+       return (buf);
+}
+
+static
+readtimeout()
+{
+       longjmp(CtxReadTimeout, 1);
+}
+\f/*
+**  FGETFOLDED -- like fgets, but know about folded lines.
+**
+**     Parameters:
+**             buf -- place to put result.
+**             n -- bytes available.
+**             f -- file to read from.
+**
+**     Returns:
+**             buf on success, NULL on error or EOF.
+**
+**     Side Effects:
+**             buf gets lines from f, with continuation lines (lines
+**             with leading white space) appended.  CRLF's are mapped
+**             into single newlines.  Any trailing NL is stripped.
+*/
+
+char *
+fgetfolded(buf, n, f)
        char *buf;
        char *buf;
+       register int n;
+       FILE *f;
 {
 {
-       register char *bp = buf;
+       register char *p = buf;
+       register int i;
 
 
-       while (*p != '\0' && *p != ',' && *p != ';')
+       n--;
+       while ((i = getc(f)) != EOF)
        {
        {
-               if (*p == '&')
+               if (i == '\r')
                {
                {
-                       (void) strcpy(bp, login);
-                       *bp = toupper(*bp);
-                       while (*bp != '\0')
-                               bp++;
-                       p++;
+                       i = getc(f);
+                       if (i != '\n')
+                       {
+                               if (i != EOF)
+                                       (void) ungetc(i, f);
+                               i = '\r';
+                       }
+               }
+               if (--n > 0)
+                       *p++ = i;
+               if (i == '\n')
+               {
+                       LineNumber++;
+                       i = getc(f);
+                       if (i != EOF)
+                               (void) ungetc(i, f);
+                       if (i != ' ' && i != '\t')
+                       {
+                               *--p = '\0';
+                               if (!EightBit)
+                               {
+                                       /* headers always have to be 7-bit */
+                                       for (p = buf; (i = *p) != '\0'; *p++)
+                                               if (bitset(0200, i))
+                                                       *p = i & ~0200;
+                               }
+                               return (buf);
+                       }
                }
                }
-               else
-                       *bp++ = *p++;
        }
        }
-       *bp = '\0';
+       return (NULL);
 }
 \f/*
 }
 \f/*
-**  SYSLOG -- fake entry to fool lint
+**  CURTIME -- return current time.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             the current time.
+**
+**     Side Effects:
+**             none.
 */
 
 */
 
-# ifdef LOG
-# ifdef lint
+time_t
+curtime()
+{
+       auto time_t t;
+
+       (void) time(&t);
+       return (t);
+}
+\f/*
+**  ATOBOOL -- convert a string representation to boolean.
+**
+**     Defaults to "TRUE"
+**
+**     Parameters:
+**             s -- string to convert.  Takes "tTyY" as true,
+**                     others as false.
+**
+**     Returns:
+**             A boolean representation of the string.
+**
+**     Side Effects:
+**             none.
+*/
 
 
-/*VARARGS2*/
-syslog(pri, fmt, args)
-       int pri;
-       char *fmt;
+bool
+atobool(s)
+       register char *s;
 {
 {
-       pri = *fmt;
-       args = pri;
-       pri = args;
+       if (*s == '\0' || index("tTyY", *s) != NULL)
+               return (TRUE);
+       return (FALSE);
 }
 }
+\f/*
+**  ATOOCT -- convert a string representation to octal.
+**
+**     Parameters:
+**             s -- string to convert.
+**
+**     Returns:
+**             An integer representing the string interpreted as an
+**             octal number.
+**
+**     Side Effects:
+**             none.
+*/
 
 
-# endif lint
-# endif LOG
+atooct(s)
+       register char *s;
+{
+       register int i = 0;
+
+       while (*s >= '0' && *s <= '7')
+               i = (i << 3) | (*s++ - '0');
+       return (i);
+}
+\f/*
+**  WAITFOR -- wait for a particular process id.
+**
+**     Parameters:
+**             pid -- process id to wait for.
+**
+**     Returns:
+**             status of pid.
+**             -1 if pid never shows up.
+**
+**     Side Effects:
+**             none.
+*/
+
+waitfor(pid)
+       int pid;
+{
+       auto int st;
+       int i;
+
+       do
+       {
+               errno = 0;
+               i = wait(&st);
+       } while ((i >= 0 || errno == EINTR) && i != pid);
+       if (i < 0)
+               st = -1;
+       return (st);
+}
+\f/*
+**  BITINTERSECT -- tell if two bitmaps intersect
+**
+**     Parameters:
+**             a, b -- the bitmaps in question
+**
+**     Returns:
+**             TRUE if they have a non-null intersection
+**             FALSE otherwise
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+bitintersect(a, b)
+       BITMAP a;
+       BITMAP b;
+{
+       int i;
+
+       for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+               if ((a[i] & b[i]) != 0)
+                       return (TRUE);
+       return (FALSE);
+}
+\f/*
+**  BITZEROP -- tell if a bitmap is all zero
+**
+**     Parameters:
+**             map -- the bit map to check
+**
+**     Returns:
+**             TRUE if map is all zero.
+**             FALSE if there are any bits set in map.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+bitzerop(map)
+       BITMAP map;
+{
+       int i;
+
+       for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+               if (map[i] != 0)
+                       return (FALSE);
+       return (TRUE);
+}