BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.sbin / sendmail / src / queue.c
index 85efc20..03b226a 100644 (file)
@@ -3,26 +3,42 @@
  * Copyright (c) 1988 Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1988 Regents of the University of California.
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 # include "sendmail.h"
 
 #ifndef lint
 #ifdef QUEUE
  */
 
 # include "sendmail.h"
 
 #ifndef lint
 #ifdef QUEUE
-static char sccsid[] = "@(#)queue.c    5.27 (Berkeley) %G% (with queueing)";
+static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (with queueing)";
 #else
 #else
-static char sccsid[] = "@(#)queue.c    5.27 (Berkeley) %G% (without queueing)";
+static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (without queueing)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
@@ -31,6 +47,7 @@ static char sccsid[] = "@(#)queue.c   5.27 (Berkeley) %G% (without queueing)";
 # include <sys/file.h>
 # include <signal.h>
 # include <errno.h>
 # include <sys/file.h>
 # include <signal.h>
 # include <errno.h>
+# include <pwd.h>
 
 # ifdef QUEUE
 
 
 # ifdef QUEUE
 
@@ -72,26 +89,37 @@ queueup(e, queueall, announce)
        bool queueall;
        bool announce;
 {
        bool queueall;
        bool announce;
 {
-       char *tf;
        char *qf;
        char *qf;
-       char buf[MAXLINE];
+       char buf[MAXLINE], tf[MAXLINE];
        register FILE *tfp;
        register HDR *h;
        register ADDRESS *q;
        MAILER nullmailer;
        register FILE *tfp;
        register HDR *h;
        register ADDRESS *q;
        MAILER nullmailer;
-       int fd;
+       int fd, ret;
 
        /*
        **  Create control file.
        */
 
 
        /*
        **  Create control file.
        */
 
-       tf = newstr(queuename(e, 't'));
-       fd = open(tf, O_CREAT|O_WRONLY, FileMode);
-       if (fd < 0)
-       {
-               syserr("queueup: cannot create temp file %s", tf);
-               return NULL;
-       }
+       do {
+               strcpy(tf, queuename(e, 't'));
+               fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
+               if (fd < 0) {
+                       if ( errno != EEXIST) {
+                               syserr("queueup: cannot create temp file %s",
+                                       tf);
+                               return NULL;
+                       }
+               } else {
+                       if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
+                               if (errno != EWOULDBLOCK)
+                                       syserr("cannot flock(%s)", tf);
+                               close(fd);
+                               fd = -1;
+                       }
+               }
+       } while (fd < 0);
+
        tfp = fdopen(fd, "w");
 
        if (tTd(40, 1))
        tfp = fdopen(fd, "w");
 
        if (tTd(40, 1))
@@ -145,9 +173,13 @@ queueup(e, queueall, announce)
        /* output list of recipient addresses */
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
        /* output list of recipient addresses */
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
-               if (queueall ? !bitset(QDONTSEND, q->q_flags) :
+               if (queueall ? !bitset(QDONTSEND|QSENT, q->q_flags) :
                               bitset(QQUEUEUP, q->q_flags))
                {
                               bitset(QQUEUEUP, q->q_flags))
                {
+                       char *ctluser, *getctluser();
+
+                       if ((ctluser = getctluser(q)) != NULL)
+                               fprintf(tfp, "C%s\n", ctluser);
                        fprintf(tfp, "R%s\n", q->q_paddr);
                        if (announce)
                        {
                        fprintf(tfp, "R%s\n", q->q_paddr);
                        if (announce)
                        {
@@ -169,7 +201,13 @@ queueup(e, queueall, announce)
        for (q = e->e_errorqueue; q != NULL; q = q->q_next)
        {
                if (!bitset(QDONTSEND, q->q_flags))
        for (q = e->e_errorqueue; q != NULL; q = q->q_next)
        {
                if (!bitset(QDONTSEND, q->q_flags))
+               {
+                       char *ctluser, *getctluser();
+
+                       if ((ctluser = getctluser(q)) != NULL)
+                               fprintf(tfp, "C%s\n", ctluser);
                        fprintf(tfp, "E%s\n", q->q_paddr);
                        fprintf(tfp, "E%s\n", q->q_paddr);
+               }
        }
 
        /*
        }
 
        /*
@@ -233,19 +271,10 @@ queueup(e, queueall, announce)
        **  Clean up.
        */
 
        **  Clean up.
        */
 
-       if (flock(fileno(tfp), LOCK_EX|LOCK_NB) < 0)
-       {
-               syserr("cannot flock(%s)", tf);
-       }
-
        qf = queuename(e, 'q');
        qf = queuename(e, 'q');
-       if (tf != NULL)
-       {
-               (void) unlink(qf);
-               if (rename(tf, qf) < 0)
-                       syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
-               errno = 0;
-       }
+       if (rename(tf, qf) < 0)
+               syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
+       errno = 0;
 
 # ifdef LOG
        /* save log info */
 
 # ifdef LOG
        /* save log info */
@@ -306,7 +335,7 @@ runqueue(forkflag)
                pid = dofork();
                if (pid != 0)
                {
                pid = dofork();
                if (pid != 0)
                {
-                       extern reapchild();
+                       extern void reapchild();
 
                        /* parent -- pick up intermediate zombie */
 #ifndef SIGCHLD
 
                        /* parent -- pick up intermediate zombie */
 #ifndef SIGCHLD
@@ -392,15 +421,6 @@ runqueue(forkflag)
 # define NEED_P                001
 # define NEED_T                002
 
 # define NEED_P                001
 # define NEED_T                002
 
-# ifndef DIR
-# define DIR           FILE
-# define direct                dir
-# define opendir(d)    fopen(d, "r")
-# define readdir(f)    ((fread(&dbuf, sizeof dbuf, 1, f) > 0) ? &dbuf : 0)
-static struct dir      dbuf;
-# define closedir(f)   fclose(f)
-# endif DIR
-
 orderq(doall)
        bool doall;
 {
 orderq(doall)
        bool doall;
 {
@@ -441,12 +461,6 @@ orderq(doall)
                char lbuf[MAXNAME];
 
                /* is this an interesting entry? */
                char lbuf[MAXNAME];
 
                /* is this an interesting entry? */
-               if (d->d_ino == 0)
-                       continue;
-# ifdef DEBUG
-               if (tTd(40, 10))
-                       printf("orderq: %12s\n", d->d_name);
-# endif DEBUG
                if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
                        continue;
 
                if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
                        continue;
 
@@ -656,12 +670,12 @@ dowork(w)
                if (!bitset(EF_FATALERRS, CurEnv->e_flags))
                        sendall(CurEnv, SM_DELIVER);
 
                if (!bitset(EF_FATALERRS, CurEnv->e_flags))
                        sendall(CurEnv, SM_DELIVER);
 
-               fclose(qflock);
                /* finish up and exit */
                if (ForkQueueRuns)
                        finis();
                else
                        dropenvelope(CurEnv);
                /* finish up and exit */
                if (ForkQueueRuns)
                        finis();
                else
                        dropenvelope(CurEnv);
+               fclose(qflock);
        }
        else
        {
        }
        else
        {
@@ -700,8 +714,8 @@ readqf(e, full)
        char buf[MAXFIELD];
        extern char *fgetfolded();
        extern long atol();
        char buf[MAXFIELD];
        extern char *fgetfolded();
        extern long atol();
+       int gotctluser = 0;
        int fd;
        int fd;
-       extern ADDRESS *sendto();
 
        /*
        **  Read and process the file.
 
        /*
        **  Read and process the file.
@@ -721,7 +735,7 @@ readqf(e, full)
 # ifdef LOG
                /* being processed by another queuer */
                if (Verbose)
 # ifdef LOG
                /* being processed by another queuer */
                if (Verbose)
-                       printf("%s: locked", CurEnv->e_id);
+                       printf("%s: locked\n", CurEnv->e_id);
 # endif LOG
                (void) fclose(qfp);
                return NULL;
 # endif LOG
                (void) fclose(qfp);
                return NULL;
@@ -740,8 +754,13 @@ readqf(e, full)
                        printf("+++++ %s\n", buf);
                switch (buf[0])
                {
                        printf("+++++ %s\n", buf);
                switch (buf[0])
                {
+                 case 'C':             /* specify controlling user */
+                       setctluser(&buf[1]);
+                       gotctluser = 1;
+                       break;
+
                  case 'R':             /* specify recipient */
                  case 'R':             /* specify recipient */
-                       (void) sendto(&buf[1], 1, (ADDRESS *) NULL, 0);
+                       sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
                        break;
 
                  case 'E':             /* specify error recipient */
                        break;
 
                  case 'E':             /* specify error recipient */
@@ -786,8 +805,25 @@ readqf(e, full)
                                LineNumber, buf);
                        break;
                }
                                LineNumber, buf);
                        break;
                }
+               /*
+               **  The `C' queue file command operates on the next line,
+               **  so we use "gotctluser" to maintain state as follows:
+               **      0 - no controlling user,
+               **      1 - controlling user has been set but not used,
+               **      2 - controlling user must be used on next iteration.
+               */
+               if (gotctluser == 1)
+                       gotctluser++;
+               else if (gotctluser == 2)
+               {
+                       clrctluser();
+                       gotctluser = 0;
+               }
        }
 
        }
 
+       /* clear controlling user in case we break out prematurely */
+       clrctluser();
+
        FileName = NULL;
 
        /*
        FileName = NULL;
 
        /*
@@ -821,6 +857,7 @@ printqueue()
        FILE *f;
        int nrequests;
        char buf[MAXLINE];
        FILE *f;
        int nrequests;
        char buf[MAXLINE];
+       char cbuf[MAXLINE];
 
        /*
        **  Read and order the queue.
 
        /*
        **  Read and order the queue.
@@ -872,6 +909,7 @@ printqueue()
                errno = 0;
 
                message[0] = '\0';
                errno = 0;
 
                message[0] = '\0';
+               cbuf[0] = '\0';
                while (fgets(buf, sizeof buf, f) != NULL)
                {
                        fixcrlf(buf, TRUE);
                while (fgets(buf, sizeof buf, f) != NULL)
                {
                        fixcrlf(buf, TRUE);
@@ -892,8 +930,23 @@ printqueue()
                                if (message[0] != '\0')
                                        printf("\n\t\t (%.60s)", message);
                                break;
                                if (message[0] != '\0')
                                        printf("\n\t\t (%.60s)", message);
                                break;
+                         case 'C':     /* controlling user */
+                               if (strlen(buf) < MAXLINE-3)    /* sanity */
+                                       (void) strcat(buf, ") ");
+                               cbuf[0] = cbuf[1] = '(';
+                               (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1);
+                               cbuf[MAXLINE-1] = '\0';
+                               break;
 
                          case 'R':     /* recipient name */
 
                          case 'R':     /* recipient name */
+                               if (cbuf[0] != '\0') {
+                                       /* prepend controlling user to `buf' */
+                                       (void) strncat(cbuf, &buf[1],
+                                                     MAXLINE-strlen(cbuf));
+                                       cbuf[MAXLINE-1] = '\0';
+                                       (void) strcpy(buf, cbuf);
+                                       cbuf[0] = '\0';
+                               }
                                if (Verbose)
                                        printf("\n\t\t\t\t\t %.38s", &buf[1]);
                                else
                                if (Verbose)
                                        printf("\n\t\t\t\t\t %.38s", &buf[1]);
                                else
@@ -1037,3 +1090,146 @@ unlockqueue(e)
                xunlink(queuename(e, 'x'));
 
 }
                xunlink(queuename(e, 'x'));
 
 }
+\f/*
+**  GETCTLUSER -- return controlling user if mailing to prog or file
+**
+**     Check for a "|" or "/" at the beginning of the address.  If
+**     found, return a controlling username.
+**
+**     Parameters:
+**             a - the address to check out
+**
+**     Returns:
+**             Either NULL, if we werent mailing to a program or file,
+**             or a controlling user name (possibly in getpwuid's
+**             static buffer).
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+getctluser(a)
+       ADDRESS *a;
+{
+       extern ADDRESS *getctladdr();
+       struct passwd *pw;
+       char *retstr;
+
+       /*
+       **  Get unquoted user for file, program or user.name check.
+       **  N.B. remove this code block to always emit controlling
+       **  addresses (at the expense of backward compatibility).
+       */
+
+       {
+               char buf[MAXNAME];
+               (void) strncpy(buf, a->q_paddr, MAXNAME);
+               buf[MAXNAME-1] = '\0';
+               stripquotes(buf, TRUE);
+
+               if (buf[0] != '|' && buf[0] != '/')
+                       return((char *)NULL);
+       }
+
+       a = getctladdr(a);              /* find controlling address */
+
+       if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL)
+               retstr = pw->pw_name;
+       else                            /* use default user */
+               retstr = DefUser;
+
+       if (tTd(40, 5))
+               printf("Set controlling user for `%s' to `%s'\n",
+                      (a == NULL)? "<null>": a->q_paddr, retstr);
+
+       return(retstr);
+}
+\f/*
+**  SETCTLUSER - sets `CtlUser' to controlling user
+**  CLRCTLUSER - clears controlling user (no params, nothing returned)
+**
+**     These routines manipulate `CtlUser'.
+**
+**     Parameters:
+**             str  - controlling user as passed to setctluser()
+**
+**     Returns:
+**             None.
+**
+**     Side Effects:
+**             `CtlUser' is changed.
+*/
+
+static char CtlUser[MAXNAME];
+
+setctluser(str)
+register char *str;
+{
+       (void) strncpy(CtlUser, str, MAXNAME);
+       CtlUser[MAXNAME-1] = '\0';
+}
+
+clrctluser()
+{
+       CtlUser[0] = '\0';
+}
+
+\f/*
+**  SETCTLADDR -- create a controlling address
+**
+**     If global variable `CtlUser' is set and we are given a valid
+**     address, make that address a controlling address; change the
+**     `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID.
+**
+**     Parameters:
+**             a - address for which control uid/gid info may apply
+**
+**     Returns:
+**             None.   
+**
+**     Side Effects:
+**             Fills in uid/gid fields in address and sets QGOODUID
+**             flag if appropriate.
+*/
+
+setctladdr(a)
+       ADDRESS *a;
+{
+       struct passwd *pw;
+
+       /*
+       **  If there is no current controlling user, or we were passed a
+       **  NULL addr ptr or we already have a controlling user, return.
+       */
+
+       if (CtlUser[0] == '\0' || a == NULL || a->q_ruser)
+               return;
+
+       /*
+       **  Set up addr fields for controlling user.  If `CtlUser' is no
+       **  longer valid, use the default user/group.
+       */
+
+       if ((pw = getpwnam(CtlUser)) != NULL)
+       {
+               if (a->q_home)
+                       free(a->q_home);
+               a->q_home = newstr(pw->pw_dir);
+               a->q_uid = pw->pw_uid;
+               a->q_gid = pw->pw_gid;
+               a->q_ruser = newstr(CtlUser);
+       }
+       else
+       {
+               a->q_uid = DefUid;
+               a->q_gid = DefGid;
+               a->q_ruser = newstr(DefUser);
+       }
+
+       a->q_flags |= QGOODUID;         /* flag as a "ctladdr"  */
+
+       if (tTd(40, 5))
+               printf("Restored controlling user for `%s' to `%s'\n",
+                      a->q_paddr, a->q_ruser);
+}