]> diplodocus.org Git - nmh/blobdiff - uip/mhshowsbr.c
Instead of printing PostScript attachments, by default, from mhshow,
[nmh] / uip / mhshowsbr.c
index 6bfc0bcd0de2646d8ba65226c3c8ef3a61f5eec5..4bab75cafeaf164ec1802d4672cb060877aaefe5 100644 (file)
 #include <fcntl.h>
 #include <h/signals.h>
 #include <h/md5.h>
-#include <errno.h>
-#include <h/m_setjmp.h>
-#include <signal.h>
+#include <setjmp.h>
 #include <h/mts.h>
 #include <h/tws.h>
 #include <h/mime.h>
 #include <h/mhparse.h>
 #include <h/utils.h>
-#include <sys/wait.h>
+#ifdef HAVE_ICONV
+#   include <iconv.h>
+#endif /* ! HAVE_ICONV */
 
 extern int debugsw;
 
@@ -69,6 +69,7 @@ static int show_multi_aux (CT, int, int, char *);
 static int show_message_rfc822 (CT, int, int);
 static int show_partial (CT, int, int);
 static int show_external (CT, int, int);
+static int convert_content_charset (CT, char **);
 static void intrser (int);
 
 
@@ -168,30 +169,30 @@ DisplayMsgHeader (CT ct, char *form)
 {
     pid_t child_id;
     int i, vecp;
-    char *vec[8];
+    char **vec;
+    char *file;
 
-    vecp = 0;
-    vec[vecp++] = r1bindex (mhlproc, '/');
-    vec[vecp++] = "-form";
-    vec[vecp++] = form;
-    vec[vecp++] = "-nobody";
-    vec[vecp++] = ct->c_file;
+    vec = argsplit(mhlproc, &file, &vecp);
+    vec[vecp++] = getcpy("-form");
+    vec[vecp++] = getcpy(form);
+    vec[vecp++] = getcpy("-nobody");
+    vec[vecp++] = getcpy(ct->c_file);
 
     /*
      * If we've specified -(no)moreproc,
      * then just pass that along.
      */
     if (nomore) {
-       vec[vecp++] = "-nomoreproc";
+       vec[vecp++] = getcpy("-nomoreproc");
     } else if (progsw) {
-       vec[vecp++] = "-moreproc";
-       vec[vecp++] = progsw;
+       vec[vecp++] = getcpy("-moreproc");
+       vec[vecp++] = getcpy(progsw);
     }
     vec[vecp] = NULL;
 
     fflush (stdout);
 
-    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
        sleep (5);
 
     switch (child_id) {
@@ -200,7 +201,7 @@ DisplayMsgHeader (CT ct, char *form)
        /* NOTREACHED */
 
     case OK:
-       execvp (mhlproc, vec);
+       execvp (file, vec);
        fprintf (stderr, "unable to exec ");
        perror (mhlproc);
        _exit (-1);
@@ -210,6 +211,8 @@ DisplayMsgHeader (CT ct, char *form)
        xpid = -child_id;
        break;
     }
+
+    arglist_free(file, vec);
 }
 
 
@@ -224,39 +227,31 @@ show_switch (CT ct, int serial, int alternate)
     switch (ct->c_type) {
        case CT_MULTIPART:
            return show_multi (ct, serial, alternate);
-           break;
 
        case CT_MESSAGE:
            switch (ct->c_subtype) {
                case MESSAGE_PARTIAL:
                    return show_partial (ct, serial, alternate);
-                   break;
 
                case MESSAGE_EXTERNAL:
                    return show_external (ct, serial, alternate);
-                   break;
 
                case MESSAGE_RFC822:
                default:
                    return show_message_rfc822 (ct, serial, alternate);
-                   break;
            }
-           break;
 
        case CT_TEXT:
            return show_text (ct, serial, alternate);
-           break;
 
        case CT_AUDIO:
        case CT_IMAGE:
        case CT_VIDEO:
        case CT_APPLICATION:
            return show_content (ct, serial, alternate);
-           break;
 
        default:
            adios (NULL, "unknown content type %d", ct->c_type);
-           break;
     }
 
     return 0;  /* NOT REACHED */
@@ -273,13 +268,13 @@ show_content (CT ct, int serial, int alternate)
     char *cp, buffer[BUFSIZ];
     CI ci = &ct->c_ctinfo;
 
-    /* Check for mhn-show-type/subtype */
+    /* Check for invo_name-show-type/subtype */
     snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
                invo_name, ci->ci_type, ci->ci_subtype);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_content_aux (ct, serial, alternate, cp, NULL);
 
-    /* Check for mhn-show-type */
+    /* Check for invo_name-show-type */
     snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_content_aux (ct, serial, alternate, cp, NULL);
@@ -319,7 +314,21 @@ show_content_aux (CT ct, int serial, int alternate, char *cp, char *cracked)
        return NOTOK;
     if (ct->c_showproc && !strcmp (ct->c_showproc, "true"))
        return (alternate ? DONE : OK);
-    
+
+    if (! strcmp(invo_name, "mhshow")  &&
+        ct->c_type == CT_TEXT  &&  ct->c_subtype == TEXT_PLAIN) {
+        /* This has to be done after calling c_ceopenfnx, so
+           unfortunately the type checks are necessary without
+           some code rearrangement.  And to make this really ugly,
+           only do it in mhshow, not mhfixmsg, mhn, or mhstore. */
+        if (convert_content_charset (ct, &file) != OK) {
+            admonish (NULL, "unable to convert character set%s to %s",
+                      ct->c_partno  ?  "of part "  :  "",
+                      ct->c_partno  ?  ct->c_partno  :  "",
+                      content_charset (ct));
+        }
+    }
+
     xlist  = 0;
     xpause = 0;
     xstdin = 0;
@@ -498,7 +507,7 @@ show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer
     pid_t child_id;
     int i;
     char *vec[4], exec[BUFSIZ + sizeof "exec "];
-    
+
     if (debugsw || cracked) {
        fflush (stdout);
 
@@ -529,13 +538,13 @@ show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer
 
        if (xpause && isatty (fileno (stdout))) {
            int intr;
+           SIGNAL_HANDLER istat;
 
            if (SOprintf ("Press <return> to show content..."))
                printf ("Press <return> to show content...");
-           SIGNAL_HANDLER istat;
 
            istat = SIGNAL (SIGINT, intrser);
-           if ((intr = m_sigsetjmp (intrenv, 1)) == OK) {
+           if ((intr = sigsetjmp (intrenv, 1)) == OK) {
                fflush (stdout);
                prompt[0] = 0;
                read (fileno (stdout), prompt, sizeof(prompt));
@@ -558,7 +567,7 @@ show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer
 
     fflush (stdout);
 
-    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
        sleep (5);
     switch (child_id) {
        case NOTOK:
@@ -604,13 +613,13 @@ show_text (CT ct, int serial, int alternate)
     char *cp, buffer[BUFSIZ];
     CI ci = &ct->c_ctinfo;
 
-    /* Check for mhn-show-type/subtype */
+    /* Check for invo_name-show-type/subtype */
     snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
                invo_name, ci->ci_type, ci->ci_subtype);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_content_aux (ct, serial, alternate, cp, NULL);
 
-    /* Check for mhn-show-type */
+    /* Check for invo_name-show-type */
     snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_content_aux (ct, serial, alternate, cp, NULL);
@@ -621,7 +630,7 @@ show_text (CT ct, int serial, int alternate)
      */
     if (!alternate || ct->c_subtype == TEXT_PLAIN) {
        snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
-               moreproc && *moreproc ? moreproc : "more");
+               moreproc && *moreproc ? moreproc : DEFAULT_PAGER);
        cp = (ct->c_showproc = add (buffer, NULL));
        return show_content_aux (ct, serial, alternate, cp, NULL);
     }
@@ -640,13 +649,13 @@ show_multi (CT ct, int serial, int alternate)
     char *cp, buffer[BUFSIZ];
     CI ci = &ct->c_ctinfo;
 
-    /* Check for mhn-show-type/subtype */
+    /* Check for invo_name-show-type/subtype */
     snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
                invo_name, ci->ci_type, ci->ci_subtype);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_multi_aux (ct, serial, alternate, cp);
 
-    /* Check for mhn-show-type */
+    /* Check for invo_name-show-type */
     snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_multi_aux (ct, serial, alternate, cp);
@@ -655,14 +664,11 @@ show_multi (CT ct, int serial, int alternate)
        return show_multi_aux (ct, serial, alternate, cp);
 
     /*
-     * Use default method to display this multipart content
-     * if it is not a (nested) part of a multipart/alternative,
-     * or if it is one of the known subtypes of multipart.
+     * Use default method to display this multipart content.  Even
+     * unknown types are displayable, since they're treated as mixed
+     * per RFC 2046.
      */
-    if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
-       return show_multi_internal (ct, serial, alternate);
-
-    return NOTOK;
+    return show_multi_internal (ct, serial, alternate);
 }
 
 
@@ -710,7 +716,7 @@ show_multi_internal (CT ct, int serial, int alternate)
 
 /*
  * alternate   -> we are a part inside an multipart/alternative
- * alternating -> we are a multipart/alternative 
+ * alternating -> we are a multipart/alternative
  */
 
     result = alternate ? NOTOK : OK;
@@ -893,7 +899,7 @@ show_multi_aux (CT ct, int serial, int alternate, char *cp)
                /* insert filename(s) containing content */
            {
                char *s = "";
-                       
+
                for (part = m->mp_parts; part; part = part->mp_next) {
                    p = part->mp_part;
 
@@ -980,7 +986,7 @@ raw:
     }
 
     if (buflen <= 0 ||
-        (ct->c_termproc && (size_t) buflen <= strlen(ct->c_termproc))) {
+        (ct->c_termproc && buflen <= (ssize_t) strlen(ct->c_termproc))) {
        /* content_error would provide a more useful error message
         * here, except that if we got overrun, it probably would
         * too.
@@ -1012,13 +1018,13 @@ show_message_rfc822 (CT ct, int serial, int alternate)
     char *cp, buffer[BUFSIZ];
     CI ci = &ct->c_ctinfo;
 
-    /* Check for mhn-show-type/subtype */
+    /* Check for invo_name-show-type/subtype */
     snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
                invo_name, ci->ci_type, ci->ci_subtype);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_content_aux (ct, serial, alternate, cp, NULL);
 
-    /* Check for mhn-show-type */
+    /* Check for invo_name-show-type */
     snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
     if ((cp = context_find (buffer)) && *cp != '\0')
        return show_content_aux (ct, serial, alternate, cp, NULL);
@@ -1072,11 +1078,180 @@ show_external (CT ct, int serial, int alternate)
        return OK;
 
     return show_switch (p, serial, alternate);
+}
 
-#if 0
-    content_error (NULL, p, "don't know how to display content");
-    return NOTOK;
-#endif
+
+int
+convert_charset (CT ct, char *dest_charset, int *message_mods) {
+    char *src_charset = content_charset (ct);
+    int status = OK;
+
+    /* norm_charmap() is case sensitive. */
+    char *src_charset_u = upcase (src_charset);
+    char *dest_charset_u = upcase (dest_charset);
+    int different_charsets =
+        strcmp (norm_charmap (src_charset), norm_charmap (dest_charset));
+
+    free (dest_charset_u);
+    free (src_charset_u);
+
+    if (different_charsets) {
+#ifdef HAVE_ICONV
+        iconv_t conv_desc = NULL;
+        char *dest;
+        int fd = -1;
+        char **file = NULL;
+        FILE **fp = NULL;
+        size_t begin;
+        size_t end;
+        int opened_input_file = 0;
+        char src_buffer[BUFSIZ];
+        HF hf;
+        char *tempfile;
+
+        if ((conv_desc = iconv_open (dest_charset, src_charset)) ==
+            (iconv_t) -1) {
+            advise (NULL, "Can't convert %s to %s", src_charset, dest_charset);
+            return NOTOK;
+        }
+
+        if ((tempfile = m_mktemp2 (NULL, invo_name, &fd, NULL)) == NULL) {
+            adios (NULL, "unable to create temporary file in %s",
+                   get_temp_dir());
+        }
+        dest = add (tempfile, NULL);
+
+        if (ct->c_cefile.ce_file) {
+            file = &ct->c_cefile.ce_file;
+            fp = &ct->c_cefile.ce_fp;
+            begin = end = 0;
+        } else if (ct->c_file) {
+            file = &ct->c_file;
+            fp = &ct->c_fp;
+            begin = (size_t) ct->c_begin;
+            end = (size_t) ct->c_end;
+        } /* else no input file: shouldn't happen */
+
+        if (file  &&  *file  &&  fp) {
+            if (! *fp) {
+                if ((*fp = fopen (*file, "r")) == NULL) {
+                    advise (*file, "unable to open for reading");
+                    status = NOTOK;
+                } else {
+                    opened_input_file = 1;
+                }
+            }
+        }
+
+        if (fp  &&  *fp) {
+            size_t inbytes;
+            size_t bytes_to_read =
+                end > 0 && end > begin  ?  end - begin  :  sizeof src_buffer;
+
+            fseeko (*fp, begin, SEEK_SET);
+            while ((inbytes = fread (src_buffer, 1,
+                                     min (bytes_to_read, sizeof src_buffer),
+                                     *fp)) > 0) {
+                char dest_buffer[BUFSIZ];
+                ICONV_CONST char *ib = src_buffer;
+                char *ob = dest_buffer;
+                size_t outbytes = sizeof dest_buffer;
+                size_t outbytes_before = outbytes;
+
+                if (end > 0) bytes_to_read -= inbytes;
+
+                if (iconv (conv_desc, &ib, &inbytes, &ob, &outbytes) ==
+                    (size_t) -1) {
+                    status = NOTOK;
+                    break;
+                } else {
+                    write (fd, dest_buffer, outbytes_before - outbytes);
+                }
+            }
+
+            if (opened_input_file) {
+                fclose (*fp);
+                *fp = NULL;
+            }
+        }
+
+        iconv_close (conv_desc);
+        close (fd);
+
+        if (status == OK) {
+            /* Replace the decoded file with the converted one. */
+            if (ct->c_cefile.ce_file) {
+                if (ct->c_cefile.ce_unlink) {
+                    (void) m_unlink (ct->c_cefile.ce_file);
+                }
+                free (ct->c_cefile.ce_file);
+            }
+            ct->c_cefile.ce_file = dest;
+            ct->c_cefile.ce_unlink = 1;
+
+            ++*message_mods;
+
+            /* Update ci_attrs. */
+            src_charset = dest_charset;
+
+            /* Update ct->c_ctline. */
+            if (ct->c_ctline) {
+                char *ctline =
+                    update_attr (ct->c_ctline, "charset=", dest_charset);
+
+                free (ct->c_ctline);
+                ct->c_ctline = ctline;
+            } /* else no CT line, which is odd */
+
+            /* Update Content-Type header field. */
+            for (hf = ct->c_first_hf; hf; hf = hf->next) {
+                if (! strcasecmp (TYPE_FIELD, hf->name)) {
+                    char *ctline_less_newline =
+                        update_attr (hf->value, "charset=", dest_charset);
+                    char *ctline = concat (ctline_less_newline, "\n", NULL);
+                    free (ctline_less_newline);
+
+                    free (hf->value);
+                    hf->value = ctline;
+                    break;
+                }
+            }
+        } else {
+            (void) m_unlink (dest);
+        }
+#else  /* ! HAVE_ICONV */
+        NMH_UNUSED (message_mods);
+
+        advise (NULL, "Can't convert %s to %s without iconv", src_charset,
+                dest_charset);
+        status = NOTOK;
+#endif /* ! HAVE_ICONV */
+    }
+
+    return status;
+}
+
+
+static int
+convert_content_charset (CT ct, char **file) {
+#ifdef HAVE_ICONV
+    /* Using current locale, see if the content needs to be converted. */
+
+    /* content_charset() cannot return NULL. */
+    char *charset = content_charset (ct);
+
+    if (! check_charset (charset, strlen (charset))) {
+        int unused = 0;
+
+        if (convert_charset (ct, get_charset (), &unused) == 0) {
+            *file = ct->c_cefile.ce_file;
+        } else {
+            return NOTOK;
+        }
+    }
+#endif /* ! HAVE_ICONV */
+
+    return OK;
 }