]> git.llucax.com Git - software/mutt-debian.git/blobdiff - handler.c
Imported Upstream version 1.5.20
[software/mutt-debian.git] / handler.c
index d2ad711268ed2d4c25d0b22463bf2aa738e01571..57dab0f4196ea37528542bdc207c6b55fec10338 100644 (file)
--- a/handler.c
+++ b/handler.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org>
  * 
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License as published by
@@ -74,7 +74,7 @@ static void state_prefix_put (const char *d, size_t dlen, STATE *s)
     fwrite (d, dlen, 1, s->fpout);
 }
 
-void mutt_convert_to_state(iconv_t cd, char *bufi, size_t *l, STATE *s)
+static void mutt_convert_to_state(iconv_t cd, char *bufi, size_t *l, STATE *s)
 {
   char bufo[BUFO_SIZE];
   ICONV_CONST char *ib;
@@ -113,7 +113,7 @@ void mutt_convert_to_state(iconv_t cd, char *bufi, size_t *l, STATE *s)
   *l = ibl;
 }
 
-void mutt_decode_xbit (STATE *s, long len, int istext, iconv_t cd)
+static void mutt_decode_xbit (STATE *s, long len, int istext, iconv_t cd)
 {
   int c, ch;
   char bufi[BUFI_SIZE];
@@ -173,9 +173,9 @@ static void qp_decode_line (char *dest, char *src, size_t *l,
                            int last)
 {
   char *d, *s;
-  char c;
+  char c = 0;
 
-  int kind;
+  int kind = -1;
   int soft = 0;
 
   /* decode the line */
@@ -191,7 +191,15 @@ static void qp_decode_line (char *dest, char *src, size_t *l,
   }
 
   if (!soft && last == '\n')
-    *d++ = '\n';
+  {
+    /* neither \r nor \n as part of line-terminating CRLF
+     * may be qp-encoded, so remove \r and \n-terminate;
+     * see RfC2045, sect. 6.7, (1): General 8bit representation */
+    if (kind == 0 && c == '\r')
+      *(d-1) = '\n';
+    else
+      *d++ = '\n';
+  }
   
   *d = '\0';
   *l = d - dest;
@@ -221,7 +229,7 @@ static void qp_decode_line (char *dest, char *src, size_t *l,
  * 
  */
 
-void mutt_decode_quoted (STATE *s, long len, int istext, iconv_t cd)
+static void mutt_decode_quoted (STATE *s, long len, int istext, iconv_t cd)
 {
   char line[STRING];
   char decline[2*STRING];
@@ -357,14 +365,14 @@ void mutt_decode_base64 (STATE *s, long len, int istext, iconv_t cd)
   state_reset_prefix(s);
 }
 
-unsigned char decode_byte (char ch)
+static unsigned char decode_byte (char ch)
 {
   if (ch == 96)
     return 0;
   return ch - 32;
 }
 
-void mutt_decode_uuencoded (STATE *s, long len, int istext, iconv_t cd)
+static void mutt_decode_uuencoded (STATE *s, long len, int istext, iconv_t cd)
 {
   char tmps[SHORT_STRING];
   char linelen, c, l, out;
@@ -427,31 +435,31 @@ enum { RICH_PARAM=0, RICH_BOLD, RICH_UNDERLINE, RICH_ITALIC, RICH_NOFILL,
   RICH_FLUSHRIGHT, RICH_COLOR, RICH_LAST_TAG };
 
 static struct {
-  const char *tag_name;
+  const wchar_t *tag_name;
   int index;
 } EnrichedTags[] = {
-  { "param",           RICH_PARAM },
-  { "bold",            RICH_BOLD },
-  { "italic",          RICH_ITALIC },
-  { "underline",       RICH_UNDERLINE },
-  { "nofill",          RICH_NOFILL },
-  { "excerpt",         RICH_EXCERPT },
-  { "indent",          RICH_INDENT },
-  { "indentright",     RICH_INDENT_RIGHT },
-  { "center",          RICH_CENTER },
-  { "flushleft",       RICH_FLUSHLEFT },
-  { "flushright",      RICH_FLUSHRIGHT },
-  { "flushboth",       RICH_FLUSHLEFT },
-  { "color",           RICH_COLOR },
-  { "x-color",         RICH_COLOR },
+  { L"param",          RICH_PARAM },
+  { L"bold",           RICH_BOLD },
+  { L"italic",         RICH_ITALIC },
+  { L"underline",      RICH_UNDERLINE },
+  { L"nofill",         RICH_NOFILL },
+  { L"excerpt",                RICH_EXCERPT },
+  { L"indent",         RICH_INDENT },
+  { L"indentright",    RICH_INDENT_RIGHT },
+  { L"center",         RICH_CENTER },
+  { L"flushleft",      RICH_FLUSHLEFT },
+  { L"flushright",     RICH_FLUSHRIGHT },
+  { L"flushboth",      RICH_FLUSHLEFT },
+  { L"color",          RICH_COLOR },
+  { L"x-color",                RICH_COLOR },
   { NULL,              -1 }
 };
 
 struct enriched_state
 {
-  char *buffer;
-  char *line;
-  char *param;
+  wchar_t *buffer;
+  wchar_t *line;
+  wchar_t *param;
   size_t buff_len;
   size_t line_len;
   size_t line_used;
@@ -478,9 +486,9 @@ static void enriched_wrap (struct enriched_state *stte)
       /* Strip trailing white space */
       size_t y = stte->line_used - 1;
 
-      while (y && ISSPACE (stte->line[y]))
+      while (y && iswspace (stte->line[y]))
       {
-       stte->line[y] = '\0';
+       stte->line[y] = (wchar_t) '\0';
        y--;
        stte->line_used--;
        stte->line_len--;
@@ -490,7 +498,7 @@ static void enriched_wrap (struct enriched_state *stte)
        /* Strip leading whitespace */
        y = 0;
 
-       while (stte->line[y] && ISSPACE (stte->line[y]))
+       while (stte->line[y] && iswspace (stte->line[y]))
          y++;
        if (y)
        {
@@ -530,11 +538,11 @@ static void enriched_wrap (struct enriched_state *stte)
        }
       }
     }
-    state_puts (stte->line, stte->s);
+    state_putws ((const wchar_t*) stte->line, stte->s);
   }
 
   state_putc ('\n', stte->s);
-  stte->line[0] = '\0';
+  stte->line[0] = (wchar_t) '\0';
   stte->line_len = 0;
   stte->line_used = 0;
   stte->indent_len = 0;
@@ -585,31 +593,32 @@ static void enriched_flush (struct enriched_state *stte, int wrap)
 
   if (stte->buff_used)
   {
-    stte->buffer[stte->buff_used] = '\0';
+    stte->buffer[stte->buff_used] = (wchar_t) '\0';
     stte->line_used += stte->buff_used;
     if (stte->line_used > stte->line_max)
     {
       stte->line_max = stte->line_used;
-      safe_realloc (&stte->line, stte->line_max + 1);
+      safe_realloc (&stte->line, (stte->line_max + 1) * sizeof (wchar_t));
     }
-    strcat (stte->line, stte->buffer); /* __STRCAT_CHECKED__ */
+    wcscat (stte->line, stte->buffer);
     stte->line_len += stte->word_len;
     stte->word_len = 0;
     stte->buff_used = 0;
   }
-  if (wrap) 
+  if (wrap)
     enriched_wrap(stte);
+  fflush (stte->s->fpout);
 }
 
 
-static void enriched_putc (int c, struct enriched_state *stte)
+static void enriched_putwc (wchar_t c, struct enriched_state *stte)
 {
   if (stte->tag_level[RICH_PARAM]) 
   {
     if (stte->tag_level[RICH_COLOR]) 
     {
       if (stte->param_used + 1 >= stte->param_len)
-       safe_realloc (&stte->param, (stte->param_len += STRING));
+       safe_realloc (&stte->param, (stte->param_len += STRING) * sizeof (wchar_t));
 
       stte->param[stte->param_used++] = c;
     }
@@ -620,12 +629,12 @@ static void enriched_putc (int c, struct enriched_state *stte)
   if (stte->buff_len < stte->buff_used + 3)
   {
     stte->buff_len += LONG_STRING;
-    safe_realloc (&stte->buffer, stte->buff_len + 1);
+    safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t));
   }
 
-  if ((!stte->tag_level[RICH_NOFILL] && ISSPACE (c)) || c == '\0' )
+  if ((!stte->tag_level[RICH_NOFILL] && iswspace (c)) || c == (wchar_t) '\0')
   {
-    if (c == '\t')
+    if (c == (wchar_t) '\t')
       stte->word_len += 8 - (stte->line_len + stte->word_len) % 8;
     else
       stte->word_len++;
@@ -640,20 +649,20 @@ static void enriched_putc (int c, struct enriched_state *stte)
       if (stte->tag_level[RICH_BOLD])
       {
        stte->buffer[stte->buff_used++] = c;
-       stte->buffer[stte->buff_used++] = '\010';
+       stte->buffer[stte->buff_used++] = (wchar_t) '\010';
        stte->buffer[stte->buff_used++] = c;
       }
       else if (stte->tag_level[RICH_UNDERLINE])
       {
 
        stte->buffer[stte->buff_used++] = '_';
-       stte->buffer[stte->buff_used++] = '\010';
+       stte->buffer[stte->buff_used++] = (wchar_t) '\010';
        stte->buffer[stte->buff_used++] = c;
       }
       else if (stte->tag_level[RICH_ITALIC])
       {
        stte->buffer[stte->buff_used++] = c;
-       stte->buffer[stte->buff_used++] = '\010';
+       stte->buffer[stte->buff_used++] = (wchar_t) '\010';
        stte->buffer[stte->buff_used++] = '_';
       }
       else
@@ -669,33 +678,33 @@ static void enriched_putc (int c, struct enriched_state *stte)
   }
 }
 
-static void enriched_puts (char *s, struct enriched_state *stte)
+static void enriched_puts (const char *s, struct enriched_state *stte)
 {
-  char *c;
+  const char *c;
 
-  if (stte->buff_len < stte->buff_used + mutt_strlen(s))
+  if (stte->buff_len < stte->buff_used + mutt_strlen (s))
   {
     stte->buff_len += LONG_STRING;
-    safe_realloc (&stte->buffer, stte->buff_len + 1);
+    safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t));
   }
   c = s;
   while (*c)
   {
-    stte->buffer[stte->buff_used++] = *c;
+    stte->buffer[stte->buff_used++] = (wchar_t) *c;
     c++;
   }
 }
 
-static void enriched_set_flags (const char *tag, struct enriched_state *stte)
+static void enriched_set_flags (const wchar_t *tag, struct enriched_state *stte)
 {
-  const char *tagptr = tag;
+  const wchar_t *tagptr = tag;
   int i, j;
 
-  if (*tagptr == '/')
+  if (*tagptr == (wchar_t) '/')
     tagptr++;
   
   for (i = 0, j = -1; EnrichedTags[i].tag_name; i++)
-    if (ascii_strcasecmp (EnrichedTags[i].tag_name,tagptr) == 0)
+    if (wcscasecmp (EnrichedTags[i].tag_name, tagptr) == 0)
     {
       j = EnrichedTags[i].index;
       break;
@@ -706,42 +715,42 @@ static void enriched_set_flags (const char *tag, struct enriched_state *stte)
     if (j == RICH_CENTER || j == RICH_FLUSHLEFT || j == RICH_FLUSHRIGHT)
       enriched_flush (stte, 1);
 
-    if (*tag == '/')
+    if (*tag == (wchar_t) '/')
     {
       if (stte->tag_level[j]) /* make sure not to go negative */
        stte->tag_level[j]--;
       if ((stte->s->flags & M_DISPLAY) && j == RICH_PARAM && stte->tag_level[RICH_COLOR])
       {
-       stte->param[stte->param_used] = '\0';
-       if (!ascii_strcasecmp(stte->param, "black"))
+       stte->param[stte->param_used] = (wchar_t) '\0';
+       if (!wcscasecmp(L"black", stte->param))
        {
          enriched_puts("\033[30m", stte);
        }
-       else if (!ascii_strcasecmp(stte->param, "red"))
+       else if (!wcscasecmp(L"red", stte->param))
        {
          enriched_puts("\033[31m", stte);
        }
-       else if (!ascii_strcasecmp(stte->param, "green"))
+       else if (!wcscasecmp(L"green", stte->param))
        {
          enriched_puts("\033[32m", stte);
        }
-       else if (!ascii_strcasecmp(stte->param, "yellow"))
+       else if (!wcscasecmp(L"yellow", stte->param))
        {
          enriched_puts("\033[33m", stte);
        }
-       else if (!ascii_strcasecmp(stte->param, "blue"))
+       else if (!wcscasecmp(L"blue", stte->param))
        {
          enriched_puts("\033[34m", stte);
        }
-       else if (!ascii_strcasecmp(stte->param, "magenta"))
+       else if (!wcscasecmp(L"magenta", stte->param))
        {
          enriched_puts("\033[35m", stte);
        }
-       else if (!ascii_strcasecmp(stte->param, "cyan"))
+       else if (!wcscasecmp(L"cyan", stte->param))
        {
          enriched_puts("\033[36m", stte);
        }
-       else if (!ascii_strcasecmp(stte->param, "white"))
+       else if (!wcscasecmp(L"white", stte->param))
        {
          enriched_puts("\033[37m", stte);
        }
@@ -755,7 +764,7 @@ static void enriched_set_flags (const char *tag, struct enriched_state *stte)
       if (j == RICH_PARAM)
       {
        stte->param_used = 0;
-       stte->param[0] = '\0';
+       stte->param[0] = (wchar_t) '\0';
       }
     }
     else
@@ -766,7 +775,7 @@ static void enriched_set_flags (const char *tag, struct enriched_state *stte)
   }
 }
 
-int text_enriched_handler (BODY *a, STATE *s)
+static int text_enriched_handler (BODY *a, STATE *s)
 {
   enum {
     TEXT, LANGLE, TAG, BOGUS_TAG, NEWLINE, ST_EOF, DONE
@@ -774,16 +783,16 @@ int text_enriched_handler (BODY *a, STATE *s)
 
   long bytes = a->length;
   struct enriched_state stte;
-  int c = 0;
+  wchar_t wc = 0;
   int tag_len = 0;
-  char tag[LONG_STRING + 1];
+  wchar_t tag[LONG_STRING + 1];
 
   memset (&stte, 0, sizeof (stte));
   stte.s = s;
   stte.WrapMargin = ((s->flags & M_DISPLAY) ? (COLS-4) : ((COLS-4)<72)?(COLS-4):72);
   stte.line_max = stte.WrapMargin * 4;
-  stte.line = (char *) safe_calloc (1, stte.line_max + 1);
-  stte.param = (char *) safe_calloc (1, STRING);
+  stte.line = (wchar_t *) safe_calloc (1, (stte.line_max + 1) * sizeof (wchar_t));
+  stte.param = (wchar_t *) safe_calloc (1, (STRING) * sizeof (wchar_t));
 
   stte.param_len = STRING;
   stte.param_used = 0;
@@ -798,7 +807,7 @@ int text_enriched_handler (BODY *a, STATE *s)
   {
     if (state != ST_EOF)
     {
-      if (!bytes || (c = fgetc (s->fpin)) == EOF)
+      if (!bytes || (wc = fgetwc (s->fpin)) == EOF)
        state = ST_EOF;
       else
        bytes--;
@@ -807,7 +816,7 @@ int text_enriched_handler (BODY *a, STATE *s)
     switch (state)
     {
       case TEXT :
-       switch (c)
+       switch (wc)
        {
          case '<' :
            state = LANGLE;
@@ -820,20 +829,20 @@ int text_enriched_handler (BODY *a, STATE *s)
            }
            else 
            {
-             enriched_putc (' ', &stte);
+             enriched_putwc ((wchar_t) ' ', &stte);
              state = NEWLINE;
            }
            break;
 
          default:
-           enriched_putc (c, &stte);
+           enriched_putwc (wc, &stte);
        }
        break;
 
       case LANGLE :
-       if (c == '<')
+       if (wc == (wchar_t) '<')
        {
-         enriched_putc (c, &stte);
+         enriched_putwc (wc, &stte);
          state = TEXT;
          break;
        }
@@ -844,36 +853,36 @@ int text_enriched_handler (BODY *a, STATE *s)
        }
        /* Yes, fall through (it wasn't a <<, so this char is first in TAG) */
       case TAG :
-       if (c == '>')
+       if (wc == (wchar_t) '>')
        {
-         tag[tag_len] = '\0';
+         tag[tag_len] = (wchar_t) '\0';
          enriched_set_flags (tag, &stte);
          state = TEXT;
        }
        else if (tag_len < LONG_STRING)  /* ignore overly long tags */
-         tag[tag_len++] = c;
+         tag[tag_len++] = wc;
        else
          state = BOGUS_TAG;
        break;
 
       case BOGUS_TAG :
-       if (c == '>')
+       if (wc == (wchar_t) '>')
          state = TEXT;
        break;
 
       case NEWLINE :
-       if (c == '\n')
+       if (wc == (wchar_t) '\n')
          enriched_flush (&stte, 1);
        else
        {
-         ungetc (c, s->fpin);
+         ungetwc (wc, s->fpin);
          bytes++;
          state = TEXT;
        }
        break;
 
       case ST_EOF :
-       enriched_putc ('\0', &stte);
+       enriched_putwc ((wchar_t) '\0', &stte);
         enriched_flush (&stte, 1);
        state = DONE;
        break;
@@ -1057,7 +1066,7 @@ static int alternative_handler (BODY *a, STATE *s)
 }
 
 /* handles message/rfc822 body parts */
-int message_handler (BODY *a, STATE *s)
+static int message_handler (BODY *a, STATE *s)
 {
   struct stat st;
   BODY *b;
@@ -1080,7 +1089,8 @@ int message_handler (BODY *a, STATE *s)
   {
     mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
        (((s->flags & M_WEED) || ((s->flags & (M_DISPLAY|M_PRINTING)) && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
-       (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM, s->prefix);
+       (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM |
+       (s->flags & M_DISPLAY) ? CH_DISPLAY : 0, s->prefix);
 
     if (s->prefix)
       state_puts (s->prefix, s);
@@ -1137,7 +1147,7 @@ int mutt_can_decode (BODY *a)
   return (0);
 }
 
-int multipart_handler (BODY *a, STATE *s)
+static int multipart_handler (BODY *a, STATE *s)
 {
   BODY *b, *p;
   char length[5];
@@ -1185,36 +1195,32 @@ int multipart_handler (BODY *a, STATE *s)
       else
        state_putc ('\n', s);
     }
-    else
-    {
-      if (p->description && mutt_can_decode (p))
-       state_printf (s, "Content-Description: %s\n", p->description);
 
-      if (p->form_name)
-       state_printf(s, "%s: \n", p->form_name);
-
-    }
     rc = mutt_body_handler (p, s);
     state_putc ('\n', s);
     
     if (rc)
     {
+      mutt_error (_("One or more parts of this message could not be displayed"));
       dprint (1, (debugfile, "Failed on attachment #%d, type %s/%s.\n", count, TYPE(p), NONULL (p->subtype)));
     }
     
-    if (rc || ((s->flags & M_REPLYING)
-               && (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE)))
+    if ((s->flags & M_REPLYING)
+        && (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE))
       break;
   }
 
   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
       a->encoding == ENCUUENCODED)
     mutt_free_body (&b);
-  
+
+  /* make failure of a single part non-fatal */
+  if (rc < 0)
+    rc = 1;
   return rc;
 }
 
-int autoview_handler (BODY *a, STATE *s)
+static int autoview_handler (BODY *a, STATE *s)
 {
   rfc1524_entry *entry = rfc1524_new_entry ();
   char buffer[LONG_STRING];
@@ -1419,7 +1425,7 @@ static int external_body_handler (BODY *b, STATE *s)
 
       mutt_copy_hdr(s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
                    (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
-                   CH_DECODE, NULL);
+                   CH_DECODE | CH_DISPLAY, NULL);
     }
   }
   else
@@ -1436,7 +1442,7 @@ static int external_body_handler (BODY *b, STATE *s)
                    access_type);
       mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
                     (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
-                    CH_DECODE , NULL);
+                    CH_DECODE | CH_DISPLAY, NULL);
     }
   }
   
@@ -1480,6 +1486,34 @@ void mutt_decode_attachment (BODY *b, STATE *s)
     iconv_close (cd);
 }
 
+/* when generating format=flowed ($text_flowed is set) from format=fixed,
+ * strip all trailing spaces to improve interoperability;
+ * if $text_flowed is unset, simply verbatim copy input
+ */
+static int text_plain_handler (BODY *b, STATE *s)
+{
+  char buf[LONG_STRING];
+  size_t l;
+
+  while (fgets (buf, sizeof (buf), s->fpin))
+  {
+    l = mutt_strlen (buf);
+    if (l > 0 && buf[l-1] == '\n')
+      buf[--l] = 0;
+    if (option (OPTTEXTFLOWED))
+    {
+      while (l > 0 && buf[l-1] == ' ')
+       buf[--l] = 0;
+    }
+    if (s->prefix)
+      state_puts (s->prefix, s);
+    state_puts (buf, s);
+    state_putc ('\n', s);
+  }
+
+  return 0;
+}
+
 int mutt_body_handler (BODY *b, STATE *s)
 {
   int decode = 0;
@@ -1520,7 +1554,7 @@ int mutt_body_handler (BODY *b, STATE *s)
       else if (ascii_strcasecmp ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
        handler = rfc3676_handler;
       else
-       plaintext = 1;
+       handler = text_plain_handler;
     }
     else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
       handler = text_enriched_handler;
@@ -1564,17 +1598,34 @@ int mutt_body_handler (BODY *b, STATE *s)
 
     if (!handler)
       handler = multipart_handler;
+    
+    if (b->encoding != ENC7BIT && b->encoding != ENC8BIT
+        && b->encoding != ENCBINARY)
+    {
+      dprint (1, (debugfile, "Bad encoding type %d for multipart entity, "
+                  "assuming 7 bit\n", b->encoding));
+      b->encoding = ENC7BIT;
+    }
   }
   else if (WithCrypto && b->type == TYPEAPPLICATION)
   {
-    if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
+    if (option (OPTDONTHANDLEPGPKEYS)
+        && !ascii_strcasecmp("pgp-keys", b->subtype))
+    {
+      /* pass raw part through for key extraction */
+      plaintext = 1;
+    }
+    else if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
       handler = crypt_pgp_application_pgp_handler;
-    if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(b))
+    else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(b))
       handler = crypt_smime_application_smime_handler;
   }
 
-
-  if (plaintext || handler)
+  /* only respect disposition == attachment if we're not
+     displaying from the attachment menu (i.e. pager) */
+  if ((!option (OPTHONORDISP) || (b->disposition != DISPATTACH ||
+                                 option(OPTVIEWATTACH))) &&
+       (plaintext || handler))
   {
     fseeko (s->fpin, b->offset, 0);
 
@@ -1624,7 +1675,7 @@ int mutt_body_handler (BODY *b, STATE *s)
       {
        b->length = ftello (s->fpout);
        b->offset = 0;
-       fclose (s->fpout);
+       safe_fclose (&s->fpout);
 
        /* restore final destination and substitute the tempfile for input */
        s->fpout = fp;
@@ -1655,16 +1706,24 @@ int mutt_body_handler (BODY *b, STATE *s)
        b->offset = tmpoffset;
 
        /* restore the original source stream */
-       fclose (s->fpin);
+       safe_fclose (&s->fpin);
        s->fpin = fp;
       }
     }
     s->flags |= M_FIRSTDONE;
   }
-  else if (s->flags & M_DISPLAY)
+  /* print hint to use attachment menu for disposition == attachment
+     if we're not already being called from there */
+  else if ((s->flags & M_DISPLAY) || (b->disposition == DISPATTACH &&
+                                     !option (OPTVIEWATTACH) &&
+                                     option (OPTHONORDISP) &&
+                                     (plaintext || handler)))
   {
     state_mark_attach (s);
-    state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
+    if (option (OPTHONORDISP) && b->disposition == DISPATTACH)
+      fputs (_("[-- This is an attachment "), s->fpout);
+    else
+      state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
     if (!option (OPTVIEWATTACH))
     {
       if (km_expand_key (type, sizeof(type),