]> granicus.if.org Git - neomutt/commitdiff
Re-enable and cleanup format-flowed space stuffing
authorKevin McCarthy <kevin@8t8.us>
Sat, 31 Aug 2019 20:37:16 +0000 (13:37 -0700)
committerRichard Russon <rich@flatcap.org>
Tue, 8 Oct 2019 16:43:58 +0000 (17:43 +0100)
Commit 04cb5bde tried to fix re-stuffing a postponed message more than
once, but unfortunately broke the normal case of composing a new
message.  So actually, space-stuffing has been turned off the past 7
years.

Move format=flowed parameter setting below the standard message
pre-processing block.  It shouldn't be performed for draft-files even
with $resume_draft_files set.  Moving out of the block makes that
clearer.

Create mutt_rfc3676_space_(un)stuff() functions, which check the
content type and stuff/unstuff apprpropriately.  Note that the
stuff/unstuff does not depend on $text_flowed, which is only in charge
of setting the format=flowed parameter.  This parameter can also come
from resumed/resent/draft messages and should still be respected.

Add unstuffing to mutt_prepare_template().  This is called by
postponed, resent, and draft files.  This will prevent double-stuffing
in those cases.

Unstuff/restuff around editing the message in the compose menu, to
keep everything transparent to the user.  I originally put the
stuffing *after* the compose menu, but previewing the messages in the
compose menu did not work properly in that case.  It's cleaner this
way too.

Change the stuff/unstuff functions to preserve the original
hdr->content->filename.  The "hack" previously used would interact
poorly with editable body files (mutt -i -E).  Fortunately space
stuffing was pretty much disabled except in unusual cases before.

Co-authored-by: Richard Russon <rich@flatcap.org>
compose.c
doc/manual.xml.head
init.h
postpone.c
rfc3676.c
rfc3676.h
send.c

index c0dc283b2173b2985857e87b19eb91868e00a726..b455f70fc1ba6b43c068df258c7a65395060dc88 100644 (file)
--- a/compose.c
+++ b/compose.c
@@ -69,6 +69,7 @@
 #include "options.h"
 #include "protos.h"
 #include "recvattach.h"
+#include "rfc3676.h"
 #include "sendlib.h"
 #include "sort.h"
 #ifdef ENABLE_NLS
@@ -1271,7 +1272,9 @@ int mutt_compose_menu(struct Email *e, char *fcc, size_t fcclen, struct Email *e
       case OP_COMPOSE_EDIT_MESSAGE:
         if (C_Editor && (mutt_str_strcmp("builtin", C_Editor) != 0) && !C_EditHeaders)
         {
+          mutt_rfc3676_space_unstuff(e);
           mutt_edit_file(C_Editor, e->content->filename);
+          mutt_rfc3676_space_stuff(e);
           mutt_update_encoding(e->content);
           menu->redraw = REDRAW_FULL;
           mutt_message_hook(NULL, e, MUTT_SEND2_HOOK);
@@ -1280,6 +1283,7 @@ int mutt_compose_menu(struct Email *e, char *fcc, size_t fcclen, struct Email *e
         /* fallthrough */
 
       case OP_COMPOSE_EDIT_HEADERS:
+        mutt_rfc3676_space_unstuff(e);
         if ((mutt_str_strcmp("builtin", C_Editor) != 0) &&
             ((op == OP_COMPOSE_EDIT_HEADERS) || ((op == OP_COMPOSE_EDIT_MESSAGE) && C_EditHeaders)))
         {
@@ -1302,6 +1306,8 @@ int mutt_compose_menu(struct Email *e, char *fcc, size_t fcclen, struct Email *e
            * code below to regenerate the index array */
           mutt_builtin_editor(e->content->filename, e, e_cur);
         }
+
+        mutt_rfc3676_space_stuff(e);
         mutt_update_encoding(e->content);
 
         /* attachments may have been added */
index 3ebd68c1328a7aa675c646ea29faa69c77bc211a..4ba45eea9b6dc4603bebab87549e66e282a1bd6f 100644 (file)
@@ -3046,8 +3046,7 @@ color sidebar_divider   color8  default     <emphasis role="comment"># Dark grey
             variable is set, specifically it does not add the trailing spaces.
           </para>
           <para>
-            After editing the initial message text and before entering the
-            compose menu, NeoMutt properly space-stuffs the message.
+            After editing, NeoMutt properly space-stuffs the message.
             <emphasis>Space-stuffing</emphasis> is required by RFC3676 defining
             <literal>format=flowed</literal> and means to prepend a space to:
           </para>
@@ -3095,11 +3094,6 @@ color sidebar_divider   color8  default     <emphasis role="comment"># Dark grey
             editor's documentation if you intend to send <literal>f=f</literal>
             messages.
           </para>
-          <para>
-            Please note that when editing messages from the compose menu
-            several times before really sending a mail, it's up to the user to
-            ensure that the message is properly space-stuffed.
-          </para>
           <para>
             For example, <emphasis>vim</emphasis> provides the
             <literal>w</literal> flag for its <literal>formatoptions</literal>
diff --git a/init.h b/init.h
index 426a38c62b61cdc2a98c2c2ec1f0a8e3570073de..8b3e0961b2e407985868847cc0e8c3ff04696179 100644 (file)
--- a/init.h
+++ b/init.h
@@ -4585,6 +4585,10 @@ struct ConfigDef MuttVars[] = {
   ** just looks like ordinary text.  To actually make use of this format's
   ** features, you'll need support in your editor.
   ** .pp
+  ** The option only controls newly composed messages.  Postponed messages,
+  ** resent messages, and draft messages (via -H on the command line) will
+  ** use the content-type of the source message.
+  ** .pp
   ** Note that $$indent_string is ignored when this option is \fIset\fP.
   */
   { "thorough_search", DT_BOOL, &C_ThoroughSearch, true },
index 295f1f8922c9e45339f215be5ef394efd7df592e..96e14ef13744ad2050ae0f553979af52fb493bf4 100644 (file)
@@ -55,6 +55,7 @@
 #include "opcodes.h"
 #include "options.h"
 #include "protos.h"
+#include "rfc3676.h"
 #include "send.h"
 #include "sendlib.h"
 #include "sort.h"
@@ -829,6 +830,8 @@ int mutt_prepare_template(FILE *fp, struct Mailbox *m, struct Email *e_new,
       e_new->security &= ~APPLICATION_SMIME;
   }
 
+  mutt_rfc3676_space_unstuff(e_new);
+
   rc = 0;
 
 bail:
index 7a22467192befe1317a5135fdcaf7b6a326089ab..02bd2e028a53c90b682f3c538671d117536aa04a 100644 (file)
--- a/rfc3676.c
+++ b/rfc3676.c
@@ -387,59 +387,157 @@ int rfc3676_handler(struct Body *a, struct State *s)
  * certain lines:
  *   - lines starting with a space
  *   - lines starting with 'From '
- * This routine is only called once right after editing the initial message so
- * it's up to the user to take care of stuffing when editing the message
- * several times before actually sending it
  *
- * This is more or less a hack as it replaces the message's content with a
- * freshly created copy in a tempfile and modifies the file's mtime so we don't
- * trigger code paths watching for mtime changes
+ * Care is taken to preserve the e->content->filename, as
+ * mutt -i -E can directly edit a passed in filename.
  */
-void rfc3676_space_stuff(struct Email *e)
+static void rfc3676_space_stuff(struct Email *e)
 {
-  int lc = 0;
-  unsigned char c = '\0';
-  char buf[1024];
-  char tmpfile[PATH_MAX];
-
-  if (!e || !e->content || !e->content->filename)
-    return;
+  FILE *fp_out = NULL;
+  char *buf = NULL;
+  size_t blen = 0;
 
-  mutt_debug(LL_DEBUG2, "f=f: postprocess %s\n", e->content->filename);
+  struct Buffer *tmpfile = mutt_buffer_pool_get();
 
   FILE *fp_in = mutt_file_fopen(e->content->filename, "r");
   if (!fp_in)
-    return;
+    goto bail;
 
-  mutt_mktemp(tmpfile, sizeof(tmpfile));
-  FILE *fp_out = mutt_file_fopen(tmpfile, "w+");
+  mutt_buffer_mktemp(tmpfile);
+  fp_out = mutt_file_fopen(mutt_b2s(tmpfile), "w+");
   if (!fp_out)
-  {
-    mutt_file_fclose(&fp_in);
-    return;
-  }
+    goto bail;
 
-  while (fgets(buf, sizeof(buf), fp_in))
+  while ((buf = mutt_file_read_line(buf, &blen, fp_in, NULL, 0)) != NULL)
   {
     if ((buf[0] == ' ') || mutt_str_startswith(buf, "From ", CASE_MATCH))
-    {
       fputc(' ', fp_out);
-      lc++;
-      size_t len = mutt_str_strlen(buf);
-      if (len > 0)
-      {
-        c = buf[len - 1];
-        buf[len - 1] = '\0';
-      }
-      mutt_debug(LL_DEBUG5, "f=f: line %d needs space-stuffing: '%s'\n", lc, buf);
-      if (len > 0)
-        buf[len - 1] = c;
-    }
     fputs(buf, fp_out);
+    fputc('\n', fp_out);
+  }
+  FREE(&buf);
+  mutt_file_fclose(&fp_in);
+  mutt_file_fclose(&fp_out);
+  mutt_file_set_mtime(e->content->filename, mutt_b2s(tmpfile));
+
+  fp_in = mutt_file_fopen(mutt_b2s(tmpfile), "r");
+  if (!fp_in)
+    goto bail;
+
+  if ((truncate(e->content->filename, 0) == -1) ||
+      ((fp_out = mutt_file_fopen(e->content->filename, "a")) == NULL))
+  {
+    goto bail;
+  }
+
+  mutt_file_copy_stream(fp_in, fp_out);
+  mutt_file_fclose(&fp_in);
+  mutt_file_fclose(&fp_out);
+  mutt_file_set_mtime(mutt_b2s(tmpfile), e->content->filename);
+  unlink(mutt_b2s(tmpfile));
+  mutt_buffer_pool_release(&tmpfile);
+  return;
+
+bail:
+  mutt_file_fclose(&fp_in);
+  mutt_file_fclose(&fp_out);
+  mutt_buffer_pool_release(&tmpfile);
+}
+
+/**
+ * rfc3676_space_unstuff - Remove RFC3676 space stuffing
+ * @param e Email
+ */
+static void rfc3676_space_unstuff(struct Email *e)
+{
+  FILE *fp_out = NULL;
+  char *buf = NULL;
+  size_t blen = 0;
+
+  struct Buffer *tmpfile = mutt_buffer_pool_get();
+
+  FILE *fp_in = mutt_file_fopen(e->content->filename, "r");
+  if (!fp_in)
+    goto bail;
+
+  mutt_buffer_mktemp(tmpfile);
+  fp_out = mutt_file_fopen(mutt_b2s(tmpfile), "w+");
+  if (!fp_out)
+    goto bail;
+
+  while ((buf = mutt_file_read_line(buf, &blen, fp_in, NULL, 0)) != NULL)
+  {
+    if (buf[0] == ' ')
+      fputs(buf + 1, fp_out);
+    else
+      fputs(buf, fp_out);
+    fputc('\n', fp_out);
   }
+  FREE(&buf);
   mutt_file_fclose(&fp_in);
   mutt_file_fclose(&fp_out);
-  mutt_file_set_mtime(e->content->filename, tmpfile);
-  unlink(e->content->filename);
-  mutt_str_replace(&e->content->filename, tmpfile);
+  mutt_file_set_mtime(e->content->filename, mutt_b2s(tmpfile));
+
+  fp_in = mutt_file_fopen(mutt_b2s(tmpfile), "r");
+  if (!fp_in)
+    goto bail;
+
+  if ((truncate(e->content->filename, 0) == -1) ||
+      ((fp_out = mutt_file_fopen(e->content->filename, "a")) == NULL))
+  {
+    goto bail;
+  }
+
+  mutt_file_copy_stream(fp_in, fp_out);
+  mutt_file_fclose(&fp_in);
+  mutt_file_fclose(&fp_out);
+  mutt_file_set_mtime(mutt_b2s(tmpfile), e->content->filename);
+  unlink(mutt_b2s(tmpfile));
+  mutt_buffer_pool_release(&tmpfile);
+  return;
+
+bail:
+  mutt_file_fclose(&fp_in);
+  mutt_file_fclose(&fp_out);
+  mutt_buffer_pool_release(&tmpfile);
+}
+
+/**
+ * mutt_rfc3676_space_stuff - Perform RFC3676 space stuffing on an Email
+ * @param e Email
+ *
+ * Note: we don't check the option C_TextFlowed because we want to
+ * stuff based the actual content type.  The option only decides
+ * whether to *set* format=flowed on new messages.
+ */
+void mutt_rfc3676_space_stuff(struct Email *e)
+{
+  if (!e || !e->content || !e->content->filename)
+    return;
+
+  if ((e->content->type == TYPE_TEXT) &&
+      (mutt_str_strcasecmp("plain", e->content->subtype) == 0))
+  {
+    const char *format = mutt_param_get(&e->content->parameter, "format");
+    if (mutt_str_strcasecmp("flowed", format) == 0)
+      rfc3676_space_stuff(e);
+  }
+}
+
+/**
+ * mutt_rfc3676_space_unstuff - Remove RFC3676 space stuffing
+ * @param e Email
+ */
+void mutt_rfc3676_space_unstuff(struct Email *e)
+{
+  if (!e || !e->content || !e->content->filename)
+    return;
+
+  if ((e->content->type == TYPE_TEXT) &&
+      !mutt_str_strcasecmp("plain", e->content->subtype))
+  {
+    const char *format = mutt_param_get(&e->content->parameter, "format");
+    if (mutt_str_strcasecmp("flowed", format) == 0)
+      rfc3676_space_unstuff(e);
+  }
 }
index 3ac20939966ddeebe2071d299044903cc501b026..21dc1f0027160b03d0f7c945e1f74d366f955c15 100644 (file)
--- a/rfc3676.h
+++ b/rfc3676.h
@@ -36,6 +36,7 @@ extern bool  C_ReflowSpaceQuotes;
 extern short C_ReflowWrap;
 
 int rfc3676_handler(struct Body *a, struct State *s);
-void rfc3676_space_stuff(struct Email *e);
+void mutt_rfc3676_space_stuff (struct Email *e);
+void mutt_rfc3676_space_unstuff (struct Email *e);
 
 #endif /* MUTT_RFC3676_H */
diff --git a/send.c b/send.c
index 0c2f5e9b2969bb3273eb4f3b16891935e62e4e37..b936f7b31de2c52ed7b30adeeb234c97f0555643 100644 (file)
--- a/send.c
+++ b/send.c
@@ -2095,15 +2095,6 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile
      * a maildir-style mailbox.  */
     e_templ->replied = false;
 
-    if (!(flags & SEND_KEY))
-    {
-      if (C_TextFlowed && (e_templ->content->type == TYPE_TEXT) &&
-          (mutt_str_strcasecmp(e_templ->content->subtype, "plain") == 0))
-      {
-        mutt_param_set(&e_templ->content->parameter, "format", "flowed");
-      }
-    }
-
     /* $use_from and/or $from might have changed in a send-hook */
     if (killfrom)
     {
@@ -2138,6 +2129,19 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile
     }
   }
 
+  /* Only set format=flowed for new messages.  postponed/resent/draftfiles
+   * should respect the original email.
+   *
+   * This is set here so that send-hook can be used to turn the option on.  */
+  if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND | SEND_DRAFT_FILE)))
+  {
+    if (C_TextFlowed && (e_templ->content->type == TYPE_TEXT) &&
+        (mutt_str_strcasecmp(e_templ->content->subtype, "plain") == 0))
+    {
+      mutt_param_set(&e_templ->content->parameter, "format", "flowed");
+    }
+  }
+
   /* This hook is even called for postponed messages, and can, e.g., be
    * used for setting the editor, the sendmail path, or the
    * envelope sender.  */
@@ -2204,18 +2208,6 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile
           mutt_perror(e_templ->content->filename);
       }
 
-      /* If using format=flowed, perform space stuffing.  Avoid stuffing when
-       * recalling a postponed message where the stuffing was already
-       * performed.  If it has already been performed, the format=flowed
-       * parameter will be present.  */
-      if (C_TextFlowed && (e_templ->content->type == TYPE_TEXT) &&
-          (mutt_str_strcasecmp("plain", e_templ->content->subtype) == 0))
-      {
-        char *p = mutt_param_get(&e_templ->content->parameter, "format");
-        if (mutt_str_strcasecmp("flowed", p) != 0)
-          rfc3676_space_stuff(e_templ);
-      }
-
       mutt_message_hook(NULL, e_templ, MUTT_SEND2_HOOK);
     }
 
@@ -2370,6 +2362,8 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile
     }
   }
 
+  mutt_rfc3676_space_stuff(e_templ);
+
   mutt_update_encoding(e_templ->content);
 
   if (!(flags & (SEND_MAILX | SEND_BATCH)))