From 931e88d1c3e9d1bc4bad3a230e50c97180daad9d Mon Sep 17 00:00:00 2001 From: David Levine Date: Tue, 4 Oct 2016 19:08:42 -0400 Subject: [PATCH 01/16] Have mhfixmsg add a C-T-E at the message level, based on the least restrictive C-T-E of its parts. --- test/mhfixmsg/test-mhfixmsg | 7 +-- uip/mhfixmsg.c | 101 ++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/test/mhfixmsg/test-mhfixmsg b/test/mhfixmsg/test-mhfixmsg index bd63446a..9e29fa00 100755 --- a/test/mhfixmsg/test-mhfixmsg +++ b/test/mhfixmsg/test-mhfixmsg @@ -697,6 +697,7 @@ From: sender@example.com Subject: mhfixmsg successful decode of text/plain with failed binary decode MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" +Content-Transfer-Encoding: binary ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="iso-8859-1" @@ -725,6 +726,7 @@ From: sender@example.com Subject: mhfixmsg binary decode test MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=\"----- =_aaaaaaaaaa0\" +Content-Transfer-Encoding: binary ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset=\"UTF-8\"; name=\"nul+square.txt\" @@ -821,6 +823,7 @@ From: sender@example.com Subject: mhfixmsg textcharset test MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" +Content-Transfer-Encoding: 8bit ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="utf-8"; name="square.txt" @@ -1204,7 +1207,6 @@ Yes, the text/plain part really was empty. ------=_Part_876302 Content-Type: text/html; charset="UTF-8" -Content-Transfer-Encoding: 8bit Content-Disposition: inline @@ -1234,7 +1236,6 @@ Content-Disposition: inline ------=_Part_876302 Content-Type: text/html; charset="UTF-8" -Content-Transfer-Encoding: 8bit Content-Disposition: inline @@ -1276,7 +1277,6 @@ Content-Disposition: inline ------=_Part_876302 Content-Type: text/html; charset="UTF-8" -Content-Transfer-Encoding: 8bit Content-Disposition: inline @@ -1723,6 +1723,7 @@ Date: Wed, 28 Sep 2016 11:24:28 -0400 Subject: invalid header parameter encoding MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=001a114dd3e8fe9c56053d92f414 +Content-Transfer-Encoding: 8bit --001a114dd3e8fe9c56053d92f414 Content-Type: text/plain; charset=UTF-8 diff --git a/uip/mhfixmsg.c b/uip/mhfixmsg.c index c237ea87..b7025514 100644 --- a/uip/mhfixmsg.c +++ b/uip/mhfixmsg.c @@ -116,6 +116,9 @@ static int decode_text_parts (CT, int, const char *, int *); static int should_decode(const char *, const char *, const char *); static int content_encoding (CT, const char **); static int strip_crs (CT, int *); +static void update_cte (CT); +static int least_restrictive_encoding (CT); +static int less_restrictive (int, int); static int convert_charsets (CT, char *, int *); static int fix_always (CT, int *); static int fix_filename_param (char *, char *, PM *, PM *); @@ -527,6 +530,7 @@ mhfixmsgsbr (CT *ctp, const fix_transformations *fx, char *outfile) { if (status == OK && fx->decodetext) { status = decode_text_parts (*ctp, fx->decodetext, fx->decodetypes, &message_mods); + update_cte (*ctp); } if (status == OK && fx->textcharset != NULL) { status = convert_charsets (*ctp, fx->textcharset, &message_mods); @@ -2278,6 +2282,103 @@ strip_crs (CT ct, int *message_mods) { } +/* + * Add/update, if necessary, the message C-T-E, based on the least restrictive + * of the part C-T-E's. + */ +static void +update_cte (CT ct) { + const int least_restrictive_enc = least_restrictive_encoding (ct); + + if (least_restrictive_enc != CE_UNKNOWN && + least_restrictive_enc != CE_7BIT) { + char *cte = concat (" ", ce_str (least_restrictive_enc), "\n", NULL); + HF hf; + int found_cte = 0; + + /* Update/add Content-Transfer-Encoding header field. */ + for (hf = ct->c_first_hf; hf; hf = hf->next) { + if (! strcasecmp (ENCODING_FIELD, hf->name)) { + found_cte = 1; + free (hf->value); + hf->value = cte; + } + } + if (! found_cte) { + add_header (ct, add (ENCODING_FIELD, NULL), cte); + } + } +} + + +/* + * Find the least restrictive encoding (7bit, 8bit, binary) of the parts + * within a message. + */ +static int +least_restrictive_encoding (CT ct) { + int encoding = CE_UNKNOWN; + + switch (ct->c_type) { + case CT_MULTIPART: { + struct multipart *m = (struct multipart *) ct->c_ctparams; + struct part *part; + + for (part = m->mp_parts; part; part = part->mp_next) { + const int part_encoding = + least_restrictive_encoding (part->mp_part); + + if (less_restrictive (encoding, part_encoding)) { + encoding = part_encoding; + } + } + break; + } + + case CT_MESSAGE: + if (ct->c_subtype == MESSAGE_EXTERNAL) { + struct exbody *e = (struct exbody *) ct->c_ctparams; + const int part_encoding = + least_restrictive_encoding (e->eb_content); + + if (less_restrictive (encoding, part_encoding)) { + encoding = part_encoding; + } + } + break; + + default: { + if (less_restrictive (encoding, ct->c_encoding)) { + encoding = ct->c_encoding; + } + }} + + return encoding; +} + + +/* + * Return whether the second encoding is less restrictive than the first, where + * "less restrictive" is in the sense used by RFC 2045 Secs. 6.1 and 6.4. So, + * CE_BINARY is less restrictive than CE_8BIT and + * CE_8BIT is less restrictive than CE_7BIT. + */ +static int +less_restrictive (int encoding, int second_encoding) { + switch (second_encoding) { + case CE_BINARY: + return encoding != CE_BINARY; + case CE_8BIT: + return encoding != CE_BINARY && encoding != CE_8BIT; + case CE_7BIT: + return encoding != CE_BINARY && encoding != CE_8BIT && + encoding != CE_7BIT; + default : + return 0; + } +} + + /* * Convert character set of each part. */ -- 2.48.1 From 1e1fb5ef266efa509026583cfc08ff3e38cdcaf1 Mon Sep 17 00:00:00 2001 From: David Levine Date: Tue, 4 Oct 2016 21:24:10 -0400 Subject: [PATCH 02/16] Enabled check for 8-bit content in all text parts, not just those with no specified character set, so that a Content-Transfer-Encoding header will be added if needed. --- docs/pending-release-notes | 5 ++++- test/mhbuild/test-utf8-body | 43 ++++++++++++++++++++++++++++++++++--- uip/mhbuildsbr.c | 6 ++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/docs/pending-release-notes b/docs/pending-release-notes index cb3f09d8..ffcfcc9f 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -56,7 +56,6 @@ NEW FEATURES filename parameters in Content-Type and Content-Disposition headers, respectively. - ----------------- OBSOLETE FEATURES ----------------- @@ -88,3 +87,7 @@ BUG FIXES scan(1), inc(1), and the other programs that rely on the format scanner. - The first character of some very short (less than 4 characters) message bodies is no longer dropped. +- mhfixmsg now adds a Content-Transfer-Encoding header at the message level, + if needed after decoding text parts. +- mhbuild now checks whether all text parts need a Content-Transfer-Encoding + header, not just those with a character set not specified. diff --git a/test/mhbuild/test-utf8-body b/test/mhbuild/test-utf8-body index 1a08a907..cb5aa953 100755 --- a/test/mhbuild/test-utf8-body +++ b/test/mhbuild/test-utf8-body @@ -49,7 +49,9 @@ test_attachment () check "${testname}.actual" "$1" } + # check that 7-bit body isn't encoded +start_test "7-bit body isn't encoded" cat > "${MH_TEST_DIR}/attachment.txt" < "${MH_TEST_DIR}/Mail/draft" < To: Somebody @@ -127,9 +130,41 @@ EOF test_attachment "${testname}.expected" -# Repeat the previous test, but make sure that the locale is set to C, which -# should cause mhbuild to fail +# check that 8-bit attachment gets C-T-E +start_test '8-bit attachment gets C-T-E' +rm -f "${MH_TEST_DIR}/attachment.txt" +cat > "${MH_TEST_DIR}/attachment.txt" < "${MH_TEST_DIR}/Mail/draft" < +To: Somebody +Subject: Test +Attach: $MH_TEST_DIR/attachment.txt +EOF + +cat > "${testname}.expected" < +To: Somebody +Subject: Test +MIME-Version: 1.0 +Content-Type: text/plain; name="attachment.txt" +Content-Description: attachment.txt +Content-Disposition: attachment; filename="attachment.txt" +Content-Transfer-Encoding: 8bit +Date: + +8-bit attachment, ¡Ay, caramba! +EOF + +test_attachment "${testname}.expected" + + +# check 8-bit body but make sure that the locale is set to C, which +# should cause mhbuild to fail +start_test '8-bit body with C locale' cat > "${MH_TEST_DIR}/Mail/draft" < To: Somebody @@ -152,8 +187,10 @@ EOF check "$expected" "$actual" + rm -f ${MHMTSCONF} "${MH_TEST_DIR}/attachment.txt" +finish_test exit ${failed:-0} # emacs hackage to ensure that it writes the inverted exclamation diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index 26d8524d..b59ea58b 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -1453,15 +1453,17 @@ scan_content (CT ct, size_t maxunencoded) * Decide what to check while scanning this content. Note that * for text content we always check for 8bit characters if the * charset is unspecified, because that controls whether or not the - * character set is us-ascii or retrieved from the locale. + * character set is us-ascii or retrieved from the locale. And + * we check even if the charset is specified, to allow setting + * the proper Content-Transfer-Encoding. */ if (ct->c_type == CT_TEXT) { t = (struct text *) ct->c_ctparams; if (t->tx_charset == CHARSET_UNSPECIFIED) { - check8bit = 1; checknul = 1; } + check8bit = 1; } switch (ct->c_reqencoding) { -- 2.48.1 From fb6ea8dca0129dbb93ecb5fe1147a7b03138bbf8 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Tue, 4 Oct 2016 22:00:12 -0400 Subject: [PATCH 03/16] Improve these comments a bit. --- sbr/netsec.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/sbr/netsec.c b/sbr/netsec.c index f66a2124..eb28cbac 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -108,17 +108,26 @@ static int checkascii(const unsigned char *byte, size_t len); /* * How this code works, in general. * - * _If_ we are using no encryption or SASL encryption, then we buffer the - * network data through ns_inbuffer and ns_outbuffer. That should be - * relatively self-explanatory. + * _If_ we are using no encryption then we buffer the network data + * through ns_inbuffer and ns_outbuffer. That should be relatively + * self-explanatory. * - * If we are using SSL for encryption, then use a buffering BIO for output - * (that's just easier). Still do buffering for reads; when we need more - * data we call the BIO_read() function to fill our local buffer. + * If we use encryption, then ns_inbuffer and ns_outbuffer contain the + * cleartext data. When it comes time to send the encrypted data on the + * (either from a flush or the buffer is full) we either use BIO_write() + * for TLS or sasl_encode() (followed by a write() for Cyrus-SASL. For + * reads we either use BIO_read() (TLS) or do a network read into a + * temporary buffer and use sasl_decode() (Cyrus-SASL). Note that if + * negotiate TLS then we disable SASL encryption. * - * For SASL, we make use of (for now) the Cyrus-SASL library. For some - * mechanisms, we implement those mechanisms directly since the Cyrus SASL - * library doesn't support them (like OAuth). + * We used to use a buffering BIO for the reads/writes for TLS, but it + * ended up being complicated to special-case the buffering for everything + * except TLS, so the buffering is now unified, no matter which encryption + * method is being used (even none). + * + * For SASL authentication, we make use of (for now) the Cyrus-SASL + * library. For some mechanisms, we implement those mechanisms directly + * since the Cyrus SASL library doesn't support them (like OAuth). */ /* -- 2.48.1 From e87f37c27828723317a71291e31b34f39ec09098 Mon Sep 17 00:00:00 2001 From: David Levine Date: Wed, 5 Oct 2016 16:02:47 -0400 Subject: [PATCH 04/16] Don't set c_reqencoding in repl, let mhbuild set it based on the content. --- test/repl/test-convert | 61 ++++++++++++++++++++++++++++++++++++++---- uip/mhbuildsbr.c | 10 ------- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/test/repl/test-convert b/test/repl/test-convert index d2d398a7..f7c15dc0 100755 --- a/test/repl/test-convert +++ b/test/repl/test-convert @@ -25,6 +25,7 @@ LC_ALL=C; export LC_ALL # check -convertarg with multiple parts and additional text in draft +start_test '-convertarg with multiple parts and additional text in draft' cat >"$expected" <<'EOF' From: recipient@example.com To: sender@example.com @@ -74,11 +75,13 @@ check "$actual" "$expected" #### Make sure that this works with 8-bit encoding. +finish_test require_locale en_US.utf-8 en_US.utf8 LC_ALL=en_US.UTF-8; export LC_ALL -# check -convertarg with multiple parts and no additional text in draft +# check -convertarg with multiple parts, 7 bit +start_test '-convertarg with multiple parts, 7 bit' cat >"$expected" <<'EOF' From: recipient@example.com To: sender@example.com @@ -89,7 +92,6 @@ Comments: In-reply-to sender@example.com message dated "Thu, 11 Dec 2014 08:19:02 -0600." MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" -Content-Transfer-Encoding: 8bit sender@example.com writes: @@ -112,7 +114,7 @@ Content-Type: text/plain This is part 1. --_001_ -Content-Type: text/plain +Content-Type: text/plain; charset="UTF-8" This is part 2. @@ -124,7 +126,55 @@ mhbuild "$actual" check "$actual" "$expected" +# check -convertarg with multiple parts, 8 bit +start_test '-convertarg with multiple parts, 8 bit' +cat >"$expected" <<'EOF' +From: recipient@example.com +To: sender@example.com +cc: +Fcc: +outbox +Subject: Re: test +Comments: In-reply-to sender@example.com + message dated "Thu, 11 Dec 2014 08:19:02 -0600." +MIME-Version: 1.0 +Content-Type: text/plain; charset="UTF-8" +Content-Transfer-Encoding: 8bit + +sender@example.com writes: + +> This is part 1. + +> This is §2, with a non-ASCII character. +EOF + +cat >`mhpath new` <<'EOF' +From: sender@example.com +To: recipient@example.com +Subject: test +Date: Thu, 11 Dec 2014 08:19:02 -0600 +Content-Type: multipart/mixed; boundary="_001_" +MIME-Version: 1.0 + +--_001_ +Content-Type: text/plain + +This is part 1. + +--_001_ +Content-Type: text/plain; charset="UTF-8" + +This is §2, with a non-ASCII character. + +--_001_-- +EOF + +repl -filter mhl.replywithoutbody -convertarg text/plain '' -nowhatnowproc last +mhbuild "$actual" +check "$actual" "$expected" + + # check message with text part in multipart/related +start_test 'check message with text part in multipart/related' cat >"$expected" <<'EOF' From: recipient@example.com To: sender@example.com @@ -135,7 +185,6 @@ Comments: In-reply-to sender@example.com message dated "." MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" -Content-Transfer-Encoding: 8bit sender@example.com writes: @@ -153,7 +202,7 @@ MIME-Version: 1.0 Content-Type: multipart/related; type="text/plain"; boundary="_002_" --_002_ -Content-Type: text/plain +Content-Type: text/plain; charset="UTF-8" This is a test. @@ -168,6 +217,7 @@ check "$actual" "$expected" # check reply to calendar request +start_test 'check reply to calendar request' cat >"$expected" <<'EOF' From: recipient@example.com To: sender@example.com @@ -276,4 +326,5 @@ SIGNATURE=Recip mhbuild - <`mhpath +`/draft | egrep -v '^DTSTAMP:' >"$actual" check "$actual" "$expected" +finish_test exit $failed diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index b59ea58b..bf6c8cb9 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -2292,11 +2292,6 @@ expand_pseudoheader (CT ct, CT *text_plain_ct, struct multipart *m, } else { set_charset (reply_ct, -1); charset = get_param (reply_ct->c_ctinfo.ci_first_pm, "charset", '?', 1); - if (reply_ct->c_reqencoding == CE_UNKNOWN) { - /* Assume that 8bit is sufficient (for text). */ - reply_ct->c_reqencoding = - strcasecmp (charset, "US-ASCII") ? CE_8BIT : CE_7BIT; - } } /* Concatenate text/plain parts. */ @@ -2309,11 +2304,6 @@ expand_pseudoheader (CT ct, CT *text_plain_ct, struct multipart *m, /* Make sure that the charset is set in the text/plain part. */ set_charset (*text_plain_ct, -1); - if ((*text_plain_ct)->c_reqencoding == CE_UNKNOWN) { - /* Assume that 8bit is sufficient (for text). */ - (*text_plain_ct)->c_reqencoding = - strcasecmp (charset, "US-ASCII") ? CE_8BIT : CE_7BIT; - } } if (*text_plain_ct) { -- 2.48.1 From 9fc84ef71655096d563cf9190ce3e127f638c6d9 Mon Sep 17 00:00:00 2001 From: David Levine Date: Wed, 5 Oct 2016 16:04:11 -0400 Subject: [PATCH 05/16] Call c_ceclosefnx in mhfixmsg set_ce(). It doesn't seem to help reduce file descriptor leaks, but can't hurt. --- uip/mhfixmsg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uip/mhfixmsg.c b/uip/mhfixmsg.c index b7025514..61b3b8b3 100644 --- a/uip/mhfixmsg.c +++ b/uip/mhfixmsg.c @@ -1160,6 +1160,10 @@ set_ce (CT ct, int encoding) { ct->c_cefile.ce_file to the name of the file containing the contents. */ + if (ct->c_ceclosefnx) { + (*ct->c_ceclosefnx) (ct); + } + /* Restore the cefile. */ ct->c_cefile = decoded_content_info; -- 2.48.1 From 641e461b10b6fd320ed6a13ec4075754d29bbb0b Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 09:11:45 -0400 Subject: [PATCH 06/16] Enable SMTP 8BITMIME for messages with 8-bit content: 1) In post, look for a Content-Transfer-Encoding header. It has to be the header for the message, not any MIME parts. If found, post trusts that it's correct. If there isn't one, post scans the entire message body for any 8-bit bytes. 2) If the message body is 8-bit: If the server supports 8BITMIME, enable it. If the server doesn't support 8BITMIME, fail with a message to user that they need to encode the message for 7-bit transport. --- docs/pending-release-notes | 3 ++ mts/smtp/smtp.c | 38 +++++++++----- mts/smtp/smtp.h | 2 +- test/mhmail/test-mhmail | 4 +- test/post/test-post-basic | 103 ++++++++++++++++++++++++++++++++++++- uip/post.c | 71 ++++++++++++++++++++++--- 6 files changed, 197 insertions(+), 24 deletions(-) diff --git a/docs/pending-release-notes b/docs/pending-release-notes index ffcfcc9f..bec2a5ae 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -55,6 +55,9 @@ NEW FEATURES - mhfixmsg now replaces RFC 2047 encoding with RFC 2231 encoding of name and filename parameters in Content-Type and Content-Disposition headers, respectively. +- If a message body contains 8-bit bytes, post(8) uses SMTP 8BITMIME if the + server supports it. If not, post fails with a message to the user to + encode the message for 7-bit transport. ----------------- OBSOLETE FEATURES diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index b5e68fa1..c94ac313 100644 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -441,19 +441,33 @@ rclient (char *server, char *service) } int -sm_winit (char *from, int smtputf8) +sm_winit (char *from, int smtputf8, int eightbit) { - const char *const mail_parameters = smtputf8 - ? " BODY=8BITMIME SMTPUTF8" - : ""; - - /* Just for information, if an attempt is made to send to an 8-bit - address without specifying SMTPUTF8, Gmail responds with - 555 5.5.2 Syntax error. - Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */ - if (smtputf8 && (! EHLOset("8BITMIME") || ! EHLOset("SMTPUTF8"))) { - sm_end (NOTOK); - return RP_UCMD; + const char *mail_parameters = ""; + + if (smtputf8) { + /* Just for information, if an attempt is made to send to an 8-bit + address without specifying SMTPUTF8, Gmail responds with + 555 5.5.2 Syntax error. + Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */ + if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) { + mail_parameters = " BODY=8BITMIME SMTPUTF8"; + } else { + sm_end (NOTOK); + return RP_UCMD; + } + } else if (eightbit) { + /* Comply with RFC 6152, for messages that have any 8-bit characters + in their body. */ + if (EHLOset ("8BITMIME")) { + mail_parameters = " BODY=8BITMIME"; + } else { + advise (NULL, "SMTP server does not support 8BITMIME, not sending.\n" + "Suggest encoding message for 7-bit transport by setting your\n" + "locale to C, and/or specifying *b64 in mhbuild directives.\n"); + sm_end (NOTOK); + return RP_UCMD; + } } switch (smtalk (SM_MAIL, "MAIL FROM:<%s>%s", from, mail_parameters)) { diff --git a/mts/smtp/smtp.h b/mts/smtp/smtp.h index fb842fe6..df02c1a4 100644 --- a/mts/smtp/smtp.h +++ b/mts/smtp/smtp.h @@ -18,7 +18,7 @@ struct smtp { /* int client (); */ int sm_init (char *, char *, char *, int, int, int, int, const char *, const char *, const char *, int); -int sm_winit (char *, int); +int sm_winit (char *, int, int); int sm_wadr (char *, char *, char *); int sm_waend (void); int sm_wtxt (char *, int); diff --git a/test/mhmail/test-mhmail b/test/mhmail/test-mhmail index ac7bc86d..2e7375f0 100755 --- a/test/mhmail/test-mhmail +++ b/test/mhmail/test-mhmail @@ -772,7 +772,7 @@ To: recipient@example.com From: sender27@example.com MIME-Version: 1.0 Content-Type: text/plain;charset=utf-8 -Content-Transfer-Encoding: 8bit +Content-Transfer-Encoding: 7bit Date: with added header fields @@ -783,7 +783,7 @@ EOF test_mhmail "$expected" \ "-from sender27@example.com -headerfield MIME-Version:1.0 \ -headerfield Content-Type:text/plain;charset=utf-8 \ --headerfield Content-Transfer-Encoding:8bit" \ +-headerfield Content-Transfer-Encoding:7bit" \ -b 'with added header fields' [ ${failed:-0} -eq 0 ] || exit ${failed:-0} diff --git a/test/post/test-post-basic b/test/post/test-post-basic index c198007b..2cd512a6 100755 --- a/test/post/test-post-basic +++ b/test/post/test-post-basic @@ -16,7 +16,7 @@ fi # Basic test - Simple message, single user, single recipient. Note that # we test dot-stuffing here as well. # - +start_test 'simple message' cat > "${MH_TEST_DIR}/Mail/draft" < To: Somebody Else @@ -49,7 +49,7 @@ test_post "${testname}.actual" "${testname}.expected" # # Make sure a draft without a From: is rejected # - +start_test 'reject draft without a From:' cat > "${MH_TEST_DIR}/Mail/draft" < Subject: Blank Test @@ -67,6 +67,7 @@ send: message not delivered to anyone" # Make sure that empty Nmh-* header lines are ignored, and that post # warns about non-empty ones. # +start_test 'ignore Nmh-* header lines' cat > "${MH_TEST_DIR}/Mail/draft" < To: Somebody Else @@ -105,5 +106,103 @@ test_post "${testname}.actual" "${testname}.expected" \ check "${testname}.send_output" "${testname}.expected_send_output" +# +# 8-bit without 8BITMIME support +# +start_test '8-bit without 8BITMIME support' +cat > "${MH_TEST_DIR}/Mail/draft" < +To: Somebody Else +Subject: Test +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +This is a test +. +EOF + +cat > "${testname}.expected" < "${testname}.err.expected" <"${testname}.err" +set +e +check "${testname}.err" "${testname}.err.expected" + +# +# 8-bit with 8BITMIME support +# +start_test '8-bit with 8BITMIME support' +# Cheat: SMTPUTF8 enables 8BITMIME in fakestmp +SMTPUTF8=1; export SMTPUTF8 +cat > "${testname}.expected" < BODY=8BITMIME +RCPT TO: +DATA +From: Mr Nobody +To: Somebody Else +Subject: Test +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit +Date: + +This is a test +.. +. +QUIT +EOF +test_post "${testname}.actual" "${testname}.expected" + + +# +# 8-bit with 8BITMIME support, inferred from content +# +start_test '8-bit, inferred, with 8BITMIME support' +cat > "${MH_TEST_DIR}/Mail/draft" < +To: Somebody Else +Subject: Test +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" + +This is a test, with a non-ascii character: § +. +EOF + +cat > "${testname}.expected" < BODY=8BITMIME +RCPT TO: +DATA +From: Mr Nobody +To: Somebody Else +Subject: Test +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Date: + +This is a test, with a non-ascii character: § +.. +. +QUIT +EOF +test_post "${testname}.actual" "${testname}.expected" + +finish_test exit ${failed:-0} diff --git a/uip/post.c b/uip/post.c index db9c0298..d1e58ebf 100644 --- a/uip/post.c +++ b/uip/post.c @@ -156,6 +156,8 @@ struct headers { #define HDCC 0x0400 /* another undocumented feature */ #define HONE 0x0800 /* Only (zero or) one address allowed */ #define HEFM 0x1000 /* Envelope-From: header */ +#define HMIM 0x2000 /* MIME-Version: header */ +#define HCTE 0x4000 /* Content-Transfer-Encoding: header */ /* * flags for headers->set @@ -168,7 +170,7 @@ struct headers { #define MSND 0x0020 /* we've seen a Sender: */ #define MRSN 0x0040 /* We've seen a Resent-Sendr:*/ #define MEFM 0x0080 /* We've seen Envelope-From: */ - +#define MMIM 0x0100 /* We've seen Mime-Version: */ static struct headers NHeaders[] = { { "Return-Path", HBAD, 0 }, @@ -185,6 +187,8 @@ static struct headers NHeaders[] = { { "Message-ID", HBAD, 0 }, { "Fcc", HFCC, 0 }, { "Envelope-From", HADR|HONE|HEFM, MEFM }, + { "MIME-Version", HMIM, MMIM }, + { "Content-Transfer-Encoding", HCTE, 0 }, { NULL, 0, 0 } }; @@ -208,6 +212,8 @@ static struct headers RHeaders[] = { { "Bcc", HADR|HTRY|HBCC|HNIL, 0 }, { "Fcc", HIGN, 0 }, { "Envelope-From", HADR|HONE|HEFM, MEFM }, + { "MIME-Version", HMIM, MMIM }, + { "Content-Transfer-Encoding", HCTE, 0 }, { NULL, 0, 0 } }; @@ -257,6 +263,8 @@ static char fullfrom[BUFSIZ]; /* full contents of From header */ static char *filter = NULL; /* the filter for BCC'ing */ static char *subject = NULL; /* the subject field for BCC'ing */ static char *fccfold[FCCS]; /* foldernames for FCC'ing */ +enum encoding { UNKNOWN = 0, BINARY = 1, SEVENBIT = 7, EIGHTBIT = 8 }; +static enum encoding cte = UNKNOWN; static struct headers *hdrtab; /* table for the message we're doing */ @@ -297,6 +305,7 @@ static void fcc (char *, char *); static void die (char *, char *, ...); static void post (char *, int, int, int, char *, int, char *); static void do_text (char *file, int fd); +static int scan_input (int, int *); static void do_an_address (struct mailname *, int); static void do_addresses (int, int); static int find_prefix (void); @@ -830,7 +839,15 @@ putfmt (char *name, char *str, int *eai, FILE *out) insert_fcc (hdr, pp); return; } - + if (hdr->flags & HCTE) { + if (strncasecmp (str, "7bit", 4) == 0) { + cte = SEVENBIT; + } else if (strncasecmp (str, "8bit", 4) == 0) { + cte = EIGHTBIT; + } else if (strncasecmp (str, "binary", 6) == 0) { + cte = BINARY; + } + } if (!(hdr->flags & HADR)) { fprintf (out, "%s: %s", name, str); return; @@ -1600,7 +1617,6 @@ static void post (char *file, int bccque, int talk, int eai, char *envelope, int oauth_flag, char *auth_svc) { - int fd; int retval, i; pid_t child_id; @@ -1646,16 +1662,33 @@ post (char *file, int bccque, int talk, int eai, char *envelope, break; } } else { + const int fd = open (file, O_RDONLY); + int eightbit = 0; + + if (fd == NOTOK) { + die (file, "unable to re-open"); + } + + if (msgflags & MMIM && cte != UNKNOWN) { + /* MIME message with C-T-E header. (BINARYMIME isn't + supported, use 8BITMIME instead for binary.) */ + eightbit = cte != SEVENBIT; + } else { + if (scan_input (fd, &eightbit) == NOTOK) { + close (fd); + die (file, "problem reading from"); + } + } + if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch, verbose, snoop, sasl, saslmech, user, oauth_flag ? auth_svc : NULL, tls)) - || rp_isbad (retval = sm_winit (envelope, eai))) { + || rp_isbad (retval = sm_winit (envelope, eai, eightbit))) { + close (fd); die (NULL, "problem initializing server; %s", rp_string (retval)); } do_addresses (bccque, talk && verbose); - if ((fd = open (file, O_RDONLY)) == NOTOK) - die (file, "unable to re-open"); do_text (file, fd); close (fd); fflush (stdout); @@ -1688,10 +1721,13 @@ verify_all_addresses (int talk, int eai, char *envelope, int oauth_flag, sigon (); if (!whomsw || checksw) { + /* Not sending message body, so don't need to use 8BITMIME. */ + const int eightbit = 0; + if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch, verbose, snoop, sasl, saslmech, user, oauth_flag ? auth_svc : NULL, tls)) - || rp_isbad (retval = sm_winit (envelope, eai))) { + || rp_isbad (retval = sm_winit (envelope, eai, eightbit))) { die (NULL, "problem initializing server; %s", rp_string (retval)); } } @@ -1819,6 +1855,27 @@ do_text (char *file, int fd) } +/* + * See if input has any 8-bit bytes. + */ +static int +scan_input (int fd, int *eightbit) { + int state; + char buf[BUFSIZ]; + + lseek (fd, (off_t) 0, SEEK_SET); + + while ((state = read (fd, buf, sizeof buf)) > 0) { + if (contains8bit (buf, buf + state)) { + *eightbit = 1; + return OK; + } + } + + return state == NOTOK ? NOTOK : OK; +} + + /* * SIGNAL HANDLING */ -- 2.48.1 From 0ed8b63e4d197d97d1e51c2a4d95318c963132a2 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 11:30:19 -0400 Subject: [PATCH 07/16] Accommodate FreeBSD script(1), and add debugging statement to diagnose failure on ubuntu buildbot host. --- test/install-mh/test-version-check | 62 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index 591eb1ee..23446608 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -6,6 +6,7 @@ # ###################################################### +set -x # ???? debug on buildbot set -e if test -z "${MH_OBJ_DIR}"; then @@ -18,6 +19,26 @@ fi setup_test require_prog script +#### FreeBSD script, e.g., doesn't use -c to identify a command to run. +if script -c 'echo OK' /dev/null 2>&1 | egrep 'OK' >/dev/null; then + script_command_opt='-c' +else + script_command_opt='' +fi + +#### Run a command but don't wait for user input. script(1) seems to do +#### what we want by not waiting when run in the background. +run_without_input() { + if [ "$script_command_opt" = -c ]; then + #### -c takes single argument with command + arguments. + script -c "$*" -f -q "$actual" >/dev/null & + else + #### E.g., FreeBSD. Don't combine command arguments. + script -f -q "$actual" "$@" >/dev/null & + fi + + wait $! +} actual="$MH_TEST_DIR/test-version-check$$.actual" context="${MH_TEST_DIR}"/Mail/context @@ -30,83 +51,68 @@ mv -f "$context.NEW" "$context" start_test 'mhparam skips the welcome message' -# The command will wait for the user to hit return. script seems to do what -# we want by not waiting when run in the background. -script -c 'mhparam path last' -f -q "$actual" >/dev/null & -wait $! +run_without_input mhparam path grep 'Welcome to nmh version ' "$actual" >/dev/null && false -rm "$actual" # Make sure that version wasn't added to context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null && false -finish_test +rm "$actual" start_test 'Welcome: disable in profile skips the welcome message' cp "${MH}" "${MH}-welcome" printf 'Welcome: disable\n' >> "${MH}-welcome" -MH="${MH}-welcome" script -c 'pick last' -f -q "$actual" >/dev/null & -wait $! +(MH="${MH}-welcome" run_without_input pick last) grep 'Welcome to nmh version ' "$actual" >/dev/null && false -rm "$actual" # Make sure that version wasn't added to context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null && false -finish_test +rm "$actual" start_test 'with welcome message' -script -c 'pick last' -f -q "$actual" >/dev/null & -wait $! +run_without_input pick last grep 'Welcome to nmh version ' "$actual" >/dev/null -rm "$actual" # Make sure that version was added to context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null -finish_test +rm "$actual" start_test 'without welcome message' # After running the previous test, this one should not have # the welcome message. -script -c 'pick last' -f -q "$actual" >/dev/null & -wait $! +run_without_input pick last grep 'Welcome to nmh version ' "$actual" >/dev/null && false -rm "$actual" # Make sure that version is still in context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null -finish_test +rm "$actual" start_test 'with MHCONTEXT, welcome only if older' MHCONTEXT="${MH_TEST_DIR}/Mail/context2"; export MHCONTEXT printf 'Version: nmh-1.5\n' >"${MHCONTEXT}" -script -c 'pick last' -f -q "$actual" >/dev/null & -wait $! +run_without_input pick last grep 'Welcome to nmh version ' "$actual" >/dev/null # And make sure that version did get updated in context. grep "^${version}$" "${MHCONTEXT}" >/dev/null rm "$actual" -finish_test start_test "with MHCONTEXT doesn't welcome if newer" printf 'Version: nmh-10000.0\n' >"${MHCONTEXT}" -script -c 'pick last' -f -q "$actual" >/dev/null & -wait $! +run_without_input pick last grep 'Welcome to nmh version ' "$actual" >/dev/null && false -rm "$actual" # And make sure that version didn't get updated in context. grep '^Version: nmh-10000.0$' "${MHCONTEXT}" >/dev/null -finish_test +rm "$actual" start_test 'with MHCONTEXT but no version, no welcome and update' printf '' >"${MHCONTEXT}" -script -c 'pick last' -f -q "$actual" >/dev/null & -wait $! +run_without_input pick last grep 'Welcome to nmh version ' "$actual" >/dev/null && false # And make sure that version did get updated in context. grep "^${version}$" "${MHCONTEXT}" >/dev/null rm "$actual" -finish_test +finish_test exit $failed -- 2.48.1 From 529443998ae91368c92b497feeabef1abc7a3d17 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 11:47:26 -0400 Subject: [PATCH 08/16] 1) Use script(1) -t 0 instead of -f on FreeBSD. 2) Added another debug printout. --- test/install-mh/test-version-check | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index 23446608..095f09db 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -31,10 +31,10 @@ fi run_without_input() { if [ "$script_command_opt" = -c ]; then #### -c takes single argument with command + arguments. - script -c "$*" -f -q "$actual" >/dev/null & + script -q -f -c "$*" "$actual" >/dev/null & else #### E.g., FreeBSD. Don't combine command arguments. - script -f -q "$actual" "$@" >/dev/null & + script -q -t 0 "$actual" "$@" >/dev/null & fi wait $! @@ -70,6 +70,7 @@ rm "$actual" start_test 'with welcome message' run_without_input pick last +cat "${MH_TEST_DIR}/Mail/context"; cat "$actual" # ???? debug grep 'Welcome to nmh version ' "$actual" >/dev/null # Make sure that version was added to context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null -- 2.48.1 From 669cbe97d3a0db2d441ee123466cda091ec9b6d5 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 11:59:24 -0400 Subject: [PATCH 09/16] More test-version-check debugging, on Linux buildbot hosts. --- test/install-mh/test-version-check | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index 095f09db..e4ec7a9e 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -29,6 +29,7 @@ fi #### Run a command but don't wait for user input. script(1) seems to do #### what we want by not waiting when run in the background. run_without_input() { + echo MHCONTEXT = $MHCONTEXT # ???? if [ "$script_command_opt" = -c ]; then #### -c takes single argument with command + arguments. script -q -f -c "$*" "$actual" >/dev/null & @@ -70,7 +71,6 @@ rm "$actual" start_test 'with welcome message' run_without_input pick last -cat "${MH_TEST_DIR}/Mail/context"; cat "$actual" # ???? debug grep 'Welcome to nmh version ' "$actual" >/dev/null # Make sure that version was added to context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null @@ -90,7 +90,9 @@ rm "$actual" start_test 'with MHCONTEXT, welcome only if older' MHCONTEXT="${MH_TEST_DIR}/Mail/context2"; export MHCONTEXT printf 'Version: nmh-1.5\n' >"${MHCONTEXT}" +cat "$MHCONTEXT" run_without_input pick last +cat "$actual" # ???? grep 'Welcome to nmh version ' "$actual" >/dev/null # And make sure that version did get updated in context. grep "^${version}$" "${MHCONTEXT}" >/dev/null -- 2.48.1 From b90476d799a2580fbd69e13325c56be5d56e1396 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 12:16:53 -0400 Subject: [PATCH 10/16] More debugging. --- test/install-mh/test-version-check | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index e4ec7a9e..8085a445 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -71,6 +71,7 @@ rm "$actual" start_test 'with welcome message' run_without_input pick last +cat "${MH_TEST_DIR}/Mail/context"; cat "$actual" # ???? grep 'Welcome to nmh version ' "$actual" >/dev/null # Make sure that version was added to context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null @@ -90,9 +91,8 @@ rm "$actual" start_test 'with MHCONTEXT, welcome only if older' MHCONTEXT="${MH_TEST_DIR}/Mail/context2"; export MHCONTEXT printf 'Version: nmh-1.5\n' >"${MHCONTEXT}" -cat "$MHCONTEXT" +cat "$MHCONTEXT" # ???? run_without_input pick last -cat "$actual" # ???? grep 'Welcome to nmh version ' "$actual" >/dev/null # And make sure that version did get updated in context. grep "^${version}$" "${MHCONTEXT}" >/dev/null -- 2.48.1 From dffeac1b8759bbedb385d0d2003a6d4c6c008cd5 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 12:45:52 -0400 Subject: [PATCH 11/16] Replaced strtod() with strtof(). --- sbr/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbr/utils.c b/sbr/utils.c index 1b1b4738..cf920a39 100644 --- a/sbr/utils.c +++ b/sbr/utils.c @@ -429,7 +429,7 @@ nmh_version_changed (int older) { if (older) { /* Convert the version strings to floats and compare them. This will break for versions with multiple decimal points, etc. */ - const float current_version = strtod (VERSION, NULL); + const float current_version = strtof (VERSION, NULL); const float old_version = context_version && strncmp (context_version, "nmh-", 4) == 0 ? strtof (context_version + 4, NULL) -- 2.48.1 From d1506bc0824ad871cd0e694ad834188fe2fc6269 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 12:47:52 -0400 Subject: [PATCH 12/16] More debugging test-version-check. --- test/install-mh/test-version-check | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index 8085a445..16bbcfef 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -6,7 +6,9 @@ # ###################################################### -set -x # ???? debug on buildbot +test -t 0 && echo stdin # ???? +test -t 1 && echo stdout # ???? +test -t 2 && echo stderr # ???? set -e if test -z "${MH_OBJ_DIR}"; then @@ -29,7 +31,6 @@ fi #### Run a command but don't wait for user input. script(1) seems to do #### what we want by not waiting when run in the background. run_without_input() { - echo MHCONTEXT = $MHCONTEXT # ???? if [ "$script_command_opt" = -c ]; then #### -c takes single argument with command + arguments. script -q -f -c "$*" "$actual" >/dev/null & @@ -71,7 +72,6 @@ rm "$actual" start_test 'with welcome message' run_without_input pick last -cat "${MH_TEST_DIR}/Mail/context"; cat "$actual" # ???? grep 'Welcome to nmh version ' "$actual" >/dev/null # Make sure that version was added to context. grep "^${version}$" "${MH_TEST_DIR}/Mail/context" >/dev/null @@ -91,7 +91,6 @@ rm "$actual" start_test 'with MHCONTEXT, welcome only if older' MHCONTEXT="${MH_TEST_DIR}/Mail/context2"; export MHCONTEXT printf 'Version: nmh-1.5\n' >"${MHCONTEXT}" -cat "$MHCONTEXT" # ???? run_without_input pick last grep 'Welcome to nmh version ' "$actual" >/dev/null # And make sure that version did get updated in context. -- 2.48.1 From 28941ade20b965d204435e5d3911dafbcd364123 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 12:57:45 -0400 Subject: [PATCH 13/16] OK, don't run test-version-check if not connected to terminal. --- test/install-mh/test-version-check | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index 16bbcfef..68f2f3d7 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -6,9 +6,6 @@ # ###################################################### -test -t 0 && echo stdin # ???? -test -t 1 && echo stdout # ???? -test -t 2 && echo stderr # ???? set -e if test -z "${MH_OBJ_DIR}"; then @@ -20,6 +17,10 @@ fi setup_test +if ! test -t 0 || ! test -t 1 || ! test -t 2; then + test_skip 'Must be connected to terminal.' +fi + require_prog script #### FreeBSD script, e.g., doesn't use -c to identify a command to run. if script -c 'echo OK' /dev/null 2>&1 | egrep 'OK' >/dev/null; then -- 2.48.1 From 1e40137c8f82911cdd866b1c7d614c4a18725305 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 13:07:20 -0400 Subject: [PATCH 14/16] Fixed test negations in test-version-check to work with make distcheck. --- test/install-mh/test-version-check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index 68f2f3d7..4b1fb8c8 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -17,7 +17,7 @@ fi setup_test -if ! test -t 0 || ! test -t 1 || ! test -t 2; then +if test ! -t 0 || test ! -t 1 || test ! -t 2; then test_skip 'Must be connected to terminal.' fi -- 2.48.1 From cefa77d7637d2581919715acc353b9203b949017 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 13:10:19 -0400 Subject: [PATCH 15/16] Updated test-version-check test_skip message. --- test/install-mh/test-version-check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/install-mh/test-version-check b/test/install-mh/test-version-check index 4b1fb8c8..459cf8b3 100755 --- a/test/install-mh/test-version-check +++ b/test/install-mh/test-version-check @@ -18,7 +18,7 @@ fi setup_test if test ! -t 0 || test ! -t 1 || test ! -t 2; then - test_skip 'Must be connected to terminal.' + test_skip 'must be connected to terminal' fi require_prog script -- 2.48.1 From e6d87a04919616cf2a6ea6cbda81d5fbc31cc8a2 Mon Sep 17 00:00:00 2001 From: David Levine Date: Thu, 6 Oct 2016 17:53:00 -0400 Subject: [PATCH 16/16] Moved scan_input() from uip/post.c to sbr/utils.c. --- h/utils.h | 14 ++++++++++++++ sbr/utils.c | 22 ++++++++++++++++++++++ uip/post.c | 22 ---------------------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/h/utils.h b/h/utils.h index 00249be4..9e515989 100644 --- a/h/utils.h +++ b/h/utils.h @@ -58,6 +58,20 @@ char *nmh_strcasestr (const char *, const char *); */ int contains8bit(const char *start, const char *end); +/* + * See if file has any 8-bit bytes. + * Arguments include: + * + * fd - file descriptor + * eightbit - address of result, will be set to 1 if the file contains + * any 8-bit bytes, 0 otherwise. + * + * Returns OK on success, NOTOK on read failure. + * + */ +int scan_input (int fd, int *eightbit); + + /* * Compares prior version of nmh with current version. Returns 1 * if they compare the be the same, 0 if not. diff --git a/sbr/utils.c b/sbr/utils.c index cf920a39..e5ed66aa 100644 --- a/sbr/utils.c +++ b/sbr/utils.c @@ -484,3 +484,25 @@ contains8bit(const char *start, const char *end) return 0; } + + +/* + * See if input has any 8-bit bytes. + */ +int +scan_input (int fd, int *eightbit) { + int state; + char buf[BUFSIZ]; + + *eightbit = 0; + lseek (fd, (off_t) 0, SEEK_SET); + + while ((state = read (fd, buf, sizeof buf)) > 0) { + if (contains8bit (buf, buf + state)) { + *eightbit = 1; + return OK; + } + } + + return state == NOTOK ? NOTOK : OK; +} diff --git a/uip/post.c b/uip/post.c index d1e58ebf..d385219e 100644 --- a/uip/post.c +++ b/uip/post.c @@ -305,7 +305,6 @@ static void fcc (char *, char *); static void die (char *, char *, ...); static void post (char *, int, int, int, char *, int, char *); static void do_text (char *file, int fd); -static int scan_input (int, int *); static void do_an_address (struct mailname *, int); static void do_addresses (int, int); static int find_prefix (void); @@ -1855,27 +1854,6 @@ do_text (char *file, int fd) } -/* - * See if input has any 8-bit bytes. - */ -static int -scan_input (int fd, int *eightbit) { - int state; - char buf[BUFSIZ]; - - lseek (fd, (off_t) 0, SEEK_SET); - - while ((state = read (fd, buf, sizeof buf)) > 0) { - if (contains8bit (buf, buf + state)) { - *eightbit = 1; - return OK; - } - } - - return state == NOTOK ? NOTOK : OK; -} - - /* * SIGNAL HANDLING */ -- 2.48.1