mail 2.6.6 → 2.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/MIT-LICENSE +1 -1
- data/README.md +134 -119
- data/lib/mail/attachments_list.rb +10 -9
- data/lib/mail/body.rb +73 -84
- data/lib/mail/check_delivery_params.rb +28 -21
- data/lib/mail/configuration.rb +2 -0
- data/lib/mail/constants.rb +27 -5
- data/lib/mail/elements/address.rb +53 -47
- data/lib/mail/elements/address_list.rb +11 -19
- data/lib/mail/elements/content_disposition_element.rb +9 -16
- data/lib/mail/elements/content_location_element.rb +6 -11
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
- data/lib/mail/elements/content_type_element.rb +16 -23
- data/lib/mail/elements/date_time_element.rb +7 -15
- data/lib/mail/elements/envelope_from_element.rb +22 -23
- data/lib/mail/elements/message_ids_element.rb +18 -13
- data/lib/mail/elements/mime_version_element.rb +7 -15
- data/lib/mail/elements/phrase_list.rb +12 -10
- data/lib/mail/elements/received_element.rb +27 -19
- data/lib/mail/encodings/7bit.rb +9 -14
- data/lib/mail/encodings/8bit.rb +2 -21
- data/lib/mail/encodings/base64.rb +11 -12
- data/lib/mail/encodings/binary.rb +3 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +6 -6
- data/lib/mail/encodings/transfer_encoding.rb +38 -29
- data/lib/mail/encodings/unix_to_unix.rb +3 -1
- data/lib/mail/encodings.rb +81 -54
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +119 -98
- data/lib/mail/field_list.rb +60 -7
- data/lib/mail/fields/bcc_field.rb +34 -52
- data/lib/mail/fields/cc_field.rb +28 -49
- data/lib/mail/fields/comments_field.rb +27 -37
- data/lib/mail/fields/common_address_field.rb +170 -0
- data/lib/mail/fields/common_date_field.rb +58 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +42 -0
- data/lib/mail/fields/content_description_field.rb +7 -14
- data/lib/mail/fields/content_disposition_field.rb +13 -38
- data/lib/mail/fields/content_id_field.rb +24 -51
- data/lib/mail/fields/content_location_field.rb +11 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
- data/lib/mail/fields/content_type_field.rb +50 -80
- data/lib/mail/fields/date_field.rb +23 -52
- data/lib/mail/fields/from_field.rb +28 -49
- data/lib/mail/fields/in_reply_to_field.rb +38 -49
- data/lib/mail/fields/keywords_field.rb +18 -31
- data/lib/mail/fields/message_id_field.rb +25 -71
- data/lib/mail/fields/mime_version_field.rb +19 -30
- data/lib/mail/fields/named_structured_field.rb +11 -0
- data/lib/mail/fields/named_unstructured_field.rb +11 -0
- data/lib/mail/fields/optional_field.rb +9 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
- data/lib/mail/fields/received_field.rb +43 -57
- data/lib/mail/fields/references_field.rb +35 -49
- data/lib/mail/fields/reply_to_field.rb +28 -49
- data/lib/mail/fields/resent_bcc_field.rb +28 -49
- data/lib/mail/fields/resent_cc_field.rb +28 -49
- data/lib/mail/fields/resent_date_field.rb +5 -30
- data/lib/mail/fields/resent_from_field.rb +28 -49
- data/lib/mail/fields/resent_message_id_field.rb +5 -29
- data/lib/mail/fields/resent_sender_field.rb +27 -56
- data/lib/mail/fields/resent_to_field.rb +28 -49
- data/lib/mail/fields/return_path_field.rb +50 -54
- data/lib/mail/fields/sender_field.rb +34 -55
- data/lib/mail/fields/structured_field.rb +3 -30
- data/lib/mail/fields/subject_field.rb +9 -11
- data/lib/mail/fields/to_field.rb +28 -49
- data/lib/mail/fields/unstructured_field.rb +32 -47
- data/lib/mail/header.rb +71 -110
- data/lib/mail/mail.rb +2 -10
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/matchers/has_sent_mail.rb +21 -1
- data/lib/mail/message.rb +113 -117
- data/lib/mail/multibyte/chars.rb +21 -178
- data/lib/mail/multibyte/unicode.rb +10 -10
- data/lib/mail/multibyte/utils.rb +26 -43
- data/lib/mail/multibyte.rb +55 -16
- data/lib/mail/network/delivery_methods/exim.rb +5 -4
- data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
- data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +62 -21
- data/lib/mail/network/delivery_methods/smtp.rb +75 -50
- data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -4
- data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
- data/lib/mail/network/retriever_methods/base.rb +8 -8
- data/lib/mail/network/retriever_methods/imap.rb +20 -7
- data/lib/mail/network/retriever_methods/pop3.rb +5 -3
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/network.rb +1 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33225 -116
- data/lib/mail/parsers/address_lists_parser.rl +179 -0
- data/lib/mail/parsers/content_disposition_parser.rb +882 -49
- data/lib/mail/parsers/content_disposition_parser.rl +89 -0
- data/lib/mail/parsers/content_location_parser.rb +809 -23
- data/lib/mail/parsers/content_location_parser.rl +78 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
- data/lib/mail/parsers/content_type_parser.rb +1037 -56
- data/lib/mail/parsers/content_type_parser.rl +90 -0
- data/lib/mail/parsers/date_time_parser.rb +877 -25
- data/lib/mail/parsers/date_time_parser.rl +69 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
- data/lib/mail/parsers/envelope_from_parser.rl +89 -0
- data/lib/mail/parsers/message_ids_parser.rb +5146 -25
- data/lib/mail/parsers/message_ids_parser.rl +93 -0
- data/lib/mail/parsers/mime_version_parser.rb +497 -26
- data/lib/mail/parsers/mime_version_parser.rl +68 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
- data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
- data/lib/mail/parsers/received_parser.rb +8776 -43
- data/lib/mail/parsers/received_parser.rl +91 -0
- data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/lib/mail/parsers/rfc5322.rl +74 -0
- data/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
- data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/lib/mail/parsers.rb +11 -25
- data/lib/mail/part.rb +6 -10
- data/lib/mail/parts_list.rb +62 -6
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +343 -74
- data/lib/mail/version.rb +2 -2
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +5 -35
- metadata +111 -66
- data/CHANGELOG.rdoc +0 -803
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -14
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/lib/mail/core_extensions/smtp.rb +0 -25
- data/lib/mail/core_extensions/string/access.rb +0 -146
- data/lib/mail/core_extensions/string/multibyte.rb +0 -79
- data/lib/mail/core_extensions/string.rb +0 -21
- data/lib/mail/fields/common/address_container.rb +0 -17
- data/lib/mail/fields/common/common_address.rb +0 -136
- data/lib/mail/fields/common/common_date.rb +0 -36
- data/lib/mail/fields/common/common_field.rb +0 -61
- data/lib/mail/fields/common/common_message_id.rb +0 -49
- data/lib/mail/multibyte/exceptions.rb +0 -9
- data/lib/mail/parsers/ragel/common.rl +0 -185
- data/lib/mail/parsers/ragel/parser_info.rb +0 -61
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
- data/lib/mail/parsers/ragel/ruby.rb +0 -40
- data/lib/mail/parsers/ragel.rb +0 -18
- data/lib/mail/version_specific/ruby_1_8.rb +0 -126
- 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 =
|
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'
|
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
|
-
|
137
|
-
|
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 =
|
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
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
162
|
-
|
164
|
+
# Cannot decode, so skip normalization
|
165
|
+
raw_source
|
163
166
|
else
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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 =
|
206
|
-
|
207
|
-
|
200
|
+
@encoding =
|
201
|
+
if val == "text" || Utilities.blank?(val)
|
202
|
+
default_encoding
|
203
|
+
else
|
208
204
|
val
|
209
|
-
|
205
|
+
end
|
210
206
|
end
|
211
207
|
|
212
|
-
# Returns the
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|
272
|
-
|
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
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
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
|
37
|
-
|
38
|
-
|
39
|
+
if addr
|
40
|
+
if addr.bytesize > 2048
|
41
|
+
yield 'may not exceed 2kB'
|
42
|
+
end
|
39
43
|
|
40
|
-
|
41
|
-
|
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
|
data/lib/mail/configuration.rb
CHANGED
data/lib/mail/constants.rb
CHANGED
@@ -16,9 +16,10 @@ module Mail
|
|
16
16
|
control = control.dup.force_encoding(Encoding::BINARY)
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
LAX_CRLF = /\r?\n/
|
20
20
|
WSP = /[#{white_space}]/
|
21
|
-
FWS = /#{
|
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 = /#{
|
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
|
-
|
38
|
-
|
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(
|
53
|
-
elsif address
|
54
|
-
[
|
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
|
-
|
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,
|
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}",
|
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),
|
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
|
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
|
-
|
163
|
-
format
|
170
|
+
format :encode
|
164
171
|
end
|
165
172
|
|
166
173
|
def decoded
|
167
|
-
|
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.
|
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
|
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
|
-
|
23
|
-
@
|
24
|
-
|
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
|