From 513587ec31160434400bb003d7160777e2db6bb6 Mon Sep 17 00:00:00 2001 From: David Levine Date: Wed, 24 Feb 2016 20:32:10 -0500 Subject: [PATCH] Added -[no]crlflinebreaks switches to mhfixmsg(1). --- docs/pending-release-notes | 4 +-- h/mhparse.h | 4 +++ man/mhfixmsg.man | 33 ++++++++++++++++------- test/mhfixmsg/test-mhfixmsg | 34 ++++++++++++++++++++--- uip/mhfixmsg.c | 54 ++++++++++++++++++++++++++++++++----- uip/mhoutsbr.c | 5 +++- 6 files changed, 111 insertions(+), 23 deletions(-) diff --git a/docs/pending-release-notes b/docs/pending-release-notes index 6163ec86..5e705130 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -25,8 +25,8 @@ NEW FEATURES - When building from source, configure will derive ${prefix} from an existing nmh installation if it finds one in your $PATH. - Added getmymbox and getmyaddr mh-format(5) function escapes. -- New -changecur, -nochangecur, -fixtype, and -decodetypes switches have been - added to mhfixmsg(1). +- New -[no]changecur, -fixtype, -decodetypes, and -[no]crlflinebreaks switches + have been added to mhfixmsg(1). - mhfixmsg now removes an extraneous trailing semicolon from header parameter lists. - Added -convertargs switch to repl(1), to pass arguments to programs diff --git a/h/mhparse.h b/h/mhparse.h index 3efec9cb..5d7668fb 100644 --- a/h/mhparse.h +++ b/h/mhparse.h @@ -193,6 +193,10 @@ struct Content { /* Structure for text content */ struct text { int tx_charset; /* flag for character set */ + int lf_line_endings; /* Whether to use CR LF (0) or LF (1) line + endings. The meaning of 0 was selected so + that CR LF is the default, in accordance + with RFC 2046, Sec. 4.1.1, Par. 1. */ }; /* diff --git a/man/mhfixmsg.man b/man/mhfixmsg.man index 17fca716..dd97fe8a 100644 --- a/man/mhfixmsg.man +++ b/man/mhfixmsg.man @@ -1,4 +1,4 @@ -.TH MHFIXMSG %manext1% "February 20, 2016" "%nmhversion%" +.TH MHFIXMSG %manext1% "February 24, 2016" "%nmhversion%" .\" .\" %nmhwarning% .\" @@ -18,6 +18,7 @@ mhfixmsg \- rewrite MIME messages with various transformations .BR \-nodecodetext ] .RB [ \-decodetypes .IR "type/[subtype][,...]" ] +.RB [ \-crlflinebreaks " | " \-nocrlflinebreaks ] .RB [ \-textcharset .I charset .RB "| " \-notextcharset ] @@ -91,6 +92,16 @@ to restrict .B \-decodetext to just text/plain parts. .PP +By default, carriage return characters are preserved or inserted at +the end of each line of text content. The +.B \-crlflinebreaks +switch selects this behavior and is enabled by default. The +.B \-nocrlflinebreaks +switch causes carriage return characters to be stripped from, and not +inserted in, text content when it is decoded and encoded. Note that +its use can cause the generation of MIME messages that do not conform +with RFC 2046, §4.1.1, paragraph 1. +.PP The .B \-textcharset switch specifies that all text/plain parts of the message(s) @@ -175,7 +186,7 @@ The switch enables a transformation to change the Content-Transfer-Encoding from an invalid value to 8bit in message parts with a Content-Type of multipart, as required by RFC 2045, -Section 6.4. That condition is indicated by a \*(lqmust be encoded in +§6.4. That condition is indicated by a \*(lqmust be encoded in 7bit, 8bit, or binary\*(rq error message from .B mhlist and other @@ -275,14 +286,15 @@ content type and/or encoding as follows: .PP .RS 5 .nf -.ta \w'\-fixboundary 'u -\-decodetext base64 and quoted-printable encoded text parts -\-decodetypes limits parts to which -decodetext applies -\-textcharset text/plain parts -\-reformat text parts that are not text/plain -\-fixboundary outermost multipart part -\-fixcte multipart part -\-fixtype all except multipart and message parts +.ta \w'\-crlflinebreaks 'u +\-decodetext base64 and quoted-printable encoded text parts +\-decodetypes limits parts to which -decodetext applies +\-crlflinebreaks text parts +\-textcharset text/plain parts +\-reformat text parts that are not text/plain +\-fixboundary outermost multipart part +\-fixcte multipart part +\-fixtype all except multipart and message parts .fi .RE .PP @@ -409,6 +421,7 @@ is checked. .RB ` msgs "' defaults to cur" .RB ` "\-decodetext 8bit"' .RB ` "\-decodetypes text"' +.RB ` \-crlflinebreaks ' .RB ` \-notextcharset ' .RB ` \-reformat ' .RB ` \-noreplacetextplain ' diff --git a/test/mhfixmsg/test-mhfixmsg b/test/mhfixmsg/test-mhfixmsg index 323fb72a..77de37da 100755 --- a/test/mhfixmsg/test-mhfixmsg +++ b/test/mhfixmsg/test-mhfixmsg @@ -52,6 +52,7 @@ Usage: mhfixmsg [+folder] [msgs] [switches] -decodetext 8bit|7bit -nodecodetext -decodetypes + -[no]crlflinebreaks -[no]textcharset -[no]reformat -[no]replacetextplain @@ -1053,7 +1054,7 @@ run_test 'mhfixmsg last -outfile '"$actual" '' check "$expected" "$actual" -# check stripping of CRs from ASCII text context +# check not stripping of CRs from ASCII text context (default) cat >"$expected" <`mhpath new` <<'EOF' @@ -1080,6 +1081,33 @@ run_prog mhfixmsg last -outfile "$actual" check "$expected" "$actual" +# check stripping of CRs from ASCII text context +cat >"$expected" <`mhpath new` <<'EOF' +MIME-Version: 1.0 +From: sender@example.com +To: recipient@example.com +Subject: message with CR's +Date: Mon, 29 Apr 2013 11:51:45 -0400 + +There were two CR-LF pairs at the end of this sentence. + +EOF + +run_prog mhfixmsg last -outfile "$actual" -nocrlflinebreaks +check "$expected" "$actual" + + # check -replacetextplain prepare_space >"$expected" <c_type == CT_TEXT) { + /* parse_mime() does not set lf_line_endings in struct text, so set it here. */ + if (ct->c_ctparams == NULL) { + if ((ct->c_ctparams = (struct text *) mh_xcalloc (1, sizeof (struct text))) == NULL) { + adios (NULL, "out of memory"); + } + } + ((struct text *) ct->c_ctparams)->lf_line_endings = fx.lf_line_endings; + } + *ctp++ = ct; + } } else { /* * message(s) are coming from a folder @@ -375,7 +397,19 @@ main (int argc, char **argv) { char *msgnam; msgnam = m_name (msgnum); - if ((ct = parse_mime (msgnam))) { *ctp++ = ct; } + if ((ct = parse_mime (msgnam))) { + if (ct->c_type == CT_TEXT) { + /* parse_mime() does not set lf_line_endings in struct text, so set it here. */ + if (ct->c_ctparams == NULL) { + if ((ct->c_ctparams = (struct text *) mh_xcalloc (1, sizeof (struct text))) == + NULL) { + adios (NULL, "out of memory"); + } + } + ((struct text *) ct->c_ctparams)->lf_line_endings = fx.lf_line_endings; + } + *ctp++ = ct; + } } } @@ -1327,8 +1361,7 @@ insert_into_new_mp_alt (CT *ct, int *message_mods) { CT mp_alt = build_multipart_alt (*ct, tp_part, CT_MULTIPART, MULTI_ALTERNATE); if (mp_alt) { - struct multipart *mp = - (struct multipart *) mp_alt->c_ctparams; + struct multipart *mp = (struct multipart *) mp_alt->c_ctparams; if (mp && mp->mp_parts) { mp->mp_parts->mp_part = tp_part; @@ -1761,7 +1794,9 @@ decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mo int status = OK; switch (ct->c_type) { - case CT_TEXT: + case CT_TEXT: { + struct text *const text_content_info = (struct text *) ct->c_ctparams; + if (! should_decode(decodetypes, ct->c_ctinfo.ci_type, ct->c_ctinfo.ci_subtype)) { break; } @@ -1817,7 +1852,9 @@ decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mo report (NULL, ct->c_partno, ct->c_file, "decode%s", ct->c_ctline ? ct->c_ctline : ""); } - strip_crs (ct, message_mods); + if (text_content_info && text_content_info->lf_line_endings) { + strip_crs (ct, message_mods); + } } else { status = NOTOK; } @@ -1829,13 +1866,16 @@ decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mo } case CE_8BIT: case CE_7BIT: - strip_crs (ct, message_mods); + if (text_content_info && text_content_info->lf_line_endings) { + strip_crs (ct, message_mods); + } break; default: break; } break; + } case CT_MULTIPART: { struct multipart *m = (struct multipart *) ct->c_ctparams; diff --git a/uip/mhoutsbr.c b/uip/mhoutsbr.c index 066a4b9a..456a2e27 100644 --- a/uip/mhoutsbr.c +++ b/uip/mhoutsbr.c @@ -426,7 +426,10 @@ writeBase64ct (CT ct, FILE *out) if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK) return NOTOK; - result = writeBase64aux (ce->ce_fp, out, (ct->c_type == CT_TEXT)); + result = writeBase64aux (ce->ce_fp, out, + ct->c_type == CT_TEXT && ct->c_ctparams + ? ((struct text *) ct->c_ctparams)->lf_line_endings == 0 + : 0); (*ct->c_ceclosefnx) (ct); return result; } -- 2.56.0