mikel / mikel/mail

mail gem breaks attachment file with encoding Quoted-Printable.

Open
#1,241 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Ruby
Stars
3.7k
Forks
934
PR merge metrics
No merged PRs in 30d

Description

I found this issue when I encode mail with binary file attachment in Quoted-Printable.

This is sample case.
```ruby
require 'mail'

# 1. Create Mail with Quoted-Printable
mail = Mail.new
part = Mail::Part.new
bin = File.binread("./some_binary.jpg")
part.body = [bin].pack("M")
part.content_transfer_encoding = "quoted-printable"
part.transport_encoding = Mail::Encodings::QuotedPrintable
mail.parts << part
File.binwrite("./tmp/original.eml", mail.to_s)

# 2. Read mail and write

mail = Mail.read("./tmp/original.eml")
File.binwrite("./tmp/after.eml", mail.to_s)
```

This is difference between original.eml and after.eml.

```diff
diff --git 1/tmp/original.eml 2/tmp/after.eml
index 7d2c038..506176e 100644
--- 1/tmp/original.eml
+++ 2/tmp/after.eml
@@ -27,15 +27,16 @@ Content-Transfer-Encoding: quoted-printable
=01=01=00=02=03=00=00=00=00=00=01=02=11=03=12!1=04A=05=13Q"a2q=15=81=B1=FF=
=DA=00=0C=03=01=00=02=11=03=11=00?=00=1C|f=98=08=A2=C6i@=E2=BFW?9=07=B2A=14=
=80=F7=A2=04=E4=FB=D2 =13=C5=02=07=B7=A5"=93=9A =1C=D2=8E=B4=0D=02=03=9E=94=
-=FBD=CCQ=12=93=CD8=14=08=08H=02=9Bl=8C=1A6=DC=F1L=13=8A=00=1E=DEim=10{
+=FBD=CCQ=12=93=CD8=14=08=08H=02=9Bl=8C=1A6=DC=F1L=13=8A=00=1E=DEim=10{=0D=
+
,sM=1C=98=A4=00=80=A5=B6=8B=B6=96=DA=07=E8=06=DAr=98=E9E=00=FBR=DB=88=A0@=
=B6=E3=8C=D3G?=D6=8B=B78=A4=A0&=80=05=1C=D3m=A2m=A5=B6=80=07=B4SE=16&i=14=
=FD(=001=DA=91O=D6=8AS=8E=0D4`=E6=90=D2=03=B6:R=83=EDE=8C=D3m=89=14=C0=19=
-Gz=8CGJ)=039=A6=89=C4=D0=82=81=ED=00qM=00=0E1E =C6M4b=90P";
+Gz=8CGJ)=039=A6=89=C4=D0=82=81=ED=00qM=00=0E1E =C6M4b=90P";=0D
b=9Fl=D1H=3D)=B6=CF\=F5=A0(=14w=A6=DBE"=04=E6=A2S@=03=81=C7=EBM=B4DQ=00=C4=
R=8A=07@=8AzTv=E4=D1J}=A9=A0=CD=00=08=A4v8=A8=C7<=D1=A2=A2S#=8A=01=A0[y=A8=
=C1=E2=8D=B7=EBLA=C9=14=82=80=ED D=7F=D8=D4v=FE=94h"=98=8Fj`=90=18=A6#"=8D=
-=1E=D5=10=07JV:=04=13Q=8C=9A)=07=E1M=04=0CM =02=A4=8C=FB=D4
+=1E=D5=10=07JV:=04=13Q=8C=9A)=07=E1M=04=0CM =02=A4=8C=FB=D4=0D
z=8A6=DE=D4=D1=88=E9@#=AA =81=EFNR>=B4M=A7=A5 =9CI=AB%=03=DA =E2=96=DFj$=01=
Kn S=00a<=C6iG1=D6=88=13=8CR=03=BD =06=13=CC=D2=DB=EDD#=9A@c=A5=00=0C=8AQ=
```

This problem cause from ruby Array#pack("M") behavior.
Ruby's Array#pack("M") doesn't support Quoted-Printable with binary, so ruby does not encode "\n".

```
["\n"].pack("M") # => "\n"
```

and Mail library converts "\n" to "\r\n" in to_crlf. so break binary file.
I found similar issue in #1010

I asked to ruby community. (https://bugs.ruby-lang.org/issues/14352 sorry this discussion in Japanese.) and I got a answer which this behavior should not be change.

so I propose to change implementation in Mail gem's Quoted-Printable encoder as follow.

```diff
diff --git i/lib/mail/encodings/quoted_printable.rb w/lib/mail/encodings/quoted_printable.rb
index 193778c..6a04833 100644
--- i/lib/mail/encodings/quoted_printable.rb
+++ w/lib/mail/encodings/quoted_printable.rb
@@ -16,11 +16,18 @@ module Mail
# Decode the string from Quoted-Printable. Cope with hard line breaks
# that were incorrectly encoded as hex instead of literal CRLF.
def self.decode(str)
- str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first
+ str.unpack("M").first
end

def self.encode(str)
- [str].pack("M")
+ [str].pack("M").gsub(/(=0D\n|(?

Contributor guide

Open the contributing guide

Research direction

Start in lib/mail/encodings/quoted_printable.rb and reproduce the binary-attachment round trip using the Ruby Array#pack("M") behavior shown in the issue. Review the quoted-printable encoding tests, including the text case that currently fails with the proposed change; done means preserving binary attachments while making the intended compatibility behavior explicit.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.