take mode 666 instead of 777 for database autorebuild
[unix-history] / usr / src / usr.sbin / sendmail / src / alias.c
index a5dc12d..f79cec8 100644 (file)
@@ -1,42 +1,38 @@
-# include <stdio.h>
-# include <ctype.h>
 # include <pwd.h>
 # include <pwd.h>
-# include "dlvrmail.h"
+# include <sys/types.h>
+# include <sys/stat.h>
+# include "sendmail.h"
 
 
-static char SccsId[] = "@(#)alias.c    1.6     %G%";
+# ifdef DBM
+static char SccsId[] = "@(#)alias.c    3.21    %G%     (with DBM)";
+# else DBM
+static char SccsId[] = "@(#)alias.c    3.21    %G%     (without DBM)";
+# endif DBM
 
 /*
 **  ALIAS -- Compute aliases.
 **
 
 /*
 **  ALIAS -- Compute aliases.
 **
-**     Scans the file ALIASFILE for a set of aliases.
-**     If found, it arranges to deliver to them by inserting the
-**     new names onto the SendQ queue.  Uses libdbm database if -DDBM.
+**     Scans the file /usr/lib/aliases for a set of aliases.
+**     If found, it arranges to deliver to them.  Uses libdbm
+**     database if -DDBM.
 **
 **     Parameters:
 **
 **     Parameters:
-**             none
+**             a -- address to alias.
 **
 **     Returns:
 **             none
 **
 **     Side Effects:
 **
 **     Returns:
 **             none
 **
 **     Side Effects:
-**             Aliases found on SendQ are removed and put onto
-**             AliasQ; replacements are added to SendQ.  This is
-**             done until no such replacement occurs.
-**
-**     Defined Constants:
-**             MAXRCRSN -- the maximum recursion depth.
-**
-**     Called By:
-**             main
+**             Aliases found are expanded.
 **
 **     Files:
 **
 **     Files:
-**             ALIASFILE -- the mail aliases.  The format is
+**             /usr/lib/aliases -- the mail aliases.  The format is
 **                     a series of lines of the form:
 **                             alias:name1,name2,name3,...
 **                     where 'alias' expands to all of
 **                     'name[i]'.  Continuations begin with
 **                     space or tab.
 **                     a series of lines of the form:
 **                             alias:name1,name2,name3,...
 **                     where 'alias' expands to all of
 **                     'name[i]'.  Continuations begin with
 **                     space or tab.
-**             ALIASFILE.pag, ALIASFILE.dir: libdbm version
+**             /usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version
 **                     of alias file.  Keys are aliases, datums
 **                     (data?) are name1,name2, ...
 **
 **                     of alias file.  Keys are aliases, datums
 **                     (data?) are name1,name2, ...
 **
@@ -51,211 +47,410 @@ static char SccsId[] = "@(#)alias.c       1.6     %G%";
 */
 
 
 */
 
 
-# define MAXRCRSN      10
-
 #ifdef DBM
 #ifdef DBM
-typedef struct {char *dptr; int dsize;} datum;
-datum lhs, rhs;
-extern datum fetch();
+typedef struct
+{
+       char    *dptr;
+       int     dsize;
+} DATUM;
+DATUM lhs, rhs;
+extern DATUM fetch();
 #endif DBM
 
 #endif DBM
 
-alias()
+alias(a)
+       register ADDRESS *a;
 {
 {
-       register addrq *q;
-       FILE *af;
-       char line[MAXLINE+1];
        register char *p;
        register char *p;
-       extern int errno;
-       bool didalias;
-       bool gotmatch;
-       auto addrq al;
-       extern bool sameaddr();
-       extern addrq *parse();
+# ifndef DBM
+       register STAB *s;
+# endif DBM
 
        if (NoAlias)
                return;
 # ifdef DEBUG
        if (Debug)
 
        if (NoAlias)
                return;
 # ifdef DEBUG
        if (Debug)
-               printf("--- alias ---\n");
+               printf("alias(%s)\n", a->q_paddr);
 # endif
 
 # endif
 
-       /* open alias file if not already open */
-#ifndef DBM
+       /* don't realias already aliased names */
+       if (bitset(QDONTSEND, a->q_flags))
+               return;
+
+       To = a->q_paddr;
+
+       /*
+       **  Look up this name
+       */
+
+# ifdef DBM
+       /* create a key for fetch */
+       lhs.dptr = a->q_user;
+       lhs.dsize = strlen(a->q_user) + 1;
+       rhs = fetch(lhs);
+
+       /* find this alias? */
+       p = rhs.dptr;
+       if (p == NULL)
+               return;
+# else DBM
+       s = stab(a->q_user, ST_ALIAS, ST_FIND);
+       if (s == NULL)
+               return;
+       p = s->s_alias;
+# endif DBM
+
+       /*
+       **  Match on Alias.
+       **      Deliver to the target list.
+       */
+
 # ifdef DEBUG
 # ifdef DEBUG
-       if (Debug && (af = fopen("mailaliases", "r")) != NULL)
-               printf(" [using local alias file]\n");
-       else
+       if (Debug)
+               printf("%s (%s, %s) aliased to %s\n",
+                   a->q_paddr, a->q_host, a->q_user, p);
 # endif
 # endif
-       if ((af = fopen(ALIASFILE, "r")) == NULL)
+       if (Verbose)
+               message(Arpa_Info, "aliased to %s", p);
+       a->q_flags |= QDONTSEND;
+       AliasLevel++;
+       sendto(p, 1);
+       AliasLevel--;
+}
+\f/*
+**  INITALIASES -- initialize for aliasing
+**
+**     Very different depending on whether we are running DBM or not.
+**
+**     Parameters:
+**             aliasfile -- location of aliases.
+**             init -- if set and if DBM, initialize the DBM files.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             initializes aliases:
+**             if DBM:  opens the database.
+**             if ~DBM: reads the aliases into the symbol table.
+*/
+
+# define DBMMODE       0666
+
+initaliases(aliasfile, init)
+       char *aliasfile;
+       bool init;
+{
+       char buf[MAXNAME];
+       struct stat stb;
+       time_t modtime;
+
+       /*
+       **  See if the DBM version of the file is out of date with
+       **  the text version.  If so, go into 'init' mode automatically.
+       **      This only happens if our effective userid owns the DBM
+       **      version or if the mode of the database is 666 -- this
+       **      is an attempt to avoid protection problems.  Note the
+       **      unpalatable hack to see if the stat succeeded.
+       */
+
+       if (stat(aliasfile, &stb) < 0)
+               return;
+# ifdef DBM
+       modtime = stb.st_mtime;
+       (void) strcpy(buf, aliasfile);
+       (void) strcat(buf, ".pag");
+       stb.st_ino = 0;
+       if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init)
+       {
+               if (stb.st_ino != 0 &&
+                   ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid()))
+               {
+                       init = TRUE;
+                       if (Verbose)
+                               message(Arpa_Info, "rebuilding alias database");
+               }
+               else
+               {
+                       message(Arpa_Info, "Warning: alias database out of date");
+               }
+       }
+# endif DBM
+
+       /*
+       **  If initializing, create the new files.
+       **      We should lock the alias file here to prevent other
+       **      instantiations of sendmail from reading an incomplete
+       **      file -- or worse yet, doing a concurrent initialize.
+       */
+
+# ifdef DBM
+       if (init)
+       {
+               (void) strcpy(buf, aliasfile);
+               (void) strcat(buf, ".dir");
+               if (close(creat(buf, DBMMODE)) < 0)
+               {
+                       syserr("cannot make %s", buf);
+                       return;
+               }
+               (void) strcpy(buf, aliasfile);
+               (void) strcat(buf, ".pag");
+               if (close(creat(buf, DBMMODE)) < 0)
+               {
+                       syserr("cannot make %s", buf);
+                       return;
+               }
+       }
+
+       /*
+       **  Open and, if necessary, load the DBM file.
+       **      If running without DBM, load the symbol table.
+       */
+
+       dbminit(aliasfile);
+       if (init)
+               readaliases(aliasfile, TRUE);
+# else DBM
+       readaliases(aliasfile, init);
+# endif DBM
+}
+\f/*
+**  READALIASES -- read and process the alias file.
+**
+**     This routine implements the part of initaliases that occurs
+**     when we are not going to use the DBM stuff.
+**
+**     Parameters:
+**             aliasfile -- the pathname of the alias file master.
+**             init -- if set, initialize the DBM stuff.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Reads aliasfile into the symbol table.
+**             Optionally, builds the .dir & .pag files.
+*/
+
+static
+readaliases(aliasfile, init)
+       char *aliasfile;
+       bool init;
+{
+       char line[BUFSIZ];
+       register char *p;
+       char *p2;
+       char *rhs;
+       bool skipping;
+       ADDRESS al, bl;
+       FILE *af;
+       int lineno;
+       register STAB *s;
+       int naliases, bytes, longest;
+
+       if ((af = fopen(aliasfile, "r")) == NULL)
        {
 # ifdef DEBUG
                if (Debug)
        {
 # ifdef DEBUG
                if (Debug)
-                       printf("Can't open %s\n", ALIASFILE);
+                       printf("Can't open %s\n", aliasfile);
 # endif
                errno = 0;
 # endif
                errno = 0;
+               NoAlias++;
                return;
        }
                return;
        }
-#else DBM
-       dbminit(ALIASFILE);
-#endif DBM
 
 
-#ifndef DBM
        /*
        /*
-       **  Scan alias file.
-       **      If we find any user that any line matches any user, we
-       **      will send to the line rather than to the user.
-       **
-       **      We pass through the file several times.  Didalias tells
-       **      us if we took some alias on this pass through the file;
-       **      when it goes false at the top of the loop we don't have
-       **      to scan any more.  Gotmatch tells the same thing, but
-       **      on a line-by-line basis; it is used for processing
-       **      continuation lines.
+       **  Read and interpret lines
        */
 
        */
 
-       do
+       lineno = 0;
+       naliases = bytes = longest = 0;
+       skipping = FALSE;
+       while (fgets(line, sizeof (line), af) != NULL)
        {
        {
-               didalias = FALSE;
-               gotmatch = FALSE;
-               rewind(af);
-               while (fgets(line, sizeof line, af) != NULL)
+               int lhssize, rhssize;
+
+               lineno++;
+               switch (line[0])
+               {
+                 case '#':
+                 case '\n':
+                 case '\0':
+                       skipping = FALSE;
+                       continue;
+
+                 case ' ':
+                 case '\t':
+                       if (!skipping)
+                               syserr("aliases: %d: Non-continuation line starts with space", lineno);
+                       skipping = TRUE;
+                       continue;
+               }
+               skipping = FALSE;
+
+               /*
+               **  Process the LHS
+               **      Find the final colon, and parse the address.
+               **      It should resolve to a local name -- this will
+               **      be checked later (we want to optionally do
+               **      parsing of the RHS first to maximize error
+               **      detection).
+               */
+
+               for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
+                       continue;
+               if (*p == '\0' || *p == '\n')
                {
                {
-                       /* comments begin with `#' */
-                       if (line[0] == '#')
-                               continue;
+                syntaxerr:
+                       syserr("aliases: %d: missing colon", lineno);
+                       continue;
+               }
+               *p++ = '\0';
+               if (parse(line, &al, 1) == NULL)
+               {
+                       *--p = ':';
+                       goto syntaxerr;
+               }
+
+               /*
+               **  Process the RHS.
+               **      'al' is the internal form of the LHS address.
+               **      'p' points to the text of the RHS.
+               */
+
+               rhs = p;
+               for (;;)
+               {
+                       register char c;
 
 
-                       /* check for continuation lines */
-                       if (isspace(line[0]))
+                       if (init)
                        {
                        {
-                               if (gotmatch)
+                               /* do parsing & compression of addresses */
+                               c = *p;
+                               while (c != '\0')
                                {
                                {
-                                       sendto(line, 1);
+                                       p2 = p;
+                                       while (*p != '\n' && *p != ',' && *p != '\0')
+                                               p++;
+                                       c = *p;
+                                       *p++ = '\0';
+                                       if (c == '\n')
+                                               c = '\0';
+                                       if (*p2 == '\0')
+                                       {
+                                               p[-1] = c;
+                                               continue;
+                                       }
+                                       (void) parse(p2, &bl, -1);
+                                       p[-1] = c;
+                                       while (isspace(*p))
+                                               p++;
                                }
                                }
-                               continue;
-                       }
-                       gotmatch = FALSE;
-
-                       /*
-                       **  Check to see if this pseudonym exists in SendQ.
-                       **      Turn the alias into canonical form.
-                       **      Then scan SendQ until you do (or do not)
-                       **      find that address.
-                       */
-
-                       /*  Get a canonical form for the alias. */
-                       for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
-                               continue;
-                       if (*p == '\0' || *p == '\n')
-                       {
-                        syntaxerr:
-                               syserr("Bad alias line `%s'", line);
-                               continue;
-                       }
-                       *p++ = '\0';
-                       if (parse(line, &al, -1) == NULL)
-                       {
-                               *--p = ':';
-                               goto syntaxerr;
                        }
                        }
+                       else
+                               p = &p[strlen(p)];
 
 
-                       /*  Scan SendQ for that canonical form. */
-                       for (q = &SendQ; (q = nxtinq(q)) != NULL; )
-                       {
-                               if (sameaddr(&al, q, TRUE))
-                                       break;
-                       }
-                       if (q != NULL)
-                       {
-                               /*
-                               **  Match on Alias.
-                               **      Deliver to the target list.
-                               **      Remove the alias from the send queue
-                               **        and put it on the Alias queue.
-                               */
+                       /* see if there should be a continuation line */
+                       c = fgetc(af);
+                       if (!feof(af))
+                               (void) ungetc(c, af);
+                       if (c != ' ' && c != '\t')
+                               break;
 
 
-# ifdef DEBUG
-                               if (Debug)
-                                       printf("%s (%s, %s) aliased to %s (%s,%s,%s)\n",
-                                           q->q_paddr, q->q_host, q->q_user,
-                                           p, al.q_paddr, al.q_host, al.q_user);
-# endif
-                               tkoffq(q, &SendQ);
-                               putonq(q, &AliasQ);
-                               didalias++;
-                               gotmatch++;
-                               sendto(p, 1);
-                       }
+                       /* read continuation line */
+                       p--;
+                       if (fgets(p, sizeof line - (p - line), af) == NULL)
+                               break;
+                       lineno++;
                }
                }
-       } while (didalias);
-       fclose(af);
-#else DBM
-       /*
-       **  Scan SendQ
-       **      We only have to do this once, since anything we alias
-       **      two is being put at the end of the queue we are
-       **      scanning.
-       */
-
-       for (q = &SendQ; (q = nxtinq(q)) != NULL; )
-       {
-               /* only alias local users */
-               if (q->q_mailer != &Mailer[0])
-                       continue;
-
-               /* create a key for fetch */
-               lhs.dptr = q->q_user;
-               lhs.dsize = strlen(q->q_user) + 1;
-               rhs = fetch(lhs);
-
-               /* find this alias? */
-               p = rhs.dptr;
-               if (p == NULL)
+               if (al.q_mailer != MN_LOCAL)
+               {
+                       syserr("aliases: %d: cannot alias non-local names", lineno);
                        continue;
                        continue;
+               }
 
                /*
 
                /*
-               **  Match on Alias.
-               **      Deliver to the target list.
-               **      Remove the alias from the send queue
-               **        and put it on the Alias queue.
+               **  Insert alias into symbol table or DBM file
                */
 
                */
 
-# ifdef DEBUG
-               if (Debug)
-                       printf("%s (%s, %s) aliased to %s\n",
-                           q->q_paddr, q->q_host, q->q_user, p);
-# endif
-               tkoffq(q, &SendQ);
-               putonq(q, &AliasQ);
-               sendto(p, 1);
+               lhssize = strlen(al.q_user) + 1;
+               rhssize = strlen(rhs) + 1;
+
+# ifdef DBM
+               if (init)
+               {
+                       DATUM key, content;
+
+                       key.dsize = lhssize;
+                       key.dptr = al.q_user;
+                       content.dsize = rhssize;
+                       content.dptr = rhs;
+                       store(key, content);
+               }
+               else
+# endif DBM
+               {
+                       s = stab(al.q_user, ST_ALIAS, ST_ENTER);
+                       s->s_alias = newstr(rhs);
+               }
+
+               /* statistics */
+               naliases++;
+               bytes += lhssize + rhssize;
+               if (rhssize > longest)
+                       longest = rhssize;
        }
        }
-#endif DBM
+       (void) fclose(af);
+       To = NULL;
+       if (Verbose)
+               message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
+                       naliases, longest, bytes);
 }
 \f/*
 **  FORWARD -- Try to forward mail
 **
 **     This is similar but not identical to aliasing.
 **
 }
 \f/*
 **  FORWARD -- Try to forward mail
 **
 **     This is similar but not identical to aliasing.
 **
-**     Currently it is undefined, until the protocol for userinfo
-**     databases is finalized.
-**
 **     Parameters:
 **     Parameters:
-**             user -- the name of the user who's mail we
-**                     would like to forward to.
+**             user -- the name of the user who's mail we would like
+**                     to forward to.  It must have been verified --
+**                     i.e., the q_home field must have been filled
+**                     in.
 **
 **     Returns:
 **
 **     Returns:
-**             TRUE -- we have forwarded it somewhere.
-**             FALSE -- not forwarded; go ahead & deliver.
+**             none.
 **
 **     Side Effects:
 **
 **     Side Effects:
-**             New names are added to SendQ.
-**
-**     Called By:
-**             recipient
+**             New names are added to send queues.
+**             Sets the QDONTSEND bit in addresses that are forwarded.
 */
 
 */
 
-bool
 forward(user)
 forward(user)
-       addrq *user;
+       ADDRESS *user;
 {
 {
-       return (FALSE);
+       char buf[60];
+       struct stat stbuf;
+
+# ifdef DEBUG
+       if (Debug)
+               printf("forward(%s)\n", user->q_paddr);
+# endif DEBUG
+
+       if (user->q_mailer != MN_LOCAL || bitset(QBADADDR, user->q_flags))
+               return;
+# ifdef DEBUG
+       if (user->q_home == NULL)
+               syserr("forward: no home");
+# endif DEBUG
+
+       /* good address -- look for .forward file in home */
+       define('z', user->q_home);
+       (void) expand("$z/.forward", buf, &buf[sizeof buf - 1]);
+       if (stat(buf, &stbuf) < 0 || stbuf.st_uid != user->q_uid ||
+           !bitset(S_IREAD, stbuf.st_mode))
+               return;
+
+       /* we do have an address to forward to -- do it */
+       user->q_flags |= QDONTSEND;
+       include(buf, "forwarding");
 }
 }