Skip to content

Commit 0b68956

Browse files
sgngitster
authored andcommitted
mailinfo: warn if CRLF found in decoded base64/QP email
When SMTP servers receive 8-bit email messages, possibly with only LF as line ending, some of them decide to change said LF to CRLF. Some mailing list softwares, when receive 8-bit email messages, decide to encode those messages in base64 or quoted-printable. If an email is transfered through above mail servers, then distributed by such mailing list softwares, the recipients will receive an email contains a patch mungled with CRLF encoded inside another encoding. Thus, such CR (in CRLF) couldn't be dropped by "mailsplit". Hence, the mailed patch couldn't be applied cleanly. Such accidents have been observed in the wild [1]. Instead of silently rejecting those messages, let's give our users some warnings if such CR (as part of CRLF) is found. [1]: https://nmbug.notmuchmail.org/nmweb/show/m2lf9ejegj.fsf%40guru.guru-group.fi Signed-off-by: Đoàn Trần Công Danh <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent dd9323b commit 0b68956

File tree

7 files changed

+121
-0
lines changed

7 files changed

+121
-0
lines changed

mailinfo.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,11 @@ static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
994994
const char *rest;
995995

996996
if (!mi->format_flowed) {
997+
if (len >= 2 &&
998+
line->buf[len - 2] == '\r' &&
999+
line->buf[len - 1] == '\n') {
1000+
mi->have_quoted_cr = 1;
1001+
}
9971002
handle_filter(mi, line);
9981003
return;
9991004
}
@@ -1033,6 +1038,12 @@ static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
10331038
handle_filter(mi, line);
10341039
}
10351040

1041+
static void summarize_quoted_cr(struct mailinfo *mi)
1042+
{
1043+
if (mi->have_quoted_cr)
1044+
warning(_("quoted CRLF detected"));
1045+
}
1046+
10361047
static void handle_body(struct mailinfo *mi, struct strbuf *line)
10371048
{
10381049
struct strbuf prev = STRBUF_INIT;
@@ -1051,6 +1062,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
10511062
handle_filter(mi, &prev);
10521063
strbuf_reset(&prev);
10531064
}
1065+
summarize_quoted_cr(mi);
1066+
mi->have_quoted_cr = 0;
10541067
if (!handle_boundary(mi, line))
10551068
goto handle_body_out;
10561069
}
@@ -1100,6 +1113,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
11001113

11011114
if (prev.len)
11021115
handle_filter(mi, &prev);
1116+
summarize_quoted_cr(mi);
11031117

11041118
flush_inbody_header_accum(mi);
11051119

mailinfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct mailinfo {
2424
struct strbuf charset;
2525
unsigned int format_flowed:1;
2626
unsigned int delsp:1;
27+
unsigned int have_quoted_cr:1;
2728
char *message_id;
2829
enum {
2930
TE_DONTCARE, TE_QP, TE_BASE64

t/t5100-mailinfo.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,34 @@ test_expect_success 'mailinfo handles unusual header whitespace' '
228228
test_cmp expect actual
229229
'
230230

231+
check_quoted_cr_mail () {
232+
mail="$1" && shift &&
233+
git mailinfo -u "$@" "$mail.msg" "$mail.patch" \
234+
<"$mail" >"$mail.info" 2>"$mail.err" &&
235+
test_cmp "$mail-expected.msg" "$mail.msg" &&
236+
test_cmp "$mail-expected.patch" "$mail.patch" &&
237+
test_cmp "$DATA/quoted-cr-info" "$mail.info"
238+
}
239+
240+
test_expect_success 'split base64 email with quoted-cr' '
241+
mkdir quoted-cr &&
242+
git mailsplit -oquoted-cr "$DATA/quoted-cr.mbox" >quoted-cr/last &&
243+
test $(cat quoted-cr/last) = 2
244+
'
245+
246+
test_expect_success 'mailinfo warn CR in base64 encoded email' '
247+
sed -e "s/%%$//" -e "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-msg" \
248+
>quoted-cr/0001-expected.msg &&
249+
sed "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-msg" \
250+
>quoted-cr/0002-expected.msg &&
251+
sed -e "s/%%$//" -e "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-patch" \
252+
>quoted-cr/0001-expected.patch &&
253+
sed "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-patch" \
254+
>quoted-cr/0002-expected.patch &&
255+
check_quoted_cr_mail quoted-cr/0001 &&
256+
test_must_be_empty quoted-cr/0001.err &&
257+
check_quoted_cr_mail quoted-cr/0002 &&
258+
grep "quoted CRLF detected" quoted-cr/0002.err
259+
'
260+
231261
test_done

t/t5100/quoted-cr-info

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Author: A U Thor
2+
3+
Subject: sample
4+
Date: Mon, 3 Aug 2020 22:40:55 +0700
5+

t/t5100/quoted-cr-msg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
On different distro, %%pytest is suffixed with different patterns.%%
2+
%%

t/t5100/quoted-cr-patch

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---%%
2+
configure | 2 +-%%
3+
1 file changed, 1 insertion(+), 1 deletion(-)%%
4+
%%
5+
diff --git a/configure b/configure%%
6+
index db3538b3..f7c1c095 100755%%
7+
--- a/configure%%
8+
+++ b/configure%%
9+
@@ -814,7 +814,7 @@ if [ $have_python3 -eq 1 ]; then%%
10+
printf "%%Checking for python3 pytest (>= 3.0)... "%%
11+
conf=$(mktemp)%%
12+
printf "[pytest]\nminversion=3.0\n" > $conf%%
13+
- if pytest-3 -c $conf --version >/dev/null 2>&1; then%%
14+
+ if "$python" -m pytest -c $conf --version >/dev/null 2>&1; then%%
15+
printf "Yes.\n"%%
16+
have_python3_pytest=1%%
17+
else%%
18+
-- %%
19+
2.28.0%%
20+
_______________________________________________
21+
example mailing list -- [email protected]
22+
To unsubscribe send an email to [email protected]

t/t5100/quoted-cr.mbox

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
From nobody Mon Sep 17 00:00:00 2001
2+
From: A U Thor <[email protected]>
3+
4+
Subject: [PATCH v2] sample
5+
Date: Mon, 3 Aug 2020 22:40:55 +0700
6+
Message-Id: <[email protected]>
7+
Content-Type: text/plain; charset="utf-8"
8+
Content-Transfer-Encoding: base64
9+
10+
T24gZGlmZmVyZW50IGRpc3RybywgDXB5dGVzdCBpcyBzdWZmaXhlZCB3aXRoIGRpZmZlcmVudCBw
11+
YXR0ZXJucy4KCi0tLQogY29uZmlndXJlIHwgMiArLQogMSBmaWxlIGNoYW5nZWQsIDEgaW5zZXJ0
12+
aW9uKCspLCAxIGRlbGV0aW9uKC0pCgpkaWZmIC0tZ2l0IGEvY29uZmlndXJlIGIvY29uZmlndXJl
13+
CmluZGV4IGRiMzUzOGIzLi5mN2MxYzA5NSAxMDA3NTUKLS0tIGEvY29uZmlndXJlCisrKyBiL2Nv
14+
bmZpZ3VyZQpAQCAtODE0LDcgKzgxNCw3IEBAIGlmIFsgJGhhdmVfcHl0aG9uMyAtZXEgMSBdOyB0
15+
aGVuCiAgICAgcHJpbnRmICINQ2hlY2tpbmcgZm9yIHB5dGhvbjMgcHl0ZXN0ICg+PSAzLjApLi4u
16+
ICIKICAgICBjb25mPSQobWt0ZW1wKQogICAgIHByaW50ZiAiW3B5dGVzdF1cbm1pbnZlcnNpb249
17+
My4wXG4iID4gJGNvbmYKLSAgICBpZiBweXRlc3QtMyAtYyAkY29uZiAtLXZlcnNpb24gPi9kZXYv
18+
bnVsbCAyPiYxOyB0aGVuCisgICAgaWYgIiRweXRob24iIC1tIHB5dGVzdCAtYyAkY29uZiAtLXZl
19+
cnNpb24gPi9kZXYvbnVsbCAyPiYxOyB0aGVuCiAgICAgICAgIHByaW50ZiAiWWVzLlxuIgogICAg
20+
ICAgICBoYXZlX3B5dGhvbjNfcHl0ZXN0PTEKICAgICBlbHNlCi0tIAoyLjI4LjAKX19fX19fX19f
21+
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KZXhhbXBsZSBtYWlsaW5nIGxp
22+
c3QgLS0gbGlzdEBleGFtcGxlLm9yZwpUbyB1bnN1YnNjcmliZSBzZW5kIGFuIGVtYWlsIHRvIGxp
23+
c3QtbGVhdmVAZXhhbXBsZS5vcmcK
24+
25+
From nobody Mon Sep 17 00:00:00 2001
26+
From: A U Thor <[email protected]>
27+
28+
Subject: [PATCH v2] sample
29+
Date: Mon, 3 Aug 2020 22:40:55 +0700
30+
Message-Id: <[email protected]>
31+
Content-Type: text/plain; charset="utf-8"
32+
Content-Transfer-Encoding: base64
33+
34+
T24gZGlmZmVyZW50IGRpc3RybywgDXB5dGVzdCBpcyBzdWZmaXhlZCB3aXRoIGRpZmZlcmVudCBw
35+
YXR0ZXJucy4NCg0KLS0tDQogY29uZmlndXJlIHwgMiArLQ0KIDEgZmlsZSBjaGFuZ2VkLCAxIGlu
36+
c2VydGlvbigrKSwgMSBkZWxldGlvbigtKQ0KDQpkaWZmIC0tZ2l0IGEvY29uZmlndXJlIGIvY29u
37+
ZmlndXJlDQppbmRleCBkYjM1MzhiMy4uZjdjMWMwOTUgMTAwNzU1DQotLS0gYS9jb25maWd1cmUN
38+
CisrKyBiL2NvbmZpZ3VyZQ0KQEAgLTgxNCw3ICs4MTQsNyBAQCBpZiBbICRoYXZlX3B5dGhvbjMg
39+
LWVxIDEgXTsgdGhlbg0KICAgICBwcmludGYgIg1DaGVja2luZyBmb3IgcHl0aG9uMyBweXRlc3Qg
40+
KD49IDMuMCkuLi4gIg0KICAgICBjb25mPSQobWt0ZW1wKQ0KICAgICBwcmludGYgIltweXRlc3Rd
41+
XG5taW52ZXJzaW9uPTMuMFxuIiA+ICRjb25mDQotICAgIGlmIHB5dGVzdC0zIC1jICRjb25mIC0t
42+
dmVyc2lvbiA+L2Rldi9udWxsIDI+JjE7IHRoZW4NCisgICAgaWYgIiRweXRob24iIC1tIHB5dGVz
43+
dCAtYyAkY29uZiAtLXZlcnNpb24gPi9kZXYvbnVsbCAyPiYxOyB0aGVuDQogICAgICAgICBwcmlu
44+
dGYgIlllcy5cbiINCiAgICAgICAgIGhhdmVfcHl0aG9uM19weXRlc3Q9MQ0KICAgICBlbHNlDQot
45+
LSANCjIuMjguMA0KX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
46+
X18KZXhhbXBsZSBtYWlsaW5nIGxpc3QgLS0gbGlzdEBleGFtcGxlLm9yZwpUbyB1bnN1YnNjcmli
47+
ZSBzZW5kIGFuIGVtYWlsIHRvIGxpc3QtbGVhdmVAZXhhbXBsZS5vcmcK

0 commit comments

Comments
 (0)