mail 2.7.0 → 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.
- checksums.yaml +5 -5
- data/README.md +59 -28
- data/lib/mail/attachments_list.rb +2 -5
- data/lib/mail/body.rb +33 -50
- data/lib/mail/check_delivery_params.rb +21 -16
- data/lib/mail/constants.rb +27 -5
- data/lib/mail/elements/address.rb +27 -27
- data/lib/mail/elements/address_list.rb +1 -1
- data/lib/mail/elements/content_disposition_element.rb +1 -1
- data/lib/mail/elements/content_location_element.rb +1 -1
- data/lib/mail/elements/content_transfer_encoding_element.rb +1 -1
- data/lib/mail/elements/content_type_element.rb +8 -4
- data/lib/mail/elements/date_time_element.rb +1 -1
- data/lib/mail/elements/envelope_from_element.rb +13 -7
- data/lib/mail/elements/message_ids_element.rb +14 -5
- data/lib/mail/elements/mime_version_element.rb +1 -1
- data/lib/mail/elements/phrase_list.rb +7 -2
- data/lib/mail/elements/received_element.rb +20 -6
- data/lib/mail/encodings/7bit.rb +5 -0
- data/lib/mail/encodings/base64.rb +2 -2
- data/lib/mail/encodings/quoted_printable.rb +2 -2
- data/lib/mail/encodings/transfer_encoding.rb +1 -1
- data/lib/mail/encodings/unix_to_unix.rb +1 -0
- data/lib/mail/encodings.rb +30 -59
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +39 -53
- 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 +47 -72
- data/lib/mail/fields/date_field.rb +23 -51
- 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 +5 -6
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
- 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 -29
- 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 +16 -48
- data/lib/mail/header.rb +69 -110
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/message.rb +53 -68
- data/lib/mail/multibyte/chars.rb +8 -166
- data/lib/mail/multibyte/unicode.rb +10 -10
- data/lib/mail/multibyte/utils.rb +26 -43
- data/lib/mail/multibyte.rb +1 -11
- 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 +2 -5
- data/lib/mail/network/delivery_methods/sendmail.rb +56 -18
- data/lib/mail/network/delivery_methods/smtp.rb +25 -9
- data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -12
- 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 +3 -3
- data/lib/mail/network/retriever_methods/pop3.rb +2 -2
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/parsers/address_lists_parser.rb +33175 -33140
- data/lib/mail/parsers/address_lists_parser.rl +7 -0
- data/lib/mail/parsers/content_disposition_parser.rb +889 -889
- data/lib/mail/parsers/content_disposition_parser.rl +7 -0
- data/lib/mail/parsers/content_location_parser.rb +796 -787
- data/lib/mail/parsers/content_location_parser.rl +7 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +496 -496
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +7 -0
- data/lib/mail/parsers/content_type_parser.rb +1008 -1005
- data/lib/mail/parsers/content_type_parser.rl +7 -0
- data/lib/mail/parsers/date_time_parser.rb +864 -859
- data/lib/mail/parsers/date_time_parser.rl +7 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3649 -3548
- data/lib/mail/parsers/envelope_from_parser.rl +7 -0
- data/lib/mail/parsers/message_ids_parser.rb +5135 -2832
- data/lib/mail/parsers/message_ids_parser.rl +12 -1
- data/lib/mail/parsers/mime_version_parser.rb +487 -483
- data/lib/mail/parsers/mime_version_parser.rl +7 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +858 -865
- data/lib/mail/parsers/phrase_lists_parser.rl +8 -1
- data/lib/mail/parsers/received_parser.rb +8756 -8728
- data/lib/mail/parsers/received_parser.rl +7 -0
- data/lib/mail/parsers/rfc5322.rl +28 -13
- data/lib/mail/parsers.rb +11 -17
- data/lib/mail/part.rb +6 -10
- data/lib/mail/parts_list.rb +57 -0
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +325 -87
- data/lib/mail/version.rb +2 -2
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +3 -20
- metadata +86 -18
- data/lib/mail/core_extensions/smtp.rb +0 -28
- data/lib/mail/core_extensions/string.rb +0 -17
- data/lib/mail/fields/common/address_container.rb +0 -17
- data/lib/mail/fields/common/common_address.rb +0 -161
- data/lib/mail/fields/common/common_date.rb +0 -36
- data/lib/mail/fields/common/common_field.rb +0 -52
- data/lib/mail/fields/common/common_message_id.rb +0 -49
- data/lib/mail/version_specific/ruby_1_8.rb +0 -163
- data/lib/mail/version_specific/ruby_1_9.rb +0 -278
data/lib/mail/utilities.rb
CHANGED
@@ -1,18 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'mail/constants'
|
4
|
+
require 'socket'
|
4
5
|
|
5
6
|
module Mail
|
6
7
|
module Utilities
|
7
|
-
|
8
|
-
LF = "\n"
|
9
|
-
CRLF = "\r\n"
|
10
|
-
|
11
|
-
include Constants
|
8
|
+
extend self
|
12
9
|
|
13
10
|
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
14
11
|
def atom_safe?( str )
|
15
|
-
not ATOM_UNSAFE === str
|
12
|
+
not Constants::ATOM_UNSAFE === str
|
16
13
|
end
|
17
14
|
|
18
15
|
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
@@ -24,28 +21,38 @@ module Mail
|
|
24
21
|
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
25
22
|
# in double quotes, otherwise returns the string unmodified
|
26
23
|
def quote_phrase( str )
|
27
|
-
if
|
24
|
+
if str.respond_to?(:force_encoding)
|
28
25
|
original_encoding = str.encoding
|
29
|
-
ascii_str = str.dup.force_encoding('ASCII-8BIT')
|
30
|
-
if
|
26
|
+
ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
|
27
|
+
if Constants::PHRASE_UNSAFE === ascii_str
|
31
28
|
dquote(ascii_str).force_encoding(original_encoding)
|
32
29
|
else
|
33
30
|
str
|
34
31
|
end
|
35
32
|
else
|
36
|
-
|
33
|
+
Constants::PHRASE_UNSAFE === str ? dquote(str) : str
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
40
37
|
# Returns true if the string supplied is free from characters not allowed as a TOKEN
|
41
38
|
def token_safe?( str )
|
42
|
-
not TOKEN_UNSAFE === str
|
39
|
+
not Constants::TOKEN_UNSAFE === str
|
43
40
|
end
|
44
41
|
|
45
42
|
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
46
43
|
# in double quotes, otherwise returns the string unmodified
|
47
44
|
def quote_token( str )
|
48
|
-
|
45
|
+
if str.respond_to?(:force_encoding)
|
46
|
+
original_encoding = str.encoding
|
47
|
+
ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
|
48
|
+
if token_safe?( ascii_str )
|
49
|
+
str
|
50
|
+
else
|
51
|
+
dquote(ascii_str).force_encoding(original_encoding)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
token_safe?( str ) ? str : dquote(str)
|
55
|
+
end
|
49
56
|
end
|
50
57
|
|
51
58
|
# Wraps supplied string in double quotes and applies \-escaping as necessary,
|
@@ -79,7 +86,6 @@ module Mail
|
|
79
86
|
str
|
80
87
|
end
|
81
88
|
end
|
82
|
-
module_function :unquote
|
83
89
|
|
84
90
|
# Removes any \-escaping.
|
85
91
|
#
|
@@ -93,7 +99,6 @@ module Mail
|
|
93
99
|
def unescape( str )
|
94
100
|
str.gsub(/\\(.)/, '\1')
|
95
101
|
end
|
96
|
-
module_function :unescape
|
97
102
|
|
98
103
|
# Wraps a string in parenthesis and escapes any that are in the string itself.
|
99
104
|
#
|
@@ -101,7 +106,7 @@ module Mail
|
|
101
106
|
#
|
102
107
|
# paren( 'This is a string' ) #=> '(This is a string)'
|
103
108
|
def paren( str )
|
104
|
-
|
109
|
+
Utilities.paren( str )
|
105
110
|
end
|
106
111
|
|
107
112
|
# Unwraps a string from being wrapped in parenthesis
|
@@ -111,8 +116,11 @@ module Mail
|
|
111
116
|
# str = '(This is a string)'
|
112
117
|
# unparen( str ) #=> 'This is a string'
|
113
118
|
def unparen( str )
|
114
|
-
|
115
|
-
|
119
|
+
if str.start_with?('(') && str.end_with?(')')
|
120
|
+
str.slice(1..-2)
|
121
|
+
else
|
122
|
+
str
|
123
|
+
end
|
116
124
|
end
|
117
125
|
|
118
126
|
# Wraps a string in angle brackets and escapes any that are in the string itself
|
@@ -121,7 +129,7 @@ module Mail
|
|
121
129
|
#
|
122
130
|
# bracket( 'This is a string' ) #=> '<This is a string>'
|
123
131
|
def bracket( str )
|
124
|
-
|
132
|
+
Utilities.bracket( str )
|
125
133
|
end
|
126
134
|
|
127
135
|
# Unwraps a string from being wrapped in parenthesis
|
@@ -131,8 +139,11 @@ module Mail
|
|
131
139
|
# str = '<This is a string>'
|
132
140
|
# unbracket( str ) #=> 'This is a string'
|
133
141
|
def unbracket( str )
|
134
|
-
|
135
|
-
|
142
|
+
if str.start_with?('<') && str.end_with?('>')
|
143
|
+
str.slice(1..-2)
|
144
|
+
else
|
145
|
+
str
|
146
|
+
end
|
136
147
|
end
|
137
148
|
|
138
149
|
# Escape parenthesies in a string
|
@@ -142,7 +153,7 @@ module Mail
|
|
142
153
|
# str = 'This is (a) string'
|
143
154
|
# escape_paren( str ) #=> 'This is \(a\) string'
|
144
155
|
def escape_paren( str )
|
145
|
-
|
156
|
+
Utilities.escape_paren( str )
|
146
157
|
end
|
147
158
|
|
148
159
|
def uri_escape( str )
|
@@ -154,7 +165,7 @@ module Mail
|
|
154
165
|
end
|
155
166
|
|
156
167
|
def uri_parser
|
157
|
-
@uri_parser ||= URI.const_defined?(:
|
168
|
+
@uri_parser ||= URI.const_defined?(:DEFAULT_PARSER) ? URI::DEFAULT_PARSER : URI
|
158
169
|
end
|
159
170
|
|
160
171
|
# Matches two objects with their to_s values case insensitively
|
@@ -197,7 +208,7 @@ module Mail
|
|
197
208
|
# string = :resent_from_field
|
198
209
|
# dasherize( string ) #=> 'resent-from-field'
|
199
210
|
def dasherize( str )
|
200
|
-
str.to_s.tr(UNDERSCORE, HYPHEN)
|
211
|
+
str.to_s.tr(Constants::UNDERSCORE, Constants::HYPHEN)
|
201
212
|
end
|
202
213
|
|
203
214
|
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
|
@@ -208,78 +219,36 @@ module Mail
|
|
208
219
|
# string = :resent_from_field
|
209
220
|
# underscoreize ( string ) #=> 'resent_from_field'
|
210
221
|
def underscoreize( str )
|
211
|
-
str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
|
222
|
+
str.to_s.downcase.tr(Constants::HYPHEN, Constants::UNDERSCORE)
|
212
223
|
end
|
213
224
|
|
214
|
-
|
215
|
-
|
216
|
-
def map_lines( str, &block )
|
217
|
-
results = []
|
218
|
-
str.each_line do |line|
|
219
|
-
results << yield(line)
|
220
|
-
end
|
221
|
-
results
|
222
|
-
end
|
223
|
-
|
224
|
-
def map_with_index( enum, &block )
|
225
|
-
results = []
|
226
|
-
enum.each_with_index do |token, i|
|
227
|
-
results[i] = yield(token, i)
|
228
|
-
end
|
229
|
-
results
|
230
|
-
end
|
231
|
-
|
232
|
-
else
|
233
|
-
|
234
|
-
def map_lines( str, &block )
|
235
|
-
str.each_line.map(&block)
|
236
|
-
end
|
237
|
-
|
238
|
-
def map_with_index( enum, &block )
|
239
|
-
enum.each_with_index.map(&block)
|
240
|
-
end
|
241
|
-
|
225
|
+
def map_lines( str, &block )
|
226
|
+
str.each_line.map(&block)
|
242
227
|
end
|
243
228
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
if ("\r".encode(:universal_newline => true) rescue nil) == LF &&
|
248
|
-
(LF.encode(:crlf_newline => true) rescue nil) == CRLF
|
249
|
-
# Using String#encode is better performing than Regexp
|
250
|
-
|
251
|
-
def self.binary_unsafe_to_lf(string) #:nodoc:
|
252
|
-
string.encode(string.encoding, :universal_newline => true)
|
253
|
-
end
|
254
|
-
|
255
|
-
def self.binary_unsafe_to_crlf(string) #:nodoc:
|
256
|
-
string.encode(string.encoding, :universal_newline => true).encode!(string.encoding, :crlf_newline => true)
|
257
|
-
end
|
229
|
+
def map_with_index( enum, &block )
|
230
|
+
enum.each_with_index.map(&block)
|
231
|
+
end
|
258
232
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
else
|
263
|
-
def self.binary_unsafe_to_lf(string) #:nodoc:
|
264
|
-
string.gsub(/\r\n|\r/, LF)
|
265
|
-
end
|
233
|
+
def self.binary_unsafe_to_lf(string) #:nodoc:
|
234
|
+
string.gsub(/\r\n|\r/, Constants::LF)
|
235
|
+
end
|
266
236
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
Regexp.new("(?<!\r)\n|\r(?!\n)")
|
273
|
-
else
|
274
|
-
/\n|\r\n|\r/
|
275
|
-
end
|
237
|
+
TO_CRLF_REGEX =
|
238
|
+
# This 1.9 only regex can save a reasonable amount of time (~20%)
|
239
|
+
# by not matching "\r\n" so the string is returned unchanged in
|
240
|
+
# the common case.
|
241
|
+
Regexp.new("(?<!\r)\n|\r(?!\n)")
|
276
242
|
|
277
|
-
|
278
|
-
|
279
|
-
|
243
|
+
def self.binary_unsafe_to_crlf(string) #:nodoc:
|
244
|
+
string.gsub(TO_CRLF_REGEX, Constants::CRLF)
|
245
|
+
end
|
280
246
|
|
281
|
-
|
247
|
+
def self.safe_for_line_ending_conversion?(string) #:nodoc:
|
248
|
+
if string.encoding == Encoding::BINARY
|
282
249
|
string.ascii_only?
|
250
|
+
else
|
251
|
+
string.valid_encoding?
|
283
252
|
end
|
284
253
|
end
|
285
254
|
|
@@ -311,7 +280,7 @@ module Mail
|
|
311
280
|
# and arrays and hashes that have nothing in them.
|
312
281
|
#
|
313
282
|
# This logic is mostly shared with ActiveSupport's blank?
|
314
|
-
def
|
283
|
+
def blank?(value)
|
315
284
|
if value.kind_of?(NilClass)
|
316
285
|
true
|
317
286
|
elsif value.kind_of?(String)
|
@@ -320,5 +289,274 @@ module Mail
|
|
320
289
|
value.respond_to?(:empty?) ? value.empty? : !value
|
321
290
|
end
|
322
291
|
end
|
292
|
+
|
293
|
+
def generate_message_id
|
294
|
+
"<#{Mail.random_tag}@#{::Socket.gethostname}.mail>"
|
295
|
+
end
|
296
|
+
|
297
|
+
class StrictCharsetEncoder
|
298
|
+
def encode(string, charset)
|
299
|
+
case charset
|
300
|
+
when /utf-?7/i
|
301
|
+
Mail::Utilities.decode_utf7(string)
|
302
|
+
else
|
303
|
+
string.force_encoding(Mail::Utilities.pick_encoding(charset))
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
class BestEffortCharsetEncoder
|
309
|
+
def encode(string, charset)
|
310
|
+
case charset
|
311
|
+
when /utf-?7/i
|
312
|
+
Mail::Utilities.decode_utf7(string)
|
313
|
+
else
|
314
|
+
string.force_encoding(pick_encoding(charset))
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
def pick_encoding(charset)
|
321
|
+
charset = case charset
|
322
|
+
when /ansi_x3.110-1983/
|
323
|
+
'ISO-8859-1'
|
324
|
+
when /Windows-?1258/i # Windows-1258 is similar to 1252
|
325
|
+
"Windows-1252"
|
326
|
+
else
|
327
|
+
charset
|
328
|
+
end
|
329
|
+
Mail::Utilities.pick_encoding(charset)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
class << self
|
334
|
+
attr_accessor :charset_encoder
|
335
|
+
end
|
336
|
+
self.charset_encoder = BestEffortCharsetEncoder.new
|
337
|
+
|
338
|
+
# Escapes any parenthesis in a string that are unescaped this uses
|
339
|
+
# a Ruby 1.9.1 regexp feature of negative look behind
|
340
|
+
def Utilities.escape_paren( str )
|
341
|
+
re = /(?<!\\)([\(\)])/ # Only match unescaped parens
|
342
|
+
str.gsub(re) { |s| '\\' + s }
|
343
|
+
end
|
344
|
+
|
345
|
+
def Utilities.paren( str )
|
346
|
+
str = ::Mail::Utilities.unparen( str )
|
347
|
+
str = escape_paren( str )
|
348
|
+
'(' + str + ')'
|
349
|
+
end
|
350
|
+
|
351
|
+
def Utilities.escape_bracket( str )
|
352
|
+
re = /(?<!\\)([\<\>])/ # Only match unescaped brackets
|
353
|
+
str.gsub(re) { |s| '\\' + s }
|
354
|
+
end
|
355
|
+
|
356
|
+
def Utilities.bracket( str )
|
357
|
+
str = ::Mail::Utilities.unbracket( str )
|
358
|
+
str = escape_bracket( str )
|
359
|
+
'<' + str + '>'
|
360
|
+
end
|
361
|
+
|
362
|
+
def Utilities.decode_base64(str)
|
363
|
+
if !str.end_with?("=") && str.length % 4 != 0
|
364
|
+
str = str.ljust((str.length + 3) & ~3, "=")
|
365
|
+
end
|
366
|
+
str.unpack( 'm' ).first
|
367
|
+
end
|
368
|
+
|
369
|
+
def Utilities.encode_base64(str)
|
370
|
+
[str].pack( 'm' )
|
371
|
+
end
|
372
|
+
|
373
|
+
def Utilities.has_constant?(klass, string)
|
374
|
+
klass.const_defined?( string, false )
|
375
|
+
end
|
376
|
+
|
377
|
+
def Utilities.get_constant(klass, string)
|
378
|
+
klass.const_get( string )
|
379
|
+
end
|
380
|
+
|
381
|
+
def Utilities.transcode_charset(str, from_encoding, to_encoding = Encoding::UTF_8)
|
382
|
+
to_encoding = Encoding.find(to_encoding)
|
383
|
+
replacement_char = to_encoding == Encoding::UTF_8 ? '�' : '?'
|
384
|
+
charset_encoder.encode(str.dup, from_encoding).encode(to_encoding, :undef => :replace, :invalid => :replace, :replace => replacement_char)
|
385
|
+
end
|
386
|
+
|
387
|
+
# From Ruby stdlib Net::IMAP
|
388
|
+
def Utilities.encode_utf7(string)
|
389
|
+
string.gsub(/(&)|[^\x20-\x7e]+/) do
|
390
|
+
if $1
|
391
|
+
"&-"
|
392
|
+
else
|
393
|
+
base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
|
394
|
+
"&" + base64.delete("=").tr("/", ",") + "-"
|
395
|
+
end
|
396
|
+
end.force_encoding(Encoding::ASCII_8BIT)
|
397
|
+
end
|
398
|
+
|
399
|
+
def Utilities.decode_utf7(utf7)
|
400
|
+
utf7.gsub(/&([^-]+)?-/n) do
|
401
|
+
if $1
|
402
|
+
($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE)
|
403
|
+
else
|
404
|
+
"&"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def Utilities.b_value_encode(str, encoding = nil)
|
410
|
+
encoding = str.encoding.to_s
|
411
|
+
[Utilities.encode_base64(str), encoding]
|
412
|
+
end
|
413
|
+
|
414
|
+
def Utilities.b_value_decode(str)
|
415
|
+
match = str.match(/\=\?(.+)?\?[Bb]\?(.*)\?\=/m)
|
416
|
+
if match
|
417
|
+
charset = match[1]
|
418
|
+
str = Utilities.decode_base64(match[2])
|
419
|
+
str = charset_encoder.encode(str, charset)
|
420
|
+
end
|
421
|
+
transcode_to_scrubbed_utf8(str)
|
422
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError, Encoding::InvalidByteSequenceError
|
423
|
+
warn "Encoding conversion failed #{$!}"
|
424
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
425
|
+
end
|
426
|
+
|
427
|
+
def Utilities.q_value_encode(str, encoding = nil)
|
428
|
+
encoding = str.encoding.to_s
|
429
|
+
[Encodings::QuotedPrintable.encode(str), encoding]
|
430
|
+
end
|
431
|
+
|
432
|
+
def Utilities.q_value_decode(str)
|
433
|
+
match = str.match(/\=\?(.+)?\?[Qq]\?(.*)\?\=/m)
|
434
|
+
if match
|
435
|
+
charset = match[1]
|
436
|
+
string = match[2].gsub(/_/, '=20')
|
437
|
+
# Remove trailing = if it exists in a Q encoding
|
438
|
+
string = string.sub(/\=$/, '')
|
439
|
+
str = Encodings::QuotedPrintable.decode(string)
|
440
|
+
str = charset_encoder.encode(str, charset)
|
441
|
+
# We assume that binary strings hold utf-8 directly to work around
|
442
|
+
# jruby/jruby#829 which subtly changes String#encode semantics.
|
443
|
+
str.force_encoding(Encoding::UTF_8) if str.encoding == Encoding::ASCII_8BIT
|
444
|
+
end
|
445
|
+
transcode_to_scrubbed_utf8(str)
|
446
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
447
|
+
warn "Encoding conversion failed #{$!}"
|
448
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
449
|
+
end
|
450
|
+
|
451
|
+
def Utilities.param_decode(str, encoding)
|
452
|
+
str = uri_parser.unescape(str)
|
453
|
+
str = charset_encoder.encode(str, encoding) if encoding
|
454
|
+
transcode_to_scrubbed_utf8(str)
|
455
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
456
|
+
warn "Encoding conversion failed #{$!}"
|
457
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
458
|
+
end
|
459
|
+
|
460
|
+
def Utilities.param_encode(str)
|
461
|
+
encoding = str.encoding.to_s.downcase
|
462
|
+
language = Configuration.instance.param_encode_language
|
463
|
+
"#{encoding}'#{language}'#{uri_parser.escape(str)}"
|
464
|
+
end
|
465
|
+
|
466
|
+
def Utilities.uri_parser
|
467
|
+
URI::DEFAULT_PARSER
|
468
|
+
end
|
469
|
+
|
470
|
+
# Pick a Ruby encoding corresponding to the message charset. Most
|
471
|
+
# charsets have a Ruby encoding, but some need manual aliasing here.
|
472
|
+
#
|
473
|
+
# TODO: add this as a test somewhere:
|
474
|
+
# Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
|
475
|
+
# Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
|
476
|
+
def Utilities.pick_encoding(charset)
|
477
|
+
charset = charset.to_s
|
478
|
+
encoding = case charset.downcase
|
479
|
+
|
480
|
+
# ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
|
481
|
+
when /^iso[-_]?8859-(\d+)(-i)?$/
|
482
|
+
"ISO-8859-#{$1}"
|
483
|
+
|
484
|
+
# ISO-8859-15, ISO-2022-JP and alike
|
485
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})$/
|
486
|
+
"ISO-#{$1}-#{$2}"
|
487
|
+
|
488
|
+
# "ISO-2022-JP-KDDI" and alike
|
489
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})-?(\w*)$/
|
490
|
+
"ISO-#{$1}-#{$2}-#{$3}"
|
491
|
+
|
492
|
+
# UTF-8, UTF-32BE and alike
|
493
|
+
when /^utf[\-_]?(\d{1,2})?(\w{1,2})$/
|
494
|
+
"UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
|
495
|
+
|
496
|
+
# Windows-1252 and alike
|
497
|
+
when /^windows-?(.*)$/
|
498
|
+
"Windows-#{$1}"
|
499
|
+
|
500
|
+
when '8bit'
|
501
|
+
Encoding::ASCII_8BIT
|
502
|
+
|
503
|
+
# alternatives/misspellings of us-ascii seen in the wild
|
504
|
+
when /^iso[-_]?646(-us)?$/, 'us=ascii'
|
505
|
+
Encoding::ASCII
|
506
|
+
|
507
|
+
# Microsoft-specific alias for MACROMAN
|
508
|
+
when 'macintosh'
|
509
|
+
Encoding::MACROMAN
|
510
|
+
|
511
|
+
# Microsoft-specific alias for CP949 (Korean)
|
512
|
+
when 'ks_c_5601-1987'
|
513
|
+
Encoding::CP949
|
514
|
+
|
515
|
+
# Wrongly written Shift_JIS (Japanese)
|
516
|
+
when 'shift-jis'
|
517
|
+
Encoding::Shift_JIS
|
518
|
+
|
519
|
+
# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
|
520
|
+
when 'gb2312'
|
521
|
+
Encoding::GB18030
|
522
|
+
|
523
|
+
when 'cp-850'
|
524
|
+
Encoding::CP850
|
525
|
+
|
526
|
+
when 'latin2'
|
527
|
+
Encoding::ISO_8859_2
|
528
|
+
|
529
|
+
else
|
530
|
+
charset
|
531
|
+
end
|
532
|
+
|
533
|
+
convert_to_encoding(encoding)
|
534
|
+
end
|
535
|
+
|
536
|
+
def Utilities.string_byteslice(str, *args)
|
537
|
+
str.byteslice(*args)
|
538
|
+
end
|
539
|
+
|
540
|
+
class << self
|
541
|
+
private
|
542
|
+
|
543
|
+
def convert_to_encoding(encoding)
|
544
|
+
if encoding.is_a?(Encoding)
|
545
|
+
encoding
|
546
|
+
else
|
547
|
+
# Fall back to ASCII for charsets that Ruby doesn't recognize
|
548
|
+
begin
|
549
|
+
Encoding.find(encoding)
|
550
|
+
rescue ArgumentError
|
551
|
+
Encoding::BINARY
|
552
|
+
end
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
def transcode_to_scrubbed_utf8(str)
|
557
|
+
decoded = str.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => "�")
|
558
|
+
decoded.valid_encoding? ? decoded : decoded.encode(Encoding::UTF_16LE, :invalid => :replace, :replace => "�").encode(Encoding::UTF_8)
|
559
|
+
end
|
560
|
+
end
|
323
561
|
end
|
324
562
|
end
|
data/lib/mail/version.rb
CHANGED
data/lib/mail/yaml.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Mail
|
4
|
+
module YAML
|
5
|
+
def self.load(yaml)
|
6
|
+
permitted_classes = [
|
7
|
+
Symbol,
|
8
|
+
|
9
|
+
Mail::Body,
|
10
|
+
|
11
|
+
# Delivery methods as listed in mail/configuration.rb
|
12
|
+
Mail::SMTP,
|
13
|
+
Mail::Sendmail,
|
14
|
+
Mail::Exim,
|
15
|
+
Mail::FileDelivery,
|
16
|
+
Mail::SMTPConnection,
|
17
|
+
Mail::TestMailer,
|
18
|
+
Mail::LoggerDelivery,
|
19
|
+
|
20
|
+
Mail.delivery_method.class,
|
21
|
+
]
|
22
|
+
|
23
|
+
if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
|
24
|
+
::YAML.safe_load(yaml, :permitted_classes => permitted_classes)
|
25
|
+
else
|
26
|
+
::YAML.safe_load(yaml, permitted_classes)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/mail.rb
CHANGED
@@ -9,26 +9,8 @@ module Mail # :doc:
|
|
9
9
|
require 'net/smtp'
|
10
10
|
require 'mini_mime'
|
11
11
|
|
12
|
-
if RUBY_VERSION <= '1.8.6'
|
13
|
-
begin
|
14
|
-
require 'tlsmail'
|
15
|
-
rescue LoadError
|
16
|
-
raise "You need to install tlsmail if you are using ruby <= 1.8.6"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
if RUBY_VERSION >= "1.9.0"
|
21
|
-
require 'mail/version_specific/ruby_1_9'
|
22
|
-
RubyVer = Ruby19
|
23
|
-
else
|
24
|
-
require 'mail/version_specific/ruby_1_8'
|
25
|
-
RubyVer = Ruby18
|
26
|
-
end
|
27
|
-
|
28
12
|
require 'mail/version'
|
29
13
|
|
30
|
-
require 'mail/core_extensions/string'
|
31
|
-
require 'mail/core_extensions/smtp'
|
32
14
|
require 'mail/indifferent_hash'
|
33
15
|
|
34
16
|
require 'mail/multibyte'
|
@@ -68,8 +50,6 @@ module Mail # :doc:
|
|
68
50
|
|
69
51
|
require 'mail/envelope'
|
70
52
|
|
71
|
-
register_autoload :Parsers, "mail/parsers"
|
72
|
-
|
73
53
|
# Autoload header field elements and transfer encodings.
|
74
54
|
require 'mail/elements'
|
75
55
|
require 'mail/encodings'
|
@@ -80,6 +60,9 @@ module Mail # :doc:
|
|
80
60
|
require 'mail/matchers/has_sent_mail'
|
81
61
|
require 'mail/matchers/attachment_matchers.rb'
|
82
62
|
|
63
|
+
# Deprecated will be removed in 3.0 release
|
64
|
+
require 'mail/check_delivery_params'
|
65
|
+
|
83
66
|
# Finally... require all the Mail.methods
|
84
67
|
require 'mail/mail'
|
85
68
|
end
|