Skip to content

Commit

Permalink
Merge remote-tracking branch 'github/split-to-spec' into HEAD
Browse files Browse the repository at this point in the history
Conflicts:
	lib/mail/message.rb
  • Loading branch information
ConradIrwin committed Apr 25, 2013
2 parents 6bc2d02 + 8f5c455 commit 812c19a
Show file tree
Hide file tree
Showing 27 changed files with 167 additions and 72 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rdoc
Expand Up @@ -9,6 +9,7 @@ Features:
* Setting the html_ or text_part sets a default text/html or text/plain content type (jeremy)
* Setting the html_ or text_part to nil removes it (jeremy)
* Addresses without a parsable email or display name still format as their raw text (jeremy)
* Close pull request 504 - Alias GB2312 charset to GB18030 Ruby encoding (bpot)
* Close pull request 399 - Accept :ca_path and :ca_file options for SMTP delivery (ndbroadbent)
* Close pull request 389 - Don't add superfluous message headers to MIME parts (djmaze, jeremy)

Expand All @@ -29,6 +30,8 @@ Bugs:
* The Received header may contain zero name/value pairs, qmail-style (jeremy)
* Fix that setting an attachment with a :mime_type and :encoding would override the :encoding (jeremy)
* Fix that declaring an html_part but no text_part would use multipart/alternative anyway (jeremy)
* Close pull request 508 - Don't add an extra CRLF to MIME parts; split MIME parts on correct CRLF boundaries (Aalanar)
* Close pull request 506 - Escape backslashes in quoted strings (ConradIrwin)
* Close pull request 496 - Correctly handle quoted-printable line breaks (jeremy)
* Close pull request 493 - Repair misencoded quoted-printable line breaks (jeremy)
* Close pull request 487 - Extract comments from group email addresses (bpot)
Expand All @@ -46,6 +49,7 @@ Housekeeping:
* Remove i18n dependency and last vestiges of activesupport dependency in specs (jeremy)
* Clarify that Sender is a single address, not an address list (jeremy)
* Add an MIT-LICENSE file to make licensing clear & obvious; update to 2013 (jeremy)
* Close pull request 501 - Tighten up header/body whitespace splitting (ConradIrwin)
* Close pull request 499 - Clean up some dead code (ConradIrwin)
* Close pull request 489, 495 - Docs typos (JackDanger, francois)
* Close pull request 485 - Be explicit about unsupported address parsing (bpot)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -339,8 +339,8 @@ mail = Mail.read('multipart_email')

mail.multipart? #=> true
mail.parts.length #=> 2
mail.preamble #=> "Text before the first part"
mail.epilogue #=> "Text after the last part"
mail.body.preamble #=> "Text before the first part"
mail.body.epilogue #=> "Text after the last part"
mail.parts.map { |p| p.content_type } #=> ['text/plain', 'application/pdf']
mail.parts.map { |p| p.class } #=> [Mail::Message, Mail::Message]
mail.parts[0].content_type_parameters #=> {'charset' => 'ISO-8859-1'}
Expand Down
6 changes: 3 additions & 3 deletions lib/mail/body.rb
Expand Up @@ -264,7 +264,7 @@ def <<( val )

def split!(boundary)
self.boundary = boundary
parts = raw_source.split(/--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
parts = raw_source.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
# Make the preamble equal to the preamble (if any)
self.preamble = parts[0].to_s.strip
# Make the epilogue equal to the epilogue (if any)
Expand All @@ -288,11 +288,11 @@ def need_to_sort?
private

def crlf_boundary
"\r\n\r\n--#{boundary}\r\n"
"\r\n--#{boundary}\r\n"
end

def end_boundary
"\r\n\r\n--#{boundary}--\r\n"
"\r\n--#{boundary}--\r\n"
end

def set_charset
Expand Down
8 changes: 4 additions & 4 deletions lib/mail/encodings.rb
Expand Up @@ -114,19 +114,19 @@ def Encodings.decode_encode(str, output_type)
# String has to be of the format =?<encoding>?[QB]?<string>?=
def Encodings.value_decode(str)
# Optimization: If there's no encoded-words in the string, just return it
return str unless str.index("=?")
return str unless str =~ /\=\?[^?]+\?[QB]\?[^?]+?\?\=/xmi

lines = collapse_adjacent_encodings(str)

# Split on white-space boundaries with capture, so we capture the white-space as well
lines.map do |line|
line.split(/([ \t])/).map do |text|
if text.index('=?') .nil?
if text.index('=?').nil?
text
else
# Search for occurences of quoted strings or plain strings
text.scan(/( # Group around entire regex to include it in matches
\=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
text.scan(/( # Group around entire regex to include it in matches
\=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
| # or
.+?(?=\=\?|$) # Plain String
)/xmi).map do |matches|
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/bcc_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.bcc #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
# mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
# mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/cc_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.cc #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
# mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
# mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/from_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.from = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.from #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:from] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
# mail['from'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
# mail['From'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/reply_to_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.reply_to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.reply_to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:reply_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
# mail['reply-to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
# mail['Reply-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/resent_bcc_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.resent_bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.resent_bcc #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:resent_bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
# mail['resent-bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
# mail['Resent-Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/resent_cc_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.resent_cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.resent_cc #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:resent_cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentCcField:0x180e1c4
# mail['resent-cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentCcField:0x180e1c4
# mail['Resent-Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentCcField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/resent_from_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.resent_from = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.resent_from #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:resent_from] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentFromField:0x180e1c4
# mail['resent-from'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentFromField:0x180e1c4
# mail['Resent-From'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentFromField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/resent_sender_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.resent_sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.resent_sender #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentSenderField:0x180e1c4
# mail.resent_sender #=> ['mikel@test.lindsaar.net']
# mail[:resent_sender] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentSenderField:0x180e1c4
# mail['resent-sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentSenderField:0x180e1c4
# mail['Resent-Sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentSenderField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/resent_to_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.resent_to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.resent_to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:resent_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
# mail['resent-to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
# mail['Resent-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/sender_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>'
# mail.sender #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
# mail.sender #=> 'mikel@test.lindsaar.net'
# mail[:sender] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
# mail['sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
# mail['Sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
Expand Down
2 changes: 1 addition & 1 deletion lib/mail/fields/to_field.rb
Expand Up @@ -16,7 +16,7 @@
#
# mail = Mail.new
# mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
# mail.to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
# mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
# mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
# mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
Expand Down
9 changes: 3 additions & 6 deletions lib/mail/message.rb
Expand Up @@ -938,8 +938,8 @@ def resent_sender( val = nil )
#
# Example:
#
# mail.sender = 'Mikel <mikel@test.lindsaar.net>'
# mail.sender #=> 'mikel@test.lindsaar.net'
# mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
# mail.resent_sender #=> 'mikel@test.lindsaar.net'
def resent_sender=( val )
header[:resent_sender] = val
end
Expand Down Expand Up @@ -1889,11 +1889,8 @@ def text?
# this standard. The body is simply a sequence of characters that
# follows the header and is separated from the header by an empty line
# (i.e., a line with nothing preceding the CRLF).
#
# Additionally, I allow for the case where someone might have put whitespace
# on the "gap line"
def parse_message
header_part, body_part = raw_source.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
header_part, body_part = raw_source.lstrip.split(/#{CRLF}#{CRLF}/, 2)
self.header = header_part
self.body = body_part
end
Expand Down
40 changes: 24 additions & 16 deletions lib/mail/utilities.rb
Expand Up @@ -41,28 +41,36 @@ def quote_token( str )
token_safe?( str ) ? str : dquote(str)
end

# Wraps supplied string in double quotes unless it is already wrapped.
#
# Additionally will escape any double quotation marks in the string with a single
# backslash in front of the '"' character.
# Wraps supplied string in double quotes and applies \-escaping as necessary,
# unless it is already wrapped.
#
# Example:
#
# string = 'This is a string'
# dquote(string) #=> '"This is a string"'
#
# string = 'This is "a string"'
# dquote(string #=> '"This is \"a string\"'
def dquote( str )
match = str.match(/^"(.*)?"$/)
str = match[1] if match
# First remove all escaped double quotes:
str = str.gsub(/\\"/, '"')
# Then wrap and re-escape all double quotes
'"' + str.gsub(/["]/n) {|s| '\\' + s } + '"'
'"' + unquote(str).gsub(/[\\"]/n) {|s| '\\' + s } + '"'
end

# Unwraps supplied string from inside double quotes.
#

# Unwraps supplied string from inside double quotes and
# removes any \-escaping.
#
# Example:
#
#
# string = '"This is a string"'
# unquote(string) #=> 'This is a string'
#
# string = '"This is \"a string\""'
# unqoute(string) #=> 'This is "a string"'
def unquote( str )
match = str.match(/^"(.*?)"$/)
match ? match[1] : str
if str =~ /^"(.*?)"$/
$1.gsub(/\\(.)/, '\1')
else
str
end
end

# Wraps a string in parenthesis and escapes any that are in the string itself.
Expand Down
4 changes: 4 additions & 0 deletions lib/mail/version_specific/ruby_1_9.rb
Expand Up @@ -132,6 +132,10 @@ def Ruby19.pick_encoding(charset)
when 'shift-jis'
Encoding::Shift_JIS

# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
when /gb2312/i
Encoding::GB18030

else
charset
end
Expand Down
1 change: 1 addition & 0 deletions mail.gemspec
Expand Up @@ -10,6 +10,7 @@ Gem::Specification.new do |s|
s.homepage = "http://github.com/mikel/mail"
s.description = "A really Ruby Mail handler."
s.summary = "Mail provides a nice Ruby DSL for making, sending and reading emails."
s.license = "MIT"

s.platform = Gem::Platform::RUBY
s.has_rdoc = true
Expand Down
12 changes: 6 additions & 6 deletions spec/fixtures/emails/mime_emails/sig_only_email.eml
Expand Up @@ -6,24 +6,24 @@ Message-ID: <20070604150131.40d4fa1e@reforged>
Mime-Version: 1.0
Content-Type: multipart/signed; boundary=Sig_2GIY2xfzqSADMmu9sKGJqWm;
protocol="application/pgp-signature"; micalg=PGP-SHA1

--Sig_2GIY2xfzqSADMmu9sKGJqWm
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: quoted-printable
This is random text, not what has been signed below, ie, this sig
email is not signed correctly.
--Sig_2GIY2xfzqSADMmu9sKGJqWm
Content-Type: application/pgp-signature; name=signature.asc
Content-Disposition: attachment; filename=signature.asc
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DB1111Iu7dfRchrkBInkRArniAKCue17JOxXBiAZHwLy3uFacU+pmhwCgwzhf
V5YSPv2xmYOA6mJ6oVaasseQ=
=T7p9
-----END PGP SIGNATURE-----

--Sig_2GIY2xfzqSADMmu9sKGJqWm--
8 changes: 8 additions & 0 deletions spec/mail/attachments_list_spec.rb
Expand Up @@ -117,6 +117,14 @@ def check_decoded(actual, expected)
expected.should eq @test_png
end

it "should only add one newline between attachment body and boundary" do
contents = "I have\ntwo lines with trailing newlines\n\n"
@mail.attachments['text.txt'] = { :content => contents}
encoded = @mail.encoded
regex = /\r\n#{Regexp.escape(contents.gsub(/\n/, "\r\n"))}\r\n--#{@mail.boundary}--\r\n\Z/
encoded.should match regex
end

end

describe "multiple attachments" do
Expand Down

0 comments on commit 812c19a

Please sign in to comment.