Reworked LINK, now named `altmsglink', similar to BACKUP_PREFIX.
[mmh] / uip / whatnowsbr.c
index 1935384..f5f78d7 100644 (file)
@@ -2,8 +2,6 @@
 /*
  * 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.
@@ -44,6 +42,7 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <h/mime.h>
+#include <h/utils.h>
 
 static struct swit whatnowswitches[] = {
 #define        DFOLDSW                 0
@@ -119,6 +118,11 @@ 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 *);
@@ -242,7 +246,7 @@ WhatNow (int argc, char **argv)
     snprintf (prompt, sizeof(prompt), myprompt, invo_name);
     for (;;) {
        if (!(argp = getans (prompt, aleqs))) {
-           unlink (LINK);
+           unlink (altmsglink);
            done (1);
        }
        switch (smatch (*argp, aleqs)) {
@@ -318,16 +322,13 @@ WhatNow (int argc, char **argv)
             *  is accustomed to.  Read back the absolute path.
             */
 
-           if (*++argp == (char *)0) {
+           if (*(argp+1) == (char *)0) {
                (void)sprintf(buf, "$SHELL -c \"cd;pwd\"");
            }
-           else if (strlen(*argp) >= BUFSIZ) {
-               adios((char *)0, "arguments too long");
-           }
            else {
-               (void)sprintf(buf, "$SHELL -c \"cd %s;cd %s;pwd\"", cwd, *argp);
+               writesomecmd(buf, BUFSIZ, "cd", "pwd", argp);
            }
-           if ((f = popen(buf, "r")) != (FILE *)0) {
+           if ((f = popen_in_dir(cwd, buf, "r")) != (FILE *)0) {
                fgets(cwd, sizeof (cwd), f);
 
                if (strchr(cwd, '\n') != (char *)0)
@@ -352,18 +353,8 @@ WhatNow (int argc, char **argv)
             *  Use the user's shell so that we can take advantage of any
             *  syntax that the user is accustomed to.
             */
-
-           cp = buf + sprintf(buf, "$SHELL -c \" cd %s;ls", cwd);
-
-           while (*++argp != (char *)0) {
-               if (cp + strlen(*argp) + 2 >= buf + BUFSIZ)
-                   adios((char *)0, "arguments too long");
-
-               cp += sprintf(cp, " %s", *argp);
-           }
-
-           (void)strcat(cp, "\"");
-           (void)system(buf);
+           writelscmd(buf, sizeof(buf), argp);
+           (void)system_in_dir(cwd, buf);
            break;
 
        case ALISTCMDSW:
@@ -418,21 +409,16 @@ WhatNow (int argc, char **argv)
                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.
             */
-
-           cp = buf + sprintf(buf, "$SHELL -c \" cd %s;ls", cwd);
-
-           while (*++argp != (char *)0) {
-               if (cp + strlen(*argp) + 3 >= buf + BUFSIZ)
-                   adios((char *)0, "arguments too long");
-
-               cp += sprintf(cp, " %s", *argp);
-           }
-
-           (void)strcat(cp, "\"");
+           writelscmd(buf, sizeof(buf), argp);
 
            /*
             *  Read back the response from the shell, which contains a number of lines
@@ -442,7 +428,7 @@ WhatNow (int argc, char **argv)
             *  draft.
             */
 
-           if ((f = popen(buf, "r")) != (FILE *)0) {
+           if ((f = popen_in_dir(cwd, buf, "r")) != (FILE *)0) {
                while (fgets(shell, sizeof (shell), f) != (char *)0) {
                    *(strchr(shell, '\n')) = '\0';
 
@@ -517,35 +503,19 @@ WhatNow (int argc, char **argv)
             *  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.
             */
-
-           else {
-               for (arguments = argp + 1; *arguments != (char *)0; arguments++) {
-                   if (**arguments == '/') {
-                       if (strlen(*arguments) + sizeof ("$SHELL -c \"ls \"") >= sizeof (buf))
-                           adios((char *)0, "arguments too long");
-
-                       (void)sprintf(buf, "$SHELL -c \"ls %s\"", *arguments);
-                   }
-                   else {
-                       if (strlen(cwd) + strlen(*arguments) + sizeof ("$SHELL -c \" cd ; ls \"") >= sizeof (buf))
-                           adios((char *)0, "arguments too long");
-
-                       (void)sprintf(buf, "$SHELL -c \" cd %s;ls %s\"", cwd, *arguments);
-                   }
-
-                   if ((f = popen(buf, "r")) != (FILE *)0) {
-                       while (fgets(shell, sizeof (cwd), 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");
-                   }
+           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;
@@ -559,6 +529,96 @@ 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;
+    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;
+    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
  */
@@ -577,7 +637,7 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
     struct stat st;
 
 #ifdef HAVE_LSTAT
-    int        slinked;
+    int        slinked = 0;
 #if 0
     int oumask;        /* PJS: for setting permissions on symlinks. */
 #endif
@@ -607,9 +667,9 @@ editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
        else
            snprintf (altpath, sizeof(altpath), "%s/%s", mp->foldpath, altmsg);
        if (cwd == NULL)
-           strncpy (linkpath, LINK, sizeof(linkpath));
+           strncpy (linkpath, altmsglink, sizeof(linkpath));
        else
-           snprintf (linkpath, sizeof(linkpath), "%s/%s", cwd, LINK);
+           snprintf (linkpath, sizeof(linkpath), "%s/%s", cwd, altmsglink);
     }
 
     if (altmsg) {
@@ -843,8 +903,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
@@ -918,12 +977,6 @@ check_draft (char *msgnam)
 }
 
 
-#ifndef CYRUS_SASL
-# define SASLminc(a) (a)
-#else /* CYRUS_SASL */
-# define SASLminc(a)  0
-#endif /* CYRUS_SASL */
-
 static struct swit  sendswitches[] = {
 #define        ALIASW            0
     { "alias aliasfile", 0 },
@@ -988,7 +1041,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
@@ -997,14 +1050,12 @@ static struct swit  sendswitches[] = {
     { "draftmessage msg", -6 },
 #define SNDRFSW          35
     { "nodraftfolder", -3 },
-#define SASLSW           36
-    { "sasl", SASLminc(-4) },
-#define SASLMECHSW       37
-    { "saslmech", SASLminc(-5) },
-#define USERSW           38
-    { "user", SASLminc(-4) },
 #define SNDATTACHSW       39
     { "attach file", 6 },
+#define SNDATTACHFORMAT   40
+    { "attachformat", 7 },
+#define PORTSW           41
+    { "port server-port-name/number", 4 },
     { NULL, 0 }
 };
 
@@ -1030,6 +1081,8 @@ sendit (char *sp, char **arg, char *file, int pushed)
     char **arguments, *vec[MAXARGS];
     struct stat st;
     char       *attach = (char *)0;    /* attachment header field name */
+    int                attachformat = 0;       /* mhbuild format specifier for
+                                          attachments */
 
 #ifndef        lint
     int        distsw = 0;
@@ -1156,7 +1209,6 @@ sendit (char *sp, char **arg, char *file, int pushed)
                case SSNDSW:
                case SOMLSW:
                case SNOOPSW:
-               case SASLSW:
                    vec[vecp++] = --cp;
                    continue;
 
@@ -1165,8 +1217,7 @@ sendit (char *sp, char **arg, char *file, int pushed)
                case WIDTHSW:
                case CLIESW:
                case SERVSW:
-               case SASLMECHSW:
-               case USERSW:
+               case PORTSW:
                    vec[vecp++] = --cp;
                    if (!(cp = *argp++) || *cp == '-') {
                        advise (NULL, "missing argument to %s", argp[-2]);
@@ -1190,6 +1241,21 @@ sendit (char *sp, char **arg, char *file, int pushed)
                        return;
                    }
                    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;
            }
        }
        advise (NULL, "usage: %s [switches]", sp);
@@ -1237,7 +1303,7 @@ 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));
        if (link (altmsg, distfile) == NOTOK)
            adios (distfile, "unable to link %s to", altmsg);
     } else {
@@ -1255,7 +1321,7 @@ sendit (char *sp, char **arg, char *file, int pushed)
     vec[0] = r1bindex (postproc, '/');
     closefds (3);
 
-    if (sendsbr (vec, vecp, file, &st, 1, attach) == OK)
+    if (sendsbr (vec, vecp, file, &st, 1, attach, attachformat) == OK)
        done (0);
 }