whatnow cooks up strings for executing external commands, and then
[mmh] / uip / whatnowsbr.c
index 0e2a998..121a79b 100644 (file)
@@ -2,13 +2,47 @@
 /*
  * whatnowsbr.c -- the WhatNow shell
  *
- * $Id$
+ * This code is Copyright (c) 2002, by the authors of nmh.  See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
+ *
+ *  Several options have been added to ease the inclusion of attachments
+ *  using the header field name mechanism added to anno and send.  The
+ *  -attach option is used to specify the header field name for attachments.
+ *
+ *  Several commands have been added at the whatnow prompt:
+ *
+ *     cd [ directory ]        This option works just like the shell's
+ *                             cd command and lets the user change the
+ *                             directory from which attachments are
+ *                             taken so that long path names are not
+ *                             needed with every file.
+ *
+ *     ls [ ls-options ]       This option works just like the normal
+ *                             ls command and exists to allow the user
+ *                             to verify file names in the directory.
+ *
+ *     pwd                     This option works just like the normal
+ *                             pwd command and exists to allow the user
+ *                             to verify the directory.
+ *
+ *     attach files            This option attaches the named files to
+ *                             the draft.
+ *
+ *     alist [-ln]             This option lists the attachments on the
+ *                             draft.  -l gets long listings, -n gets
+ *                             numbered listings.
+ *
+ *     detach files            This option removes attachments from the
+ *     detach -n numbers       draft.  This can be done by file name or
+ *                             by attachment number.
  */
 
 #include <h/mh.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <h/mime.h>
+#include <h/utils.h>
 
 static struct swit whatnowswitches[] = {
 #define        DFOLDSW                 0
@@ -27,6 +61,10 @@ static struct swit whatnowswitches[] = {
     { "version", 0 },
 #define        HELPSW                  7
     { "help", 0 },
+#define        ATTACHSW                8
+    { "attach header-field-name", 0 },
+#define NOATTACHSW              9
+    { "noattach", 0 },
     { NULL, 0 }
 };
 
@@ -54,6 +92,18 @@ static struct swit aleqs[] = {
     { "quit [-delete]", 0 },
 #define DELETESW                       9
     { "delete", 0 },
+#define        CDCMDSW                       10
+    { "cd [directory]", 0 },
+#define        PWDCMDSW                      11
+    { "pwd", 0 },
+#define        LSCMDSW                       12
+    { "ls", 0 },
+#define        ATTACHCMDSW                   13
+    { "attach", 0 },
+#define        DETACHCMDSW                   14
+    { "detach [-n]", 2 },
+#define        ALISTCMDSW                    15
+    { "alist [-ln] ", 2 },
     { NULL, 0 }
 };
 
@@ -63,13 +113,18 @@ static char *myprompt = "\nWhat now? ";
  * static prototypes
  */
 static int editfile (char **, char **, char *, int, struct msgs *,
-       char *, char *, int);
+       char *, char *, int, int);
 static int sendfile (char **, char *, int);
 static void sendit (char *, char **, char *, int);
 static int buildfile (char **, char *);
 static int check_draft (char *);
 static int whomfile (char **, char *);
 static int removefile (char *);
+static void writelscmd(char *, int, char **);
+static void writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp);
+static FILE* popen_in_dir(const char *dir, const char *cmd, const char *type);
+static int system_in_dir(const char *dir, const char *cmd);
+
 
 #ifdef HAVE_LSTAT
 static int copyf (char *, char *);
@@ -79,12 +134,19 @@ static int copyf (char *, char *);
 int
 WhatNow (int argc, char **argv)
 {
-    int isdf = 0, nedit = 0, use = 0;
+    int isdf = 0, nedit = 0, use = 0, atfile = 1;
     char *cp, *dfolder = NULL, *dmsg = NULL;
     char *ed = NULL, *drft = NULL, *msgnam = NULL;
     char buf[BUFSIZ], prompt[BUFSIZ];
     char **argp, **arguments;
     struct stat st;
+    char       *attach = NMH_ATTACH_HEADER;/* attachment header field name */
+    char       cwd[PATH_MAX + 1];      /* current working directory */
+    char       file[PATH_MAX + 1];     /* file name buffer */
+    char       shell[PATH_MAX + 1];    /* shell response buffer */
+    FILE       *f;                     /* read pointer for bgnd proc */
+    char       *l;                     /* set on -l to alist  command */
+    int                n;                      /* set on -n to alist command */
 
     invo_name = r1bindex (argv[0], '/');
 
@@ -94,24 +156,32 @@ WhatNow (int argc, char **argv)
     arguments = getarguments (invo_name, argc, argv, 1);
     argp = arguments;
 
+    /*
+     * Get the initial current working directory.
+     */
+
+    if (getcwd(cwd, sizeof (cwd)) == (char *)0) {
+       adios("getcwd", "could not get working directory");
+    }
+
     while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch (++cp, whatnowswitches)) {
-           case AMBIGSW: 
+           case AMBIGSW:
                ambigsw (cp, whatnowswitches);
                done (1);
-           case UNKWNSW: 
+           case UNKWNSW:
                adios (NULL, "-%s unknown", cp);
 
-           case HELPSW: 
+           case HELPSW:
                snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
                print_help (buf, whatnowswitches, 1);
-               done (1);
+               done (0);
            case VERSIONSW:
                print_version(invo_name);
-               done (1);
+               done (0);
 
-           case DFOLDSW: 
+           case DFOLDSW:
                if (dfolder)
                    adios (NULL, "only one draft folder at a time!");
                if (!(cp = *argp++) || *cp == '-')
@@ -119,23 +189,23 @@ WhatNow (int argc, char **argv)
                dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
                                *cp != '@' ? TFOLDER : TSUBCWF);
                continue;
-           case DMSGSW: 
+           case DMSGSW:
                if (dmsg)
                    adios (NULL, "only one draft message at a time!");
                if (!(dmsg = *argp++) || *dmsg == '-')
                    adios (NULL, "missing argument to %s", argp[-2]);
                continue;
-           case NDFLDSW: 
+           case NDFLDSW:
                dfolder = NULL;
                isdf = NOTOK;
                continue;
 
-           case EDITRSW: 
+           case EDITRSW:
                if (!(ed = *argp++) || *ed == '-')
                    adios (NULL, "missing argument to %s", argp[-2]);
                nedit = 0;
                continue;
-           case NEDITSW: 
+           case NEDITSW:
                nedit++;
                continue;
 
@@ -143,6 +213,15 @@ WhatNow (int argc, char **argv)
                if (!(myprompt = *argp++) || *myprompt == '-')
                    adios (NULL, "missing argument to %s", argp[-2]);
                continue;
+
+           case ATTACHSW:
+               if (!(attach = *argp++) || *attach == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case NOATTACHSW:
+               attach = NULL;
+               continue;
            }
        }
        if (drft)
@@ -156,6 +235,9 @@ WhatNow (int argc, char **argv)
 
     msgnam = (cp = getenv ("mhaltmsg")) && *cp ? getcpy (cp) : NULL;
 
+    if ((cp = getenv ("mhatfile")) && *cp)
+       atfile = atoi(cp);
+
     if ((cp = getenv ("mhuse")) && *cp)
        use = atoi (cp);
 
@@ -165,12 +247,17 @@ WhatNow (int argc, char **argv)
     }
 
     /* start editing the draft, unless -noedit was given */
-    if (!nedit && editfile (&ed, NULL, drft, use, NULL, msgnam, NULL, 1) < 0)
+    if (!nedit && editfile (&ed, NULL, drft, use, NULL, msgnam,
+                           NULL, 1, atfile) < 0)
        done (1);
 
     snprintf (prompt, sizeof(prompt), myprompt, invo_name);
     for (;;) {
+#ifdef READLINE_SUPPORT
+       if (!(argp = getans_via_readline (prompt, aleqs))) {
+#else /* ! READLINE_SUPPORT */
        if (!(argp = getans (prompt, aleqs))) {
+#endif /* READLINE_SUPPORT */
            unlink (LINK);
            done (1);
        }
@@ -192,11 +279,12 @@ WhatNow (int argc, char **argv)
            /* Call an editor on the draft file */
            if (*++argp)
                ed = *argp++;
-           if (editfile (&ed, argp, drft, NOUSE, NULL, msgnam, NULL, 1) == NOTOK)
+           if (editfile (&ed, argp, drft, NOUSE, NULL, msgnam,
+                         NULL, 1, atfile) == NOTOK)
                done (1);
            break;
 
-       case LISTSW: 
+       case LISTSW:
            /* display the draft file */
            showfile (++argp, drft);
            break;
@@ -228,18 +316,224 @@ WhatNow (int argc, char **argv)
                done (1);
            break;
 
-       case SENDSW: 
+       case SENDSW:
            /* Send draft */
            sendfile (++argp, drft, 0);
            break;
 
-       case REFILEOPT: 
+       case REFILEOPT:
            /* Refile the draft */
            if (refile (++argp, drft) == 0)
                done (0);
            break;
 
-       default: 
+       case CDCMDSW:
+           /* Change the working directory for attachments
+            *
+            *  Run the directory through the user's shell so that
+            *  we can take advantage of any syntax that the user
+            *  is accustomed to.  Read back the absolute path.
+            */
+
+           if (*(argp+1) == (char *)0) {
+               (void)sprintf(buf, "$SHELL -c \"cd;pwd\"");
+           }
+           else {
+               writesomecmd(buf, BUFSIZ, "cd", "pwd", argp);
+           }
+           if ((f = popen_in_dir(cwd, buf, "r")) != (FILE *)0) {
+               fgets(cwd, sizeof (cwd), f);
+
+               if (strchr(cwd, '\n') != (char *)0)
+                       *strchr(cwd, '\n') = '\0';
+
+               pclose(f);
+           }
+           else {
+               advise("popen", "could not get directory");
+           }
+
+           break;
+
+       case PWDCMDSW:
+           /* Print the working directory for attachments */
+           printf("%s\n", cwd);
+           break;
+
+       case LSCMDSW:
+           /* List files in the current attachment working directory
+            *
+            *  Use the user's shell so that we can take advantage of any
+            *  syntax that the user is accustomed to.
+            */
+           writelscmd(buf, sizeof(buf), argp);
+           (void)system_in_dir(cwd, buf);
+           break;
+
+       case ALISTCMDSW:
+           /*
+            *  List attachments on current draft.  Options are:
+            *
+            *   -l     long listing (full path names)
+            *   -n     numbers listing
+            */
+
+           if (attach == (char *)0) {
+               advise((char *)0, "can't list because no header field name was given.");
+               break;
+           }
+
+           l = (char *)0;
+           n = 0;
+
+           while (*++argp != (char *)0) {
+               if (strcmp(*argp, "-l") == 0)
+                   l = "/";
+
+               else if (strcmp(*argp, "-n") == 0)
+                   n = 1;
+
+               else if (strcmp(*argp, "-ln") == 0 || strcmp(*argp, "-nl") == 0) {
+                   l = "/";
+                   n = 1;
+               }
+
+               else {
+                   n = -1;
+                   break;
+               }
+           }
+
+           if (n == -1)
+               advise((char *)0, "usage is alist [-ln].");
+
+           else
+               annolist(drft, attach, l, n);
+
+           break;
+
+       case ATTACHCMDSW:
+           /*
+            *  Attach files to current draft.
+            */
+
+           if (attach == (char *)0) {
+               advise((char *)0, "can't attach because no header field name was given.");
+               break;
+           }
+
+           if (*(argp+1) == (char *)0) {
+               advise((char *)0, "attach command requires file argument(s).");
+               break;
+           }
+
+           /*
+            *  Build a command line that causes the user's shell to list the file name
+            *  arguments.  This handles and wildcard expansion, tilde expansion, etc.
+            */
+           writelscmd(buf, sizeof(buf), argp);
+
+           /*
+            *  Read back the response from the shell, which contains a number of lines
+            *  with one file name per line.  Remove off the newline.  Determine whether
+            *  we have an absolute or relative path name.  Prepend the current working
+            *  directory to relative path names.  Add the attachment annotation to the
+            *  draft.
+            */
+
+           if ((f = popen_in_dir(cwd, buf, "r")) != (FILE *)0) {
+               while (fgets(shell, sizeof (shell), f) != (char *)0) {
+                   *(strchr(shell, '\n')) = '\0';
+
+                   if (*shell == '/')
+                       (void)annotate(drft, attach, shell, 1, 0, -2, 1);
+                   else {
+                       (void)sprintf(file, "%s/%s", cwd, shell);
+                       (void)annotate(drft, attach, file, 1, 0, -2, 1);
+                   }
+               }
+
+               pclose(f);
+           }
+           else {
+               advise("popen", "could not get file from shell");
+           }
+
+           break;
+
+       case DETACHCMDSW:
+           /*
+            *  Detach files from current draft.
+            */
+
+           if (attach == (char *)0) {
+               advise((char *)0, "can't detach because no header field name was given.");
+               break;
+           }
+
+           /*
+            *  Scan the arguments for a -n.  Mixed file names and numbers aren't allowed,
+            *  so this catches a -n anywhere in the argument list.
+            */
+
+           for (n = 0, arguments = argp + 1; *arguments != (char *)0; arguments++) {
+               if (strcmp(*arguments, "-n") == 0) {
+                       n = 1;
+                       break;
+               }
+           }
+
+           /*
+            *  A -n was found so interpret the arguments as attachment numbers.
+            *  Decrement any remaining argument number that is greater than the one
+            *  just processed after processing each one so that the numbering stays
+            *  correct.
+            */
+
+           if (n == 1) {
+               for (arguments = argp + 1; *arguments != (char *)0; arguments++) {
+                   if (strcmp(*arguments, "-n") == 0)
+                       continue;
+
+                   if (**arguments != '\0') {
+                       n = atoi(*arguments);
+                       (void)annotate(drft, attach, (char *)0, 1, 0, n, 1);
+
+                       for (argp = arguments + 1; *argp != (char *)0; argp++) {
+                           if (atoi(*argp) > n) {
+                               if (atoi(*argp) == 1)
+                                   *argp = "";
+                               else
+                                   (void)sprintf(*argp, "%d", atoi(*argp) - 1);
+                           }
+                       }
+                   }
+               }
+           }
+
+           /*
+            *  The arguments are interpreted as file names.  Run them through the
+            *  user's shell for wildcard expansion and other goodies.  Do this from
+            *  the current working directory if the argument is not an absolute path
+            *  name (does not begin with a /).
+            *
+            * We feed all the file names to the shell at once, otherwise you can't
+            * provide a file name with a space in it.
+            */
+           writelscmd(buf, sizeof(buf), argp);
+           if ((f = popen_in_dir(cwd, buf, "r")) != (FILE *)0) {
+               while (fgets(shell, sizeof (shell), f) != (char *)0) {
+                   *(strchr(shell, '\n')) = '\0';
+                   (void)annotate(drft, attach, shell, 1, 0, 0, 1);
+               }
+               pclose(f);
+           } else {
+               advise("popen", "could not get file from shell");
+           }
+
+           break;
+
+       default:
            /* Unknown command */
            advise (NULL, "say what?");
            break;
@@ -248,6 +542,106 @@ WhatNow (int argc, char **argv)
     /*NOTREACHED*/
 }
 
+
+
+/* Build a command line of the form $SHELL -c "cd 'cwd'; cmd argp ... ; trailcmd". */
+static void
+writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp)
+{
+    char *cp;
+    /* Note that we do not quote -- the argp from the user
+     * is assumed to be quoted as they desire. (We can't treat
+     * it as pure literal as that would prevent them using ~,
+     * wildcards, etc.) The buffer produced by this function
+     * should be given to popen_in_dir() or system_in_dir() so
+     * that the current working directory is set correctly.
+     */
+    int ln = snprintf(buf, bufsz, "$SHELL -c \"%s", cmd);
+    /* NB that some snprintf() return -1 on overflow rather than the
+     * new C99 mandated 'number of chars that would have been written'
+     */
+    /* length checks here and inside the loop allow for the
+     * trailing ';', trailcmd, '"' and NUL
+     */
+    int trailln = strlen(trailcmd) + 3;
+    if (ln < 0 || ln + trailln > bufsz)
+       adios((char *)0, "arguments too long");
+    
+    cp = buf + ln;
+    
+    while (*++argp != (char *)0) {
+       ln = strlen(*argp);
+       /* +1 for leading space */
+       if (ln + trailln + 1 > bufsz - (cp-buf))
+           adios((char *)0, "arguments too long");
+       *cp++ = ' ';
+       memcpy(cp, *argp, ln+1);
+       cp += ln;
+    }
+    if (*trailcmd) {
+       *cp++ = ';';
+       strcpy(cp, trailcmd);
+       cp += trailln - 3;
+    }
+    *cp++ = '"';
+    *cp = 0;
+}
+
+/*
+ * Build a command line that causes the user's shell to list the file name
+ * arguments.  This handles and wildcard expansion, tilde expansion, etc.
+ */
+static void
+writelscmd(char *buf, int bufsz, char **argp)
+{
+    writesomecmd(buf, bufsz, "ls", "", argp);
+}
+
+/* Like system(), but run the command in directory dir.
+ * This assumes the program is single-threaded!
+ */
+static int
+system_in_dir(const char *dir, const char *cmd)
+{
+    char olddir[BUFSIZ];
+    int r;
+
+    /* ensure that $SHELL exists, as the cmd was written relying on
+       a non-blank $SHELL... */
+    setenv("SHELL","/bin/sh",0); /* don't overwrite */
+
+    if (getcwd(olddir, sizeof(olddir)) == 0)
+       adios("getcwd", "could not get working directory");
+    if (chdir(dir) != 0)
+       adios("chdir", "could not change working directory");
+    r = system(cmd);
+    if (chdir(olddir) != 0)
+       adios("chdir", "could not change working directory");
+    return r;
+}
+
+/* ditto for popen() */
+static FILE*
+popen_in_dir(const char *dir, const char *cmd, const char *type)
+{
+    char olddir[BUFSIZ];
+    FILE *f;
+
+    /* ensure that $SHELL exists, as the cmd was written relying on
+       a non-blank $SHELL... */
+    setenv("SHELL","/bin/sh",0); /* don't overwrite */
+    
+    if (getcwd(olddir, sizeof(olddir)) == 0)
+       adios("getcwd", "could not get working directory");
+    if (chdir(dir) != 0)
+       adios("chdir", "could not change working directory");
+    f = popen(cmd, type);
+    if (chdir(olddir) != 0)
+       adios("chdir", "could not change working directory");
+    return f;
+}
+
+
 /*
  * EDIT
  */
@@ -258,7 +652,7 @@ static char *edsave = NULL; /* the editor we used previously */
 
 static int
 editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
-         char *altmsg, char *cwd, int save_editor)
+         char *altmsg, char *cwd, int save_editor, int atfile)
 {
     int pid, status, vecp;
     char altpath[BUFSIZ], linkpath[BUFSIZ];
@@ -266,10 +660,7 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
     struct stat st;
 
 #ifdef HAVE_LSTAT
-    int        slinked;
-#if 0
-    int oumask;        /* PJS: for setting permissions on symlinks. */
-#endif
+    int        slinked = 0;
 #endif /* HAVE_LSTAT */
 
     /* Was there a previous edit session? */
@@ -290,7 +681,7 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
            *ed = defaulteditor;
     }
 
-    if (altmsg) {
+    if (altmsg && atfile) {
        if (mp == NULL || *altmsg == '/' || cwd == NULL)
            strncpy (altpath, altmsg, sizeof(altpath));
        else
@@ -299,20 +690,11 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
            strncpy (linkpath, LINK, sizeof(linkpath));
        else
            snprintf (linkpath, sizeof(linkpath), "%s/%s", cwd, LINK);
-    }
 
-    if (altmsg) {
        unlink (linkpath);
 #ifdef HAVE_LSTAT
        if (link (altpath, linkpath) == NOTOK) {
-#if 0
-           /* I don't think permission on symlinks matters /JLR */
-           oumask = umask(0044);       /* PJS: else symlinks are world 'r' */
-#endif
            symlink (altpath, linkpath);
-#if 0
-           umask(oumask);              /* PJS: else symlinks are world 'r' */
-#endif
            slinked = 1;
        } else {
            slinked = 0;
@@ -325,13 +707,13 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
     context_save ();   /* save the context file */
     fflush (stdout);
 
-    switch (pid = vfork ()) {
-       case NOTOK: 
+    switch (pid = vfork()) {
+       case NOTOK:
            advise ("fork", "unable to");
            status = NOTOK;
            break;
 
-       case OK: 
+       case OK:
            if (cwd)
                chdir (cwd);
            if (altmsg) {
@@ -353,15 +735,8 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
            perror (*ed);
            _exit (-1);
 
-       default: 
+       default:
            if ((status = pidwait (pid, NOTOK))) {
-#ifdef ATTVIBUG
-               if ((cp = r1bindex (*ed, '/'))
-                       && strcmp (cp, "vi") == 0
-                       && (status & 0x00ff) == 0)
-                   status = 0;
-               else {
-#endif
                if (((status & 0xff00) != 0xff00)
                    && (!reedit || (status & 0x00ff))) {
                    if (!use && (status & 0xff00) &&
@@ -373,9 +748,6 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
                }
                status = -2;    /* maybe "reedit ? -2 : -1"? */
                break;
-#ifdef ATTVIBUG
-               }
-#endif
            }
 
            reedit++;
@@ -409,7 +781,7 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
        edsave = getcpy (*ed);
 
     *ed = NULL;
-    if (altmsg)
+    if (altmsg && atfile)
        unlink (linkpath);
 
     return status;
@@ -459,16 +831,14 @@ sendfile (char **arg, char *file, int pushsw)
     /* Translate MIME composition file, if necessary */
     if ((cp = context_find ("automimeproc"))
            && (!strcmp (cp, "1"))
-           && !getenv ("NOMHNPROC")
            && check_draft (file)
            && (buildfile (NULL, file) == NOTOK))
        return 0;
 
     /* For backwards compatibility */
     if ((cp = context_find ("automhnproc"))
-           && !getenv ("NOMHNPROC")
            && check_draft (file)
-           && (i = editfile (&cp, NULL, file, NOUSE, NULL, NULL, NULL, 0)))
+           && (i = editfile (&cp, NULL, file, NOUSE, NULL, NULL, NULL, 0, 0)))
        return 0;
 
     /*
@@ -488,9 +858,9 @@ sendfile (char **arg, char *file, int pushsw)
     for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
        sleep (5);
     switch (child_id) {
-       case NOTOK: 
+       case NOTOK:
            advise (NULL, "unable to fork, so sending directly...");
-       case OK: 
+       case OK:
            vecp = 0;
            vec[vecp++] = invo_name;
            if (pushsw)
@@ -506,7 +876,7 @@ sendfile (char **arg, char *file, int pushsw)
            perror (sendproc);
            _exit (-1);
 
-       default: 
+       default:
            if (pidwait(child_id, OK) == 0)
                done (0);
            return 1;
@@ -532,8 +902,7 @@ buildfile (char **argp, char *file)
        while (argp[i])
            i++;
     }
-    if ((args = (char **) malloc((i + 2) * sizeof(char *))) == NULL)
-       adios (NULL, "unable to malloc memory");
+    args = (char **) mh_xmalloc((i + 2) * sizeof(char *));
 
     /*
      * For backward compatibility, we need to add -build
@@ -548,7 +917,7 @@ buildfile (char **argp, char *file)
        args[i++] = *argp++;
     args[i] = NULL;
 
-    i = editfile (&ed, args, file, NOUSE, NULL, NULL, NULL, 0);
+    i = editfile (&ed, args, file, NOUSE, NULL, NULL, NULL, 0, 0);
     free (args);
 
     return (i ? NOTOK : OK);
@@ -607,6 +976,18 @@ check_draft (char *msgnam)
 }
 
 
+#ifndef CYRUS_SASL
+# define SASLminc(a) (a)
+#else /* CYRUS_SASL */
+# define SASLminc(a)  0
+#endif /* CYRUS_SASL */
+
+#ifndef TLS_SUPPORT
+# define TLSminc(a)  (a)
+#else /* TLS_SUPPORT */
+# define TLSminc(a)   0
+#endif /* TLS_SUPPORT */
+
 static struct swit  sendswitches[] = {
 #define        ALIASW            0
     { "alias aliasfile", 0 },
@@ -671,7 +1052,7 @@ static struct swit  sendswitches[] = {
 #define        CLIESW           30
     { "client host", -6 },
 #define        SERVSW           31
-    { "server host", -6 },
+    { "server host", 6 },
 #define        SNOOPSW          32
     { "snoop", -5 },
 #define SDRFSW           33
@@ -680,6 +1061,32 @@ static struct swit  sendswitches[] = {
     { "draftmessage msg", -6 },
 #define SNDRFSW          35
     { "nodraftfolder", -3 },
+#define SASLSW           36
+    { "sasl", SASLminc(-4) },
+#define NOSASLSW         37
+    { "nosasl", SASLminc(-6) },
+#define SASLMXSSFSW      38
+    { "saslmaxssf", SASLminc(-10) },
+#define SASLMECHSW       39
+    { "saslmech", SASLminc(-5) },
+#define USERSW           40
+    { "user", SASLminc(-4) },
+#define SNDATTACHSW       41
+    { "attach file", 6 },
+#define SNDNOATTACHSW     42
+    { "noattach", 0 },
+#define SNDATTACHFORMAT   43
+    { "attachformat", 7 },
+#define PORTSW           44
+    { "port server-port-name/number", 4 },
+#define TLSSW            45
+    { "tls", TLSminc(-3) },
+#define NTLSSW            46
+    { "notls", TLSminc(-5) },
+#define MTSSW            47
+    { "mts smtp|sendmail/smtp|sendmail/pipe", 2 },
+#define MESSAGEIDSW      48
+    { "messageid localname|random", 2 },
     { NULL, 0 }
 };
 
@@ -704,13 +1111,13 @@ sendit (char *sp, char **arg, char *file, int pushed)
     char *cp, buf[BUFSIZ], **argp;
     char **arguments, *vec[MAXARGS];
     struct stat st;
+    char       *attach = NMH_ATTACH_HEADER;/* attachment header field name */
+    int                attachformat = 1;       /* mhbuild format specifier for
+                                          attachments */
 
 #ifndef        lint
     int        distsw = 0;
 #endif
-#ifdef UCI
-    FILE *fp;
-#endif
 
     /*
      * Make sure these are defined.  In particular, we need
@@ -758,17 +1165,27 @@ sendit (char *sp, char **arg, char *file, int pushed)
     vec[vecp++] = "-library";
     vec[vecp++] = getcpy (m_maildir (""));
 
+    if ((cp = context_find ("fileproc"))) {
+      vec[vecp++] = "-fileproc";
+      vec[vecp++] = cp;
+    }
+
+    if ((cp = context_find ("mhlproc"))) {
+      vec[vecp++] = "-mhlproc";
+      vec[vecp++] = cp;
+    }
+
     while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch (++cp, sendswitches)) {
-               case AMBIGSW: 
+               case AMBIGSW:
                    ambigsw (cp, sendswitches);
                    return;
-               case UNKWNSW: 
+               case UNKWNSW:
                    advise (NULL, "-%s unknown\n", cp);
                    return;
 
-               case SHELPSW: 
+               case SHELPSW:
                    snprintf (buf, sizeof(buf), "%s [switches]", sp);
                    print_help (buf, sendswitches, 1);
                    return;
@@ -776,34 +1193,34 @@ sendit (char *sp, char **arg, char *file, int pushed)
                    print_version (invo_name);
                    return;
 
-               case SPSHSW: 
+               case SPSHSW:
                    pushed++;
                    continue;
-               case NSPSHSW: 
+               case NSPSHSW:
                    pushed = 0;
                    continue;
 
-               case SPLITSW: 
+               case SPLITSW:
                    if (!(cp = *argp++) || sscanf (cp, "%d", &splitsw) != 1) {
                        advise (NULL, "missing argument to %s", argp[-2]);
                        return;
                    }
                    continue;
 
-               case UNIQSW: 
+               case UNIQSW:
                    unique++;
                    continue;
-               case NUNIQSW: 
+               case NUNIQSW:
                    unique = 0;
                    continue;
-               case FORWSW: 
+               case FORWSW:
                    forwsw++;
                    continue;
-               case NFORWSW: 
+               case NFORWSW:
                    forwsw = 0;
                    continue;
 
-               case VERBSW: 
+               case VERBSW:
                    verbsw++;
                    vec[vecp++] = --cp;
                    continue;
@@ -812,32 +1229,42 @@ sendit (char *sp, char **arg, char *file, int pushed)
                    vec[vecp++] = --cp;
                    continue;
 
-               case DEBUGSW: 
+               case DEBUGSW:
                    debugsw++;  /* fall */
-               case NFILTSW: 
-               case FRMTSW: 
-               case NFRMTSW: 
+               case NFILTSW:
+               case FRMTSW:
+               case NFRMTSW:
                case BITSTUFFSW:
                case NBITSTUFFSW:
-               case MIMESW: 
-               case NMIMESW: 
-               case MSGDSW: 
-               case NMSGDSW: 
-               case WATCSW: 
-               case NWATCSW: 
-               case MAILSW: 
-               case SAMLSW: 
-               case SSNDSW: 
-               case SOMLSW: 
-               case SNOOPSW: 
+               case MIMESW:
+               case NMIMESW:
+               case MSGDSW:
+               case NMSGDSW:
+               case WATCSW:
+               case NWATCSW:
+               case MAILSW:
+               case SAMLSW:
+               case SSNDSW:
+               case SOMLSW:
+               case SNOOPSW:
+               case SASLSW:
+               case NOSASLSW:
+               case TLSSW:
+               case NTLSSW:
                    vec[vecp++] = --cp;
                    continue;
 
-               case ALIASW: 
-               case FILTSW: 
-               case WIDTHSW: 
-               case CLIESW: 
-               case SERVSW: 
+               case ALIASW:
+               case FILTSW:
+               case WIDTHSW:
+               case CLIESW:
+               case SERVSW:
+               case SASLMXSSFSW:
+               case SASLMECHSW:
+               case USERSW:
+               case PORTSW:
+               case MTSSW:
+               case MESSAGEIDSW:
                    vec[vecp++] = --cp;
                    if (!(cp = *argp++) || *cp == '-') {
                        advise (NULL, "missing argument to %s", argp[-2]);
@@ -846,13 +1273,38 @@ sendit (char *sp, char **arg, char *file, int pushed)
                    vec[vecp++] = cp;
                    continue;
 
-               case SDRFSW: 
-               case SDRMSW: 
+               case SDRFSW:
+               case SDRMSW:
                    if (!(cp = *argp++) || *cp == '-') {
                        advise (NULL, "missing argument to %s", argp[-2]);
                        return;
                    }
-               case SNDRFSW: 
+               case SNDRFSW:
+                   continue;
+
+               case SNDATTACHSW:
+                   if (!(attach = *argp++) || *attach == '-') {
+                       advise (NULL, "missing argument to %s", argp[-2]);
+                       return;
+                   }
+                   continue;
+               case SNDNOATTACHSW:
+                   attach = NULL;
+                   continue;
+
+               case SNDATTACHFORMAT:
+                   if (! *argp || **argp == '-')
+                       adios (NULL, "missing argument to %s", argp[-1]);
+                   else {
+                       attachformat = atoi (*argp);
+                       if (attachformat < 0 ||
+                           attachformat > ATTACHFORMATS - 1) {
+                           advise (NULL, "unsupported attachformat %d",
+                                   attachformat);
+                           continue;
+                       }
+                   }
+                   ++argp;
                    continue;
            }
        }
@@ -874,18 +1326,6 @@ sendit (char *sp, char **arg, char *file, int pushed)
     if ((cp = getenv ("SIGNATURE")) == NULL || *cp == 0)
        if ((cp = context_find ("signature")) && *cp)
            m_putenv ("SIGNATURE", cp);
-#ifdef UCI
-       else {
-           snprintf (buf, sizeof(buf), "%s/.signature", mypath);
-           if ((fp = fopen (buf, "r")) != NULL
-               && fgets (buf, sizeof(buf), fp) != NULL) {
-                   fclose (fp);
-                   if (cp = strchr (buf, '\n'))
-                       *cp = 0;
-                   m_putenv ("SIGNATURE", buf);
-           }
-       }
-#endif /* UCI */
 
     if ((annotext = getenv ("mhannotate")) == NULL || *annotext == 0)
        annotext = NULL;
@@ -901,7 +1341,8 @@ sendit (char *sp, char **arg, char *file, int pushed)
 #endif /* not lint */
            && altmsg) {
        vec[vecp++] = "-dist";
-       distfile = getcpy (m_scratch (altmsg, invo_name));
+       distfile = getcpy (m_mktemp2(altmsg, invo_name, NULL, NULL));
+       unlink(distfile);
        if (link (altmsg, distfile) == NOTOK)
            adios (distfile, "unable to link %s to", altmsg);
     } else {
@@ -919,7 +1360,7 @@ sendit (char *sp, char **arg, char *file, int pushed)
     vec[0] = r1bindex (postproc, '/');
     closefds (3);
 
-    if (sendsbr (vec, vecp, file, &st, 1) == OK)
+    if (sendsbr (vec, vecp, file, &st, 1, attach, attachformat) == OK)
        done (0);
 }
 
@@ -937,12 +1378,12 @@ whomfile (char **arg, char *file)
     context_save ();   /* save the context file */
     fflush (stdout);
 
-    switch (pid = vfork ()) {
-       case NOTOK: 
+    switch (pid = vfork()) {
+       case NOTOK:
            advise ("fork", "unable to");
            return 1;
 
-       case OK: 
+       case OK:
            vecp = 0;
            vec[vecp++] = r1bindex (whomproc, '/');
            vec[vecp++] = file;
@@ -956,7 +1397,7 @@ whomfile (char **arg, char *file)
            perror (whomproc);
            _exit (-1);         /* NOTREACHED */
 
-       default: 
+       default:
            return (pidwait (pid, NOTOK) & 0377 ? 1 : 0);
     }
 }