mail 2.6.6 → 2.8.1

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 (180) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +134 -119
  4. data/lib/mail/attachments_list.rb +10 -9
  5. data/lib/mail/body.rb +73 -84
  6. data/lib/mail/check_delivery_params.rb +28 -21
  7. data/lib/mail/configuration.rb +2 -0
  8. data/lib/mail/constants.rb +27 -5
  9. data/lib/mail/elements/address.rb +53 -47
  10. data/lib/mail/elements/address_list.rb +11 -19
  11. data/lib/mail/elements/content_disposition_element.rb +9 -16
  12. data/lib/mail/elements/content_location_element.rb +6 -11
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  14. data/lib/mail/elements/content_type_element.rb +16 -23
  15. data/lib/mail/elements/date_time_element.rb +7 -15
  16. data/lib/mail/elements/envelope_from_element.rb +22 -23
  17. data/lib/mail/elements/message_ids_element.rb +18 -13
  18. data/lib/mail/elements/mime_version_element.rb +7 -15
  19. data/lib/mail/elements/phrase_list.rb +12 -10
  20. data/lib/mail/elements/received_element.rb +27 -19
  21. data/lib/mail/encodings/7bit.rb +9 -14
  22. data/lib/mail/encodings/8bit.rb +2 -21
  23. data/lib/mail/encodings/base64.rb +11 -12
  24. data/lib/mail/encodings/binary.rb +3 -22
  25. data/lib/mail/encodings/identity.rb +24 -0
  26. data/lib/mail/encodings/quoted_printable.rb +6 -6
  27. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  28. data/lib/mail/encodings/unix_to_unix.rb +3 -1
  29. data/lib/mail/encodings.rb +81 -54
  30. data/lib/mail/envelope.rb +11 -14
  31. data/lib/mail/field.rb +119 -98
  32. data/lib/mail/field_list.rb +60 -7
  33. data/lib/mail/fields/bcc_field.rb +34 -52
  34. data/lib/mail/fields/cc_field.rb +28 -49
  35. data/lib/mail/fields/comments_field.rb +27 -37
  36. data/lib/mail/fields/common_address_field.rb +170 -0
  37. data/lib/mail/fields/common_date_field.rb +58 -0
  38. data/lib/mail/fields/common_field.rb +77 -0
  39. data/lib/mail/fields/common_message_id_field.rb +42 -0
  40. data/lib/mail/fields/content_description_field.rb +7 -14
  41. data/lib/mail/fields/content_disposition_field.rb +13 -38
  42. data/lib/mail/fields/content_id_field.rb +24 -51
  43. data/lib/mail/fields/content_location_field.rb +11 -25
  44. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  45. data/lib/mail/fields/content_type_field.rb +50 -80
  46. data/lib/mail/fields/date_field.rb +23 -52
  47. data/lib/mail/fields/from_field.rb +28 -49
  48. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  49. data/lib/mail/fields/keywords_field.rb +18 -31
  50. data/lib/mail/fields/message_id_field.rb +25 -71
  51. data/lib/mail/fields/mime_version_field.rb +19 -30
  52. data/lib/mail/fields/named_structured_field.rb +11 -0
  53. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  54. data/lib/mail/fields/optional_field.rb +9 -7
  55. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  56. data/lib/mail/fields/received_field.rb +43 -57
  57. data/lib/mail/fields/references_field.rb +35 -49
  58. data/lib/mail/fields/reply_to_field.rb +28 -49
  59. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  60. data/lib/mail/fields/resent_cc_field.rb +28 -49
  61. data/lib/mail/fields/resent_date_field.rb +5 -30
  62. data/lib/mail/fields/resent_from_field.rb +28 -49
  63. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  64. data/lib/mail/fields/resent_sender_field.rb +27 -56
  65. data/lib/mail/fields/resent_to_field.rb +28 -49
  66. data/lib/mail/fields/return_path_field.rb +50 -54
  67. data/lib/mail/fields/sender_field.rb +34 -55
  68. data/lib/mail/fields/structured_field.rb +3 -30
  69. data/lib/mail/fields/subject_field.rb +9 -11
  70. data/lib/mail/fields/to_field.rb +28 -49
  71. data/lib/mail/fields/unstructured_field.rb +32 -47
  72. data/lib/mail/header.rb +71 -110
  73. data/lib/mail/mail.rb +2 -10
  74. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  75. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  76. data/lib/mail/message.rb +113 -117
  77. data/lib/mail/multibyte/chars.rb +21 -178
  78. data/lib/mail/multibyte/unicode.rb +10 -10
  79. data/lib/mail/multibyte/utils.rb +26 -43
  80. data/lib/mail/multibyte.rb +55 -16
  81. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  82. data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
  83. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  84. data/lib/mail/network/delivery_methods/sendmail.rb +62 -21
  85. data/lib/mail/network/delivery_methods/smtp.rb +75 -50
  86. data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -4
  87. data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
  88. data/lib/mail/network/retriever_methods/base.rb +8 -8
  89. data/lib/mail/network/retriever_methods/imap.rb +20 -7
  90. data/lib/mail/network/retriever_methods/pop3.rb +5 -3
  91. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  92. data/lib/mail/network.rb +1 -0
  93. data/lib/mail/parser_tools.rb +15 -0
  94. data/lib/mail/parsers/address_lists_parser.rb +33225 -116
  95. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  96. data/lib/mail/parsers/content_disposition_parser.rb +882 -49
  97. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  98. data/lib/mail/parsers/content_location_parser.rb +809 -23
  99. data/lib/mail/parsers/content_location_parser.rl +78 -0
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
  101. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  102. data/lib/mail/parsers/content_type_parser.rb +1037 -56
  103. data/lib/mail/parsers/content_type_parser.rl +90 -0
  104. data/lib/mail/parsers/date_time_parser.rb +877 -25
  105. data/lib/mail/parsers/date_time_parser.rl +69 -0
  106. data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
  107. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  108. data/lib/mail/parsers/message_ids_parser.rb +5146 -25
  109. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  110. data/lib/mail/parsers/mime_version_parser.rb +497 -26
  111. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  112. data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
  113. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  114. data/lib/mail/parsers/received_parser.rb +8776 -43
  115. data/lib/mail/parsers/received_parser.rl +91 -0
  116. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  117. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  118. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  119. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  120. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  121. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  122. data/lib/mail/parsers/rfc5322.rl +74 -0
  123. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  124. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  125. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  126. data/lib/mail/parsers.rb +11 -25
  127. data/lib/mail/part.rb +6 -10
  128. data/lib/mail/parts_list.rb +62 -6
  129. data/lib/mail/smtp_envelope.rb +57 -0
  130. data/lib/mail/utilities.rb +343 -74
  131. data/lib/mail/version.rb +2 -2
  132. data/lib/mail/yaml.rb +30 -0
  133. data/lib/mail.rb +5 -35
  134. metadata +111 -66
  135. data/CHANGELOG.rdoc +0 -803
  136. data/CONTRIBUTING.md +0 -60
  137. data/Dependencies.txt +0 -2
  138. data/Gemfile +0 -14
  139. data/Rakefile +0 -29
  140. data/TODO.rdoc +0 -9
  141. data/lib/mail/core_extensions/smtp.rb +0 -25
  142. data/lib/mail/core_extensions/string/access.rb +0 -146
  143. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  144. data/lib/mail/core_extensions/string.rb +0 -21
  145. data/lib/mail/fields/common/address_container.rb +0 -17
  146. data/lib/mail/fields/common/common_address.rb +0 -136
  147. data/lib/mail/fields/common/common_date.rb +0 -36
  148. data/lib/mail/fields/common/common_field.rb +0 -61
  149. data/lib/mail/fields/common/common_message_id.rb +0 -49
  150. data/lib/mail/multibyte/exceptions.rb +0 -9
  151. data/lib/mail/parsers/ragel/common.rl +0 -185
  152. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  173. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  177. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  178. data/lib/mail/parsers/ragel.rb +0 -18
  179. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  180. data/lib/mail/version_specific/ruby_1_9.rb +0 -226
data/lib/mail/body.rb CHANGED
@@ -32,24 +32,29 @@ module Mail
32
32
  @preamble = nil
33
33
  @epilogue = nil
34
34
  @charset = nil
35
- @part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
35
+ @part_sort_order = [ "text/plain", "text/enriched", "text/html", "multipart/alternative" ]
36
36
  @parts = Mail::PartsList.new
37
37
  if Utilities.blank?(string)
38
38
  @raw_source = ''
39
39
  else
40
40
  # Do join first incase we have been given an Array in Ruby 1.9
41
41
  if string.respond_to?(:join)
42
- @raw_source = string.join('')
42
+ @raw_source = ::Mail::Utilities.to_crlf(string.join(''))
43
43
  elsif string.respond_to?(:to_s)
44
- @raw_source = string.to_s
44
+ @raw_source = ::Mail::Utilities.to_crlf(string.to_s)
45
45
  else
46
46
  raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
47
47
  end
48
48
  end
49
- @encoding = (only_us_ascii? ? '7bit' : '8bit')
49
+ @encoding = default_encoding
50
50
  set_charset
51
51
  end
52
-
52
+
53
+ def init_with(coder)
54
+ coder.map.each { |k, v| instance_variable_set(:"@#{k}", v) }
55
+ @parts = Mail::PartsList.new(coder['parts'])
56
+ end
57
+
53
58
  # Matches this body with another body. Also matches the decoded value of this
54
59
  # body with a string.
55
60
  #
@@ -115,8 +120,8 @@ module Mail
115
120
  end
116
121
 
117
122
  # Allows you to set the sort order of the parts, overriding the default sort order.
118
- # Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content
119
- # type coming after.
123
+ # Defaults to 'text/plain', then 'text/enriched', then 'text/html', then 'multipart/alternative'
124
+ # with any other content type coming after.
120
125
  def set_sort_order(order)
121
126
  @part_sort_order = order
122
127
  end
@@ -133,46 +138,44 @@ module Mail
133
138
  @parts.sort!(@part_sort_order)
134
139
  end
135
140
 
136
- # Returns the raw source that the body was initialized with, without
137
- # any tampering
138
- def raw_source
139
- @raw_source
140
- end
141
-
142
- def get_best_encoding(target)
143
- target_encoding = Mail::Encodings.get_encoding(target)
144
- target_encoding.get_best_compatible(encoding, raw_source)
141
+ def negotiate_best_encoding(message_encoding, allowed_encodings = nil)
142
+ Mail::Encodings::TransferEncoding.negotiate(message_encoding, encoding, raw_source, allowed_encodings)
145
143
  end
146
-
144
+
147
145
  # Returns a body encoded using transfer_encoding. Multipart always uses an
148
146
  # identiy encoding (i.e. no encoding).
149
147
  # Calling this directly is not a good idea, but supported for compatibility
150
148
  # TODO: Validate that preamble and epilogue are valid for requested encoding
151
- def encoded(transfer_encoding = '8bit')
149
+ def encoded(transfer_encoding = nil)
152
150
  if multipart?
153
151
  self.sort_parts!
154
152
  encoded_parts = parts.map { |p| p.encoded }
155
153
  ([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
156
154
  else
157
- be = get_best_encoding(transfer_encoding)
158
- dec = Mail::Encodings::get_encoding(encoding)
159
- enc = Mail::Encodings::get_encoding(be)
155
+ dec = Mail::Encodings.get_encoding(encoding)
156
+ enc =
157
+ if Utilities.blank?(transfer_encoding)
158
+ dec
159
+ else
160
+ negotiate_best_encoding(transfer_encoding)
161
+ end
162
+
160
163
  if dec.nil?
161
- # Cannot decode, so skip normalization
162
- raw_source
164
+ # Cannot decode, so skip normalization
165
+ raw_source
163
166
  else
164
- # Decode then encode to normalize and allow transforming
165
- # from base64 to Q-P and vice versa
166
- decoded = dec.decode(raw_source)
167
- if defined?(Encoding) && charset && charset != "US-ASCII"
168
- decoded.encode!(charset)
169
- decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
170
- end
171
- enc.encode(decoded)
167
+ # Decode then encode to normalize and allow transforming
168
+ # from base64 to Q-P and vice versa
169
+ decoded = dec.decode(raw_source)
170
+ if defined?(Encoding) && charset && charset != "US-ASCII"
171
+ decoded = decoded.encode(charset)
172
+ decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
173
+ end
174
+ enc.encode(decoded)
172
175
  end
173
176
  end
174
177
  end
175
-
178
+
176
179
  def decoded
177
180
  if !Encodings.defined?(encoding)
178
181
  raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
@@ -184,14 +187,6 @@ module Mail
184
187
  def to_s
185
188
  decoded
186
189
  end
187
-
188
- def charset
189
- @charset
190
- end
191
-
192
- def charset=( val )
193
- @charset = val
194
- end
195
190
 
196
191
  def encoding(val = nil)
197
192
  if val
@@ -200,54 +195,41 @@ module Mail
200
195
  @encoding
201
196
  end
202
197
  end
203
-
198
+
204
199
  def encoding=( val )
205
- @encoding = if val == "text" || Utilities.blank?(val)
206
- (only_us_ascii? ? '7bit' : '8bit')
207
- else
200
+ @encoding =
201
+ if val == "text" || Utilities.blank?(val)
202
+ default_encoding
203
+ else
208
204
  val
209
- end
205
+ end
210
206
  end
211
207
 
212
- # Returns the preamble (any text that is before the first MIME boundary)
213
- def preamble
214
- @preamble
215
- end
208
+ # Returns the raw source that the body was initialized with, without
209
+ # any tampering
210
+ attr_reader :raw_source
211
+
212
+ # Returns parts of the body
213
+ attr_reader :parts
214
+
215
+ # Returns and sets the original character encoding
216
+ attr_accessor :charset
217
+
218
+ # Returns and sets the preamble as a string (any text that is before the first MIME boundary)
219
+ attr_accessor :preamble
220
+
221
+ # Returns and sets the epilogue as a string (any text that is after the last MIME boundary)
222
+ attr_accessor :epilogue
223
+
224
+ # Returns and sets the boundary used by the body
225
+ # Allows you to change the boundary of this Body object
226
+ attr_accessor :boundary
216
227
 
217
- # Sets the preamble to a string (adds text before the first MIME boundary)
218
- def preamble=( val )
219
- @preamble = val
220
- end
221
-
222
- # Returns the epilogue (any text that is after the last MIME boundary)
223
- def epilogue
224
- @epilogue
225
- end
226
-
227
- # Sets the epilogue to a string (adds text after the last MIME boundary)
228
- def epilogue=( val )
229
- @epilogue = val
230
- end
231
-
232
228
  # Returns true if there are parts defined in the body
233
229
  def multipart?
234
230
  true unless parts.empty?
235
231
  end
236
-
237
- # Returns the boundary used by the body
238
- def boundary
239
- @boundary
240
- end
241
-
242
- # Allows you to change the boundary of this Body object
243
- def boundary=( val )
244
- @boundary = val
245
- end
246
232
 
247
- def parts
248
- @parts
249
- end
250
-
251
233
  def <<( val )
252
234
  if @parts
253
235
  @parts << val
@@ -268,14 +250,21 @@ module Mail
268
250
  self
269
251
  end
270
252
 
271
- def only_us_ascii?
272
- !(raw_source =~ /[^\x01-\x7f]/)
253
+ def ascii_only?
254
+ unless defined? @ascii_only
255
+ @ascii_only = raw_source.ascii_only?
256
+ end
257
+ @ascii_only
273
258
  end
274
-
259
+
275
260
  def empty?
276
261
  !!raw_source.to_s.empty?
277
262
  end
278
-
263
+
264
+ def default_encoding
265
+ ascii_only? ? '7bit' : '8bit'
266
+ end
267
+
279
268
  private
280
269
 
281
270
  # split parts by boundary, ignore first part if empty, append final part when closing boundary was missing
@@ -308,9 +297,9 @@ module Mail
308
297
  def end_boundary
309
298
  "\r\n--#{boundary}--\r\n"
310
299
  end
311
-
300
+
312
301
  def set_charset
313
- only_us_ascii? ? @charset = 'US-ASCII' : @charset = nil
302
+ @charset = ascii_only? ? 'US-ASCII' : nil
314
303
  end
315
304
  end
316
305
  end
@@ -1,48 +1,54 @@
1
1
  # frozen_string_literal: true
2
+ #
3
+ # This whole class and associated specs is deprecated and will go away in the version 3 release of mail.
2
4
  module Mail
3
5
  module CheckDeliveryParams #:nodoc:
4
6
  class << self
7
+
8
+ extend Gem::Deprecate
9
+
5
10
  def check(mail)
6
- [ check_from(mail.smtp_envelope_from),
7
- check_to(mail.smtp_envelope_to),
8
- check_message(mail) ]
11
+ envelope = Mail::SmtpEnvelope.new(mail)
12
+ [ envelope.from,
13
+ envelope.to,
14
+ envelope.message ]
9
15
  end
16
+ deprecate :check, 'Mail::SmtpEnvelope.new created in commit c106bebea066782a72e4f24dd37b532d95773df7', 2023, 6
10
17
 
11
18
  def check_from(addr)
12
- if Utilities.blank?(addr)
13
- raise ArgumentError, "SMTP From address may not be blank: #{addr.inspect}"
14
- end
15
-
16
- check_addr 'From', addr
19
+ mail = Mail.new(from: 'deprecated@example.com', to: 'deprecated@example.com')
20
+ Mail::SmtpEnvelope.new(mail).send(:validate_addr, 'From', addr)
17
21
  end
22
+ deprecate :check_from, :none, 2023, 6
18
23
 
19
24
  def check_to(addrs)
20
- if Utilities.blank?(addrs)
21
- raise ArgumentError, "SMTP To address may not be blank: #{addrs.inspect}"
22
- end
23
-
25
+ mail = Mail.new(from: 'deprecated@example.com', to: 'deprecated@example.com')
24
26
  Array(addrs).map do |addr|
25
- check_addr 'To', addr
27
+ Mail::SmtpEnvelope.new(mail).send(:validate_addr, 'To', addr)
26
28
  end
27
29
  end
30
+ deprecate :check_to, :none, 2023, 6
28
31
 
29
32
  def check_addr(addr_name, addr)
30
- validate_smtp_addr addr do |error_message|
31
- raise ArgumentError, "SMTP #{addr_name} address #{error_message}: #{addr.inspect}"
32
- end
33
+ mail = Mail.new(from: 'deprecated@example.com', to: 'deprecated@example.com')
34
+ Mail::SmtpEnvelope.new(mail).send(:validate_addr, addr_name, addr)
33
35
  end
36
+ deprecate :check_addr, :none, 2023, 6
34
37
 
35
38
  def validate_smtp_addr(addr)
36
- if addr.bytesize > 2048
37
- yield 'may not exceed 2kB'
38
- end
39
+ if addr
40
+ if addr.bytesize > 2048
41
+ yield 'may not exceed 2kB'
42
+ end
39
43
 
40
- if /[\r\n]/ =~ addr
41
- yield 'may not contain CR or LF line breaks'
44
+ if /[\r\n]/ =~ addr
45
+ yield 'may not contain CR or LF line breaks'
46
+ end
42
47
  end
43
48
 
44
49
  addr
45
50
  end
51
+ deprecate :validate_smtp_addr, :none, 2023, 6
46
52
 
47
53
  def check_message(message)
48
54
  message = message.encoded if message.respond_to?(:encoded)
@@ -53,6 +59,7 @@ module Mail
53
59
 
54
60
  message
55
61
  end
62
+ deprecate :check_message, :none, 2023, 6
56
63
  end
57
64
  end
58
65
  end
@@ -42,6 +42,8 @@ module Mail
42
42
  Mail::SMTPConnection
43
43
  when :test
44
44
  Mail::TestMailer
45
+ when :logger
46
+ Mail::LoggerDelivery
45
47
  else
46
48
  method
47
49
  end
@@ -16,9 +16,10 @@ module Mail
16
16
  control = control.dup.force_encoding(Encoding::BINARY)
17
17
  end
18
18
 
19
- CRLF = /\r\n/
19
+ LAX_CRLF = /\r?\n/
20
20
  WSP = /[#{white_space}]/
21
- FWS = /#{CRLF}#{WSP}*/
21
+ FWS = /#{LAX_CRLF}#{WSP}*/
22
+ UNFOLD_WS = /#{LAX_CRLF}(#{WSP})/m
22
23
  TEXT = /[#{text}]/ # + obs-text
23
24
  FIELD_NAME = /[#{field_name}]+/
24
25
  FIELD_PREFIX = /\A(#{FIELD_NAME})/
@@ -26,7 +27,7 @@ module Mail
26
27
  FIELD_LINE = /^[#{field_name}]+:\s*.+$/
27
28
  FIELD_SPLIT = /^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/
28
29
  HEADER_LINE = /^([#{field_name}]+:\s*.+)$/
29
- HEADER_SPLIT = /#{CRLF}(?!#{WSP})/
30
+ HEADER_SPLIT = /#{LAX_CRLF}(?!#{WSP})/
30
31
 
31
32
  QP_UNSAFE = /[^#{qp_safe}]/
32
33
  QP_SAFE = /[#{qp_safe}]/
@@ -34,8 +35,28 @@ module Mail
34
35
  ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{sp}]/n
35
36
  PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
36
37
  TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{sp}]/n
37
- ENCODED_VALUE = /\=\?([^?]+)\?([QB])\?[^?]*?\?\=/mi
38
- FULL_ENCODED_VALUE = /(\=\?[^?]+\?[QB]\?[^?]*?\?\=)/mi
38
+
39
+ ENCODED_VALUE = %r{
40
+ \=\? # literal =?
41
+ ([^?]+) #
42
+ \? # literal ?
43
+ ([QB]) # either a "Q" or a "B"
44
+ \? # literal ?
45
+ .*? # lazily match all characters
46
+ \?\= # literal ?=
47
+ }mix # m is multi-line, i is case-insensitive, x is free-spacing
48
+
49
+ FULL_ENCODED_VALUE = %r{ # Identical to ENCODED_VALUE but captures the whole rather than components of
50
+ (
51
+ \=\? # literal =?
52
+ [^?]+ #
53
+ \? # literal ?
54
+ [QB] # either a "Q" or a "B"
55
+ \? # literal ?
56
+ .*? # lazily match all characters
57
+ \?\= # literal ?=
58
+ )
59
+ }mix # m is multi-line, i is case-insensitive, x is free-spacing
39
60
 
40
61
  EMPTY = ''
41
62
  SPACE = ' '
@@ -43,6 +64,7 @@ module Mail
43
64
  HYPHEN = '-'
44
65
  COLON = ':'
45
66
  ASTERISK = '*'
67
+ CRLF = "\r\n"
46
68
  CR = "\r"
47
69
  LF = "\n"
48
70
  CR_ENCODED = "=0D"
@@ -1,28 +1,28 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
+ require 'mail/parsers/address_lists_parser'
4
+ require 'mail/constants'
5
+ require 'mail/utilities'
6
+
3
7
  module Mail
8
+ # Mail::Address handles all email addresses in Mail. It takes an email address string
9
+ # and parses it, breaking it down into its component parts and allowing you to get the
10
+ # address, comments, display name, name, local part, domain part and fully formatted
11
+ # address.
12
+ #
13
+ # Mail::Address requires a correctly formatted email address per RFC2822 or RFC822. It
14
+ # handles all obsolete versions including obsolete domain routing on the local part.
15
+ #
16
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
17
+ # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
18
+ # a.address #=> 'mikel@test.lindsaar.net'
19
+ # a.display_name #=> 'Mikel Lindsaar'
20
+ # a.local #=> 'mikel'
21
+ # a.domain #=> 'test.lindsaar.net'
22
+ # a.comments #=> ['My email address']
23
+ # a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
4
24
  class Address
5
-
6
- include Mail::Utilities
7
-
8
- # Mail::Address handles all email addresses in Mail. It takes an email address string
9
- # and parses it, breaking it down into its component parts and allowing you to get the
10
- # address, comments, display name, name, local part, domain part and fully formatted
11
- # address.
12
- #
13
- # Mail::Address requires a correctly formatted email address per RFC2822 or RFC822. It
14
- # handles all obsolete versions including obsolete domain routing on the local part.
15
- #
16
- # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
17
- # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
18
- # a.address #=> 'mikel@test.lindsaar.net'
19
- # a.display_name #=> 'Mikel Lindsaar'
20
- # a.local #=> 'mikel'
21
- # a.domain #=> 'test.lindsaar.net'
22
- # a.comments #=> ['My email address']
23
- # a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
24
25
  def initialize(value = nil)
25
- @output_type = :decode
26
26
  if value.nil?
27
27
  @parsed = false
28
28
  @data = nil
@@ -44,14 +44,14 @@ module Mail
44
44
  #
45
45
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
46
46
  # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
47
- def format
47
+ def format(output_type = :decode)
48
48
  parse unless @parsed
49
49
  if @data.nil?
50
- EMPTY
51
- elsif display_name
52
- [quote_phrase(display_name), "<#{address}>", format_comments].compact.join(SPACE)
53
- elsif address
54
- [address, format_comments].compact.join(SPACE)
50
+ Constants::EMPTY
51
+ elsif name = display_name(output_type)
52
+ [Utilities.quote_phrase(name), "<#{address(output_type)}>", format_comments].compact.join(Constants::SPACE)
53
+ elsif a = address(output_type)
54
+ [a, format_comments].compact.join(Constants::SPACE)
55
55
  else
56
56
  raw
57
57
  end
@@ -62,9 +62,13 @@ module Mail
62
62
  #
63
63
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
64
64
  # a.address #=> 'mikel@test.lindsaar.net'
65
- def address
65
+ def address(output_type = :decode)
66
66
  parse unless @parsed
67
- domain ? "#{local}@#{domain}" : local
67
+ if d = domain(output_type)
68
+ "#{local(output_type)}@#{d}"
69
+ else
70
+ local(output_type)
71
+ end
68
72
  end
69
73
 
70
74
  # Provides a way to assign an address to an already made Mail::Address object.
@@ -80,10 +84,10 @@ module Mail
80
84
  #
81
85
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
82
86
  # a.display_name #=> 'Mikel Lindsaar'
83
- def display_name
87
+ def display_name(output_type = :decode)
84
88
  parse unless @parsed
85
89
  @display_name ||= get_display_name
86
- Encodings.decode_encode(@display_name.to_s, @output_type) if @display_name
90
+ Encodings.decode_encode(@display_name.to_s, output_type) if @display_name
87
91
  end
88
92
 
89
93
  # Provides a way to assign a display name to an already made Mail::Address object.
@@ -101,9 +105,9 @@ module Mail
101
105
  #
102
106
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
103
107
  # a.local #=> 'mikel'
104
- def local
108
+ def local(output_type = :decode)
105
109
  parse unless @parsed
106
- Encodings.decode_encode("#{@data.obs_domain_list}#{get_local.strip}", @output_type) if get_local
110
+ Encodings.decode_encode("#{@data.obs_domain_list}#{get_local.strip}", output_type) if get_local
107
111
  end
108
112
 
109
113
  # Returns the domain part (the right hand side of the @ sign in the email address) of
@@ -111,23 +115,27 @@ module Mail
111
115
  #
112
116
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
113
117
  # a.domain #=> 'test.lindsaar.net'
114
- def domain
118
+ def domain(output_type = :decode)
115
119
  parse unless @parsed
116
- Encodings.decode_encode(strip_all_comments(get_domain), @output_type) if get_domain
120
+ Encodings.decode_encode(strip_all_comments(get_domain), output_type) if get_domain
117
121
  end
118
122
 
119
- # Returns an array of comments that are in the email, or an empty array if there
123
+ # Returns an array of comments that are in the email, or nil if there
120
124
  # are no comments
121
125
  #
122
126
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
123
127
  # a.comments #=> ['My email address']
128
+ #
129
+ # b = Address.new('Mikel Lindsaar <mikel@test.lindsaar.net>')
130
+ # b.comments #=> nil
131
+
124
132
  def comments
125
133
  parse unless @parsed
126
134
  comments = get_comments
127
135
  if comments.nil? || comments.none?
128
136
  nil
129
137
  else
130
- comments.map { |c| c.squeeze(SPACE) }
138
+ comments.map { |c| c.squeeze(Constants::SPACE) }
131
139
  end
132
140
  end
133
141
 
@@ -159,13 +167,11 @@ module Mail
159
167
  end
160
168
 
161
169
  def encoded
162
- @output_type = :encode
163
- format
170
+ format :encode
164
171
  end
165
172
 
166
173
  def decoded
167
- @output_type = :decode
168
- format
174
+ format :decode
169
175
  end
170
176
 
171
177
  def group
@@ -179,11 +185,11 @@ module Mail
179
185
  @data = nil
180
186
 
181
187
  case value
182
- when Mail::Parsers::AddressStruct
188
+ when Mail::Parsers::AddressListsParser::AddressStruct
183
189
  @data = value
184
190
  when String
185
191
  unless Utilities.blank?(value)
186
- address_list = Mail::Parsers::AddressListsParser.new.parse(value)
192
+ address_list = Mail::Parsers::AddressListsParser.parse(value)
187
193
  @data = address_list.addresses.first
188
194
  end
189
195
  end
@@ -192,7 +198,7 @@ module Mail
192
198
  def strip_all_comments(string)
193
199
  unless Utilities.blank?(comments)
194
200
  comments.each do |comment|
195
- string = string.gsub("(#{comment})", EMPTY)
201
+ string = string.gsub("(#{comment})", Constants::EMPTY)
196
202
  end
197
203
  end
198
204
  string.strip
@@ -202,7 +208,7 @@ module Mail
202
208
  unless Utilities.blank?(comments)
203
209
  comments.each do |comment|
204
210
  if @data.domain && @data.domain.include?("(#{comment})")
205
- value = value.gsub("(#{comment})", EMPTY)
211
+ value = value.gsub("(#{comment})", Constants::EMPTY)
206
212
  end
207
213
  end
208
214
  end
@@ -222,15 +228,15 @@ module Mail
222
228
  if display_name
223
229
  str = display_name
224
230
  elsif comments
225
- str = "(#{comments.join(SPACE).squeeze(SPACE)})"
231
+ str = "(#{comments.join(Constants::SPACE).squeeze(Constants::SPACE)})"
226
232
  end
227
233
 
228
- unparen(str) unless Utilities.blank?(str)
234
+ Utilities.unparen(str) unless Utilities.blank?(str)
229
235
  end
230
236
 
231
237
  def format_comments
232
238
  if comments
233
- comment_text = comments.map {|c| escape_paren(c) }.join(SPACE).squeeze(SPACE)
239
+ comment_text = comments.map {|c| Utilities.escape_paren(c) }.join(Constants::SPACE).squeeze(Constants::SPACE)
234
240
  @format_comments ||= "(#{comment_text})"
235
241
  else
236
242
  nil
@@ -1,42 +1,34 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
+ require 'mail/parsers/address_lists_parser'
4
+
3
5
  module Mail
4
- class AddressList # :nodoc:
6
+ class AddressList #:nodoc:
7
+ attr_reader :addresses, :group_names
5
8
 
6
9
  # Mail::AddressList is the class that parses To, From and other address fields from
7
10
  # emails passed into Mail.
8
- #
11
+ #
9
12
  # AddressList provides a way to query the groups and mailbox lists of the passed in
10
13
  # string.
11
- #
14
+ #
12
15
  # It can supply all addresses in an array, or return each address as an address object.
13
- #
16
+ #
14
17
  # Mail::AddressList requires a correctly formatted group or mailbox list per RFC2822 or
15
18
  # RFC822. It also handles all obsolete versions in those RFCs.
16
- #
19
+ #
17
20
  # list = 'ada@test.lindsaar.net, My Group: mikel@test.lindsaar.net, Bob <bob@test.lindsaar.net>;'
18
21
  # a = AddressList.new(list)
19
22
  # a.addresses #=> [#<Mail::Address:14943130 Address: |ada@test.lindsaar.net...
20
23
  # a.group_names #=> ["My Group"]
21
24
  def initialize(string)
22
- @addresses_grouped_by_group = nil
23
- @address_list = Mail::Parsers::AddressListsParser.new.parse(string)
24
- end
25
-
26
- # Returns a list of address objects from the parsed line
27
- def addresses
28
- @addresses ||= @address_list.addresses.map do |address_data|
29
- Mail::Address.new(address_data)
30
- end
25
+ address_list = Parsers::AddressListsParser.parse(string)
26
+ @addresses = address_list.addresses.map { |a| Address.new(a) }
27
+ @group_names = address_list.group_names
31
28
  end
32
29
 
33
30
  def addresses_grouped_by_group
34
31
  addresses.select(&:group).group_by(&:group)
35
32
  end
36
-
37
- # Returns the names as an array of strings of all groups
38
- def group_names # :nodoc:
39
- @address_list.group_names
40
- end
41
33
  end
42
34
  end