mail 2.6.3 → 2.7.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.
- checksums.yaml +5 -5
- data/MIT-LICENSE +1 -1
- data/README.md +91 -79
- data/lib/mail/attachments_list.rb +11 -5
- data/lib/mail/body.rb +54 -41
- data/lib/mail/check_delivery_params.rb +50 -10
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/constants.rb +5 -3
- data/lib/mail/core_extensions/smtp.rb +20 -16
- data/lib/mail/core_extensions/string.rb +1 -30
- data/lib/mail/elements/address.rb +43 -32
- data/lib/mail/elements/address_list.rb +11 -18
- data/lib/mail/elements/content_disposition_element.rb +9 -15
- data/lib/mail/elements/content_location_element.rb +8 -12
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -10
- data/lib/mail/elements/content_type_element.rb +9 -19
- data/lib/mail/elements/date_time_element.rb +7 -14
- data/lib/mail/elements/envelope_from_element.rb +15 -21
- data/lib/mail/elements/message_ids_element.rb +12 -14
- data/lib/mail/elements/mime_version_element.rb +7 -14
- data/lib/mail/elements/phrase_list.rb +7 -9
- data/lib/mail/elements/received_element.rb +10 -15
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +6 -15
- data/lib/mail/encodings/8bit.rb +5 -18
- data/lib/mail/encodings/base64.rb +15 -10
- data/lib/mail/encodings/binary.rb +4 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +13 -7
- data/lib/mail/encodings/transfer_encoding.rb +47 -28
- data/lib/mail/encodings/unix_to_unix.rb +4 -1
- data/lib/mail/encodings.rb +114 -60
- data/lib/mail/envelope.rb +2 -1
- data/lib/mail/field.rb +114 -62
- data/lib/mail/field_list.rb +1 -0
- data/lib/mail/fields/bcc_field.rb +17 -5
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +2 -1
- data/lib/mail/fields/common/address_container.rb +3 -2
- data/lib/mail/fields/common/common_address.rb +40 -14
- data/lib/mail/fields/common/common_date.rb +2 -1
- data/lib/mail/fields/common/common_field.rb +5 -11
- data/lib/mail/fields/common/common_message_id.rb +3 -2
- data/lib/mail/fields/common/parameter_hash.rb +2 -1
- data/lib/mail/fields/content_description_field.rb +2 -1
- data/lib/mail/fields/content_disposition_field.rb +14 -13
- data/lib/mail/fields/content_id_field.rb +5 -4
- data/lib/mail/fields/content_location_field.rb +3 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +3 -2
- data/lib/mail/fields/content_type_field.rb +7 -11
- data/lib/mail/fields/date_field.rb +4 -4
- data/lib/mail/fields/from_field.rb +2 -2
- data/lib/mail/fields/in_reply_to_field.rb +2 -1
- data/lib/mail/fields/keywords_field.rb +3 -3
- data/lib/mail/fields/message_id_field.rb +3 -2
- data/lib/mail/fields/mime_version_field.rb +4 -3
- data/lib/mail/fields/optional_field.rb +5 -1
- data/lib/mail/fields/received_field.rb +5 -4
- data/lib/mail/fields/references_field.rb +2 -1
- data/lib/mail/fields/reply_to_field.rb +2 -2
- data/lib/mail/fields/resent_bcc_field.rb +2 -2
- data/lib/mail/fields/resent_cc_field.rb +2 -2
- data/lib/mail/fields/resent_date_field.rb +2 -2
- data/lib/mail/fields/resent_from_field.rb +2 -2
- data/lib/mail/fields/resent_message_id_field.rb +2 -1
- data/lib/mail/fields/resent_sender_field.rb +2 -2
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/return_path_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +2 -2
- data/lib/mail/fields/structured_field.rb +1 -0
- data/lib/mail/fields/subject_field.rb +2 -1
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +25 -7
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +15 -12
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +3 -10
- data/lib/mail/matchers/attachment_matchers.rb +29 -0
- data/lib/mail/matchers/has_sent_mail.rb +51 -7
- data/lib/mail/message.rb +91 -86
- data/lib/mail/multibyte/chars.rb +32 -30
- data/lib/mail/multibyte/unicode.rb +31 -26
- data/lib/mail/multibyte/utils.rb +1 -0
- data/lib/mail/multibyte.rb +65 -15
- data/lib/mail/network/delivery_methods/exim.rb +7 -10
- data/lib/mail/network/delivery_methods/file_delivery.rb +5 -8
- data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +17 -11
- data/lib/mail/network/delivery_methods/smtp.rb +60 -53
- data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -6
- data/lib/mail/network/delivery_methods/test_mailer.rb +6 -8
- data/lib/mail/network/retriever_methods/base.rb +1 -0
- data/lib/mail/network/retriever_methods/imap.rb +19 -5
- data/lib/mail/network/retriever_methods/pop3.rb +4 -1
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/network.rb +2 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33208 -104
- data/lib/mail/parsers/address_lists_parser.rl +172 -0
- data/lib/mail/parsers/content_disposition_parser.rb +877 -49
- data/lib/mail/parsers/content_disposition_parser.rl +82 -0
- data/lib/mail/parsers/content_location_parser.rb +804 -23
- data/lib/mail/parsers/content_location_parser.rl +71 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +502 -19
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
- data/lib/mail/parsers/content_type_parser.rb +1024 -48
- data/lib/mail/parsers/content_type_parser.rl +83 -0
- data/lib/mail/parsers/date_time_parser.rb +872 -23
- data/lib/mail/parsers/date_time_parser.rl +62 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3570 -34
- data/lib/mail/parsers/envelope_from_parser.rl +82 -0
- data/lib/mail/parsers/message_ids_parser.rb +2840 -25
- data/lib/mail/parsers/message_ids_parser.rl +82 -0
- data/lib/mail/parsers/mime_version_parser.rb +492 -26
- data/lib/mail/parsers/mime_version_parser.rl +61 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +862 -17
- data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
- data/lib/mail/parsers/received_parser.rb +8765 -36
- data/lib/mail/parsers/received_parser.rl +84 -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 +59 -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 +17 -24
- data/lib/mail/part.rb +8 -5
- data/lib/mail/parts_list.rb +31 -14
- data/lib/mail/utilities.rb +109 -10
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +3 -2
- data/lib/mail/version_specific/ruby_1_8.rb +50 -6
- data/lib/mail/version_specific/ruby_1_9.rb +103 -18
- data/lib/mail.rb +5 -12
- metadata +47 -57
- data/CHANGELOG.rdoc +0 -759
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -15
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/string/access.rb +0 -145
- data/lib/mail/core_extensions/string/multibyte.rb +0 -78
- data/lib/mail/multibyte/exceptions.rb +0 -8
- 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 -17
data/lib/mail/encodings.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module Mail
|
|
4
5
|
# Raised when attempting to decode an unknown encoding type
|
|
@@ -6,7 +7,6 @@ module Mail
|
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
module Encodings
|
|
9
|
-
|
|
10
10
|
include Mail::Constants
|
|
11
11
|
extend Mail::Utilities
|
|
12
12
|
|
|
@@ -18,7 +18,7 @@ module Mail
|
|
|
18
18
|
#
|
|
19
19
|
# Encodings.register "base64", Mail::Encodings::Base64
|
|
20
20
|
def Encodings.register(name, cls)
|
|
21
|
-
|
|
21
|
+
@transfer_encodings[get_name(name)] = cls
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# Is the encoding we want defined?
|
|
@@ -26,8 +26,8 @@ module Mail
|
|
|
26
26
|
# Example:
|
|
27
27
|
#
|
|
28
28
|
# Encodings.defined?(:base64) #=> true
|
|
29
|
-
def Encodings.defined?(
|
|
30
|
-
@transfer_encodings.include? get_name(
|
|
29
|
+
def Encodings.defined?(name)
|
|
30
|
+
@transfer_encodings.include? get_name(name)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# Gets a defined encoding type, QuotedPrintable or Base64 for now.
|
|
@@ -38,16 +38,24 @@ module Mail
|
|
|
38
38
|
# Example:
|
|
39
39
|
#
|
|
40
40
|
# Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
|
|
41
|
-
def Encodings.get_encoding(
|
|
42
|
-
@transfer_encodings[get_name(
|
|
41
|
+
def Encodings.get_encoding(name)
|
|
42
|
+
@transfer_encodings[get_name(name)]
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def Encodings.get_all
|
|
46
46
|
@transfer_encodings.values
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def Encodings.get_name(
|
|
50
|
-
|
|
49
|
+
def Encodings.get_name(name)
|
|
50
|
+
underscoreize(name).downcase
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def Encodings.transcode_charset(str, from_charset, to_charset = 'UTF-8')
|
|
54
|
+
if from_charset
|
|
55
|
+
RubyVer.transcode_charset str, from_charset, to_charset
|
|
56
|
+
else
|
|
57
|
+
str
|
|
58
|
+
end
|
|
51
59
|
end
|
|
52
60
|
|
|
53
61
|
# Encodes a parameter value using URI Escaping, note the language field 'en' can
|
|
@@ -121,7 +129,7 @@ module Mail
|
|
|
121
129
|
# Split on white-space boundaries with capture, so we capture the white-space as well
|
|
122
130
|
lines.each do |line|
|
|
123
131
|
line.gsub!(ENCODED_VALUE) do |string|
|
|
124
|
-
case $
|
|
132
|
+
case $2
|
|
125
133
|
when *B_VALUES then b_value_decode(string)
|
|
126
134
|
when *Q_VALUES then q_value_decode(string)
|
|
127
135
|
end
|
|
@@ -159,32 +167,51 @@ module Mail
|
|
|
159
167
|
|
|
160
168
|
def Encodings.address_encode(address, charset = 'utf-8')
|
|
161
169
|
if address.is_a?(Array)
|
|
162
|
-
# loop back through for each element
|
|
163
170
|
address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
encode_non_usascii(address, charset) if address
|
|
171
|
+
elsif address
|
|
172
|
+
encode_non_usascii(address, charset)
|
|
167
173
|
end
|
|
168
174
|
end
|
|
169
175
|
|
|
170
176
|
def Encodings.encode_non_usascii(address, charset)
|
|
171
177
|
return address if address.ascii_only? or charset.nil?
|
|
172
|
-
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if
|
|
183
|
-
word
|
|
178
|
+
|
|
179
|
+
# With KCODE=u we can't use regexps on other encodings. Go ASCII.
|
|
180
|
+
with_ascii_kcode do
|
|
181
|
+
# Encode all strings embedded inside of quotes
|
|
182
|
+
address = address.gsub(/("[^"]*[^\/]")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
|
|
183
|
+
|
|
184
|
+
# Then loop through all remaining items and encode as needed
|
|
185
|
+
tokens = address.split(/\s/)
|
|
186
|
+
|
|
187
|
+
map_with_index(tokens) do |word, i|
|
|
188
|
+
if word.ascii_only?
|
|
189
|
+
word
|
|
190
|
+
else
|
|
191
|
+
previous_non_ascii = i>0 && tokens[i-1] && !tokens[i-1].ascii_only?
|
|
192
|
+
if previous_non_ascii #why are we adding an extra space here?
|
|
193
|
+
word = " #{word}"
|
|
194
|
+
end
|
|
195
|
+
Encodings.b_value_encode(word, charset)
|
|
184
196
|
end
|
|
185
|
-
|
|
197
|
+
end.join(' ')
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
if RUBY_VERSION < '1.9'
|
|
202
|
+
# With KCODE=u we can't use regexps on other encodings. Go ASCII.
|
|
203
|
+
def Encodings.with_ascii_kcode #:nodoc:
|
|
204
|
+
if $KCODE
|
|
205
|
+
$KCODE, original_kcode = '', $KCODE
|
|
186
206
|
end
|
|
187
|
-
|
|
207
|
+
yield
|
|
208
|
+
ensure
|
|
209
|
+
$KCODE = original_kcode if original_kcode
|
|
210
|
+
end
|
|
211
|
+
else
|
|
212
|
+
def Encodings.with_ascii_kcode #:nodoc:
|
|
213
|
+
yield
|
|
214
|
+
end
|
|
188
215
|
end
|
|
189
216
|
|
|
190
217
|
# Encode a string with Base64 Encoding and returns it ready to be inserted
|
|
@@ -194,12 +221,15 @@ module Mail
|
|
|
194
221
|
#
|
|
195
222
|
# Encodings.b_value_encode('This is あ string', 'UTF-8')
|
|
196
223
|
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
|
|
197
|
-
def Encodings.b_value_encode(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
def Encodings.b_value_encode(string, encoding = nil)
|
|
225
|
+
if string.to_s.ascii_only?
|
|
226
|
+
string
|
|
227
|
+
else
|
|
228
|
+
Encodings.each_base64_chunk_byterange(string, 60).map do |chunk|
|
|
229
|
+
str, encoding = RubyVer.b_value_encode(chunk, encoding)
|
|
230
|
+
"=?#{encoding}?B?#{str.chomp}?="
|
|
231
|
+
end.join(" ")
|
|
232
|
+
end
|
|
203
233
|
end
|
|
204
234
|
|
|
205
235
|
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
|
|
@@ -240,50 +270,74 @@ module Mail
|
|
|
240
270
|
RubyVer.q_value_decode(str)
|
|
241
271
|
end
|
|
242
272
|
|
|
243
|
-
def Encodings.split_encoding_from_string( str )
|
|
244
|
-
match = str.match(/\=\?([^?]+)?\?[QB]\?(.*)\?\=/mi)
|
|
245
|
-
if match
|
|
246
|
-
match[1]
|
|
247
|
-
else
|
|
248
|
-
nil
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
273
|
def Encodings.find_encoding(str)
|
|
253
274
|
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
|
|
254
275
|
end
|
|
255
276
|
|
|
256
277
|
# Gets the encoding type (Q or B) from the string.
|
|
257
|
-
def Encodings.
|
|
258
|
-
|
|
259
|
-
if match
|
|
260
|
-
match[1]
|
|
261
|
-
else
|
|
262
|
-
nil
|
|
263
|
-
end
|
|
278
|
+
def Encodings.value_encoding_from_string(str)
|
|
279
|
+
str[ENCODED_VALUE, 1]
|
|
264
280
|
end
|
|
265
281
|
|
|
266
|
-
#
|
|
267
|
-
# encoding (Q or B) can be joined together.
|
|
282
|
+
# Split header line into proper encoded and unencoded parts.
|
|
268
283
|
#
|
|
269
284
|
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
|
285
|
+
#
|
|
286
|
+
# Omit unencoded space after an encoded-word.
|
|
270
287
|
def Encodings.collapse_adjacent_encodings(str)
|
|
271
|
-
lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
|
|
272
288
|
results = []
|
|
273
|
-
|
|
289
|
+
last_encoded = nil # Track whether to preserve or drop whitespace
|
|
274
290
|
|
|
275
|
-
lines
|
|
276
|
-
|
|
291
|
+
lines = str.split(FULL_ENCODED_VALUE)
|
|
292
|
+
lines.each_slice(2) do |unencoded, encoded|
|
|
293
|
+
if last_encoded = encoded
|
|
294
|
+
if !Utilities.blank?(unencoded) || (!last_encoded && unencoded != EMPTY)
|
|
295
|
+
results << unencoded
|
|
296
|
+
end
|
|
277
297
|
|
|
278
|
-
|
|
279
|
-
|
|
298
|
+
results << encoded
|
|
299
|
+
else
|
|
300
|
+
results << unencoded
|
|
280
301
|
end
|
|
281
|
-
|
|
282
|
-
previous_encoding = encoding
|
|
283
|
-
results << line
|
|
284
302
|
end
|
|
285
303
|
|
|
286
304
|
results
|
|
287
305
|
end
|
|
306
|
+
|
|
307
|
+
# Partition the string into bounded-size chunks without splitting
|
|
308
|
+
# multibyte characters.
|
|
309
|
+
def Encodings.each_base64_chunk_byterange(str, max_bytesize_per_base64_chunk, &block)
|
|
310
|
+
raise "size per chunk must be multiple of 4" if (max_bytesize_per_base64_chunk % 4).nonzero?
|
|
311
|
+
|
|
312
|
+
if block_given?
|
|
313
|
+
max_bytesize = ((3 * max_bytesize_per_base64_chunk) / 4.0).floor
|
|
314
|
+
each_chunk_byterange(str, max_bytesize, &block)
|
|
315
|
+
else
|
|
316
|
+
enum_for :each_base64_chunk_byterange, str, max_bytesize_per_base64_chunk
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Partition the string into bounded-size chunks without splitting
|
|
321
|
+
# multibyte characters.
|
|
322
|
+
def Encodings.each_chunk_byterange(str, max_bytesize_per_chunk)
|
|
323
|
+
return enum_for(:each_chunk_byterange, str, max_bytesize_per_chunk) unless block_given?
|
|
324
|
+
|
|
325
|
+
offset = 0
|
|
326
|
+
chunksize = 0
|
|
327
|
+
|
|
328
|
+
str.each_char do |chr|
|
|
329
|
+
charsize = chr.bytesize
|
|
330
|
+
|
|
331
|
+
if chunksize + charsize > max_bytesize_per_chunk
|
|
332
|
+
yield RubyVer.string_byteslice(str, offset, chunksize)
|
|
333
|
+
offset += chunksize
|
|
334
|
+
chunksize = charsize
|
|
335
|
+
else
|
|
336
|
+
chunksize += charsize
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
yield RubyVer.string_byteslice(str, offset, chunksize)
|
|
341
|
+
end
|
|
288
342
|
end
|
|
289
343
|
end
|
data/lib/mail/envelope.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# = Mail Envelope
|
|
4
5
|
#
|
|
@@ -11,7 +12,7 @@ module Mail
|
|
|
11
12
|
class Envelope < StructuredField
|
|
12
13
|
|
|
13
14
|
def initialize(*args)
|
|
14
|
-
super(FIELD_NAME,
|
|
15
|
+
super(FIELD_NAME, args.last.to_s)
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def element
|
data/lib/mail/field.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'mail/fields'
|
|
3
|
+
require 'mail/constants'
|
|
2
4
|
|
|
3
5
|
# encoding: utf-8
|
|
4
6
|
module Mail
|
|
@@ -82,9 +84,31 @@ module Mail
|
|
|
82
84
|
|
|
83
85
|
def initialize(element, value, reason)
|
|
84
86
|
@element = element
|
|
85
|
-
@value = value
|
|
86
|
-
@reason = reason
|
|
87
|
-
super("#{element} can not parse |#{value}
|
|
87
|
+
@value = to_utf8(value)
|
|
88
|
+
@reason = to_utf8(reason)
|
|
89
|
+
super("#{@element} can not parse |#{@value}|: #{@reason}")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
def to_utf8(text)
|
|
94
|
+
if text.respond_to?(:force_encoding)
|
|
95
|
+
text.dup.force_encoding(Encoding::UTF_8)
|
|
96
|
+
else
|
|
97
|
+
text
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class NilParseError < ParseError #:nodoc:
|
|
103
|
+
def initialize(element)
|
|
104
|
+
super element, nil, 'nil is invalid'
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class IncompleteParseError < ParseError #:nodoc:
|
|
109
|
+
def initialize(element, original_text, unparsed_index)
|
|
110
|
+
parsed_text = to_utf8(original_text[0...unparsed_index])
|
|
111
|
+
super element, original_text, "Only able to parse up to #{parsed_text.inspect}"
|
|
88
112
|
end
|
|
89
113
|
end
|
|
90
114
|
|
|
@@ -92,41 +116,64 @@ module Mail
|
|
|
92
116
|
class SyntaxError < FieldError #:nodoc:
|
|
93
117
|
end
|
|
94
118
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
119
|
+
class << self
|
|
120
|
+
# Parse a field from a raw header line:
|
|
121
|
+
#
|
|
122
|
+
# Mail::Field.parse("field-name: field data")
|
|
123
|
+
# # => #<Mail::Field …>
|
|
124
|
+
def parse(field, charset = nil)
|
|
125
|
+
name, value = split(field)
|
|
126
|
+
if name && value
|
|
127
|
+
new name, value, charset
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def split(raw_field) #:nodoc:
|
|
132
|
+
if raw_field.index(Constants::COLON)
|
|
133
|
+
name, value = raw_field.split(Constants::COLON, 2)
|
|
134
|
+
name.rstrip!
|
|
135
|
+
if name =~ /\A#{Constants::FIELD_NAME}\z/
|
|
136
|
+
[ name.rstrip, value.strip ]
|
|
137
|
+
else
|
|
138
|
+
Kernel.warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: invalid header name syntax: #{name.inspect}"
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
141
|
+
else
|
|
142
|
+
raw_field.strip
|
|
143
|
+
end
|
|
144
|
+
rescue => error
|
|
145
|
+
warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: #{error.class}: #{error.message}"
|
|
146
|
+
nil
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
attr_reader :unparsed_value
|
|
151
|
+
|
|
152
|
+
# Create a field by name and optional value:
|
|
102
153
|
#
|
|
103
|
-
#
|
|
154
|
+
# Mail::Field.new("field-name", "value")
|
|
155
|
+
# # => #<Mail::Field …>
|
|
104
156
|
#
|
|
105
|
-
#
|
|
157
|
+
# Values that aren't strings or arrays are coerced to Strings with `#to_s`.
|
|
106
158
|
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
# it will be passed through as is, for example, content-type
|
|
110
|
-
# field can accept an array with the type and a hash of
|
|
111
|
-
# parameters:
|
|
159
|
+
# Mail::Field.new("field-name", 1234)
|
|
160
|
+
# # => #<Mail::Field …>
|
|
112
161
|
#
|
|
113
|
-
# Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
|
|
162
|
+
# Mail::Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
|
|
163
|
+
# # => #<Mail::Field …>
|
|
114
164
|
def initialize(name, value = nil, charset = 'utf-8')
|
|
115
165
|
case
|
|
116
|
-
when name.index(COLON)
|
|
117
|
-
|
|
118
|
-
@name = name
|
|
119
|
-
@
|
|
120
|
-
|
|
121
|
-
when value.blank? # Field.new("field-name")
|
|
166
|
+
when name.index(COLON)
|
|
167
|
+
Kernel.warn 'Passing an unparsed header field to Mail::Field.new is deprecated and will be removed in Mail 2.8.0. Use Mail::Field.parse instead.'
|
|
168
|
+
@name, @unparsed_value = self.class.split(name)
|
|
169
|
+
@charset = Utilities.blank?(value) ? charset : value
|
|
170
|
+
when Utilities.blank?(value)
|
|
122
171
|
@name = name
|
|
123
|
-
@
|
|
124
|
-
@raw_value = nil
|
|
172
|
+
@unparsed_value = nil
|
|
125
173
|
@charset = charset
|
|
126
|
-
else
|
|
174
|
+
else
|
|
127
175
|
@name = name
|
|
128
|
-
@
|
|
129
|
-
@raw_value = nil
|
|
176
|
+
@unparsed_value = value
|
|
130
177
|
@charset = charset
|
|
131
178
|
end
|
|
132
179
|
@name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
|
|
@@ -137,8 +184,7 @@ module Mail
|
|
|
137
184
|
end
|
|
138
185
|
|
|
139
186
|
def field
|
|
140
|
-
|
|
141
|
-
@field ||= create_field(@name, @value, @charset)
|
|
187
|
+
@field ||= create_field(@name, @unparsed_value, @charset)
|
|
142
188
|
end
|
|
143
189
|
|
|
144
190
|
def name
|
|
@@ -168,15 +214,19 @@ module Mail
|
|
|
168
214
|
end
|
|
169
215
|
|
|
170
216
|
def same( other )
|
|
217
|
+
return false unless other.kind_of?(self.class)
|
|
171
218
|
match_to_s(other.name, self.name)
|
|
172
219
|
end
|
|
173
220
|
|
|
221
|
+
def ==( other )
|
|
222
|
+
return false unless other.kind_of?(self.class)
|
|
223
|
+
match_to_s(other.name, self.name) && match_to_s(other.value, self.value)
|
|
224
|
+
end
|
|
225
|
+
|
|
174
226
|
def responsible_for?( val )
|
|
175
227
|
name.to_s.casecmp(val.to_s) == 0
|
|
176
228
|
end
|
|
177
229
|
|
|
178
|
-
alias_method :==, :same
|
|
179
|
-
|
|
180
230
|
def <=>( other )
|
|
181
231
|
self.field_order_id <=> other.field_order_id
|
|
182
232
|
end
|
|
@@ -189,6 +239,16 @@ module Mail
|
|
|
189
239
|
field.send(name, *args, &block)
|
|
190
240
|
end
|
|
191
241
|
|
|
242
|
+
if RUBY_VERSION >= '1.9.2'
|
|
243
|
+
def respond_to_missing?(method_name, include_private)
|
|
244
|
+
field.respond_to?(method_name, include_private) || super
|
|
245
|
+
end
|
|
246
|
+
else
|
|
247
|
+
def respond_to?(method_name, include_private = false)
|
|
248
|
+
field.respond_to?(method_name, include_private) || super
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
192
252
|
FIELD_ORDER = %w[ return-path received
|
|
193
253
|
resent-date resent-from resent-sender resent-to
|
|
194
254
|
resent-cc resent-bcc resent-message-id
|
|
@@ -202,11 +262,26 @@ module Mail
|
|
|
202
262
|
|
|
203
263
|
private
|
|
204
264
|
|
|
205
|
-
def
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
265
|
+
def create_field(name, value, charset)
|
|
266
|
+
new_field(name, value, charset)
|
|
267
|
+
rescue Mail::Field::ParseError => e
|
|
268
|
+
field = Mail::UnstructuredField.new(name, value)
|
|
269
|
+
field.errors << [name, value, e]
|
|
270
|
+
field
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def new_field(name, value, charset)
|
|
274
|
+
value = unfold(value) if value.is_a?(String)
|
|
275
|
+
|
|
276
|
+
if klass = field_class_for(name)
|
|
277
|
+
klass.new(value, charset)
|
|
278
|
+
else
|
|
279
|
+
OptionalField.new(name, value, charset)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def field_class_for(name)
|
|
284
|
+
FIELDS_MAP[name.to_s.downcase]
|
|
210
285
|
end
|
|
211
286
|
|
|
212
287
|
# 2.2.3. Long Header Fields
|
|
@@ -218,30 +293,7 @@ module Mail
|
|
|
218
293
|
# treated in its unfolded form for further syntactic and semantic
|
|
219
294
|
# evaluation.
|
|
220
295
|
def unfold(string)
|
|
221
|
-
string.gsub(/
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def create_field(name, value, charset)
|
|
225
|
-
value = unfold(value) if value.is_a?(String)
|
|
226
|
-
|
|
227
|
-
begin
|
|
228
|
-
new_field(name, value, charset)
|
|
229
|
-
rescue Mail::Field::ParseError => e
|
|
230
|
-
field = Mail::UnstructuredField.new(name, value)
|
|
231
|
-
field.errors << [name, value, e]
|
|
232
|
-
field
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
def new_field(name, value, charset)
|
|
237
|
-
lower_case_name = name.to_s.downcase
|
|
238
|
-
if field_klass = FIELDS_MAP[lower_case_name]
|
|
239
|
-
field_klass.new(value, charset)
|
|
240
|
-
else
|
|
241
|
-
OptionalField.new(name, value, charset)
|
|
242
|
-
end
|
|
296
|
+
string.gsub(/#{Constants::CRLF}(#{Constants::WSP})/m, '\1')
|
|
243
297
|
end
|
|
244
|
-
|
|
245
298
|
end
|
|
246
|
-
|
|
247
299
|
end
|
data/lib/mail/field_list.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# = Blind Carbon Copy Field
|
|
4
5
|
#
|
|
@@ -36,16 +37,27 @@ module Mail
|
|
|
36
37
|
FIELD_NAME = 'bcc'
|
|
37
38
|
CAPITALIZED_FIELD = 'Bcc'
|
|
38
39
|
|
|
39
|
-
def initialize(value =
|
|
40
|
+
def initialize(value = nil, charset = 'utf-8')
|
|
40
41
|
@charset = charset
|
|
41
|
-
super(CAPITALIZED_FIELD,
|
|
42
|
-
self.parse
|
|
42
|
+
super(CAPITALIZED_FIELD, value, charset)
|
|
43
43
|
self
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
def include_in_headers=(include_in_headers)
|
|
47
|
+
@include_in_headers = include_in_headers
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def include_in_headers
|
|
51
|
+
defined?(@include_in_headers) ? @include_in_headers : self.include_in_headers = false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Bcc field should not be :encoded by default
|
|
47
55
|
def encoded
|
|
48
|
-
|
|
56
|
+
if include_in_headers
|
|
57
|
+
do_encode(CAPITALIZED_FIELD)
|
|
58
|
+
else
|
|
59
|
+
''
|
|
60
|
+
end
|
|
49
61
|
end
|
|
50
62
|
|
|
51
63
|
def decoded
|
data/lib/mail/fields/cc_field.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# = Carbon Copy Field
|
|
4
5
|
#
|
|
@@ -38,8 +39,7 @@ module Mail
|
|
|
38
39
|
|
|
39
40
|
def initialize(value = nil, charset = 'utf-8')
|
|
40
41
|
self.charset = charset
|
|
41
|
-
super(CAPITALIZED_FIELD,
|
|
42
|
-
self.parse
|
|
42
|
+
super(CAPITALIZED_FIELD, value, charset)
|
|
43
43
|
self
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# = Comments Field
|
|
4
5
|
#
|
|
@@ -32,7 +33,7 @@ module Mail
|
|
|
32
33
|
|
|
33
34
|
def initialize(value = nil, charset = 'utf-8')
|
|
34
35
|
@charset = charset
|
|
35
|
-
super(CAPITALIZED_FIELD,
|
|
36
|
+
super(CAPITALIZED_FIELD, value)
|
|
36
37
|
self.parse
|
|
37
38
|
self
|
|
38
39
|
end
|