mail 2.4.4 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +156 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +52 -32
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +2 -2
  10. data/lib/mail/body.rb +33 -9
  11. data/lib/mail/check_delivery_params.rb +20 -0
  12. data/lib/mail/configuration.rb +1 -1
  13. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  14. data/lib/mail/core_extensions/nil.rb +4 -2
  15. data/lib/mail/core_extensions/object.rb +8 -8
  16. data/lib/mail/core_extensions/smtp.rb +12 -13
  17. data/lib/mail/core_extensions/string.rb +19 -6
  18. data/lib/mail/elements/address.rb +82 -141
  19. data/lib/mail/elements/address_list.rb +9 -42
  20. data/lib/mail/elements/content_disposition_element.rb +3 -7
  21. data/lib/mail/elements/content_location_element.rb +2 -6
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  23. data/lib/mail/elements/content_type_element.rb +4 -8
  24. data/lib/mail/elements/date_time_element.rb +3 -7
  25. data/lib/mail/elements/envelope_from_element.rb +18 -13
  26. data/lib/mail/elements/message_ids_element.rb +1 -6
  27. data/lib/mail/elements/mime_version_element.rb +3 -7
  28. data/lib/mail/elements/phrase_list.rb +2 -7
  29. data/lib/mail/elements/received_element.rb +3 -7
  30. data/lib/mail/elements.rb +12 -12
  31. data/lib/mail/encodings/quoted_printable.rb +4 -3
  32. data/lib/mail/encodings/unix_to_unix.rb +17 -0
  33. data/lib/mail/encodings.rb +53 -38
  34. data/lib/mail/envelope.rb +0 -5
  35. data/lib/mail/field.rb +131 -118
  36. data/lib/mail/field_list.rb +18 -18
  37. data/lib/mail/fields/bcc_field.rb +2 -2
  38. data/lib/mail/fields/cc_field.rb +2 -2
  39. data/lib/mail/fields/comments_field.rb +1 -1
  40. data/lib/mail/fields/common/common_address.rb +32 -22
  41. data/lib/mail/fields/common/common_date.rb +0 -7
  42. data/lib/mail/fields/common/common_field.rb +10 -3
  43. data/lib/mail/fields/common/common_message_id.rb +9 -5
  44. data/lib/mail/fields/common/parameter_hash.rb +3 -3
  45. data/lib/mail/fields/content_disposition_field.rb +1 -0
  46. data/lib/mail/fields/content_id_field.rb +1 -2
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  48. data/lib/mail/fields/content_type_field.rb +5 -2
  49. data/lib/mail/fields/date_field.rb +14 -14
  50. data/lib/mail/fields/from_field.rb +2 -2
  51. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  52. data/lib/mail/fields/keywords_field.rb +1 -1
  53. data/lib/mail/fields/message_id_field.rb +2 -3
  54. data/lib/mail/fields/references_field.rb +2 -1
  55. data/lib/mail/fields/reply_to_field.rb +2 -2
  56. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  57. data/lib/mail/fields/resent_cc_field.rb +2 -2
  58. data/lib/mail/fields/resent_from_field.rb +2 -2
  59. data/lib/mail/fields/resent_sender_field.rb +3 -3
  60. data/lib/mail/fields/resent_to_field.rb +2 -2
  61. data/lib/mail/fields/sender_field.rb +8 -8
  62. data/lib/mail/fields/to_field.rb +2 -2
  63. data/lib/mail/fields/unstructured_field.rb +44 -31
  64. data/lib/mail/fields.rb +32 -32
  65. data/lib/mail/header.rb +47 -37
  66. data/lib/mail/mail.rb +15 -1
  67. data/lib/mail/matchers/has_sent_mail.rb +36 -3
  68. data/lib/mail/message.rb +164 -57
  69. data/lib/mail/multibyte/chars.rb +4 -4
  70. data/lib/mail/multibyte/unicode.rb +9 -1
  71. data/lib/mail/network/delivery_methods/exim.rb +2 -3
  72. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  73. data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
  74. data/lib/mail/network/delivery_methods/smtp.rb +34 -45
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
  77. data/lib/mail/network/retriever_methods/imap.rb +32 -19
  78. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  79. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  80. data/lib/mail/network.rb +9 -9
  81. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  82. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  83. data/lib/mail/parsers/content_location_parser.rb +35 -0
  84. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  85. data/lib/mail/parsers/content_type_parser.rb +66 -0
  86. data/lib/mail/parsers/date_time_parser.rb +36 -0
  87. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  88. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  89. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  90. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  91. data/lib/mail/parsers/ragel/common.rl +185 -0
  92. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  93. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  105. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  106. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  107. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  108. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  109. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  110. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  111. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  112. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  113. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  114. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  115. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  117. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  118. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  119. data/lib/mail/parsers/ragel.rb +17 -0
  120. data/lib/mail/parsers/received_parser.rb +47 -0
  121. data/lib/mail/parsers.rb +26 -0
  122. data/lib/mail/part.rb +6 -2
  123. data/lib/mail/parts_list.rb +5 -3
  124. data/lib/mail/utilities.rb +70 -60
  125. data/lib/mail/values/unicode_tables.dat +0 -0
  126. data/lib/mail/version.rb +7 -15
  127. data/lib/mail/version_specific/ruby_1_8.rb +25 -4
  128. data/lib/mail/version_specific/ruby_1_9.rb +110 -30
  129. data/lib/mail.rb +20 -19
  130. metadata +129 -73
  131. data/Gemfile.lock +0 -36
  132. data/lib/VERSION +0 -4
  133. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  134. data/lib/mail/parsers/address_lists.rb +0 -64
  135. data/lib/mail/parsers/address_lists.treetop +0 -19
  136. data/lib/mail/parsers/content_disposition.rb +0 -535
  137. data/lib/mail/parsers/content_disposition.treetop +0 -46
  138. data/lib/mail/parsers/content_location.rb +0 -139
  139. data/lib/mail/parsers/content_location.treetop +0 -20
  140. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  141. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  142. data/lib/mail/parsers/content_type.rb +0 -967
  143. data/lib/mail/parsers/content_type.treetop +0 -68
  144. data/lib/mail/parsers/date_time.rb +0 -114
  145. data/lib/mail/parsers/date_time.treetop +0 -11
  146. data/lib/mail/parsers/envelope_from.rb +0 -194
  147. data/lib/mail/parsers/envelope_from.treetop +0 -32
  148. data/lib/mail/parsers/message_ids.rb +0 -45
  149. data/lib/mail/parsers/message_ids.treetop +0 -15
  150. data/lib/mail/parsers/mime_version.rb +0 -144
  151. data/lib/mail/parsers/mime_version.treetop +0 -19
  152. data/lib/mail/parsers/phrase_lists.rb +0 -45
  153. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  154. data/lib/mail/parsers/received.rb +0 -71
  155. data/lib/mail/parsers/received.treetop +0 -11
  156. data/lib/mail/parsers/rfc2045.rb +0 -464
  157. data/lib/mail/parsers/rfc2045.treetop +0 -36
  158. data/lib/mail/parsers/rfc2822.rb +0 -5341
  159. data/lib/mail/parsers/rfc2822.treetop +0 -410
  160. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  161. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  162. data/lib/tasks/corpus.rake +0 -125
  163. data/lib/tasks/treetop.rake +0 -10
@@ -4,16 +4,9 @@ module Mail
4
4
 
5
5
  include Mail::Utilities
6
6
 
7
- def initialize( string )
8
- parser = Mail::ContentTransferEncodingParser.new
9
- case
10
- when string.blank?
11
- @encoding = ''
12
- when tree = parser.parse(string.to_s.downcase)
13
- @encoding = tree.encoding.text_value
14
- else
15
- raise Mail::Field::ParseError.new(ContentTransferEncodingElement, string, parser.failure_reason)
16
- end
7
+ def initialize(string)
8
+ content_transfer_encoding = Mail::Parsers::ContentTransferEncodingParser.new.parse(string)
9
+ @encoding = content_transfer_encoding.encoding
17
10
  end
18
11
 
19
12
  def encoding
@@ -5,14 +5,10 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::ContentTypeParser.new
9
- if tree = parser.parse(cleaned(string))
10
- @main_type = tree.main_type.text_value.downcase
11
- @sub_type = tree.sub_type.text_value.downcase
12
- @parameters = tree.parameters
13
- else
14
- raise Mail::Field::ParseError.new(ContentTypeElement, string, parser.failure_reason)
15
- end
8
+ content_type = Mail::Parsers::ContentTypeParser.new.parse(cleaned(string))
9
+ @main_type = content_type.main_type
10
+ @sub_type = content_type.sub_type
11
+ @parameters = content_type.parameters
16
12
  end
17
13
 
18
14
  def main_type
@@ -5,13 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::DateTimeParser.new
9
- if tree = parser.parse(string)
10
- @date_string = tree.date.text_value
11
- @time_string = tree.time.text_value
12
- else
13
- raise Mail::Field::ParseError.new(DateTimeElement, string, parser.failure_reason)
14
- end
8
+ date_time = Mail::Parsers::DateTimeParser.new.parse(string)
9
+ @date_string = date_time.date_string
10
+ @time_string = date_time.time_string
15
11
  end
16
12
 
17
13
  def date_string
@@ -5,17 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::EnvelopeFromParser.new
9
- if @tree = parser.parse(string)
10
- @address = tree.addr_spec.text_value.strip
11
- @date_time = ::DateTime.parse("#{tree.ctime_date.text_value}")
12
- else
13
- raise Mail::Field::ParseError.new(EnvelopeFromElement, string, parser.failure_reason)
14
- end
15
- end
16
-
17
- def tree
18
- @tree
8
+ @envelope_from = Mail::Parsers::EnvelopeFromParser.new.parse(string)
9
+ @address = @envelope_from.address
10
+ @date_time = ::DateTime.parse(@envelope_from.ctime_date)
19
11
  end
20
12
 
21
13
  def date_time
@@ -26,8 +18,21 @@ module Mail
26
18
  @address
27
19
  end
28
20
 
29
- def to_s(*args)
30
- "#{@info}; #{@date_time.to_s(*args)}"
21
+ # RFC 4155:
22
+ # a timestamp indicating the UTC date and time when the message
23
+ # was originally received, conformant with the syntax of the
24
+ # traditional UNIX 'ctime' output sans timezone (note that the
25
+ # use of UTC precludes the need for a timezone indicator);
26
+ def formatted_date_time
27
+ if @date_time.respond_to?(:ctime)
28
+ @date_time.ctime
29
+ else
30
+ @date_time.strftime '%a %b %e %T %Y'
31
+ end
32
+ end
33
+
34
+ def to_s
35
+ "#{@address} #{formatted_date_time}"
31
36
  end
32
37
 
33
38
  end
@@ -5,12 +5,7 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize(string)
8
- parser = Mail::MessageIdsParser.new
9
- if tree = parser.parse(string)
10
- @message_ids = tree.message_ids.map { |msg_id| clean_msg_id(msg_id.text_value) }
11
- else
12
- raise Mail::Field::ParseError.new(MessageIdsElement, string, parser.failure_reason)
13
- end
8
+ @message_ids = Mail::Parsers::MessageIdsParser.new.parse(string).message_ids.map { |msg_id| clean_msg_id(msg_id) }
14
9
  end
15
10
 
16
11
  def message_ids
@@ -5,13 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::MimeVersionParser.new
9
- if tree = parser.parse(string)
10
- @major = tree.major.text_value
11
- @minor = tree.minor.text_value
12
- else
13
- raise Mail::Field::ParseError.new(MimeVersionElement, string, parser.failure_reason)
14
- end
8
+ mime_version = Mail::Parsers::MimeVersionParser.new.parse(string)
9
+ @major = mime_version.major
10
+ @minor = mime_version.minor
15
11
  end
16
12
 
17
13
  def major
@@ -5,16 +5,11 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize(string)
8
- parser = Mail::PhraseListsParser.new
9
- if tree = parser.parse(string)
10
- @phrases = tree.phrases
11
- else
12
- raise Mail::Field::ParseError.new(PhraseList, string, parser.failure_reason)
13
- end
8
+ @phrase_lists = Mail::Parsers::PhraseListsParser.new.parse(string)
14
9
  end
15
10
 
16
11
  def phrases
17
- @phrases.map { |p| unquote(p.text_value) }
12
+ @phrase_lists.phrases.map { |p| unquote(p) }
18
13
  end
19
14
 
20
15
  end
@@ -5,13 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::ReceivedParser.new
9
- if tree = parser.parse(string)
10
- @date_time = ::DateTime.parse("#{tree.date_time.date.text_value} #{tree.date_time.time.text_value}")
11
- @info = tree.name_val_list.text_value
12
- else
13
- raise Mail::Field::ParseError.new(ReceivedElement, string, parser.failure_reason)
14
- end
8
+ received = Mail::Parsers::ReceivedParser.new.parse(string)
9
+ @date_time = ::DateTime.parse("#{received.date} #{received.time}")
10
+ @info = received.info
15
11
  end
16
12
 
17
13
  def date_time
data/lib/mail/elements.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  module Mail
2
- autoload :Address, 'mail/elements/address'
3
- autoload :AddressList, 'mail/elements/address_list'
4
- autoload :ContentDispositionElement, 'mail/elements/content_disposition_element'
5
- autoload :ContentLocationElement, 'mail/elements/content_location_element'
6
- autoload :ContentTransferEncodingElement, 'mail/elements/content_transfer_encoding_element'
7
- autoload :ContentTypeElement, 'mail/elements/content_type_element'
8
- autoload :DateTimeElement, 'mail/elements/date_time_element'
9
- autoload :EnvelopeFromElement, 'mail/elements/envelope_from_element'
10
- autoload :MessageIdsElement, 'mail/elements/message_ids_element'
11
- autoload :MimeVersionElement, 'mail/elements/mime_version_element'
12
- autoload :PhraseList, 'mail/elements/phrase_list'
13
- autoload :ReceivedElement, 'mail/elements/received_element'
2
+ register_autoload :Address, 'mail/elements/address'
3
+ register_autoload :AddressList, 'mail/elements/address_list'
4
+ register_autoload :ContentDispositionElement, 'mail/elements/content_disposition_element'
5
+ register_autoload :ContentLocationElement, 'mail/elements/content_location_element'
6
+ register_autoload :ContentTransferEncodingElement, 'mail/elements/content_transfer_encoding_element'
7
+ register_autoload :ContentTypeElement, 'mail/elements/content_type_element'
8
+ register_autoload :DateTimeElement, 'mail/elements/date_time_element'
9
+ register_autoload :EnvelopeFromElement, 'mail/elements/envelope_from_element'
10
+ register_autoload :MessageIdsElement, 'mail/elements/message_ids_element'
11
+ register_autoload :MimeVersionElement, 'mail/elements/mime_version_element'
12
+ register_autoload :PhraseList, 'mail/elements/phrase_list'
13
+ register_autoload :ReceivedElement, 'mail/elements/received_element'
14
14
  end
@@ -12,13 +12,14 @@ module Mail
12
12
  EightBit.can_encode? str
13
13
  end
14
14
 
15
- # Decode the string from Quoted-Printable
15
+ # Decode the string from Quoted-Printable. Cope with hard line breaks
16
+ # that were incorrectly encoded as hex instead of literal CRLF.
16
17
  def self.decode(str)
17
- str.unpack("M*").first
18
+ str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first.to_lf
18
19
  end
19
20
 
20
21
  def self.encode(str)
21
- [str].pack("M").gsub(/\n/, "\r\n")
22
+ [str.to_lf].pack("M").to_crlf
22
23
  end
23
24
 
24
25
  def self.cost(str)
@@ -0,0 +1,17 @@
1
+ module Mail
2
+ module Encodings
3
+ module UnixToUnix
4
+ NAME = "x-uuencode"
5
+
6
+ def self.decode(str)
7
+ str.sub(/\Abegin \d+ [^\n]*\n/, '').unpack('u').first
8
+ end
9
+
10
+ def self.encode(str)
11
+ [str].pack("u")
12
+ end
13
+
14
+ Encodings.register(NAME, self)
15
+ end
16
+ end
17
+ end
@@ -7,7 +7,7 @@ module Mail
7
7
 
8
8
  module Encodings
9
9
 
10
- include Mail::Patterns
10
+ include Mail::Constants
11
11
  extend Mail::Utilities
12
12
 
13
13
  @transfer_encodings = {}
@@ -47,13 +47,13 @@ module Mail
47
47
  end
48
48
 
49
49
  def Encodings.get_name(enc)
50
- enc = enc.to_s.gsub("-", "_").downcase
50
+ enc = underscoreize(enc).downcase
51
51
  end
52
52
 
53
53
  # Encodes a parameter value using URI Escaping, note the language field 'en' can
54
54
  # be set using Mail::Configuration, like so:
55
55
  #
56
- # Mail.defaults.do
56
+ # Mail.defaults do
57
57
  # param_encode_language 'jp'
58
58
  # end
59
59
  #
@@ -114,32 +114,16 @@ module Mail
114
114
  # String has to be of the format =?<encoding>?[QB]?<string>?=
115
115
  def Encodings.value_decode(str)
116
116
  # Optimization: If there's no encoded-words in the string, just return it
117
- return str unless str.index("=?")
117
+ return str unless str =~ ENCODED_VALUE
118
118
 
119
- str = str.gsub(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's
119
+ lines = collapse_adjacent_encodings(str)
120
120
 
121
121
  # Split on white-space boundaries with capture, so we capture the white-space as well
122
- str.split(/([ \t])/).map do |text|
123
- if text.index('=?') .nil?
124
- text
125
- else
126
- # Join QP encoded-words that are adjacent to avoid decoding partial chars
127
- text.gsub!(/\?\=\=\?.+?\?[Qq]\?/m, '') if text =~ /\?==\?/
128
-
129
- # Search for occurences of quoted strings or plain strings
130
- text.scan(/( # Group around entire regex to include it in matches
131
- \=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
132
- | # or
133
- .+?(?=\=\?|$) # Plain String
134
- )/xmi).map do |matches|
135
- string, method = *matches
136
- if method == 'b' || method == 'B'
137
- b_value_decode(string)
138
- elsif method == 'q' || method == 'Q'
139
- q_value_decode(string)
140
- else
141
- string
142
- end
122
+ lines.each do |line|
123
+ line.gsub!(ENCODED_VALUE) do |string|
124
+ case $1
125
+ when *B_VALUES then b_value_decode(string)
126
+ when *Q_VALUES then q_value_decode(string)
143
127
  end
144
128
  end
145
129
  end.join("")
@@ -147,19 +131,17 @@ module Mail
147
131
 
148
132
  # Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
149
133
  def Encodings.unquote_and_convert_to(str, to_encoding)
150
- original_encoding = split_encoding_from_string( str )
151
-
152
- output = value_decode( str ).to_s
134
+ output = value_decode( str ).to_s # output is already converted to UTF-8
153
135
 
154
- if original_encoding.to_s.downcase.gsub("-", "") == to_encoding.to_s.downcase.gsub("-", "")
136
+ if 'utf8' == to_encoding.to_s.downcase.gsub("-", "")
155
137
  output
156
- elsif original_encoding && to_encoding
138
+ elsif to_encoding
157
139
  begin
158
140
  if RUBY_VERSION >= '1.9'
159
141
  output.encode(to_encoding)
160
142
  else
161
143
  require 'iconv'
162
- Iconv.iconv(to_encoding, original_encoding, output).first
144
+ Iconv.iconv(to_encoding, 'UTF-8', output).first
163
145
  end
164
146
  rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
165
147
  # the 'from' parameter specifies a charset other than what the text
@@ -178,10 +160,10 @@ module Mail
178
160
  def Encodings.address_encode(address, charset = 'utf-8')
179
161
  if address.is_a?(Array)
180
162
  # loop back through for each element
181
- address.map { |a| Encodings.address_encode(a, charset) }.join(", ")
163
+ address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
182
164
  else
183
165
  # find any word boundary that is not ascii and encode it
184
- encode_non_usascii(address, charset)
166
+ encode_non_usascii(address, charset) if address
185
167
  end
186
168
  end
187
169
 
@@ -189,15 +171,15 @@ module Mail
189
171
  return address if address.ascii_only? or charset.nil?
190
172
  us_ascii = %Q{\x00-\x7f}
191
173
  # Encode any non usascii strings embedded inside of quotes
192
- address.gsub!(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
174
+ address = address.gsub(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
193
175
  # Then loop through all remaining items and encode as needed
194
176
  tokens = address.split(/\s/)
195
177
  map_with_index(tokens) do |word, i|
196
178
  if word.ascii_only?
197
179
  word
198
180
  else
199
- previous_non_ascii = tokens[i-1] && !tokens[i-1].ascii_only?
200
- if previous_non_ascii
181
+ previous_non_ascii = i>0 && tokens[i-1] && !tokens[i-1].ascii_only?
182
+ if previous_non_ascii #why are we adding an extra space here?
201
183
  word = " #{word}"
202
184
  end
203
185
  Encodings.b_value_encode(word, charset)
@@ -259,7 +241,7 @@ module Mail
259
241
  end
260
242
 
261
243
  def Encodings.split_encoding_from_string( str )
262
- match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
244
+ match = str.match(/\=\?([^?]+)?\?[QB]\?(.*)\?\=/mi)
263
245
  if match
264
246
  match[1]
265
247
  else
@@ -270,5 +252,38 @@ module Mail
270
252
  def Encodings.find_encoding(str)
271
253
  RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
272
254
  end
255
+
256
+ # Gets the encoding type (Q or B) from the string.
257
+ def Encodings.split_value_encoding_from_string(str)
258
+ match = str.match(/\=\?[^?]+?\?([QB])\?(.*)\?\=/mi)
259
+ if match
260
+ match[1]
261
+ else
262
+ nil
263
+ end
264
+ end
265
+
266
+ # When the encoded string consists of multiple lines, lines with the same
267
+ # encoding (Q or B) can be joined together.
268
+ #
269
+ # String has to be of the format =?<encoding>?[QB]?<string>?=
270
+ def Encodings.collapse_adjacent_encodings(str)
271
+ lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
272
+ results = []
273
+ previous_encoding = nil
274
+
275
+ lines.each do |line|
276
+ encoding = split_value_encoding_from_string(line)
277
+
278
+ if encoding == previous_encoding
279
+ line = results.pop + line
280
+ end
281
+
282
+ previous_encoding = encoding
283
+ results << line
284
+ end
285
+
286
+ results
287
+ end
273
288
  end
274
289
  end
data/lib/mail/envelope.rb CHANGED
@@ -14,11 +14,6 @@ module Mail
14
14
  super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
15
15
  end
16
16
 
17
- def tree
18
- @element ||= Mail::EnvelopeFromElement.new(value)
19
- @tree ||= @element.tree
20
- end
21
-
22
17
  def element
23
18
  @element ||= Mail::EnvelopeFromElement.new(value)
24
19
  end