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/utilities.rb
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
require 'mail/constants'
|
|
4
|
+
|
|
2
5
|
module Mail
|
|
3
6
|
module Utilities
|
|
7
|
+
|
|
8
|
+
LF = "\n"
|
|
9
|
+
CRLF = "\r\n"
|
|
10
|
+
|
|
4
11
|
include Constants
|
|
5
12
|
|
|
6
13
|
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
|
@@ -17,15 +24,13 @@ module Mail
|
|
|
17
24
|
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
|
18
25
|
# in double quotes, otherwise returns the string unmodified
|
|
19
26
|
def quote_phrase( str )
|
|
20
|
-
if
|
|
27
|
+
if str.respond_to?(:force_encoding)
|
|
21
28
|
original_encoding = str.encoding
|
|
22
|
-
str.force_encoding('ASCII-8BIT')
|
|
23
|
-
if (PHRASE_UNSAFE ===
|
|
24
|
-
|
|
25
|
-
str.force_encoding(original_encoding)
|
|
26
|
-
quoted_str
|
|
29
|
+
ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
|
|
30
|
+
if (PHRASE_UNSAFE === ascii_str)
|
|
31
|
+
dquote(ascii_str).force_encoding(original_encoding)
|
|
27
32
|
else
|
|
28
|
-
str
|
|
33
|
+
str
|
|
29
34
|
end
|
|
30
35
|
else
|
|
31
36
|
(PHRASE_UNSAFE === str) ? dquote(str) : str
|
|
@@ -40,7 +45,17 @@ module Mail
|
|
|
40
45
|
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
|
41
46
|
# in double quotes, otherwise returns the string unmodified
|
|
42
47
|
def quote_token( str )
|
|
43
|
-
|
|
48
|
+
if str.respond_to?(:force_encoding)
|
|
49
|
+
original_encoding = str.encoding
|
|
50
|
+
ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
|
|
51
|
+
if token_safe?( ascii_str )
|
|
52
|
+
str
|
|
53
|
+
else
|
|
54
|
+
dquote(ascii_str).force_encoding(original_encoding)
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
token_safe?( str ) ? str : dquote(str)
|
|
58
|
+
end
|
|
44
59
|
end
|
|
45
60
|
|
|
46
61
|
# Wraps supplied string in double quotes and applies \-escaping as necessary,
|
|
@@ -69,11 +84,26 @@ module Mail
|
|
|
69
84
|
# unqoute(string) #=> 'This is "a string"'
|
|
70
85
|
def unquote( str )
|
|
71
86
|
if str =~ /^"(.*?)"$/
|
|
72
|
-
$1
|
|
87
|
+
unescape($1)
|
|
73
88
|
else
|
|
74
89
|
str
|
|
75
90
|
end
|
|
76
91
|
end
|
|
92
|
+
module_function :unquote
|
|
93
|
+
|
|
94
|
+
# Removes any \-escaping.
|
|
95
|
+
#
|
|
96
|
+
# Example:
|
|
97
|
+
#
|
|
98
|
+
# string = 'This is \"a string\"'
|
|
99
|
+
# unescape(string) #=> 'This is "a string"'
|
|
100
|
+
#
|
|
101
|
+
# string = '"This is \"a string\""'
|
|
102
|
+
# unescape(string) #=> '"This is "a string""'
|
|
103
|
+
def unescape( str )
|
|
104
|
+
str.gsub(/\\(.)/, '\1')
|
|
105
|
+
end
|
|
106
|
+
module_function :unescape
|
|
77
107
|
|
|
78
108
|
# Wraps a string in parenthesis and escapes any that are in the string itself.
|
|
79
109
|
#
|
|
@@ -175,7 +205,7 @@ module Mail
|
|
|
175
205
|
# Example:
|
|
176
206
|
#
|
|
177
207
|
# string = :resent_from_field
|
|
178
|
-
# dasherize
|
|
208
|
+
# dasherize( string ) #=> 'resent-from-field'
|
|
179
209
|
def dasherize( str )
|
|
180
210
|
str.to_s.tr(UNDERSCORE, HYPHEN)
|
|
181
211
|
end
|
|
@@ -221,5 +251,74 @@ module Mail
|
|
|
221
251
|
|
|
222
252
|
end
|
|
223
253
|
|
|
254
|
+
def self.binary_unsafe_to_lf(string) #:nodoc:
|
|
255
|
+
string.gsub(/\r\n|\r/, LF)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
TO_CRLF_REGEX =
|
|
259
|
+
if RUBY_VERSION >= '1.9'
|
|
260
|
+
# This 1.9 only regex can save a reasonable amount of time (~20%)
|
|
261
|
+
# by not matching "\r\n" so the string is returned unchanged in
|
|
262
|
+
# the common case.
|
|
263
|
+
Regexp.new("(?<!\r)\n|\r(?!\n)")
|
|
264
|
+
else
|
|
265
|
+
/\n|\r\n|\r/
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def self.binary_unsafe_to_crlf(string) #:nodoc:
|
|
269
|
+
string.gsub(TO_CRLF_REGEX, CRLF)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
if RUBY_VERSION < '1.9'
|
|
273
|
+
def self.safe_for_line_ending_conversion?(string) #:nodoc:
|
|
274
|
+
string.ascii_only?
|
|
275
|
+
end
|
|
276
|
+
else
|
|
277
|
+
def self.safe_for_line_ending_conversion?(string) #:nodoc:
|
|
278
|
+
if string.encoding == Encoding::BINARY
|
|
279
|
+
string.ascii_only?
|
|
280
|
+
else
|
|
281
|
+
string.valid_encoding?
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Convert line endings to \n unless the string is binary. Used for
|
|
287
|
+
# sendmail delivery and for decoding 8bit Content-Transfer-Encoding.
|
|
288
|
+
def self.to_lf(string)
|
|
289
|
+
string = string.to_s
|
|
290
|
+
if safe_for_line_ending_conversion? string
|
|
291
|
+
binary_unsafe_to_lf string
|
|
292
|
+
else
|
|
293
|
+
string
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Convert line endings to \r\n unless the string is binary. Used for
|
|
298
|
+
# encoding 8bit and base64 Content-Transfer-Encoding and for convenience
|
|
299
|
+
# when parsing emails with \n line endings instead of the required \r\n.
|
|
300
|
+
def self.to_crlf(string)
|
|
301
|
+
string = string.to_s
|
|
302
|
+
if safe_for_line_ending_conversion? string
|
|
303
|
+
binary_unsafe_to_crlf string
|
|
304
|
+
else
|
|
305
|
+
string
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Returns true if the object is considered blank.
|
|
310
|
+
# A blank includes things like '', ' ', nil,
|
|
311
|
+
# and arrays and hashes that have nothing in them.
|
|
312
|
+
#
|
|
313
|
+
# This logic is mostly shared with ActiveSupport's blank?
|
|
314
|
+
def self.blank?(value)
|
|
315
|
+
if value.kind_of?(NilClass)
|
|
316
|
+
true
|
|
317
|
+
elsif value.kind_of?(String)
|
|
318
|
+
value !~ /\S/
|
|
319
|
+
else
|
|
320
|
+
value.respond_to?(:empty?) ? value.empty? : !value
|
|
321
|
+
end
|
|
322
|
+
end
|
|
224
323
|
end
|
|
225
324
|
end
|
|
Binary file
|
data/lib/mail/version.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
require 'net/imap' # for decode_utf7
|
|
2
4
|
|
|
3
5
|
module Mail
|
|
4
6
|
class Ruby18
|
|
@@ -54,11 +56,35 @@ module Mail
|
|
|
54
56
|
klass.const_get( string )
|
|
55
57
|
end
|
|
56
58
|
|
|
59
|
+
def Ruby18.transcode_charset(str, from_encoding, to_encoding = 'UTF-8')
|
|
60
|
+
case from_encoding
|
|
61
|
+
when /utf-?7/i
|
|
62
|
+
decode_utf7(str)
|
|
63
|
+
else
|
|
64
|
+
retried = false
|
|
65
|
+
begin
|
|
66
|
+
Iconv.conv("#{normalize_iconv_charset_encoding(to_encoding)}//IGNORE", normalize_iconv_charset_encoding(from_encoding), str)
|
|
67
|
+
rescue Iconv::InvalidEncoding
|
|
68
|
+
if retried
|
|
69
|
+
raise
|
|
70
|
+
else
|
|
71
|
+
from_encoding = 'ASCII'
|
|
72
|
+
retried = true
|
|
73
|
+
retry
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def Ruby18.decode_utf7(str)
|
|
80
|
+
Net::IMAP.decode_utf7(str)
|
|
81
|
+
end
|
|
82
|
+
|
|
57
83
|
def Ruby18.b_value_encode(str, encoding)
|
|
58
84
|
# Ruby 1.8 requires an encoding to work
|
|
59
85
|
raise ArgumentError, "Must supply an encoding" if encoding.nil?
|
|
60
86
|
encoding = encoding.to_s.upcase.gsub('_', '-')
|
|
61
|
-
[Encodings::Base64.encode(str),
|
|
87
|
+
[Encodings::Base64.encode(str), normalize_iconv_charset_encoding(encoding)]
|
|
62
88
|
end
|
|
63
89
|
|
|
64
90
|
def Ruby18.b_value_decode(str)
|
|
@@ -66,7 +92,7 @@ module Mail
|
|
|
66
92
|
if match
|
|
67
93
|
encoding = match[1]
|
|
68
94
|
str = Ruby18.decode_base64(match[2])
|
|
69
|
-
str =
|
|
95
|
+
str = transcode_charset(str, encoding)
|
|
70
96
|
end
|
|
71
97
|
str
|
|
72
98
|
end
|
|
@@ -86,13 +112,18 @@ module Mail
|
|
|
86
112
|
# Remove trailing = if it exists in a Q encoding
|
|
87
113
|
string = string.sub(/\=$/, '')
|
|
88
114
|
str = Encodings::QuotedPrintable.decode(string)
|
|
89
|
-
str =
|
|
115
|
+
str = transcode_charset(str, encoding)
|
|
90
116
|
end
|
|
91
117
|
str
|
|
92
118
|
end
|
|
93
119
|
|
|
94
120
|
def Ruby18.param_decode(str, encoding)
|
|
95
|
-
URI.unescape(str)
|
|
121
|
+
str = URI.unescape(str)
|
|
122
|
+
if encoding
|
|
123
|
+
transcode_charset(str, encoding)
|
|
124
|
+
else
|
|
125
|
+
str
|
|
126
|
+
end
|
|
96
127
|
end
|
|
97
128
|
|
|
98
129
|
def Ruby18.param_encode(str)
|
|
@@ -101,9 +132,13 @@ module Mail
|
|
|
101
132
|
"#{encoding}'#{language}'#{URI.escape(str)}"
|
|
102
133
|
end
|
|
103
134
|
|
|
135
|
+
def Ruby18.string_byteslice(str, *args)
|
|
136
|
+
str.slice(*args)
|
|
137
|
+
end
|
|
138
|
+
|
|
104
139
|
private
|
|
105
140
|
|
|
106
|
-
def Ruby18.
|
|
141
|
+
def Ruby18.normalize_iconv_charset_encoding(encoding)
|
|
107
142
|
case encoding.upcase
|
|
108
143
|
when 'UTF8', 'UTF_8'
|
|
109
144
|
'UTF-8'
|
|
@@ -111,8 +146,17 @@ module Mail
|
|
|
111
146
|
'UTF-16BE'
|
|
112
147
|
when 'UTF32', 'UTF-32'
|
|
113
148
|
'UTF-32BE'
|
|
149
|
+
when 'KS_C_5601-1987'
|
|
150
|
+
'CP949'
|
|
114
151
|
else
|
|
115
|
-
|
|
152
|
+
# Fall back to ASCII for charsets that Iconv doesn't recognize
|
|
153
|
+
begin
|
|
154
|
+
Iconv.new('UTF-8', encoding)
|
|
155
|
+
rescue Iconv::InvalidEncoding => e
|
|
156
|
+
'ASCII'
|
|
157
|
+
else
|
|
158
|
+
encoding
|
|
159
|
+
end
|
|
116
160
|
end
|
|
117
161
|
end
|
|
118
162
|
end
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module Mail
|
|
4
5
|
class Ruby19
|
|
5
6
|
class StrictCharsetEncoder
|
|
6
7
|
def encode(string, charset)
|
|
7
|
-
|
|
8
|
+
case charset
|
|
9
|
+
when /utf-?7/i
|
|
10
|
+
Mail::Ruby19.decode_utf7(string)
|
|
11
|
+
else
|
|
12
|
+
string.force_encoding(Mail::Ruby19.pick_encoding(charset))
|
|
13
|
+
end
|
|
8
14
|
end
|
|
9
15
|
end
|
|
10
16
|
|
|
11
17
|
class BestEffortCharsetEncoder
|
|
12
18
|
def encode(string, charset)
|
|
13
|
-
|
|
19
|
+
case charset
|
|
20
|
+
when /utf-?7/i
|
|
21
|
+
Mail::Ruby19.decode_utf7(string)
|
|
22
|
+
else
|
|
23
|
+
string.force_encoding(pick_encoding(charset))
|
|
24
|
+
end
|
|
14
25
|
end
|
|
15
26
|
|
|
16
27
|
private
|
|
@@ -31,7 +42,7 @@ module Mail
|
|
|
31
42
|
class << self
|
|
32
43
|
attr_accessor :charset_encoder
|
|
33
44
|
end
|
|
34
|
-
self.charset_encoder =
|
|
45
|
+
self.charset_encoder = BestEffortCharsetEncoder.new
|
|
35
46
|
|
|
36
47
|
# Escapes any parenthesis in a string that are unescaped this uses
|
|
37
48
|
# a Ruby 1.9.1 regexp feature of negative look behind
|
|
@@ -58,6 +69,9 @@ module Mail
|
|
|
58
69
|
end
|
|
59
70
|
|
|
60
71
|
def Ruby19.decode_base64(str)
|
|
72
|
+
if !str.end_with?("=") && str.length % 4 != 0
|
|
73
|
+
str = str.ljust((str.length + 3) & ~3, "=")
|
|
74
|
+
end
|
|
61
75
|
str.unpack( 'm' ).first
|
|
62
76
|
end
|
|
63
77
|
|
|
@@ -73,6 +87,35 @@ module Mail
|
|
|
73
87
|
klass.const_get( string )
|
|
74
88
|
end
|
|
75
89
|
|
|
90
|
+
def Ruby19.transcode_charset(str, from_encoding, to_encoding = Encoding::UTF_8)
|
|
91
|
+
to_encoding = to_encoding.to_s if RUBY_VERSION < '1.9.3'
|
|
92
|
+
to_encoding = Encoding.find(to_encoding)
|
|
93
|
+
replacement_char = to_encoding == Encoding::UTF_8 ? '�' : '?'
|
|
94
|
+
charset_encoder.encode(str.dup, from_encoding).encode(to_encoding, :undef => :replace, :invalid => :replace, :replace => replacement_char)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# From Ruby stdlib Net::IMAP
|
|
98
|
+
def Ruby19.encode_utf7(string)
|
|
99
|
+
string.gsub(/(&)|[^\x20-\x7e]+/) do
|
|
100
|
+
if $1
|
|
101
|
+
"&-"
|
|
102
|
+
else
|
|
103
|
+
base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
|
|
104
|
+
"&" + base64.delete("=").tr("/", ",") + "-"
|
|
105
|
+
end
|
|
106
|
+
end.force_encoding(Encoding::ASCII_8BIT)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def Ruby19.decode_utf7(utf7)
|
|
110
|
+
utf7.gsub(/&([^-]+)?-/n) do
|
|
111
|
+
if $1
|
|
112
|
+
($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE)
|
|
113
|
+
else
|
|
114
|
+
"&"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
76
119
|
def Ruby19.b_value_encode(str, encoding = nil)
|
|
77
120
|
encoding = str.encoding.to_s
|
|
78
121
|
[Ruby19.encode_base64(str), encoding]
|
|
@@ -85,8 +128,7 @@ module Mail
|
|
|
85
128
|
str = Ruby19.decode_base64(match[2])
|
|
86
129
|
str = charset_encoder.encode(str, charset)
|
|
87
130
|
end
|
|
88
|
-
|
|
89
|
-
decoded.valid_encoding? ? decoded : decoded.encode(Encoding::UTF_16LE, :invalid => :replace, :replace => "").encode(Encoding::UTF_8)
|
|
131
|
+
transcode_to_scrubbed_utf8(str)
|
|
90
132
|
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
|
91
133
|
warn "Encoding conversion failed #{$!}"
|
|
92
134
|
str.dup.force_encoding(Encoding::UTF_8)
|
|
@@ -110,8 +152,7 @@ module Mail
|
|
|
110
152
|
# jruby/jruby#829 which subtly changes String#encode semantics.
|
|
111
153
|
str.force_encoding(Encoding::UTF_8) if str.encoding == Encoding::ASCII_8BIT
|
|
112
154
|
end
|
|
113
|
-
|
|
114
|
-
decoded.valid_encoding? ? decoded : decoded.encode(Encoding::UTF_16LE, :invalid => :replace, :replace => "").encode(Encoding::UTF_8)
|
|
155
|
+
transcode_to_scrubbed_utf8(str)
|
|
115
156
|
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
|
116
157
|
warn "Encoding conversion failed #{$!}"
|
|
117
158
|
str.dup.force_encoding(Encoding::UTF_8)
|
|
@@ -120,7 +161,10 @@ module Mail
|
|
|
120
161
|
def Ruby19.param_decode(str, encoding)
|
|
121
162
|
str = uri_parser.unescape(str)
|
|
122
163
|
str = charset_encoder.encode(str, encoding) if encoding
|
|
123
|
-
str
|
|
164
|
+
transcode_to_scrubbed_utf8(str)
|
|
165
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
|
166
|
+
warn "Encoding conversion failed #{$!}"
|
|
167
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
|
124
168
|
end
|
|
125
169
|
|
|
126
170
|
def Ruby19.param_encode(str)
|
|
@@ -140,37 +184,38 @@ module Mail
|
|
|
140
184
|
# Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
|
|
141
185
|
# Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
|
|
142
186
|
def Ruby19.pick_encoding(charset)
|
|
143
|
-
|
|
187
|
+
charset = charset.to_s
|
|
188
|
+
encoding = case charset.downcase
|
|
144
189
|
|
|
145
190
|
# ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
|
|
146
|
-
when /^iso
|
|
191
|
+
when /^iso[-_]?8859-(\d+)(-i)?$/
|
|
147
192
|
"ISO-8859-#{$1}"
|
|
148
193
|
|
|
149
194
|
# ISO-8859-15, ISO-2022-JP and alike
|
|
150
|
-
when
|
|
195
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})$/
|
|
151
196
|
"ISO-#{$1}-#{$2}"
|
|
152
197
|
|
|
153
198
|
# "ISO-2022-JP-KDDI" and alike
|
|
154
|
-
when
|
|
199
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})-?(\w*)$/
|
|
155
200
|
"ISO-#{$1}-#{$2}-#{$3}"
|
|
156
201
|
|
|
157
202
|
# UTF-8, UTF-32BE and alike
|
|
158
|
-
when
|
|
203
|
+
when /^utf[\-_]?(\d{1,2})?(\w{1,2})$/
|
|
159
204
|
"UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
|
|
160
205
|
|
|
161
206
|
# Windows-1252 and alike
|
|
162
|
-
when
|
|
207
|
+
when /^windows-?(.*)$/
|
|
163
208
|
"Windows-#{$1}"
|
|
164
209
|
|
|
165
|
-
when
|
|
210
|
+
when '8bit'
|
|
166
211
|
Encoding::ASCII_8BIT
|
|
167
212
|
|
|
168
213
|
# alternatives/misspellings of us-ascii seen in the wild
|
|
169
|
-
when /^iso
|
|
214
|
+
when /^iso[-_]?646(-us)?$/, 'us=ascii'
|
|
170
215
|
Encoding::ASCII
|
|
171
216
|
|
|
172
217
|
# Microsoft-specific alias for MACROMAN
|
|
173
|
-
when
|
|
218
|
+
when 'macintosh'
|
|
174
219
|
Encoding::MACROMAN
|
|
175
220
|
|
|
176
221
|
# Microsoft-specific alias for CP949 (Korean)
|
|
@@ -182,12 +227,52 @@ module Mail
|
|
|
182
227
|
Encoding::Shift_JIS
|
|
183
228
|
|
|
184
229
|
# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
|
|
185
|
-
when
|
|
230
|
+
when 'gb2312'
|
|
186
231
|
Encoding::GB18030
|
|
187
232
|
|
|
233
|
+
when 'cp-850'
|
|
234
|
+
Encoding::CP850
|
|
235
|
+
|
|
236
|
+
when 'latin2'
|
|
237
|
+
Encoding::ISO_8859_2
|
|
238
|
+
|
|
188
239
|
else
|
|
189
240
|
charset
|
|
190
241
|
end
|
|
242
|
+
|
|
243
|
+
convert_to_encoding(encoding)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
if "string".respond_to?(:byteslice)
|
|
247
|
+
def Ruby19.string_byteslice(str, *args)
|
|
248
|
+
str.byteslice(*args)
|
|
249
|
+
end
|
|
250
|
+
else
|
|
251
|
+
def Ruby19.string_byteslice(str, *args)
|
|
252
|
+
str.unpack('C*').slice(*args).pack('C*').force_encoding(str.encoding)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
class << self
|
|
257
|
+
private
|
|
258
|
+
|
|
259
|
+
def convert_to_encoding(encoding)
|
|
260
|
+
if encoding.is_a?(Encoding)
|
|
261
|
+
encoding
|
|
262
|
+
else
|
|
263
|
+
# Fall back to ASCII for charsets that Ruby doesn't recognize
|
|
264
|
+
begin
|
|
265
|
+
Encoding.find(encoding)
|
|
266
|
+
rescue ArgumentError
|
|
267
|
+
Encoding::BINARY
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def transcode_to_scrubbed_utf8(str)
|
|
273
|
+
decoded = str.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => "�")
|
|
274
|
+
decoded.valid_encoding? ? decoded : decoded.encode(Encoding::UTF_16LE, :invalid => :replace, :replace => "�").encode(Encoding::UTF_8)
|
|
275
|
+
end
|
|
191
276
|
end
|
|
192
277
|
end
|
|
193
278
|
end
|
data/lib/mail.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
module Mail # :doc:
|
|
3
4
|
|
|
4
5
|
require 'date'
|
|
@@ -6,7 +7,7 @@ module Mail # :doc:
|
|
|
6
7
|
|
|
7
8
|
require 'uri'
|
|
8
9
|
require 'net/smtp'
|
|
9
|
-
require '
|
|
10
|
+
require 'mini_mime'
|
|
10
11
|
|
|
11
12
|
if RUBY_VERSION <= '1.8.6'
|
|
12
13
|
begin
|
|
@@ -26,20 +27,11 @@ module Mail # :doc:
|
|
|
26
27
|
|
|
27
28
|
require 'mail/version'
|
|
28
29
|
|
|
29
|
-
require 'mail/core_extensions/nil'
|
|
30
|
-
require 'mail/core_extensions/object'
|
|
31
30
|
require 'mail/core_extensions/string'
|
|
32
|
-
require 'mail/core_extensions/smtp'
|
|
31
|
+
require 'mail/core_extensions/smtp'
|
|
33
32
|
require 'mail/indifferent_hash'
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
if defined?(ActiveSupport)
|
|
37
|
-
require 'active_support/inflector'
|
|
38
|
-
else
|
|
39
|
-
require 'mail/core_extensions/string/access'
|
|
40
|
-
require 'mail/core_extensions/string/multibyte'
|
|
41
|
-
require 'mail/multibyte'
|
|
42
|
-
end
|
|
34
|
+
require 'mail/multibyte'
|
|
43
35
|
|
|
44
36
|
require 'mail/constants'
|
|
45
37
|
require 'mail/utilities'
|
|
@@ -86,6 +78,7 @@ module Mail # :doc:
|
|
|
86
78
|
require 'mail/encodings/unix_to_unix'
|
|
87
79
|
|
|
88
80
|
require 'mail/matchers/has_sent_mail'
|
|
81
|
+
require 'mail/matchers/attachment_matchers.rb'
|
|
89
82
|
|
|
90
83
|
# Finally... require all the Mail.methods
|
|
91
84
|
require 'mail/mail'
|