fix bogus implementation of name overflow limiting
[unix-history] / usr / src / usr.sbin / sendmail / src / util.c
index 3f046f3..e06b0fa 100644 (file)
@@ -1,25 +1,19 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)util.c     5.23 (Berkeley) %G%";
+static char sccsid[] = "@(#)util.c     8.20 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
-# include <stdio.h>
-# include <pwd.h>
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <sysexits.h>
-# include <errno.h>
 # include "sendmail.h"
 # include "sendmail.h"
+# include <sysexits.h>
 # include "conf.h"
 # include "conf.h"
-
-/*
+\f/*
 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
 **
 **     Runs through a string and strips off unquoted quote
 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
 **
 **     Runs through a string and strips off unquoted quote
@@ -27,8 +21,6 @@ static char sccsid[] = "@(#)util.c    5.23 (Berkeley) %G%";
 **
 **     Parameters:
 **             s -- the string to strip.
 **
 **     Parameters:
 **             s -- the string to strip.
-**             qf -- if set, remove actual `` " '' characters
-**                     as well as the quote bits.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -40,9 +32,8 @@ static char sccsid[] = "@(#)util.c    5.23 (Berkeley) %G%";
 **             deliver
 */
 
 **             deliver
 */
 
-stripquotes(s, qf)
+stripquotes(s)
        char *s;
        char *s;
-       bool qf;
 {
        register char *p;
        register char *q;
 {
        register char *p;
        register char *q;
@@ -51,76 +42,16 @@ stripquotes(s, qf)
        if (s == NULL)
                return;
 
        if (s == NULL)
                return;
 
-       for (p = q = s; (c = *p++) != '\0'; )
-       {
-               if (c != '"' || !qf)
-                       *q++ = c & 0177;
-       }
-       *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:
-**             s -- the string to capitalize.
-**
-**     Returns:
-**             a pointer to a properly capitalized string.
-**
-**     Side Effects:
-**             none.
-*/
-
-char *
-capitalize(s)
-       register char *s;
-{
-       static char buf[50];
-       register char *p;
-
-       p = buf;
-
-       for (;;)
+       p = q = s;
+       do
        {
        {
-               while (!isalpha(*s) && *s != '\0')
-                       *p++ = *s++;
-               if (*s == '\0')
-                       break;
-               *p++ = toupper(*s);
-               s++;
-               while (isalpha(*s))
-                       *p++ = *s++;
-       }
-
-       *p = '\0';
-       return (buf);
+               c = *p++;
+               if (c == '\\')
+                       c = *p++;
+               else if (c == '"')
+                       continue;
+               *q++ = c;
+       } while (c != '\0');
 }
 \f/*
 **  XALLOC -- Allocate memory and bitch wildly on failure.
 }
 \f/*
 **  XALLOC -- Allocate memory and bitch wildly on failure.
@@ -143,7 +74,6 @@ xalloc(sz)
        register int sz;
 {
        register char *p;
        register int sz;
 {
        register char *p;
-       extern char *malloc();
 
        p = malloc((unsigned) sz);
        if (p == NULL)
 
        p = malloc((unsigned) sz);
        if (p == NULL)
@@ -198,6 +128,45 @@ copyplist(list, copycont)
        return (newvp);
 }
 \f/*
        return (newvp);
 }
 \f/*
+**  COPYQUEUE -- copy address queue.
+**
+**     This routine is the equivalent of newstr for address queues
+**     addresses marked with QDONTSEND aren't copied
+**
+**     Parameters:
+**             addr -- list of address structures to copy.
+**
+**     Returns:
+**             a copy of 'addr'.
+**
+**     Side Effects:
+**             none.
+*/
+
+ADDRESS *
+copyqueue(addr)
+       ADDRESS *addr;
+{
+       register ADDRESS *newaddr;
+       ADDRESS *ret;
+       register ADDRESS **tail = &ret;
+
+       while (addr != NULL)
+       {
+               if (!bitset(QDONTSEND, addr->q_flags))
+               {
+                       newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
+                       STRUCTCOPY(*addr, *newaddr);
+                       *tail = newaddr;
+                       tail = &newaddr->q_next;
+               }
+               addr = addr->q_next;
+       }
+       *tail = NULL;
+       
+       return ret;
+}
+\f/*
 **  PRINTAV -- print argument vector.
 **
 **     Parameters:
 **  PRINTAV -- print argument vector.
 **
 **     Parameters:
@@ -240,7 +209,7 @@ char
 lower(c)
        register char c;
 {
 lower(c)
        register char c;
 {
-       return(isascii(c) && isupper(c) ? tolower(c) : c);
+       return((isascii(c) && isupper(c)) ? tolower(c) : c);
 }
 \f/*
 **  XPUTS -- put string doing control escapes.
 }
 \f/*
 **  XPUTS -- put string doing control escapes.
@@ -258,7 +227,7 @@ lower(c)
 xputs(s)
        register char *s;
 {
 xputs(s)
        register char *s;
 {
-       register char c;
+       register int c;
        register struct metamac *mp;
        extern struct metamac MetaMacros[];
 
        register struct metamac *mp;
        extern struct metamac MetaMacros[];
 
@@ -267,54 +236,58 @@ xputs(s)
                printf("<null>");
                return;
        }
                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')
+       while ((c = (*s++ & 0377)) != '\0')
        {
                if (!isascii(c))
                {
        {
                if (!isascii(c))
                {
+                       if (c == MATCHREPL || c == MACROEXPAND)
+                       {
+                               putchar('$');
+                               continue;
+                       }
+                       for (mp = MetaMacros; mp->metaname != '\0'; mp++)
+                       {
+                               if ((mp->metaval & 0377) == c)
+                               {
+                                       printf("$%c", mp->metaname);
+                                       break;
+                               }
+                       }
+                       if (mp->metaname != '\0')
+                               continue;
                        (void) putchar('\\');
                        c &= 0177;
                }
                        (void) putchar('\\');
                        c &= 0177;
                }
-               if (c < 040 || c >= 0177)
+               if (isprint(c))
                {
                {
-                       switch (c)
-                       {
-                         case '\n':
-                               c = 'n';
-                               break;
+                       putchar(c);
+                       continue;
+               }
 
 
-                         case '\r':
-                               c = 'r';
-                               break;
+               /* wasn't a meta-macro -- find another way to print it */
+               switch (c)
+               {
+                 case '\0':
+                       continue;
 
 
-                         case '\t':
-                               c = 't';
-                               break;
+                 case '\n':
+                       c = 'n';
+                       break;
 
 
-                         default:
-                               (void) putchar('^');
-                               (void) putchar(c ^ 0100);
-                               continue;
-                       }
-                       (void) putchar('\\');
+                 case '\r':
+                       c = 'r';
+                       break;
+
+                 case '\t':
+                       c = 't';
+                       break;
+
+                 default:
+                       (void) putchar('^');
+                       (void) putchar(c ^ 0100);
+                       continue;
                }
                }
-               (void) putchar(c);
        }
        }
-       (void) putchar('"');
        (void) fflush(stdout);
 }
 \f/*
        (void) fflush(stdout);
 }
 \f/*
@@ -353,65 +326,264 @@ makelower(p)
 **             buf -- buffer to store result in.
 **
 **     Returns:
 **             buf -- buffer to store result in.
 **
 **     Returns:
-**             none.
+**             TRUE -- if the resulting message should be a MIME format.
+**             FALSE -- if MIME is not necessary.
 **
 **     Side Effects:
 **             none.
 */
 
 **
 **     Side Effects:
 **             none.
 */
 
-fullname(pw, buf)
-       register struct passwd *pw;
-       char *buf;
+/* values for should_quote */
+#define NO_QUOTE       0
+#define SHOULD_QUOTE   1
+#define SHOULD_MIME    2
+
+int
+       register unsigned char *gecos;
+       const unsigned char *login;
+       unsigned char *buf;
 {
 {
-       register char *bp = buf;
+       register unsigned char *bp = buf;
+       unsigned char *p;
+       int should_quote = NO_QUOTE;
        register char *p = pw->pw_gecos;
 
        register char *p = pw->pw_gecos;
 
-       if (*p == '*')
-               p++;
-       while (*p != '\0' && *p != ',' && *p != ';' && *p != '%')
+       /* make sure specials, SPACE and CTLs are quoted within " " */
+       for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
        {
        {
-               if (*p == '&')
+               if (*p >= 0200)
                {
                {
-                       (void) strcpy(bp, pw->pw_name);
-                       *bp = toupper(*bp);
-                       while (*bp != '\0')
-                               bp++;
-                       p++;
+                       should_quote = SHOULD_MIME;
+                       break;
                }
                }
-               else
-                       *bp++ = *p++;
+               switch (*p)
+               {
+                 case '(':
+                 case ')':
+                 case '<':
+                 case '>':
+                 case '@':
+                 case ':':
+                 case '\\':
+                 case '"':
+                 case '.':
+                 case '[':
+                 case ']':
+                       should_quote = SHOULD_QUOTE;
+                       break;
+               }       
+       }
+       if (should_quote == SHOULD_MIME)
+       {
+               strcpy (bp, "=?iso-8859-1?Q?");
+               bp += 15;
+               for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
+               {
+                       if (*p == ' ')
+                               *bp++ = '_';
+                        else if (*p == '&')
+                        {
+                               (void) strcpy(bp, login);
+                               *bp = toupper(*bp);
+                               bp += strlen (bp);
+                       }
+                       else if (*p < 040 || *p >= 200 || 
+                                strchr("_?()<>@:\\\".[]", *p) != NULL)
+                       {
+                               *bp++ = '=';
+                               *bp++ = "0123456789ABCDEF"[(*p >> 4) & 0xf];
+                               *bp++ = "0123456789ABCDEF"[*p & 0xf];
+                       }
+                       else
+                               *bp++ = *p;
+               }
+               strcpy (bp, "?= ");
+               bp += 3;
+       }
+       else
+       {
+               if (should_quote)
+                       *bp++ = '"';
+               for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
+               {
+                       if (*p == '&')
+                       {
+                               (void) strcpy(bp, login);
+                               *bp = toupper(*bp);
+                               while (*bp != '\0')
+                                       bp++;
+                       }
+                       else
+                       {
+                               if (*p == '"')
+                                       *bp++ = '\\';
+                               *bp++ = *p;
+                       }
+               }
+               if (bp[-1] == '\\')
+                       *bp++ = '\\';
+               if (should_quote)
+                       *bp++ = '"';
        }
        }
+
        *bp = '\0';
        *bp = '\0';
+       return should_quote == SHOULD_MIME;
 }
 \f/*
 **  SAFEFILE -- return true if a file exists and is safe for a user.
 **
 **     Parameters:
 **             fn -- filename to check.
 }
 \f/*
 **  SAFEFILE -- return true if a file exists and is safe for a user.
 **
 **     Parameters:
 **             fn -- filename to check.
-**             uid -- uid to compare against.
+**             uid -- user id to compare against.
+**             gid -- group id to compare against.
+**             uname -- user name to compare against (used for group
+**                     sets).
+**             flags -- modifiers:
+**                     SF_MUSTOWN -- "uid" must own this file.
+**                     SF_NOSLINK -- file cannot be a symbolic link.
 **             mode -- mode bits that must match.
 **
 **     Returns:
 **             mode -- mode bits that must match.
 **
 **     Returns:
-**             TRUE if fn exists, is owned by uid, and matches mode.
-**             FALSE otherwise.
+**             0 if fn exists, is owned by uid, and matches mode.
+**             An errno otherwise.  The actual errno is cleared.
 **
 **     Side Effects:
 **             none.
 */
 
 **
 **     Side Effects:
 **             none.
 */
 
-bool
-safefile(fn, uid, mode)
+#include <grp.h>
+
+#ifndef S_IXOTH
+# define S_IXOTH       (S_IEXEC >> 6)
+#endif
+
+#ifndef S_IXGRP
+# define S_IXGRP       (S_IEXEC >> 3)
+#endif
+
+#ifndef S_IXUSR
+# define S_IXUSR       (S_IEXEC)
+#endif
+
+int
+safefile(fn, uid, gid, uname, flags, mode)
        char *fn;
        char *fn;
-       int uid;
+       uid_t uid;
+       gid_t gid;
+       char *uname;
+       int flags;
        int mode;
 {
        int mode;
 {
+       register char *p;
+       register struct group *gr = NULL;
        struct stat stbuf;
 
        struct stat stbuf;
 
-       if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
-           (stbuf.st_mode & mode) == mode)
-               return (TRUE);
+       if (tTd(54, 4))
+               printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
+                       fn, uid, gid, flags, mode);
        errno = 0;
        errno = 0;
-       return (FALSE);
+
+       for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
+       {
+               *p = '\0';
+               if (stat(fn, &stbuf) < 0)
+                       break;
+               if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
+                       continue;
+               if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
+                       continue;
+#ifndef NO_GROUP_SET
+               if (uname != NULL &&
+                   ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
+                    (gr = getgrgid(stbuf.st_gid)) != NULL))
+               {
+                       register char **gp;
+
+                       for (gp = gr->gr_mem; *gp != NULL; gp++)
+                               if (strcmp(*gp, uname) == 0)
+                                       break;
+                       if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
+                               continue;
+               }
+#endif
+               if (!bitset(S_IXOTH, stbuf.st_mode))
+                       break;
+       }
+       if (p != NULL)
+       {
+               int ret = errno;
+
+               if (ret == 0)
+                       ret = EACCES;
+               if (tTd(54, 4))
+                       printf("\t[dir %s] %s\n", fn, errstring(ret));
+               *p = '/';
+               return ret;
+       }
+
+#ifdef HASLSTAT
+       if ((bitset(SF_NOSLINK, flags) ? lstat(fn, &stbuf)
+                                      : stat(fn, &stbuf)) < 0)
+#else
+       if (stat(fn, &stbuf) < 0)
+#endif
+       {
+               int ret = errno;
+
+               if (tTd(54, 4))
+                       printf("\t%s\n", errstring(ret));
+
+               errno = 0;
+               return ret;
+       }
+
+#ifdef S_ISLNK
+       if (bitset(SF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
+       {
+               if (tTd(54, 4))
+                       printf("\t[mode %o]\tEPERM\n");
+               return EPERM;
+       }
+#endif
+
+       if (uid == 0)
+               mode >>= 6;
+       else if (stbuf.st_uid != uid)
+       {
+               mode >>= 3;
+               if (stbuf.st_gid == gid)
+                       ;
+#ifndef NO_GROUP_SET
+               else if (uname != NULL &&
+                        ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
+                         (gr = getgrgid(stbuf.st_gid)) != NULL))
+               {
+                       register char **gp;
+
+                       for (gp = gr->gr_mem; *gp != NULL; gp++)
+                               if (strcmp(*gp, uname) == 0)
+                                       break;
+                       if (*gp == NULL)
+                               mode >>= 3;
+               }
+#endif
+               else
+                       mode >>= 3;
+       }
+       if (tTd(54, 4))
+               printf("\t[uid %d, stat %o, mode %o] ",
+                       stbuf.st_uid, stbuf.st_mode, mode);
+       if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
+            !bitset(SF_MUSTOWN, flags)) &&
+           (stbuf.st_mode & mode) == mode)
+       {
+               if (tTd(54, 4))
+                       printf("\tOK\n");
+               return 0;
+       }
+       if (tTd(54, 4))
+               printf("\tEACCES\n");
+       return EACCES;
 }
 \f/*
 **  FIXCRLF -- fix <CR><LF> in line.
 }
 \f/*
 **  FIXCRLF -- fix <CR><LF> in line.
@@ -438,7 +610,7 @@ fixcrlf(line, stripnl)
 {
        register char *p;
 
 {
        register char *p;
 
-       p = index(line, '\n');
+       p = strchr(line, '\n');
        if (p == NULL)
                return;
        if (p > line && p[-1] == '\r')
        if (p == NULL)
                return;
        if (p > line && p[-1] == '\r')
@@ -456,26 +628,66 @@ fixcrlf(line, stripnl)
 **     whatever), so this tries to get around it.
 */
 
 **     whatever), so this tries to get around it.
 */
 
+#ifndef O_ACCMODE
+# define O_ACCMODE     (O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+
+struct omodes
+{
+       int     mask;
+       int     mode;
+       char    *farg;
+} OpenModes[] =
+{
+       O_ACCMODE,              O_RDONLY,               "r",
+       O_ACCMODE|O_APPEND,     O_WRONLY,               "w",
+       O_ACCMODE|O_APPEND,     O_WRONLY|O_APPEND,      "a",
+       O_TRUNC,                0,                      "w+",
+       O_APPEND,               O_APPEND,               "a+",
+       0,                      0,                      "r+",
+};
+
 FILE *
 FILE *
-dfopen(filename, mode)
+dfopen(filename, omode, cmode)
        char *filename;
        char *filename;
-       char *mode;
+       int omode;
+       int cmode;
 {
        register int tries;
 {
        register int tries;
-       register FILE *fp;
+       int fd;
+       register struct omodes *om;
+       struct stat st;
+
+       for (om = OpenModes; om->mask != 0; om++)
+               if ((omode & om->mask) == om->mode)
+                       break;
 
        for (tries = 0; tries < 10; tries++)
        {
                sleep((unsigned) (10 * tries));
                errno = 0;
 
        for (tries = 0; tries < 10; tries++)
        {
                sleep((unsigned) (10 * tries));
                errno = 0;
-               fp = fopen(filename, mode);
-               if (fp != NULL)
+               fd = open(filename, omode, cmode);
+               if (fd >= 0)
                        break;
                if (errno != ENFILE && errno != EINTR)
                        break;
        }
                        break;
                if (errno != ENFILE && errno != EINTR)
                        break;
        }
-       errno = 0;
-       return (fp);
+       if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
+       {
+               int locktype;
+
+               /* lock the file to avoid accidental conflicts */
+               if ((omode & O_ACCMODE) != O_RDONLY)
+                       locktype = LOCK_EX;
+               else
+                       locktype = LOCK_SH;
+               (void) lockfile(fd, filename, NULL, locktype);
+               errno = 0;
+       }
+       if (fd < 0)
+               return NULL;
+       else
+               return fdopen(fd, om->farg);
 }
 \f/*
 **  PUTLINE -- put a line like fputs obeying SMTP conventions
 }
 \f/*
 **  PUTLINE -- put a line like fputs obeying SMTP conventions
@@ -506,18 +718,21 @@ putline(l, fp, m)
        /* strip out 0200 bits -- these can look like TELNET protocol */
        if (bitnset(M_7BITS, m->m_flags))
        {
        /* 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)
+               for (p = l; (svchar = *p) != '\0'; ++p)
+                       if (bitset(0200, svchar))
                                *p = svchar &~ 0200;
        }
 
        do
        {
                /* find the end of the line */
                                *p = svchar &~ 0200;
        }
 
        do
        {
                /* find the end of the line */
-               p = index(l, '\n');
+               p = strchr(l, '\n');
                if (p == NULL)
                        p = &l[strlen(l)];
 
                if (p == NULL)
                        p = &l[strlen(l)];
 
+               if (TrafficLogFile != NULL)
+                       fprintf(TrafficLogFile, "%05d >>> ", getpid());
+
                /* check for line overflow */
                while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
                {
                /* check for line overflow */
                while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
                {
@@ -526,17 +741,30 @@ putline(l, fp, m)
                        svchar = *q;
                        *q = '\0';
                        if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
                        svchar = *q;
                        *q = '\0';
                        if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                       {
                                (void) putc('.', fp);
                                (void) putc('.', fp);
+                               if (TrafficLogFile != NULL)
+                                       (void) putc('.', TrafficLogFile);
+                       }
                        fputs(l, fp);
                        (void) putc('!', fp);
                        fputs(m->m_eol, fp);
                        fputs(l, fp);
                        (void) putc('!', fp);
                        fputs(m->m_eol, fp);
+                       if (TrafficLogFile != NULL)
+                               fprintf(TrafficLogFile, "%s!\n%05d >>> ",
+                                       l, getpid());
                        *q = svchar;
                        l = q;
                }
 
                /* output last part */
                if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
                        *q = svchar;
                        l = q;
                }
 
                /* output last part */
                if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+               {
                        (void) putc('.', fp);
                        (void) putc('.', fp);
+                       if (TrafficLogFile != NULL)
+                               (void) putc('.', TrafficLogFile);
+               }
+               if (TrafficLogFile != NULL)
+                       fprintf(TrafficLogFile, "%.*s\n", p - l, l);
                for ( ; l < p; ++l)
                        (void) putc(*l, fp);
                fputs(m->m_eol, fp);
                for ( ; l < p; ++l)
                        (void) putc(*l, fp);
                fputs(m->m_eol, fp);
@@ -563,15 +791,42 @@ xunlink(f)
        register int i;
 
 # ifdef LOG
        register int i;
 
 # ifdef LOG
-       if (LogLevel > 20)
-               syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
-# endif LOG
+       if (LogLevel > 98)
+               syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
+# endif /* LOG */
 
        i = unlink(f);
 # ifdef LOG
 
        i = unlink(f);
 # ifdef LOG
-       if (i < 0 && LogLevel > 21)
+       if (i < 0 && LogLevel > 97)
                syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
                syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
-# endif LOG
+# endif /* LOG */
+}
+\f/*
+**  XFCLOSE -- close a file, doing logging as appropriate.
+**
+**     Parameters:
+**             fp -- file pointer for the file to close
+**             a, b -- miscellaneous crud to print for debugging
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             fp is closed.
+*/
+
+xfclose(fp, a, b)
+       FILE *fp;
+       char *a, *b;
+{
+       if (tTd(53, 99))
+               printf("xfclose(%x) %s %s\n", fp, a, b);
+#ifdef XDEBUG
+       if (fileno(fp) == 1)
+               syserr("xfclose(%s %s): fd = 1", a, b);
+#endif
+       if (fclose(fp) < 0 && tTd(53, 99))
+               printf("xfclose FAILURE: %s\n", errstring(errno));
 }
 \f/*
 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
 }
 \f/*
 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
@@ -580,6 +835,8 @@ xunlink(f)
 **             buf -- place to put the input line.
 **             siz -- size of buf.
 **             fp -- file to read from.
 **             buf -- place to put the input line.
 **             siz -- size of buf.
 **             fp -- file to read from.
+**             timeout -- the timeout before error occurs.
+**             during -- what we are trying to read (for error messages).
 **
 **     Returns:
 **             NULL on error (including timeout).  This will also leave
 **
 **     Returns:
 **             NULL on error (including timeout).  This will also leave
@@ -591,33 +848,39 @@ xunlink(f)
 */
 
 static jmp_buf CtxReadTimeout;
 */
 
 static jmp_buf CtxReadTimeout;
+static int     readtimeout();
 
 char *
 
 char *
-sfgets(buf, siz, fp)
+sfgets(buf, siz, fp, timeout, during)
        char *buf;
        int siz;
        FILE *fp;
        char *buf;
        int siz;
        FILE *fp;
+       time_t timeout;
+       char *during;
 {
        register EVENT *ev = NULL;
        register char *p;
 {
        register EVENT *ev = NULL;
        register char *p;
-       static int readtimeout();
 
        /* set the timeout */
 
        /* set the timeout */
-       if (ReadTimeout != 0)
+       if (timeout != 0)
        {
                if (setjmp(CtxReadTimeout) != 0)
                {
 # ifdef LOG
                        syslog(LOG_NOTICE,
        {
                if (setjmp(CtxReadTimeout) != 0)
                {
 # ifdef LOG
                        syslog(LOG_NOTICE,
-                           "timeout waiting for input from %s\n",
-                           RealHostName? RealHostName: "local");
+                           "timeout waiting for input from %s during %s\n",
+                           CurHostName? CurHostName: "local", during);
 # endif
                        errno = 0;
 # endif
                        errno = 0;
-                       usrerr("451 timeout waiting for input");
+                       usrerr("451 timeout waiting for input during %s",
+                               during);
                        buf[0] = '\0';
                        buf[0] = '\0';
+#ifdef XDEBUG
+                       checkfd012(during);
+#endif
                        return (NULL);
                }
                        return (NULL);
                }
-               ev = setevent((time_t) ReadTimeout, readtimeout, 0);
+               ev = setevent(timeout, readtimeout, 0);
        }
 
        /* try to read */
        }
 
        /* try to read */
@@ -638,9 +901,13 @@ sfgets(buf, siz, fp)
        if (p == NULL)
        {
                buf[0] = '\0';
        if (p == NULL)
        {
                buf[0] = '\0';
+               if (TrafficLogFile != NULL)
+                       fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
                return (NULL);
        }
                return (NULL);
        }
-       if (!EightBit)
+       if (TrafficLogFile != NULL)
+               fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
+       if (SevenBit)
                for (p = buf; *p != '\0'; p++)
                        *p &= ~0200;
        return (buf);
                for (p = buf; *p != '\0'; p++)
                        *p &= ~0200;
        return (buf);
@@ -660,7 +927,9 @@ readtimeout()
 **             f -- file to read from.
 **
 **     Returns:
 **             f -- file to read from.
 **
 **     Returns:
-**             buf on success, NULL on error or EOF.
+**             input line(s) on success, NULL on error or EOF.
+**             This will normally be buf -- unless the line is too
+**                     long, when it will be xalloc()ed.
 **
 **     Side Effects:
 **             buf gets lines from f, with continuation lines (lines
 **
 **     Side Effects:
 **             buf gets lines from f, with continuation lines (lines
@@ -675,6 +944,7 @@ fgetfolded(buf, n, f)
        FILE *f;
 {
        register char *p = buf;
        FILE *f;
 {
        register char *p = buf;
+       char *bp = buf;
        register int i;
 
        n--;
        register int i;
 
        n--;
@@ -690,8 +960,26 @@ fgetfolded(buf, n, f)
                                i = '\r';
                        }
                }
                                i = '\r';
                        }
                }
-               if (--n > 0)
-                       *p++ = i;
+               if (--n <= 0)
+               {
+                       /* allocate new space */
+                       char *nbp;
+                       int nn;
+
+                       nn = (p - bp);
+                       if (nn < MEMCHUNKSIZE)
+                               nn *= 2;
+                       else
+                               nn += MEMCHUNKSIZE;
+                       nbp = xalloc(nn);
+                       bcopy(bp, nbp, p - bp);
+                       p = &nbp[p - bp];
+                       if (bp != buf)
+                               free(bp);
+                       bp = nbp;
+                       n = nn - (p - bp);
+               }
+               *p++ = i;
                if (i == '\n')
                {
                        LineNumber++;
                if (i == '\n')
                {
                        LineNumber++;
@@ -699,20 +987,13 @@ fgetfolded(buf, n, f)
                        if (i != EOF)
                                (void) ungetc(i, f);
                        if (i != ' ' && i != '\t')
                        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);
-                       }
+                               break;
                }
        }
                }
        }
-       return (NULL);
+       if (p == bp)
+               return (NULL);
+       *--p = '\0';
+       return (bp);
 }
 \f/*
 **  CURTIME -- return current time.
 }
 \f/*
 **  CURTIME -- return current time.
@@ -755,7 +1036,7 @@ bool
 atobool(s)
        register char *s;
 {
 atobool(s)
        register char *s;
 {
-       if (*s == '\0' || index("tTyY", *s) != NULL)
+       if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
                return (TRUE);
        return (FALSE);
 }
                return (TRUE);
        return (FALSE);
 }
@@ -796,10 +1077,15 @@ atooct(s)
 **             none.
 */
 
 **             none.
 */
 
+int
 waitfor(pid)
        int pid;
 {
 waitfor(pid)
        int pid;
 {
+#ifdef WAITUNION
+       union wait st;
+#else
        auto int st;
        auto int st;
+#endif
        int i;
 
        do
        int i;
 
        do
@@ -808,8 +1094,12 @@ waitfor(pid)
                i = wait(&st);
        } while ((i >= 0 || errno == EINTR) && i != pid);
        if (i < 0)
                i = wait(&st);
        } while ((i >= 0 || errno == EINTR) && i != pid);
        if (i < 0)
-               st = -1;
-       return (st);
+               return -1;
+#ifdef WAITUNION
+       return st.w_status;
+#else
+       return st;
+#endif
 }
 \f/*
 **  BITINTERSECT -- tell if two bitmaps intersect
 }
 \f/*
 **  BITINTERSECT -- tell if two bitmaps intersect
@@ -862,3 +1152,247 @@ bitzerop(map)
                        return (FALSE);
        return (TRUE);
 }
                        return (FALSE);
        return (TRUE);
 }
+\f/*
+**  STRCONTAINEDIN -- tell if one string is contained in another
+**
+**     Parameters:
+**             a -- possible substring.
+**             b -- possible superstring.
+**
+**     Returns:
+**             TRUE if a is contained in b.
+**             FALSE otherwise.
+*/
+
+bool
+strcontainedin(a, b)
+       register char *a;
+       register char *b;
+{
+       int la;
+       int lb;
+       int c;
+
+       la = strlen(a);
+       lb = strlen(b);
+       c = *a;
+       if (isascii(c) && isupper(c))
+               c = tolower(c);
+       for (; lb-- >= la; b++)
+       {
+               if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
+                       continue;
+               if (strncasecmp(a, b, la) == 0)
+                       return TRUE;
+       }
+       return FALSE;
+}
+\f/*
+**  CHECKFD012 -- check low numbered file descriptors
+**
+**     File descriptors 0, 1, and 2 should be open at all times.
+**     This routine verifies that, and fixes it if not true.
+**
+**     Parameters:
+**             where -- a tag printed if the assertion failed
+**
+**     Returns:
+**             none
+*/
+
+checkfd012(where)
+       char *where;
+{
+#ifdef XDEBUG
+       register int i;
+       struct stat stbuf;
+
+       for (i = 0; i < 3; i++)
+       {
+               if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
+               {
+                       /* oops.... */
+                       int fd;
+
+                       syserr("%s: fd %d not open", where, i);
+                       fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
+                       if (fd != i)
+                       {
+                               (void) dup2(fd, i);
+                               (void) close(fd);
+                       }
+               }
+       }
+#endif /* XDEBUG */
+}
+\f/*
+**  PRINTOPENFDS -- print the open file descriptors (for debugging)
+**
+**     Parameters:
+**             logit -- if set, send output to syslog; otherwise
+**                     print for debugging.
+**
+**     Returns:
+**             none.
+*/
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+printopenfds(logit)
+       bool logit;
+{
+       register int fd;
+       extern int DtableSize;
+
+       for (fd = 0; fd < DtableSize; fd++)
+               dumpfd(fd, FALSE, logit);
+}
+\f/*
+**  DUMPFD -- dump a file descriptor
+**
+**     Parameters:
+**             fd -- the file descriptor to dump.
+**             printclosed -- if set, print a notification even if
+**                     it is closed; otherwise print nothing.
+**             logit -- if set, send output to syslog instead of stdout.
+*/
+
+dumpfd(fd, printclosed, logit)
+       int fd;
+       bool printclosed;
+       bool logit;
+{
+       register struct hostent *hp;
+       register char *p;
+       struct sockaddr_in sin;
+       auto int slen;
+       struct stat st;
+       char buf[200];
+
+       p = buf;
+       sprintf(p, "%3d: ", fd);
+       p += strlen(p);
+
+       if (fstat(fd, &st) < 0)
+       {
+               if (printclosed || errno != EBADF)
+               {
+                       sprintf(p, "CANNOT STAT (%s)", errstring(errno));
+                       goto printit;
+               }
+               return;
+       }
+
+       slen = fcntl(fd, F_GETFL, NULL);
+       if (slen != -1)
+       {
+               sprintf(p, "fl=0x%x, ", slen);
+               p += strlen(p);
+       }
+
+       sprintf(p, "mode=%o: ", st.st_mode);
+       p += strlen(p);
+       switch (st.st_mode & S_IFMT)
+       {
+#ifdef S_IFSOCK
+         case S_IFSOCK:
+               sprintf(p, "SOCK ");
+               p += strlen(p);
+               slen = sizeof sin;
+               if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
+                       sprintf(p, "(badsock)");
+               else
+               {
+                       hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
+                       sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
+                                                  : hp->h_name, ntohs(sin.sin_port));
+               }
+               p += strlen(p);
+               sprintf(p, "->");
+               p += strlen(p);
+               slen = sizeof sin;
+               if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
+                       sprintf(p, "(badsock)");
+               else
+               {
+                       hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
+                       sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
+                                                  : hp->h_name, ntohs(sin.sin_port));
+               }
+               break;
+#endif
+
+         case S_IFCHR:
+               sprintf(p, "CHR: ");
+               p += strlen(p);
+               goto defprint;
+
+         case S_IFBLK:
+               sprintf(p, "BLK: ");
+               p += strlen(p);
+               goto defprint;
+
+         default:
+defprint:
+               sprintf(p, "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld",
+                       major(st.st_dev), minor(st.st_dev), st.st_ino,
+                       st.st_nlink, st.st_uid, st.st_gid, st.st_size);
+               break;
+       }
+
+printit:
+       if (logit)
+               syslog(LOG_DEBUG, "%s", buf);
+       else
+               printf("%s\n", buf);
+}
+\f/*
+**  SHORTENSTRING -- return short version of a string
+**
+**     If the string is already short, just return it.  If it is too
+**     long, return the head and tail of the string.
+**
+**     Parameters:
+**             s -- the string to shorten.
+**             m -- the max length of the string.
+**
+**     Returns:
+**             Either s or a short version of s.
+*/
+
+#ifndef MAXSHORTSTR
+# define MAXSHORTSTR   83
+#endif
+
+char *
+shortenstring(s, m)
+       register char *s;
+       int m;
+{
+       int l;
+       static char buf[MAXSHORTSTR + 1];
+
+       l = strlen(s);
+       if (l < m)
+               return s;
+       if (m > MAXSHORTSTR)
+               m = MAXSHORTSTR;
+       else if (m < 10)
+       {
+               if (m < 5)
+               {
+                       strncpy(buf, s, m);
+                       buf[m] = '\0';
+                       return buf;
+               }
+               strncpy(buf, s, m - 3);
+               strcpy(buf + m - 3, "...");
+               return buf;
+       }
+       m = (m - 3) / 2;
+       strncpy(buf, s, m);
+       strcpy(buf + m, "...");
+       strcpy(buf + m + 3, s + l - m);
+       return buf;
+}