]> diplodocus.org Git - nmh/blobdiff - uip/mhstoresbr.c
lock_file.c: close(2) file descriptor on failure, avoiding leak.
[nmh] / uip / mhstoresbr.c
index 89a2f27854fe4a1d89481f72a1e26a76eb5ab02d..0e9b94cd112d0e68f9eb25bf481d46a874889443 100644 (file)
@@ -1,6 +1,4 @@
-
-/*
- * mhstoresbr.c -- routines to save/store the contents of MIME messages
+/* mhstoresbr.c -- routines to save/store the contents of MIME messages
  *
  * This code is Copyright (c) 2002, by the authors of nmh.  See the
  * COPYRIGHT file in the root directory of the nmh distribution for
@@ -9,13 +7,17 @@
 
 #include <h/mh.h>
 #include <fcntl.h>
-#include <h/signals.h>
 #include <h/md5.h>
 #include <h/mts.h>
 #include <h/tws.h>
+#include <h/fmt_scan.h>
 #include <h/mime.h>
 #include <h/mhparse.h>
 #include <h/utils.h>
+#include "mhmisc.h"
+#include "mhshowsbr.h"
+#include "sbr/m_maildir.h"
+#include "sbr/m_mktemp.h"
 
 enum clobber_policy_t {
   NMH_CLOBBER_ALWAYS = 0,
@@ -25,7 +27,7 @@ enum clobber_policy_t {
   NMH_CLOBBER_NEVER
 };
 
-static enum clobber_policy_t clobber_policy (const char *);
+static enum clobber_policy_t clobber_policy (const char *) PURE;
 
 struct mhstoreinfo {
     CT *cts;                 /* Top-level list of contents to store. */
@@ -41,12 +43,13 @@ struct mhstoreinfo {
     enum clobber_policy_t clobber_policy;  /* -clobber selection */
 };
 
-typedef struct mhstoreinfo *mhstoreinfo_t;
+static bool use_param_as_filename(const char *p);
 
 mhstoreinfo_t
 mhstoreinfo_create (CT *ct, char *pwd, const char *csw, int asw, int vsw) {
-    mhstoreinfo_t info = mh_xmalloc (sizeof *info);
+    mhstoreinfo_t info;
 
+    NEW(info);
     info->cts = ct;
     info->cwd = pwd;
     info->autosw = asw;
@@ -61,6 +64,7 @@ mhstoreinfo_create (CT *ct, char *pwd, const char *csw, int asw, int vsw) {
 void
 mhstoreinfo_free (mhstoreinfo_t info) {
     free (info->cwd);
+    free (info->dir);
     free (info);
 }
 
@@ -77,19 +81,6 @@ mhstoreinfo_files_not_clobbered (const mhstoreinfo_t info) {
 typedef int (*qsort_comp) (const void *, const void *);
 
 
-/* mhmisc.c */
-int part_ok (CT, int);
-int type_ok (CT, int);
-void flush_errors (void);
-
-/* mhshowsbr.c */
-int show_content_aux (CT, int, int, char *, char *);
-
-/*
- * prototypes
- */
-void store_all_messages (mhstoreinfo_t);
-
 /*
  * static prototypes
  */
@@ -125,7 +116,7 @@ store_all_messages (mhstoreinfo_t info)
      * store any contents.
      */
     if ((cp = context_find (nmhstorage)) && *cp)
-       info->dir = getcpy (cp);
+       info->dir = mh_xstrdup(cp);
     else
        info->dir = getcpy (info->cwd);
 
@@ -184,6 +175,7 @@ store_switch (CT ct, mhstoreinfo_t info)
            }
 
        case CT_APPLICATION:
+       default:
            return store_application (ct, info);
 
        case CT_TEXT:
@@ -191,9 +183,6 @@ store_switch (CT ct, mhstoreinfo_t info)
        case CT_IMAGE:
        case CT_VIDEO:
            return store_generic (ct, info);
-
-       default:
-           adios (NULL, "unknown content type %d", ct->c_type);
     }
 
     return OK; /* NOT REACHED */
@@ -227,7 +216,6 @@ store_generic (CT ct, mhstoreinfo_t info)
 static int
 store_application (CT ct, mhstoreinfo_t info)
 {
-    char **ap, **ep;
     CI ci = &ct->c_ctinfo;
 
     /* Check if the content specifies a filename */
@@ -241,28 +229,23 @@ store_application (CT ct, mhstoreinfo_t info)
      */
     if (!ct->c_storeproc && ct->c_subtype == APPLICATION_OCTETS) {
        int tarP = 0, zP = 0, gzP = 0;
+       char *cp;
 
-       for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
-           /* check for "type=tar" attribute */
-           if (!strcasecmp (*ap, "type")) {
-               if (strcasecmp (*ep, "tar"))
-                   break;
-
+       if ((cp = get_param(ci->ci_first_pm, "type", ' ', 1))) {
+           if (strcasecmp (cp, "tar") == 0)
                tarP = 1;
-               continue;
-           }
+       }
 
-           /* check for "conversions=compress" attribute */
-           if ((!strcasecmp (*ap, "conversions") || !strcasecmp (*ap, "x-conversions"))
-               && (!strcasecmp (*ep, "compress") || !strcasecmp (*ep, "x-compress"))) {
+       /* check for "conversions=compress" attribute */
+       if ((cp = get_param(ci->ci_first_pm, "conversions", ' ', 1)) ||
+           (cp = get_param(ci->ci_first_pm, "x-conversions", ' ', 1))) {
+           if (strcasecmp (cp, "compress") == 0 ||
+                   strcasecmp (cp, "x-compress") == 0) {
                zP = 1;
-               continue;
            }
-           /* check for "conversions=gzip" attribute */
-           if ((!strcasecmp (*ap, "conversions") || !strcasecmp (*ap, "x-conversions"))
-               && (!strcasecmp (*ep, "gzip") || !strcasecmp (*ep, "x-gzip"))) {
+           if (strcasecmp (cp, "gzip") == 0 ||
+                   strcasecmp (cp, "x-gzip") == 0) {
                gzP = 1;
-               continue;
            }
        }
 
@@ -304,15 +287,14 @@ store_multi (CT ct, mhstoreinfo_t info)
     for (part = m->mp_parts; part; part = part->mp_next) {
        CT  p = part->mp_part;
 
-       if (part_ok (p, 1) && type_ok (p, 1)) {
+       if (part_ok (p) && type_ok (p, 1)) {
            if (ct->c_storage) {
                /* Support mhstore -outfile.  The MIME parser doesn't
                   load c_storage, so we know that p->c_storage is
                   NULL here. */
-               p->c_storage = ct->c_storage;
+               p->c_storage = mh_xstrdup(ct->c_storage);
            }
            result = store_switch (p, info);
-           p->c_storage = NULL;
 
            if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
                break;
@@ -359,13 +341,11 @@ store_partial (CT ct, mhstoreinfo_t info)
     }
 
     if (hi == 0) {
-       advise (NULL, "missing (at least) last part of multipart message");
+       inform("missing (at least) last part of multipart message");
        return NOTOK;
     }
 
-    if ((base = (CT *) calloc ((size_t) (i + 1), sizeof(*base))) == NULL)
-       adios (NULL, "out of memory");
-
+    base = mh_xcalloc(i + 1, sizeof *base);
     ctq = base;
     for (ctp = info->cts; *ctp; ctp++) {
        p = *ctp;
@@ -384,22 +364,21 @@ store_partial (CT ct, mhstoreinfo_t info)
     for (ctq = base; *ctq; ctq++) {
        p = *ctq;
        pm = (struct partial *) p->c_ctparams;
-       if (pm->pm_marked != cur) {
-           if (pm->pm_marked == cur - 1) {
-               admonish (NULL,
-                         "duplicate part %d of %d part multipart message",
-                         pm->pm_marked, hi);
-               continue;
-           }
+       if (pm->pm_marked == cur) {
+           cur++;
+            continue;
+        }
+
+        if (pm->pm_marked == cur - 1) {
+            inform("duplicate part %d of %d part multipart message, continuing...",
+                      pm->pm_marked, hi);
+            continue;
+        }
 
 missing_part:
-           advise (NULL,
-                   "missing %spart %d of %d part multipart message",
-                   cur != hi ? "(at least) " : "", cur, hi);
-           goto losing;
-       }
-        else
-           cur++;
+        inform("missing %spart %d of %d part multipart message",
+            cur != hi ? "(at least) " : "", cur, hi);
+        goto losing;
     }
     if (hi != --cur) {
        cur = hi;
@@ -415,7 +394,7 @@ missing_part:
     ct = *ctq++;
     if (store_content (ct, NULL, info) == NOTOK) {
 losing:
-       free ((char *) base);
+       free(base);
        return NOTOK;
     }
 
@@ -425,7 +404,7 @@ losing:
            goto losing;
     }
 
-    free ((char *) base);
+    free(base);
     return OK;
 }
 
@@ -451,16 +430,11 @@ store_external (CT ct, mhstoreinfo_t info)
     if (info->autosw) {
        char *cp;
 
-       if ((cp = e->eb_name)
-           && *cp != '/'
-           && *cp != '.'
-           && *cp != '|'
-           && *cp != '!'
-           && !strchr (cp, '%')) {
+       if ((cp = e->eb_name) && use_param_as_filename(cp)) {
            if (!ct->c_storeproc)
-               ct->c_storeproc = add (cp, NULL);
+               ct->c_storeproc = mh_xstrdup(cp);
            if (!p->c_storeproc)
-               p->c_storeproc = add (cp, NULL);
+               p->c_storeproc = mh_xstrdup(cp);
        }
     }
 
@@ -476,10 +450,9 @@ store_external (CT ct, mhstoreinfo_t info)
     if (ct->c_storage) {
        /* Support mhstore -outfile.  The MIME parser doesn't load
           c_storage, so we know that p->c_storage is NULL here. */
-       p->c_storage = ct->c_storage;
+       p->c_storage = mh_xstrdup(ct->c_storage);
     }
     result = store_switch (p, info);
-    p->c_storage = NULL;
 
     p->c_partno = NULL;
     return result;
@@ -554,11 +527,11 @@ store_content (CT ct, CT p, mhstoreinfo_t info)
        if (p) {
            appending = 1;
             if (! ct->c_storage) {
-               ct->c_storage = add (p->c_storage, NULL);
+               ct->c_storage = mh_xstrdup(FENDNULL(p->c_storage));
 
                /* record the folder name */
                if (p->c_folder) {
-                   ct->c_folder = add (p->c_folder, NULL);
+                   ct->c_folder = mh_xstrdup(p->c_folder);
                }
            }
            goto got_filename;
@@ -577,13 +550,9 @@ store_content (CT ct, CT p, mhstoreinfo_t info)
     if ((cp = ct->c_storeproc) == NULL || *cp == '\0') {
        CI ci = &ct->c_ctinfo;
 
-       snprintf (buffer, sizeof(buffer), "%s-store-%s/%s",
-               invo_name, ci->ci_type, ci->ci_subtype);
-       if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
-           snprintf (buffer, sizeof(buffer), "%s-store-%s", invo_name, ci->ci_type);
-           if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
-               cp = ct->c_type == CT_MESSAGE ? "+" : "%m%P.%s";
-           }
+       cp = context_find_by_type ("store", ci->ci_type, ci->ci_subtype);
+       if (cp == NULL) {
+           cp = ct->c_type == CT_MESSAGE ? "+" : "%m%P.%s";
        }
     }
 
@@ -600,7 +569,7 @@ store_content (CT ct, CT p, mhstoreinfo_t info)
                adios(NULL, "unable to create temporary file in %s",
                      get_temp_dir());
            }
-           ct->c_storage = add (tmpfilenam, NULL);
+           ct->c_storage = mh_xstrdup(tmpfilenam);
 
            /* Get the folder name */
            if (cp[1])
@@ -612,7 +581,7 @@ store_content (CT ct, CT p, mhstoreinfo_t info)
            create_folder(m_mailpath(folder), 0, exit);
 
            /* Record the folder name */
-           ct->c_folder = add (folder, NULL);
+           ct->c_folder = mh_xstrdup(folder);
 
            if (cp[1])
                free (folder);
@@ -631,16 +600,16 @@ store_content (CT ct, CT p, mhstoreinfo_t info)
         * content to standard input of a command and return.
         */
        if (buffer[0] == '|' || buffer[0] == '!')
-           return show_content_aux (ct, 1, 0, buffer + 1, info->dir);
+           return show_content_aux (ct, 0, buffer + 1, info->dir, NULL);
 
         /* record the filename */
-       if ((ct->c_storage = clobber_check (add (buffer, NULL), info)) ==
+       if ((ct->c_storage = clobber_check (mh_xstrdup(buffer), info)) ==
            NULL) {
            return NOTOK;
        }
     } else {
        /* The output filename was explicitly specified, so use it. */
-       if ((ct->c_storage = clobber_check (add (ct->c_storage, NULL), info)) ==
+       if ((ct->c_storage = clobber_check (ct->c_storage, info)) ==
            NULL) {
            return NOTOK;
        }
@@ -675,7 +644,7 @@ got_filename:
             if (first_partial)
                 fprintf (stderr, "reassembling partials ");
             if (last_partial)
-                fprintf (stderr, "%s", ct->c_file);
+                fputs(ct->c_file, stderr);
             else
                 fprintf (stderr, "%s,", ct->c_file);
         } else {
@@ -699,7 +668,7 @@ got_filename:
                 int cwdlen = strlen (info->cwd);
 
                 fprintf (stderr, " as file %s\n",
-                         strncmp (ct->c_storage, info->cwd, cwdlen)
+                         !has_prefix(ct->c_storage, info->cwd)
                          || ct->c_storage[cwdlen] != '/'
                          ? ct->c_storage : ct->c_storage + cwdlen + 1);
             }
@@ -734,7 +703,7 @@ output_content_file (CT ct, int appending)
        int cc, fd;
 
        if (!ct->c_ceopenfnx) {
-           advise (NULL, "don't know how to decode part %s of message %s",
+           inform("don't know how to decode part %s of message %s",
                    ct->c_partno, ct->c_file);
            return NOTOK;
        }
@@ -798,7 +767,9 @@ losing:
                    break;
 
                default:
-                   fwrite (buffer, sizeof(*buffer), cc, fp);
+                   if ((int) fwrite (buffer, sizeof(*buffer), cc, fp) < cc) {
+                       advise ("output_content_file", "fwrite");
+                   }
                    continue;
            }
            break;
@@ -858,7 +829,7 @@ losing:
        }
     }
 
-    while (fgets (buffer, sizeof(buffer) - 1, ct->c_fp)) {
+    while (fgets (buffer, sizeof buffer, ct->c_fp)) {
        if ((pos += strlen (buffer)) > last) {
            int diff;
 
@@ -927,13 +898,12 @@ output_content_folder (char *folder, char *filename)
     struct msgs *mp;
 
     /* Read the folder. */
-    if ((mp = folder_read (folder, 0))) {
-       /* Link file into folder */
-       msgnum = folder_addmsg (&mp, filename, 0, 0, 0, 0, (char *)0);
-    } else {
-       advise (NULL, "unable to read folder %s", folder);
+    if (!(mp = folder_read(folder, 0))) {
+       inform("unable to read folder %s", folder);
        return NOTOK;
     }
+    /* Link file into folder */
+    msgnum = folder_addmsg(&mp, filename, 0, 0, 0, 0, NULL);
 
     /* free folder structure */
     folder_free (mp);
@@ -976,7 +946,9 @@ parse_format_string (CT ct, char *cp, char *buffer, int buflen, char *dir)
      * appropriate directory.
      */
     if (*cp != '/' && *cp != '|' && *cp != '!') {
-       snprintf (bp, buflen, "%s/", dir[1] ? dir : "");
+        if (!strcmp(dir, "/"))
+            dir = ""; /* Don't start with "//". */
+       snprintf (bp, buflen, "%s/", dir);
        len = strlen (bp);
        bp += len;
        buflen -= len;
@@ -997,13 +969,14 @@ parse_format_string (CT ct, char *cp, char *buffer, int buflen, char *dir)
                        *bp = '\0';
                        buflen--;
                        continue;
-                   } else {
-                       char **ap, **ep;
+                   }
+                    {
+                       PM pm;
                        char *s = "";
 
-                       for (ap = ci->ci_attrs, ep = ci->ci_values;
-                                *ap; ap++, ep++) {
-                           snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
+                       for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
+                           snprintf (bp, buflen, "%s%s=\"%s\"", s,
+                                     pm->pm_name, get_param_value(pm, '?'));
                            len = strlen (bp);
                            bp += len;
                            buflen -= len;
@@ -1024,7 +997,7 @@ parse_format_string (CT ct, char *cp, char *buffer, int buflen, char *dir)
                    break;
 
                case 'p':
-                   /* insert part number withouth leading dot */
+                   /* insert part number without leading dot */
                    if (ct->c_partno)
                        strncpy (bp, ct->c_partno, buflen);
                    break;
@@ -1075,7 +1048,7 @@ raw:
 static void
 get_storeproc (CT ct)
 {
-    char **ap, **ep, *cp;
+    char *cp;
     CI ci;
 
     /*
@@ -1091,29 +1064,13 @@ get_storeproc (CT ct)
      * use that (RFC-2183).
      */
     if (ct->c_dispo) {
-       char *cp = strchr (ct->c_dispo, ';');
-       CI ci = calloc (1, sizeof *ci);
-       int status;
-       int found_filename = 0;
-
-       if (cp  &&  parse_header_attrs (ct->c_file, strlen (invo_name) + 2, &cp,
-                                        ci, &status) == OK) {
-           for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
-               if (! strcasecmp (*ap, "filename")
-                   && *(cp = *ep) != '/'
-                   && *cp != '.'
-                   && *cp != '|'
-                   && *cp != '!'
-                   && !strchr (cp, '%')) {
-                   ct->c_storeproc = add (cp, NULL);
-                   found_filename = 1;
-               }
-               free (*ap);
-           }
+       if ((cp = get_param(ct->c_dispo_first, "filename", '_', 0)) &&
+            use_param_as_filename(cp)) {
+               ct->c_storeproc = mh_xstrdup(cp);
+               free(cp);
+               return;
        }
-
-       free (ci);
-       if (found_filename) return;
+        free(cp);
     }
 
     /*
@@ -1122,17 +1079,12 @@ get_storeproc (CT ct)
      * the storeproc.
      */
     ci = &ct->c_ctinfo;
-    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
-       if (! strcasecmp (*ap, "name")
-           && *(cp = *ep) != '/'
-           && *cp != '.'
-           && *cp != '|'
-           && *cp != '!'
-           && !strchr (cp, '%')) {
-           ct->c_storeproc = add (cp, NULL);
-           return;
-       }
+    if ((cp = get_param(ci->ci_first_pm, "name", '_', 0)) &&
+        use_param_as_filename(cp)) {
+           ct->c_storeproc = mh_xstrdup(cp);
+
     }
+    free(cp);
 }
 
 
@@ -1172,19 +1124,22 @@ static
 enum clobber_policy_t
 clobber_policy (const char *value) {
   if (value == NULL  ||  ! strcasecmp (value, "always")) {
-  } else if (! strcasecmp (value, "auto")) {
+    return NMH_CLOBBER_ALWAYS;
+  }
+  if (! strcasecmp (value, "auto")) {
     return NMH_CLOBBER_AUTO;
-  } else if (! strcasecmp (value, "suffix")) {
+  }
+  if (! strcasecmp (value, "suffix")) {
     return NMH_CLOBBER_SUFFIX;
-  } else if (! strcasecmp (value, "ask")) {
+  }
+  if (! strcasecmp (value, "ask")) {
     return NMH_CLOBBER_ASK;
-  } else if (! strcasecmp (value, "never")) {
+  }
+  if (! strcasecmp (value, "never")) {
     return NMH_CLOBBER_NEVER;
-  } else {
-    adios (NULL, "invalid argument, %s, to clobber", value);
   }
 
-  return NMH_CLOBBER_ALWAYS;
+  adios (NULL, "invalid argument, %s, to clobber", value);
 }
 
 
@@ -1219,7 +1174,7 @@ next_version (char *file, enum clobber_policy_t clobber_policy) {
 
       default:
         /* Should never get here. */
-        advise (NULL, "will not overwrite %s, invalid clobber policy", buffer);
+        inform("will not overwrite %s, invalid clobber policy", buffer);
         free (buffer);
         return NULL;
     }
@@ -1240,7 +1195,7 @@ next_version (char *file, enum clobber_policy_t clobber_policy) {
   free (file);
 
   if (version >= max_versions) {
-    advise (NULL, "will not overwrite %s, too many versions", buffer);
+    inform("will not overwrite %s, too many versions", buffer);
     free (buffer);
     buffer = NULL;
   }
@@ -1264,17 +1219,23 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
   char *cwd = NULL;
   int check_again;
 
+  if (! strcmp (original_file, "-")) {
+      return original_file;
+  }
+
   if (info->clobber_policy == NMH_CLOBBER_ASK) {
     /* Save cwd for possible use in loop below. */
     char *slash;
 
-    cwd = add (original_file, NULL);
+    cwd = mh_xstrdup(original_file);
     slash = strrchr (cwd, '/');
 
     if (slash) {
       *slash = '\0';
     } else {
-      /* original_file wasn't a full path, which shouldn't happen. */
+      /* original_file isn't a full path, which should only happen if
+         it is -. */
+      free (cwd);
       cwd = NULL;
     }
   }
@@ -1312,11 +1273,11 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
           if (isatty (fileno (stdin))) {
             char *prompt =
               concat ("Overwrite \"", file, "\" [y/n/rename]? ", NULL);
-            ans = getans (prompt, answer);
+            ans = read_switch_multiword (prompt, answer);
             free (prompt);
           } else {
             /* Overwrite, that's what nmh used to do.  And warn. */
-            advise (NULL, "-clobber ask but no tty, so overwrite %s", file);
+            inform("-clobber ask but no tty, so overwrite %s", file);
             break;
           }
 
@@ -1336,20 +1297,17 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
                 file = NULL;
                 ++info->files_not_clobbered;
               } else {
-                char *newline = strchr (buf, '\n');
-                if (newline) {
-                  *newline = '\0';
-                }
+                trim_suffix_c(buf, '\n');
               }
 
               free (file);
 
               if (buf[0] == '/') {
                 /* Full path, use it. */
-                file = add (buf, NULL);
+                file = mh_xstrdup(buf);
               } else {
                 /* Relative path. */
-                file = cwd  ?  concat (cwd, "/", buf, NULL)  :  add (buf, NULL);
+                file = cwd  ?  concat (cwd, "/", buf, NULL)  :  mh_xstrdup(buf);
               }
 
               check_again = 1;
@@ -1363,7 +1321,7 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
         if (stat (file, &st) == OK) {
           /* Keep count of files that would have been clobbered,
              and return that as process exit status. */
-          advise (NULL, "will not overwrite %s with -clobber never", file);
+          inform("will not overwrite %s with -clobber never", file);
           free (file);
           file = NULL;
           ++info->files_not_clobbered;
@@ -1374,12 +1332,17 @@ clobber_check (char *original_file, mhstoreinfo_t info) {
     original_file = file;
   } while (check_again);
 
-  if (cwd) {
-    free (cwd);
-  }
+  free (cwd);
 
   return file;
 }
 
+static bool use_param_as_filename(const char *p)
+{
+    /* Preserve result of original test that considered an empty string
+     * OK. */
+    return !*p || (!strchr("/.|!", *p) && !strchr(p, '%'));
+}
+
 /* -clobber support */
 /******************************************************************************/