mail 2.6.1 → 2.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/MIT-LICENSE +1 -1
- data/README.md +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/utilities.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/constants'
|
4
|
+
|
2
5
|
module Mail
|
3
6
|
module Utilities
|
4
|
-
|
7
|
+
|
8
|
+
LF = "\n"
|
9
|
+
CRLF = "\r\n"
|
10
|
+
|
11
|
+
include Constants
|
5
12
|
|
6
13
|
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
7
14
|
def atom_safe?( str )
|
@@ -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,9 +205,9 @@ 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
|
-
str.to_s.
|
210
|
+
str.to_s.tr(UNDERSCORE, HYPHEN)
|
181
211
|
end
|
182
212
|
|
183
213
|
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
|
@@ -188,7 +218,7 @@ module Mail
|
|
188
218
|
# string = :resent_from_field
|
189
219
|
# underscoreize ( string ) #=> 'resent_from_field'
|
190
220
|
def underscoreize( str )
|
191
|
-
str.to_s.downcase.
|
221
|
+
str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
|
192
222
|
end
|
193
223
|
|
194
224
|
if RUBY_VERSION <= '1.8.6'
|
@@ -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,24 +1,17 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Mail
|
3
3
|
module VERSION
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
version[type] = value
|
10
|
-
end
|
11
|
-
|
12
|
-
MAJOR = version['major']
|
13
|
-
MINOR = version['minor']
|
14
|
-
PATCH = version['patch']
|
15
|
-
BUILD = version['build']
|
4
|
+
|
5
|
+
MAJOR = 2
|
6
|
+
MINOR = 7
|
7
|
+
PATCH = 1
|
8
|
+
BUILD = nil
|
16
9
|
|
17
10
|
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
|
18
|
-
|
11
|
+
|
19
12
|
def self.version
|
20
13
|
STRING
|
21
14
|
end
|
22
|
-
|
15
|
+
|
23
16
|
end
|
24
17
|
end
|
@@ -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,19 +56,43 @@ 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)
|
65
|
-
match = str.match(/\=\?(.+)?\?[Bb]\?(
|
91
|
+
match = str.match(/\=\?(.+)?\?[Bb]\?(.*)\?\=/m)
|
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
|
@@ -79,20 +105,25 @@ module Mail
|
|
79
105
|
end
|
80
106
|
|
81
107
|
def Ruby18.q_value_decode(str)
|
82
|
-
match = str.match(/\=\?(.+)?\?[Qq]\?(
|
108
|
+
match = str.match(/\=\?(.+)?\?[Qq]\?(.*)\?\=/m)
|
83
109
|
if match
|
84
110
|
encoding = match[1]
|
85
111
|
string = match[2].gsub(/_/, '=20')
|
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,7 +1,48 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Mail
|
4
5
|
class Ruby19
|
6
|
+
class StrictCharsetEncoder
|
7
|
+
def encode(string, charset)
|
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
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class BestEffortCharsetEncoder
|
18
|
+
def encode(string, charset)
|
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
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def pick_encoding(charset)
|
30
|
+
charset = case charset
|
31
|
+
when /ansi_x3.110-1983/
|
32
|
+
'ISO-8859-1'
|
33
|
+
when /Windows-?1258/i # Windows-1258 is similar to 1252
|
34
|
+
"Windows-1252"
|
35
|
+
else
|
36
|
+
charset
|
37
|
+
end
|
38
|
+
Mail::Ruby19.pick_encoding(charset)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
attr_accessor :charset_encoder
|
44
|
+
end
|
45
|
+
self.charset_encoder = BestEffortCharsetEncoder.new
|
5
46
|
|
6
47
|
# Escapes any parenthesis in a string that are unescaped this uses
|
7
48
|
# a Ruby 1.9.1 regexp feature of negative look behind
|
@@ -28,6 +69,9 @@ module Mail
|
|
28
69
|
end
|
29
70
|
|
30
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
|
31
75
|
str.unpack( 'm' ).first
|
32
76
|
end
|
33
77
|
|
@@ -43,20 +87,51 @@ module Mail
|
|
43
87
|
klass.const_get( string )
|
44
88
|
end
|
45
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
|
+
|
46
119
|
def Ruby19.b_value_encode(str, encoding = nil)
|
47
120
|
encoding = str.encoding.to_s
|
48
121
|
[Ruby19.encode_base64(str), encoding]
|
49
122
|
end
|
50
123
|
|
51
124
|
def Ruby19.b_value_decode(str)
|
52
|
-
match = str.match(/\=\?(.+)?\?[Bb]\?(
|
125
|
+
match = str.match(/\=\?(.+)?\?[Bb]\?(.*)\?\=/m)
|
53
126
|
if match
|
54
127
|
charset = match[1]
|
55
128
|
str = Ruby19.decode_base64(match[2])
|
56
|
-
str.
|
129
|
+
str = charset_encoder.encode(str, charset)
|
57
130
|
end
|
58
|
-
|
59
|
-
|
131
|
+
transcode_to_scrubbed_utf8(str)
|
132
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
133
|
+
warn "Encoding conversion failed #{$!}"
|
134
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
60
135
|
end
|
61
136
|
|
62
137
|
def Ruby19.q_value_encode(str, encoding = nil)
|
@@ -65,28 +140,31 @@ module Mail
|
|
65
140
|
end
|
66
141
|
|
67
142
|
def Ruby19.q_value_decode(str)
|
68
|
-
match = str.match(/\=\?(.+)?\?[Qq]\?(
|
143
|
+
match = str.match(/\=\?(.+)?\?[Qq]\?(.*)\?\=/m)
|
69
144
|
if match
|
70
145
|
charset = match[1]
|
71
146
|
string = match[2].gsub(/_/, '=20')
|
72
147
|
# Remove trailing = if it exists in a Q encoding
|
73
148
|
string = string.sub(/\=$/, '')
|
74
149
|
str = Encodings::QuotedPrintable.decode(string)
|
75
|
-
str.
|
150
|
+
str = charset_encoder.encode(str, charset)
|
76
151
|
# We assume that binary strings hold utf-8 directly to work around
|
77
152
|
# jruby/jruby#829 which subtly changes String#encode semantics.
|
78
|
-
str.force_encoding(
|
153
|
+
str.force_encoding(Encoding::UTF_8) if str.encoding == Encoding::ASCII_8BIT
|
79
154
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
str.dup.force_encoding(
|
155
|
+
transcode_to_scrubbed_utf8(str)
|
156
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
157
|
+
warn "Encoding conversion failed #{$!}"
|
158
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
84
159
|
end
|
85
160
|
|
86
161
|
def Ruby19.param_decode(str, encoding)
|
87
|
-
|
88
|
-
|
89
|
-
|
162
|
+
str = uri_parser.unescape(str)
|
163
|
+
str = charset_encoder.encode(str, encoding) if encoding
|
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)
|
90
168
|
end
|
91
169
|
|
92
170
|
def Ruby19.param_encode(str)
|
@@ -106,37 +184,38 @@ module Mail
|
|
106
184
|
# Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
|
107
185
|
# Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
|
108
186
|
def Ruby19.pick_encoding(charset)
|
109
|
-
|
187
|
+
charset = charset.to_s
|
188
|
+
encoding = case charset.downcase
|
110
189
|
|
111
190
|
# ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
|
112
|
-
when /^iso
|
191
|
+
when /^iso[-_]?8859-(\d+)(-i)?$/
|
113
192
|
"ISO-8859-#{$1}"
|
114
193
|
|
115
194
|
# ISO-8859-15, ISO-2022-JP and alike
|
116
|
-
when
|
195
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})$/
|
117
196
|
"ISO-#{$1}-#{$2}"
|
118
197
|
|
119
198
|
# "ISO-2022-JP-KDDI" and alike
|
120
|
-
when
|
199
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})-?(\w*)$/
|
121
200
|
"ISO-#{$1}-#{$2}-#{$3}"
|
122
201
|
|
123
202
|
# UTF-8, UTF-32BE and alike
|
124
|
-
when
|
203
|
+
when /^utf[\-_]?(\d{1,2})?(\w{1,2})$/
|
125
204
|
"UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
|
126
205
|
|
127
206
|
# Windows-1252 and alike
|
128
|
-
when
|
207
|
+
when /^windows-?(.*)$/
|
129
208
|
"Windows-#{$1}"
|
130
209
|
|
131
|
-
when
|
210
|
+
when '8bit'
|
132
211
|
Encoding::ASCII_8BIT
|
133
212
|
|
134
213
|
# alternatives/misspellings of us-ascii seen in the wild
|
135
|
-
when /^iso
|
214
|
+
when /^iso[-_]?646(-us)?$/, 'us=ascii'
|
136
215
|
Encoding::ASCII
|
137
216
|
|
138
217
|
# Microsoft-specific alias for MACROMAN
|
139
|
-
when
|
218
|
+
when 'macintosh'
|
140
219
|
Encoding::MACROMAN
|
141
220
|
|
142
221
|
# Microsoft-specific alias for CP949 (Korean)
|
@@ -148,12 +227,52 @@ module Mail
|
|
148
227
|
Encoding::Shift_JIS
|
149
228
|
|
150
229
|
# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
|
151
|
-
when
|
230
|
+
when 'gb2312'
|
152
231
|
Encoding::GB18030
|
153
232
|
|
233
|
+
when 'cp-850'
|
234
|
+
Encoding::CP850
|
235
|
+
|
236
|
+
when 'latin2'
|
237
|
+
Encoding::ISO_8859_2
|
238
|
+
|
154
239
|
else
|
155
240
|
charset
|
156
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
|
157
276
|
end
|
158
277
|
end
|
159
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,22 +27,13 @@ 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
|
-
require 'mail/
|
36
|
+
require 'mail/constants'
|
45
37
|
require 'mail/utilities'
|
46
38
|
require 'mail/configuration'
|
47
39
|
|
@@ -76,15 +68,17 @@ module Mail # :doc:
|
|
76
68
|
|
77
69
|
require 'mail/envelope'
|
78
70
|
|
79
|
-
|
71
|
+
register_autoload :Parsers, "mail/parsers"
|
80
72
|
|
81
73
|
# Autoload header field elements and transfer encodings.
|
82
74
|
require 'mail/elements'
|
83
75
|
require 'mail/encodings'
|
84
76
|
require 'mail/encodings/base64'
|
85
77
|
require 'mail/encodings/quoted_printable'
|
78
|
+
require 'mail/encodings/unix_to_unix'
|
86
79
|
|
87
80
|
require 'mail/matchers/has_sent_mail'
|
81
|
+
require 'mail/matchers/attachment_matchers.rb'
|
88
82
|
|
89
83
|
# Finally... require all the Mail.methods
|
90
84
|
require 'mail/mail'
|