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/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'
|