From: Kevin McCarthy Date: Sat, 31 Aug 2019 20:37:16 +0000 (-0700) Subject: Re-enable and cleanup format-flowed space stuffing X-Git-Tag: 2019-10-25~15^2~18 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e4222d7332ae3056bddfdeb39b3fec4f6cc04197;p=neomutt Re-enable and cleanup format-flowed space stuffing 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 --- diff --git a/compose.c b/compose.c index c0dc283b2..b455f70fc 100644 --- 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 */ diff --git a/doc/manual.xml.head b/doc/manual.xml.head index 3ebd68c13..4ba45eea9 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -3046,8 +3046,7 @@ color sidebar_divider color8 default # Dark grey variable is set, specifically it does not add the trailing spaces. - 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. Space-stuffing is required by RFC3676 defining format=flowed and means to prepend a space to: @@ -3095,11 +3094,6 @@ color sidebar_divider color8 default # Dark grey editor's documentation if you intend to send f=f messages. - - 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. - For example, vim provides the w flag for its formatoptions diff --git a/init.h b/init.h index 426a38c62..8b3e0961b 100644 --- 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 }, diff --git a/postpone.c b/postpone.c index 295f1f892..96e14ef13 100644 --- a/postpone.c +++ b/postpone.c @@ -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: diff --git a/rfc3676.c b/rfc3676.c index 7a2246719..02bd2e028 100644 --- 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); + } } diff --git a/rfc3676.h b/rfc3676.h index 3ac209399..21dc1f002 100644 --- 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 0c2f5e9b2..b936f7b31 100644 --- 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)))