From 4b1e543b5b624ea674c59f7081c45a47819b9db0 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 9 Dec 2015 13:01:29 -0600 Subject: [PATCH] Recognize multi-line reply headers when there are quoted ones below --- lib/email_reply_parser.rb | 28 ++++++++++++++++++++++------ test/email_reply_parser_test.rb | 7 +++++++ test/emails/broken_reply_header.txt | 29 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 test/emails/broken_reply_header.txt diff --git a/lib/email_reply_parser.rb b/lib/email_reply_parser.rb index 982eaec..eb5880a 100644 --- a/lib/email_reply_parser.rb +++ b/lib/email_reply_parser.rb @@ -82,12 +82,7 @@ def read(text) # Normalize line endings. text.gsub!("\r\n", "\n") - # Check for multi-line reply headers. Some clients break up - # the "On DATE, NAME wrote:" line into multiple lines. - if text =~ /^(?!On.*On\s.+?wrote:)(On\s(.+?)wrote:)$/nm - # Remove all new lines from the reply header. - text.gsub! $1, $1.gsub("\n", " ") - end + text = repair_broken_reply_header(text) # Some users may reply directly above a line of underscores. # In order to ensure that these fragments are split correctly, @@ -229,6 +224,27 @@ def finish_fragment end @fragment = nil end + + # Stitch together multi-line reply headers. Some clients break up + # the "On DATE, NAME wrote:" line into multiple lines. + # + # If the latest reply has a line beginning with 'On', then the match + # will start too early and contain another match. + # So we repair the inner match via recursion + def repair_broken_reply_header(text) + pattern = /^(On\s(.+?)wrote:)$/nm + if text =~ pattern + match = $1 + tail = match[1..-1] + if tail =~ pattern + text.gsub tail, repair_broken_reply_header(tail) + else + text.gsub match, match.gsub("\n", " ") + end + else + text + end + end end ### Fragments diff --git a/test/email_reply_parser_test.rb b/test/email_reply_parser_test.rb index 6710838..54948b3 100644 --- a/test/email_reply_parser_test.rb +++ b/test/email_reply_parser_test.rb @@ -115,6 +115,13 @@ def test_deals_with_multiline_reply_headers assert_match /Was this/, reply.fragments[1].to_s end + def test_multiline_and_quoted_reply_headers + reply = email :broken_reply_header + + assert_match /^On the other hand/, reply.fragments[0].to_s + assert_match /^On Wed, Dec 9/, reply.fragments[1].to_s + end + def test_deals_with_windows_line_endings reply = email :email_1_7 diff --git a/test/emails/broken_reply_header.txt b/test/emails/broken_reply_header.txt new file mode 100644 index 0000000..fadbd30 --- /dev/null +++ b/test/emails/broken_reply_header.txt @@ -0,0 +1,29 @@ +This is the latest reply. + +The thread contains two previous replies, and the most recent reply header has a line break in it. + +If we don't patch the line break, the reply header ends up in THIS fragment, instead of in the quoted fragment. + +On the one hand, there could be a line in the reply starting with the word 'On.' + +On the other hand, it could happen TWICE! + +Stranger + +On Wed, Dec 9, Mister Example + +wrote: +> Hey, Stranger, +> It's mrmister again, and I hope you reply to me. I also hope this fragment has my reply header in it! +> +> -mrmister +> +> On Tue, 2011-03-01 at 18:02 +0530, Stranger Jones wrote: +>> Hey, mrmister, +>> +>> I'm getting tired of writing fake email text. +>> +>> Stranger + +-- +My name is Stranger