Skip to content

Commit

Permalink
Massive clean up in folding and encoding, making it a lot more smart …
Browse files Browse the repository at this point in the history
…and cleaner
  • Loading branch information
mikel committed Apr 10, 2010
1 parent b69273d commit cdf244c
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 38 deletions.
2 changes: 1 addition & 1 deletion lib/mail/fields/common/common_field.rb
Expand Up @@ -29,7 +29,7 @@ def default
end

def field_length
@length ||= name.length + value.length + ': '.length
@length ||= "#{name}: #{encode(decoded)}".length
end

def responsible_for?( val )
Expand Down
41 changes: 24 additions & 17 deletions lib/mail/fields/unstructured_field.rb
Expand Up @@ -101,18 +101,26 @@ def do_decode
# preference to other places where the field could be folded, even if
# it is allowed elsewhere.
def wrapped_value # :nodoc:
case
when decoded.to_s.ascii_only? && field_length <= 78
"#{name}: #{decoded}"
when field_length <= 25 # Allow for =?ISO-8859-1?B?=...=
"#{name}: #{encode(decoded)}"
else
@folded_line = []
@unfolded_line = decoded.clone
fold("#{name}: ".length)
folded = @folded_line.map { |l| l unless l.blank? }.compact.join("\r\n\t")
"#{name}: #{folded}"
@folded_line = []
@unfolded_line = decoded.to_s.clone
fold("#{name}: ".length)
wrap_lines(name, @folded_line)
end

def wrap_lines(name, folded_lines)
result = []
index = 0
result[index] = "#{name}: #{folded_lines.shift}"
folded_lines.each do |line|
if (result[index] + line).length < 77
result[index] << " " + line
else
result[index] << "\r\n\t"
index += 1
result[index] = line
end
end
result.join
end

def fold(prepend = 0) # :nodoc:
Expand All @@ -124,29 +132,28 @@ def fold(prepend = 0) # :nodoc:
wrap = true
wspp = limit if wspp == 0
@folded_line << encode(@unfolded_line.slice!(0...wspp).strip)
@folded_line.flatten!
# if no last whitespace, find the first
elsif wspp = @unfolded_line =~ /[ \t][^ \T]/
wrap = true
wspp = limit if wspp == 0
@folded_line << encode(@unfolded_line.slice!(0...wspp).strip)
@folded_line.flatten!
# if no whitespace, don't wrap
else
wrap = false
end

if wrap && @unfolded_line.length > limit
fold
else
@folded_line << encode(@unfolded_line)
@folded_line.flatten!
end
end

def encode(value)
if value.ascii_only?
value
else
Encodings.q_value_encode(value, @charset).gsub(" ", "\r\n\t")
end
Encodings.q_value_encode(value, @charset).split(" ")
end

end
Expand Down
26 changes: 17 additions & 9 deletions spec/mail/encoding_spec.rb
Expand Up @@ -14,58 +14,66 @@
mail = Mail.new
mail.charset = 'utf-8'
mail.subject = "This is あ string"
mail[:subject].encoded.gsub("UTF-8", "UTF8").should == "Subject: =?UTF8?Q?This_is_=E3=81=82=?=\r\n\t string\r\n"
result = "Subject: =?UTF8?Q?This_is_=E3=81=82=?= string\r\n"
mail[:subject].encoded.gsub("UTF-8", "UTF8").should == result
end

it "should allow you to send in unencoded strings to address fields and encode them" do
mail = Mail.new
mail.charset = 'utf-8'
mail.to = "Mikel Lindsああr <mikel@test.lindsaar.net>"
mail[:to].encoded.should == "To: Mikel =?UTF-8?B?TGluZHPjgYLjgYJy?= <mikel@test.lindsaar.net>\r\n"
result = "To: Mikel =?UTF-8?B?TGluZHPjgYLjgYJy?= <mikel@test.lindsaar.net>\r\n"
mail[:to].encoded.should == result
end

it "should allow you to send in unencoded strings to address fields and encode them" do
mail = Mail.new
mail.charset = 'utf-8'
mail.to = "あdあ <ada@test.lindsaar.net>"
mail[:to].encoded.should == "To: =?UTF-8?B?44GCZOOBgg==?= <ada@test.lindsaar.net>\r\n"
result = "To: =?UTF-8?B?44GCZOOBgg==?= <ada@test.lindsaar.net>\r\n"
mail[:to].encoded.should == result
end

it "should allow you to send in multiple unencoded strings to address fields and encode them" do
mail = Mail.new
mail.charset = 'utf-8'
mail.to = ["Mikel Lindsああr <mikel@test.lindsaar.net>", "あdあ <ada@test.lindsaar.net>"]
mail[:to].encoded.should == "To: Mikel =?UTF-8?B?TGluZHPjgYLjgYJy?= <mikel@test.lindsaar.net>, \r\n\t=?UTF-8?B?44GCZOOBgg==?= <ada@test.lindsaar.net>\r\n"
result = "To: Mikel =?UTF-8?B?TGluZHPjgYLjgYJy?= <mikel@test.lindsaar.net>, \r\n\t=?UTF-8?B?44GCZOOBgg==?= <ada@test.lindsaar.net>\r\n"
mail[:to].encoded.should == result
end

it "should allow you to send unquoted non us-ascii strings, with spaces in them" do
mail = Mail.new
mail.charset = 'utf-8'
mail.to = ["Foo áëô îü <extended@example.net>"]
mail[:to].encoded.should == "To: Foo =?UTF-8?B?w6HDq8O0?= =?UTF-8?B?IMOuw7w=?= <extended@example.net>\r\n"
result = "To: Foo =?UTF-8?B?w6HDq8O0?= =?UTF-8?B?IMOuw7w=?= <extended@example.net>\r\n"
mail[:to].encoded.should == result
end

it "should allow you to send in multiple unencoded strings to any address field" do
mail = Mail.new
mail.charset = 'utf-8'
['To', 'From', 'Cc', 'Reply-To'].each do |field|
mail.send("#{field.downcase.gsub("-", '_')}=", ["Mikel Lindsああr <mikel@test.lindsaar.net>", "あdあ <ada@test.lindsaar.net>"])
mail[field].encoded.should == "#{field}: Mikel =?UTF-8?B?TGluZHPjgYLjgYJy?= <mikel@test.lindsaar.net>, \r\n\t=?UTF-8?B?44GCZOOBgg==?= <ada@test.lindsaar.net>\r\n"
result = "#{field}: Mikel =?UTF-8?B?TGluZHPjgYLjgYJy?= <mikel@test.lindsaar.net>, \r\n\t=?UTF-8?B?44GCZOOBgg==?= <ada@test.lindsaar.net>\r\n"
mail[field].encoded.should == result
end
end

it "should handle groups" do
mail = Mail.new
mail.charset = 'utf-8'
mail.to = "test1@lindsaar.net, group: test2@lindsaar.net, me@lindsaar.net;"
mail[:to].encoded.should == "To: test1@lindsaar.net, \r\n\tgroup: test2@lindsaar.net, \r\n\tme@lindsaar.net;\r\n"
result = "To: test1@lindsaar.net, \r\n\tgroup: test2@lindsaar.net, \r\n\tme@lindsaar.net;\r\n"
mail[:to].encoded.should == result
end

it "should handle groups with funky characters" do
mail = Mail.new
mail.charset = 'utf-8'
mail.to = '"Mikel Lindsああr" <test1@lindsaar.net>, group: "あdあ" <test2@lindsaar.net>, me@lindsaar.net;'
mail[:to].encoded.should == "To: =?UTF-8?B?TWlrZWwgTGluZHPjgYLjgYJy?= <test1@lindsaar.net>, \r\n\tgroup: =?UTF-8?B?44GCZOOBgg==?= <test2@lindsaar.net>, \r\n\tme@lindsaar.net;\r\n"
result = "To: =?UTF-8?B?TWlrZWwgTGluZHPjgYLjgYJy?= <test1@lindsaar.net>, \r\n\tgroup: =?UTF-8?B?44GCZOOBgg==?= <test2@lindsaar.net>, \r\n\tme@lindsaar.net;\r\n"
mail[:to].encoded.should == result
end

describe "quouting token safe chars" do
Expand Down Expand Up @@ -112,7 +120,7 @@
subject.force_encoding('ISO8859-1') if RUBY_VERSION > '1.9'
mail.subject = subject
result = mail[:subject].encoded
string = "Subject: =?ISO-8859-1?Q?This_is_=E3=81=82=?=\r\n\t string\r\n"
string = "Subject: =?ISO-8859-1?Q?This_is_=E3=81=82=?= string\r\n"
if RUBY_VERSION > '1.9'
string.force_encoding('ISO8859-1')
result.force_encoding('ISO8859-1')
Expand Down
2 changes: 1 addition & 1 deletion spec/mail/encodings_spec.rb
Expand Up @@ -226,7 +226,7 @@
if RUBY_VERSION >= '1.9'
original.force_encoding('UTF-8') if RUBY_VERSION >= '1.9'
end
result = "Subject: =?UTF8?Q?=D0=92=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=?=\r\n\t=?UTF8?Q?=B8=D0=B5=D0=92=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=?=\r\n\t=?UTF8?Q?=D0=BD=D0=B8=D0=B5=D0=92=D0=B0=D1=88=D0=B5=D0=B3=D0=BE=D0=BF=D0=B0=D1=80=D0=?=\r\n\t=?UTF8?Q?=BE=D0=BB=D1=8F_This=?=\r\n\tis a NUT?????Z__string that== could (break)\r\n\t anything\r\n"
result = "Subject: =?UTF8?Q?=D0=92=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=?=\r\n\t=?UTF8?Q?=B8=D0=B5=D0=92=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=?=\r\n\t=?UTF8?Q?=D0=BD=D0=B8=D0=B5=D0=92=D0=B0=D1=88=D0=B5=D0=B3=D0=BE=D0=BF=D0=B0=D1=80=D0=?=\r\n\t=?UTF8?Q?=BE=D0=BB=D1=8F_This=?= is a NUT?????Z__string that== could (break)\r\n\tanything\r\n"
mail = Mail.new
mail.subject = original
mail[:subject].decoded.should == original
Expand Down
2 changes: 1 addition & 1 deletion spec/mail/field_spec.rb
Expand Up @@ -156,7 +156,7 @@
describe "passing an encoding" do
it "should allow you to send in unencoded strings to fields and encode them" do
subject = Mail::SubjectField.new("This is あ string", 'utf-8')
subject.encoded.should == "Subject: =?UTF-8?Q?This_is_=E3=81=82=?=\r\n\t string\r\n"
subject.encoded.should == "Subject: =?UTF-8?Q?This_is_=E3=81=82=?= string\r\n"
subject.decoded.should == "This is あ string"
end

Expand Down
20 changes: 11 additions & 9 deletions spec/mail/fields/unstructured_field_spec.rb
Expand Up @@ -66,7 +66,8 @@

it "should just add the CRLF at the end of the line" do
@field = Mail::SubjectField.new("Subject: =?utf-8?Q?testing_testing_=D6=A4?=")
@field.encoded.gsub("UTF-8", "UTF8").should == "Subject: testing\r\n\t=?UTF8?Q?_testing_=D6=A4=?=\r\n"
result = "Subject: testing =?UTF8?Q?_testing_=D6=A4=?=\r\n"
@field.encoded.gsub("UTF-8", "UTF8").should == result
@field.decoded.should == "testing testing \326\244"
end

Expand All @@ -75,30 +76,31 @@
describe "folding" do

it "should not fold itself if it is 78 chracters long" do
@field = Mail::UnstructuredField.new("Subject", "This is a subject header message that is _exactly_ 78 characters long")
@field.encoded.should == "Subject: This is a subject header message that is _exactly_ 78 characters long\r\n"
@field = Mail::UnstructuredField.new("Subject", "This is a subject header message that is _exactly_ 78 characters....")
@field.encoded.should == "Subject: This is a subject header message that is _exactly_ 78 characters....\r\n"
end

it "should fold itself if it is 79 chracters long" do
@field = Mail::UnstructuredField.new("Subject", "This is a subject header message that is _exactly_ 79 characters long.")
@field.encoded.should == "Subject: This is a subject header message that is _exactly_ 79 characters\r\n\t long.\r\n"
@field = Mail::UnstructuredField.new("Subject", "This is a subject header message that is _exactly_ 79 characters long")
result = "Subject: This is a subject header message that is _exactly_ 79 characters\r\n\tlong\r\n"
@field.encoded.should == result
end

it "should fold itself if it is 997 chracters long" do
@field = Mail::UnstructuredField.new("Subject", "This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. And this makes it 997 long")
@field = Mail::UnstructuredField.new("Subject", "This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. This is a subject header message that is going to be 997 characters long. And this makes it 997....")
lines = @field.encoded.split("\r\n\t")
lines.each { |line| line.length.should < 78 }
end

it "should fold itself if it is 998 characters long" do
value = "This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. And this makes it 998 long."
value = "This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. This is a subject header message that is going to be 998 characters long. And this makes it 998 long"
@field = Mail::UnstructuredField.new("Subject", value)
lines = @field.encoded.split("\r\n\t")
lines.each { |line| line.length.should < 78 }
end

it "should fold itself if it is 999 characters long" do
value = "This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. And this makes it 999 long.."
value = "This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. This is a subject header message that is going to be 999 characters long. And this makes it 999 long."
@field = Mail::UnstructuredField.new("Subject", value)
lines = @field.encoded.split("\r\n\t")
lines.each { |line| line.length.should < 78 }
Expand All @@ -113,7 +115,7 @@
else
$KCODE = 'u'
end
result = "Subject: =?UTF8?Q?This_is_=E3=81=82=?=\r\n\treally long string Thi\r\n\t=?UTF8?Q?s_is_=E3=81=82_really_long=?=\r\n\t=?UTF8?Q?string_This_is_=E3=81=82=?=\r\n\treally long string Thi\r\n\t=?UTF8?Q?s_is_=E3=81=82_really_long=?=\r\n\t=?UTF8?Q?string_This_is_=E3=81=82=?=\r\n\t really long string\r\n"
result = "Subject: =?UTF8?Q?This_is_=E3=81=82=?= really long string Thi\r\n\t=?UTF8?Q?s_is_=E3=81=82_really_long=?= =?UTF8?Q?string_This_is_=E3=81=82=?=\r\n\treally long string Thi =?UTF8?Q?s_is_=E3=81=82_really_long=?=\r\n\t=?UTF8?Q?string_This_is_=E3=81=82=?= really long string\r\n"
@field.encoded.gsub("UTF-8", "UTF8").should == result
@field.decoded.should == string
$KCODE = @original if RUBY_VERSION < '1.9'
Expand Down

0 comments on commit cdf244c

Please sign in to comment.