mail 2.6.1 → 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 +92 -80
- data/lib/mail/attachments_list.rb +11 -5
- data/lib/mail/body.rb +81 -44
- data/lib/mail/check_delivery_params.rb +50 -10
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/{patterns.rb → constants.rb} +26 -6
- data/lib/mail/core_extensions/smtp.rb +20 -16
- data/lib/mail/core_extensions/string.rb +1 -27
- data/lib/mail/elements/address.rb +81 -93
- data/lib/mail/elements/address_list.rb +12 -29
- 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 +20 -0
- data/lib/mail/encodings.rb +121 -82
- data/lib/mail/envelope.rb +2 -1
- data/lib/mail/field.rb +114 -62
- data/lib/mail/field_list.rb +2 -1
- 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 +6 -11
- data/lib/mail/fields/common/common_message_id.rb +3 -2
- data/lib/mail/fields/common/parameter_hash.rb +5 -4
- 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 +28 -10
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +18 -14
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +6 -11
- data/lib/mail/matchers/attachment_matchers.rb +29 -0
- data/lib/mail/matchers/has_sent_mail.rb +53 -9
- data/lib/mail/message.rb +99 -89
- 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 -46
- 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 +112 -13
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +8 -15
- data/lib/mail/version_specific/ruby_1_8.rb +52 -8
- data/lib/mail/version_specific/ruby_1_9.rb +143 -24
- data/lib/mail.rb +8 -14
- metadata +71 -81
- data/CHANGELOG.rdoc +0 -752
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -15
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/VERSION +0 -4
- 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 -184
- 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 -2129
- 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 -39
- 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,8 +7,7 @@ module Mail
|
|
6
7
|
end
|
7
8
|
|
8
9
|
module Encodings
|
9
|
-
|
10
|
-
include Mail::Patterns
|
10
|
+
include Mail::Constants
|
11
11
|
extend Mail::Utilities
|
12
12
|
|
13
13
|
@transfer_encodings = {}
|
@@ -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
|
@@ -114,34 +122,19 @@ module Mail
|
|
114
122
|
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
115
123
|
def Encodings.value_decode(str)
|
116
124
|
# Optimization: If there's no encoded-words in the string, just return it
|
117
|
-
return str unless str =~
|
125
|
+
return str unless str =~ ENCODED_VALUE
|
118
126
|
|
119
127
|
lines = collapse_adjacent_encodings(str)
|
120
128
|
|
121
129
|
# Split on white-space boundaries with capture, so we capture the white-space as well
|
122
|
-
lines.
|
123
|
-
line.
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
# Search for occurences of quoted strings or plain strings
|
128
|
-
text.scan(/( # Group around entire regex to include it in matches
|
129
|
-
\=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
|
130
|
-
| # or
|
131
|
-
.+?(?=\=\?|$) # Plain String
|
132
|
-
)/xmi).map do |matches|
|
133
|
-
string, method = *matches
|
134
|
-
if method == 'b' || method == 'B'
|
135
|
-
b_value_decode(string)
|
136
|
-
elsif method == 'q' || method == 'Q'
|
137
|
-
q_value_decode(string)
|
138
|
-
else
|
139
|
-
string
|
140
|
-
end
|
141
|
-
end
|
130
|
+
lines.each do |line|
|
131
|
+
line.gsub!(ENCODED_VALUE) do |string|
|
132
|
+
case $2
|
133
|
+
when *B_VALUES then b_value_decode(string)
|
134
|
+
when *Q_VALUES then q_value_decode(string)
|
142
135
|
end
|
143
136
|
end
|
144
|
-
end.
|
137
|
+
end.join("")
|
145
138
|
end
|
146
139
|
|
147
140
|
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
|
@@ -174,32 +167,51 @@ module Mail
|
|
174
167
|
|
175
168
|
def Encodings.address_encode(address, charset = 'utf-8')
|
176
169
|
if address.is_a?(Array)
|
177
|
-
# loop back through for each element
|
178
170
|
address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
|
179
|
-
|
180
|
-
|
181
|
-
encode_non_usascii(address, charset) if address
|
171
|
+
elsif address
|
172
|
+
encode_non_usascii(address, charset)
|
182
173
|
end
|
183
174
|
end
|
184
175
|
|
185
176
|
def Encodings.encode_non_usascii(address, charset)
|
186
177
|
return address if address.ascii_only? or charset.nil?
|
187
|
-
|
188
|
-
#
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
if
|
198
|
-
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)
|
199
196
|
end
|
200
|
-
|
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
|
201
206
|
end
|
202
|
-
|
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
|
203
215
|
end
|
204
216
|
|
205
217
|
# Encode a string with Base64 Encoding and returns it ready to be inserted
|
@@ -209,12 +221,15 @@ module Mail
|
|
209
221
|
#
|
210
222
|
# Encodings.b_value_encode('This is あ string', 'UTF-8')
|
211
223
|
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
|
212
|
-
def Encodings.b_value_encode(
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
218
233
|
end
|
219
234
|
|
220
235
|
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
|
@@ -255,50 +270,74 @@ module Mail
|
|
255
270
|
RubyVer.q_value_decode(str)
|
256
271
|
end
|
257
272
|
|
258
|
-
def Encodings.split_encoding_from_string( str )
|
259
|
-
match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
|
260
|
-
if match
|
261
|
-
match[1]
|
262
|
-
else
|
263
|
-
nil
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
273
|
def Encodings.find_encoding(str)
|
268
274
|
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
|
269
275
|
end
|
270
276
|
|
271
277
|
# Gets the encoding type (Q or B) from the string.
|
272
|
-
def Encodings.
|
273
|
-
|
274
|
-
if match
|
275
|
-
match[1]
|
276
|
-
else
|
277
|
-
nil
|
278
|
-
end
|
278
|
+
def Encodings.value_encoding_from_string(str)
|
279
|
+
str[ENCODED_VALUE, 1]
|
279
280
|
end
|
280
281
|
|
281
|
-
#
|
282
|
-
# encoding (Q or B) can be joined together.
|
282
|
+
# Split header line into proper encoded and unencoded parts.
|
283
283
|
#
|
284
284
|
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
285
|
+
#
|
286
|
+
# Omit unencoded space after an encoded-word.
|
285
287
|
def Encodings.collapse_adjacent_encodings(str)
|
286
|
-
lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
|
287
288
|
results = []
|
288
|
-
|
289
|
+
last_encoded = nil # Track whether to preserve or drop whitespace
|
289
290
|
|
290
|
-
lines
|
291
|
-
|
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
|
292
297
|
|
293
|
-
|
294
|
-
|
298
|
+
results << encoded
|
299
|
+
else
|
300
|
+
results << unencoded
|
295
301
|
end
|
296
|
-
|
297
|
-
previous_encoding = encoding
|
298
|
-
results << line
|
299
302
|
end
|
300
303
|
|
301
304
|
results
|
302
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
|
303
342
|
end
|
304
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
|
117
|
-
|
118
|
-
@name = name
|
119
|
-
@
|
120
|
-
|
121
|
-
when name !~ /:/ && 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
|
module Mail
|
3
4
|
|
4
5
|
# Field List class provides an enhanced array that keeps a list of
|
@@ -19,7 +20,7 @@ module Mail
|
|
19
20
|
hi = size
|
20
21
|
|
21
22
|
while lo < hi
|
22
|
-
mid = (lo + hi)
|
23
|
+
mid = (lo + hi).div(2)
|
23
24
|
if new_field < self[mid]
|
24
25
|
hi = mid
|
25
26
|
else
|
@@ -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
|