Skip to content

Commit

Permalink
When parsing UTF-7, do not raise exceptions (except for B encodings)
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Grayson committed Aug 15, 2020
1 parent 8fbb17d commit 9240215
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 5 deletions.
16 changes: 11 additions & 5 deletions lib/mail/version_specific/ruby_1_9.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ def encode(string, charset)
end

class BestEffortCharsetEncoder
def encode(string, charset)
def encode(string, charset, raise_utf7_exceptions = false)
case charset
when /utf-?7/i
Mail::Ruby19.decode_utf7(string)
Mail::Ruby19.decode_utf7(string, raise_utf7_exceptions)
else
string.force_encoding(pick_encoding(charset))
end
Expand Down Expand Up @@ -106,10 +106,16 @@ def Ruby19.encode_utf7(string)
end.force_encoding(Encoding::ASCII_8BIT)
end

def Ruby19.decode_utf7(utf7)
def Ruby19.decode_utf7(utf7, raise_utf7_exceptions)
utf7.gsub(/&([^-]+)?-/n) do
if $1
($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE)
utf_16be = ($1.tr(",", "/") + "===").unpack("m")[0].force_encoding(Encoding::UTF_16BE)
if raise_utf7_exceptions
# special case - in B encoding, raise exceptions on conversion errors
utf_16be.encode(Encoding::UTF_8)
else
transcode_to_scrubbed_utf8(utf_16be)
end
else
"&"
end
Expand All @@ -126,7 +132,7 @@ def Ruby19.b_value_decode(str)
if match
charset = match[1]
str = Ruby19.decode_base64(match[2])
str = charset_encoder.encode(str, charset)
str = charset_encoder.encode(str, charset, true)
end
transcode_to_scrubbed_utf8(str)
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError, Encoding::InvalidByteSequenceError
Expand Down
4 changes: 4 additions & 0 deletions spec/fixtures/emails/multi_charset/quoted-printable.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

Valid: =E6=98=AF Invalid: =E6
3 changes: 3 additions & 0 deletions spec/fixtures/emails/multi_charset/utf7.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Content-Type: text/plain; charset="utf-7"

Valid: &Zi8- Invalid: &5g-
10 changes: 10 additions & 0 deletions spec/mail/message_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,16 @@ def create_mail_with_splat_args
expect(raw_message.encoding).to eq original_encoding if raw_message.respond_to?(:encoding)
end

it "should parse utf-7 email without raising exceptions" do
mail = read_fixture('emails', 'multi_charset', 'utf7.eml')
expect(mail.decoded.chomp).to eq "Valid: 是 Invalid: �"
end

it "should parse quoted-printable email without raising exceptions" do
mail = read_fixture('emails', 'multi_charset', 'quoted-printable.eml')
expect(mail.decoded.chomp).to eq "Valid: 是 Invalid: �"
end

if '1.9+'.respond_to?(:encoding)
it "should be able to normalize CRLFs on non-UTF8 encodings" do
File.open(fixture_path('emails', 'multi_charset', 'japanese_shift_jis.eml'), 'rb') do |io|
Expand Down

0 comments on commit 9240215

Please sign in to comment.