X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/55b8f7b6a738adb4dc9833233fc6111f3cd56969..b62a02a47bef30c4d5d052b5a0dcc582ce9d784e:/uip/mhshowsbr.c diff --git a/uip/mhshowsbr.c b/uip/mhshowsbr.c index e28c73ea..4bab75ca 100644 --- a/uip/mhshowsbr.c +++ b/uip/mhshowsbr.c @@ -2,8 +2,6 @@ /* * mhshowsbr.c -- routines to display the contents of MIME messages * - * $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. @@ -13,28 +11,15 @@ #include #include #include -#include #include -#include #include #include #include #include #include - -#ifdef HAVE_SYS_WAIT_H -# include -#endif - -/* - * Just use sigjmp/longjmp on older machines that - * don't have sigsetjmp/siglongjmp. - */ -#ifndef HAVE_SIGSETJMP -# define sigjmp_buf jmp_buf -# define sigsetjmp(env,mask) setjmp(env) -# define siglongjmp(env,val) longjmp(env,val) -#endif +#ifdef HAVE_ICONV +# include +#endif /* ! HAVE_ICONV */ extern int debugsw; @@ -53,12 +38,6 @@ pid_t xpid = 0; static sigjmp_buf intrenv; -/* termsbr.c */ -int SOprintf (char *, ...); - -/* mhparse.c */ -int pidcheck (int); - /* mhmisc.c */ int part_ok (CT, int); int type_ok (CT, int); @@ -90,7 +69,8 @@ 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 RETSIGTYPE intrser (int); +static int convert_content_charset (CT, char **); +static void intrser (int); /* @@ -134,11 +114,7 @@ show_single_message (CT ct, char *form) { sigset_t set, oset; -#ifdef WAITINT int status; -#else - union wait status; -#endif /* Allow user executable bit so that temporary directories created by * the viewer (e.g., lynx) are going to be accessible */ @@ -169,19 +145,15 @@ show_single_message (CT ct, char *form) sigaddset (&set, SIGINT); sigaddset (&set, SIGQUIT); sigaddset (&set, SIGTERM); - SIGPROCMASK (SIG_BLOCK, &set, &oset); + sigprocmask (SIG_BLOCK, &set, &oset); while (wait (&status) != NOTOK) { -#ifdef WAITINT pidcheck (status); -#else - pidcheck (status.w_status); -#endif continue; } /* reset the signal mask */ - SIGPROCMASK (SIG_SETMASK, &oset, &set); + sigprocmask (SIG_SETMASK, &oset, &set); xpid = 0; flush_errors (); @@ -197,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) { @@ -229,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); @@ -239,6 +211,8 @@ DisplayMsgHeader (CT ct, char *form) xpid = -child_id; break; } + + arglist_free(file, vec); } @@ -253,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 */ @@ -302,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); @@ -348,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; @@ -492,7 +472,8 @@ raw: } } - if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) { + if (buflen <= 0 || + (ct->c_termproc && (size_t) buflen <= strlen(ct->c_termproc))) { /* content_error would provide a more useful error message * here, except that if we got overrun, it probably would * too. @@ -526,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); @@ -555,13 +536,13 @@ show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer else list_switch (ct, -1, 1, 0, 0); - if (xpause && SOprintf ("Press to show content...")) - printf ("Press to show content..."); - - if (xpause) { + if (xpause && isatty (fileno (stdout))) { int intr; SIGNAL_HANDLER istat; + if (SOprintf ("Press to show content...")) + printf ("Press to show content..."); + istat = SIGNAL (SIGINT, intrser); if ((intr = sigsetjmp (intrenv, 1)) == OK) { fflush (stdout); @@ -586,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: @@ -632,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); @@ -649,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); } @@ -668,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); @@ -683,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); } @@ -733,12 +711,12 @@ show_multi_internal (CT ct, int serial, int alternate) sigaddset (&set, SIGINT); sigaddset (&set, SIGQUIT); sigaddset (&set, SIGTERM); - SIGPROCMASK (SIG_BLOCK, &set, &oset); + sigprocmask (SIG_BLOCK, &set, &oset); } /* * 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; @@ -746,7 +724,7 @@ show_multi_internal (CT ct, int serial, int alternate) for (part = m->mp_parts; part; part = part->mp_next) { p = part->mp_part; - if (part_ok (p, 0) && type_ok (p, 0)) { + if (part_ok (p, 1) && type_ok (p, 1)) { int inneresult; inneresult = show_switch (p, nowserial, nowalternate); @@ -785,11 +763,7 @@ show_multi_internal (CT ct, int serial, int alternate) if (serial && !nowserial) { pid_t pid; int kids; -#ifdef WAITINT int status; -#else - union wait status; -#endif kids = 0; for (part = m->mp_parts; part; part = part->mp_next) { @@ -804,11 +778,7 @@ show_multi_internal (CT ct, int serial, int alternate) } while (kids > 0 && (pid = wait (&status)) != NOTOK) { -#ifdef WAITINT pidcheck (status); -#else - pidcheck (status.w_status); -#endif for (part = m->mp_parts; part; part = part->mp_next) { p = part->mp_part; @@ -827,7 +797,7 @@ show_multi_internal (CT ct, int serial, int alternate) out: if (!nowserial) { /* reset the signal mask */ - SIGPROCMASK (SIG_SETMASK, &oset, &set); + sigprocmask (SIG_SETMASK, &oset, &set); } return result; @@ -929,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; @@ -1015,7 +985,8 @@ raw: } } - if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) { + if (buflen <= 0 || + (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. @@ -1047,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); @@ -1082,6 +1053,9 @@ show_message_rfc822 (CT ct, int serial, int alternate) static int show_partial (CT ct, int serial, int alternate) { + NMH_UNUSED (serial); + NMH_UNUSED (alternate); + content_error (NULL, ct, "in order to display this message, you must reassemble it"); return NOTOK; @@ -1104,20 +1078,187 @@ 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 RETSIGTYPE +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; +} + + +static void intrser (int i) { -#ifndef RELIABLE_SIGNALS - SIGNAL (SIGINT, intrser); -#endif + NMH_UNUSED (i); putchar ('\n'); siglongjmp (intrenv, DONE);