mail 2.7.1 → 2.9.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 +4 -4
- data/README.md +135 -59
- data/lib/mail/attachments_list.rb +5 -6
- data/lib/mail/body.rb +51 -74
- 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/unix_to_unix.rb +1 -1
- data/lib/mail/encodings.rb +32 -61
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +100 -86
- data/lib/mail/field_list.rb +61 -8
- data/lib/mail/fields/bcc_field.rb +33 -52
- data/lib/mail/fields/cc_field.rb +27 -49
- data/lib/mail/fields/comments_field.rb +26 -37
- data/lib/mail/fields/common_address_field.rb +162 -0
- data/lib/mail/fields/common_date_field.rb +56 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +41 -0
- data/lib/mail/fields/content_description_field.rb +6 -14
- data/lib/mail/fields/content_disposition_field.rb +11 -38
- data/lib/mail/fields/content_id_field.rb +23 -51
- data/lib/mail/fields/content_location_field.rb +10 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
- data/lib/mail/fields/content_type_field.rb +49 -75
- data/lib/mail/fields/date_field.rb +22 -51
- data/lib/mail/fields/from_field.rb +27 -49
- data/lib/mail/fields/in_reply_to_field.rb +37 -49
- data/lib/mail/fields/keywords_field.rb +17 -31
- data/lib/mail/fields/message_id_field.rb +24 -71
- data/lib/mail/fields/mime_version_field.rb +18 -30
- data/lib/mail/fields/named_structured_field.rb +10 -0
- data/lib/mail/fields/named_unstructured_field.rb +10 -0
- data/lib/mail/fields/optional_field.rb +5 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
- data/lib/mail/fields/received_field.rb +42 -57
- data/lib/mail/fields/references_field.rb +34 -49
- data/lib/mail/fields/reply_to_field.rb +27 -49
- data/lib/mail/fields/resent_bcc_field.rb +27 -49
- data/lib/mail/fields/resent_cc_field.rb +27 -49
- data/lib/mail/fields/resent_date_field.rb +4 -29
- data/lib/mail/fields/resent_from_field.rb +27 -49
- data/lib/mail/fields/resent_message_id_field.rb +4 -29
- data/lib/mail/fields/resent_sender_field.rb +26 -56
- data/lib/mail/fields/resent_to_field.rb +27 -49
- data/lib/mail/fields/return_path_field.rb +49 -54
- data/lib/mail/fields/sender_field.rb +33 -55
- data/lib/mail/fields/structured_field.rb +2 -30
- data/lib/mail/fields/subject_field.rb +8 -11
- data/lib/mail/fields/to_field.rb +27 -49
- data/lib/mail/fields/unstructured_field.rb +15 -48
- data/lib/mail/fields.rb +9 -0
- data/lib/mail/header.rb +69 -110
- data/lib/mail/mail.rb +32 -27
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/message.rb +67 -78
- data/lib/mail/multibyte/chars.rb +9 -167
- data/lib/mail/multibyte/unicode.rb +1 -1
- 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 +13 -12
- data/lib/mail/network/delivery_methods/logger_delivery.rb +2 -5
- data/lib/mail/network/delivery_methods/sendmail.rb +27 -35
- data/lib/mail/network/delivery_methods/smtp.rb +81 -30
- data/lib/mail/network/delivery_methods/smtp_connection.rb +10 -19
- data/lib/mail/network/delivery_methods/test_mailer.rb +8 -6
- data/lib/mail/network/retriever_methods/base.rb +13 -13
- data/lib/mail/network/retriever_methods/imap.rb +8 -5
- data/lib/mail/network/retriever_methods/pop3.rb +22 -22
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/parsers/address_lists_parser.rb +33073 -33064
- data/lib/mail/parsers/address_lists_parser.rl +11 -0
- data/lib/mail/parsers/content_disposition_parser.rb +837 -828
- data/lib/mail/parsers/content_disposition_parser.rl +11 -0
- data/lib/mail/parsers/content_location_parser.rb +773 -764
- data/lib/mail/parsers/content_location_parser.rl +12 -1
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +477 -468
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +11 -0
- data/lib/mail/parsers/content_type_parser.rb +974 -965
- data/lib/mail/parsers/content_type_parser.rl +11 -0
- data/lib/mail/parsers/date_time_parser.rb +841 -832
- data/lib/mail/parsers/date_time_parser.rl +11 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3627 -3530
- data/lib/mail/parsers/envelope_from_parser.rl +11 -0
- data/lib/mail/parsers/message_ids_parser.rb +5110 -2800
- data/lib/mail/parsers/message_ids_parser.rl +16 -1
- data/lib/mail/parsers/mime_version_parser.rb +466 -457
- data/lib/mail/parsers/mime_version_parser.rl +11 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +839 -830
- data/lib/mail/parsers/phrase_lists_parser.rl +12 -1
- data/lib/mail/parsers/received_parser.rb +8692 -8683
- data/lib/mail/parsers/received_parser.rl +11 -0
- data/lib/mail/parsers/rfc5322.rl +28 -13
- data/lib/mail/parsers.rb +11 -17
- data/lib/mail/part.rb +24 -28
- data/lib/mail/parts_list.rb +57 -0
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +321 -69
- data/lib/mail/version.rb +1 -1
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +2 -22
- metadata +83 -19
- data/lib/mail/check_delivery_params.rb +0 -60
- 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
|
|
@@ -27,19 +24,19 @@ module Mail
|
|
|
27
24
|
if str.respond_to?(:force_encoding)
|
|
28
25
|
original_encoding = str.encoding
|
|
29
26
|
ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
|
|
30
|
-
if
|
|
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
|
|
@@ -89,7 +86,6 @@ module Mail
|
|
|
89
86
|
str
|
|
90
87
|
end
|
|
91
88
|
end
|
|
92
|
-
module_function :unquote
|
|
93
89
|
|
|
94
90
|
# Removes any \-escaping.
|
|
95
91
|
#
|
|
@@ -103,7 +99,6 @@ module Mail
|
|
|
103
99
|
def unescape( str )
|
|
104
100
|
str.gsub(/\\(.)/, '\1')
|
|
105
101
|
end
|
|
106
|
-
module_function :unescape
|
|
107
102
|
|
|
108
103
|
# Wraps a string in parenthesis and escapes any that are in the string itself.
|
|
109
104
|
#
|
|
@@ -111,7 +106,7 @@ module Mail
|
|
|
111
106
|
#
|
|
112
107
|
# paren( 'This is a string' ) #=> '(This is a string)'
|
|
113
108
|
def paren( str )
|
|
114
|
-
|
|
109
|
+
Utilities.paren( str )
|
|
115
110
|
end
|
|
116
111
|
|
|
117
112
|
# Unwraps a string from being wrapped in parenthesis
|
|
@@ -121,8 +116,11 @@ module Mail
|
|
|
121
116
|
# str = '(This is a string)'
|
|
122
117
|
# unparen( str ) #=> 'This is a string'
|
|
123
118
|
def unparen( str )
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
if str.start_with?('(') && str.end_with?(')')
|
|
120
|
+
str.slice(1..-2)
|
|
121
|
+
else
|
|
122
|
+
str
|
|
123
|
+
end
|
|
126
124
|
end
|
|
127
125
|
|
|
128
126
|
# Wraps a string in angle brackets and escapes any that are in the string itself
|
|
@@ -131,7 +129,7 @@ module Mail
|
|
|
131
129
|
#
|
|
132
130
|
# bracket( 'This is a string' ) #=> '<This is a string>'
|
|
133
131
|
def bracket( str )
|
|
134
|
-
|
|
132
|
+
Utilities.bracket( str )
|
|
135
133
|
end
|
|
136
134
|
|
|
137
135
|
# Unwraps a string from being wrapped in parenthesis
|
|
@@ -141,8 +139,11 @@ module Mail
|
|
|
141
139
|
# str = '<This is a string>'
|
|
142
140
|
# unbracket( str ) #=> 'This is a string'
|
|
143
141
|
def unbracket( str )
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
if str.start_with?('<') && str.end_with?('>')
|
|
143
|
+
str.slice(1..-2)
|
|
144
|
+
else
|
|
145
|
+
str
|
|
146
|
+
end
|
|
146
147
|
end
|
|
147
148
|
|
|
148
149
|
# Escape parenthesies in a string
|
|
@@ -152,7 +153,7 @@ module Mail
|
|
|
152
153
|
# str = 'This is (a) string'
|
|
153
154
|
# escape_paren( str ) #=> 'This is \(a\) string'
|
|
154
155
|
def escape_paren( str )
|
|
155
|
-
|
|
156
|
+
Utilities.escape_paren( str )
|
|
156
157
|
end
|
|
157
158
|
|
|
158
159
|
def uri_escape( str )
|
|
@@ -164,7 +165,11 @@ module Mail
|
|
|
164
165
|
end
|
|
165
166
|
|
|
166
167
|
def uri_parser
|
|
167
|
-
@uri_parser ||= URI.const_defined?(:
|
|
168
|
+
@uri_parser ||= if URI.const_defined?(:DEFAULT_PARSER)
|
|
169
|
+
defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
|
|
170
|
+
else
|
|
171
|
+
URI
|
|
172
|
+
end
|
|
168
173
|
end
|
|
169
174
|
|
|
170
175
|
# Matches two objects with their to_s values case insensitively
|
|
@@ -207,7 +212,7 @@ module Mail
|
|
|
207
212
|
# string = :resent_from_field
|
|
208
213
|
# dasherize( string ) #=> 'resent-from-field'
|
|
209
214
|
def dasherize( str )
|
|
210
|
-
str.to_s.tr(UNDERSCORE, HYPHEN)
|
|
215
|
+
str.to_s.tr(Constants::UNDERSCORE, Constants::HYPHEN)
|
|
211
216
|
end
|
|
212
217
|
|
|
213
218
|
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
|
|
@@ -218,68 +223,36 @@ module Mail
|
|
|
218
223
|
# string = :resent_from_field
|
|
219
224
|
# underscoreize ( string ) #=> 'resent_from_field'
|
|
220
225
|
def underscoreize( str )
|
|
221
|
-
str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
|
|
226
|
+
str.to_s.downcase.tr(Constants::HYPHEN, Constants::UNDERSCORE)
|
|
222
227
|
end
|
|
223
228
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
results = []
|
|
228
|
-
str.each_line do |line|
|
|
229
|
-
results << yield(line)
|
|
230
|
-
end
|
|
231
|
-
results
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def map_with_index( enum, &block )
|
|
235
|
-
results = []
|
|
236
|
-
enum.each_with_index do |token, i|
|
|
237
|
-
results[i] = yield(token, i)
|
|
238
|
-
end
|
|
239
|
-
results
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
else
|
|
243
|
-
|
|
244
|
-
def map_lines( str, &block )
|
|
245
|
-
str.each_line.map(&block)
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def map_with_index( enum, &block )
|
|
249
|
-
enum.each_with_index.map(&block)
|
|
250
|
-
end
|
|
229
|
+
def map_lines( str, &block )
|
|
230
|
+
str.each_line.map(&block)
|
|
231
|
+
end
|
|
251
232
|
|
|
233
|
+
def map_with_index( enum, &block )
|
|
234
|
+
enum.each_with_index.map(&block)
|
|
252
235
|
end
|
|
253
236
|
|
|
254
237
|
def self.binary_unsafe_to_lf(string) #:nodoc:
|
|
255
|
-
string.gsub(/\r\n|\r/, LF)
|
|
238
|
+
string.gsub(/\r\n|\r/, Constants::LF)
|
|
256
239
|
end
|
|
257
240
|
|
|
258
241
|
TO_CRLF_REGEX =
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
Regexp.new("(?<!\r)\n|\r(?!\n)")
|
|
264
|
-
else
|
|
265
|
-
/\n|\r\n|\r/
|
|
266
|
-
end
|
|
242
|
+
# This 1.9 only regex can save a reasonable amount of time (~20%)
|
|
243
|
+
# by not matching "\r\n" so the string is returned unchanged in
|
|
244
|
+
# the common case.
|
|
245
|
+
Regexp.new("(?<!\r)\n|\r(?!\n)")
|
|
267
246
|
|
|
268
247
|
def self.binary_unsafe_to_crlf(string) #:nodoc:
|
|
269
|
-
string.gsub(TO_CRLF_REGEX, CRLF)
|
|
248
|
+
string.gsub(TO_CRLF_REGEX, Constants::CRLF)
|
|
270
249
|
end
|
|
271
250
|
|
|
272
|
-
|
|
273
|
-
|
|
251
|
+
def self.safe_for_line_ending_conversion?(string) #:nodoc:
|
|
252
|
+
if string.encoding == Encoding::BINARY
|
|
274
253
|
string.ascii_only?
|
|
275
|
-
|
|
276
|
-
|
|
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
|
|
254
|
+
else
|
|
255
|
+
string.valid_encoding?
|
|
283
256
|
end
|
|
284
257
|
end
|
|
285
258
|
|
|
@@ -311,7 +284,7 @@ module Mail
|
|
|
311
284
|
# and arrays and hashes that have nothing in them.
|
|
312
285
|
#
|
|
313
286
|
# This logic is mostly shared with ActiveSupport's blank?
|
|
314
|
-
def
|
|
287
|
+
def blank?(value)
|
|
315
288
|
if value.kind_of?(NilClass)
|
|
316
289
|
true
|
|
317
290
|
elsif value.kind_of?(String)
|
|
@@ -320,5 +293,284 @@ module Mail
|
|
|
320
293
|
value.respond_to?(:empty?) ? value.empty? : !value
|
|
321
294
|
end
|
|
322
295
|
end
|
|
296
|
+
|
|
297
|
+
def generate_message_id
|
|
298
|
+
"<#{Mail.random_tag}@#{::Socket.gethostname}.mail>"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
class StrictCharsetEncoder
|
|
302
|
+
def encode(string, charset)
|
|
303
|
+
case charset
|
|
304
|
+
when /utf-?7/i
|
|
305
|
+
Mail::Utilities.decode_utf7(string)
|
|
306
|
+
else
|
|
307
|
+
string.force_encoding(Mail::Utilities.pick_encoding(charset))
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
class BestEffortCharsetEncoder
|
|
313
|
+
def encode(string, charset)
|
|
314
|
+
case charset
|
|
315
|
+
when /utf-?7/i
|
|
316
|
+
Mail::Utilities.decode_utf7(string)
|
|
317
|
+
else
|
|
318
|
+
string.force_encoding(pick_encoding(charset))
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
private
|
|
323
|
+
|
|
324
|
+
def pick_encoding(charset)
|
|
325
|
+
charset = case charset
|
|
326
|
+
when /ansi_x3.110-1983/
|
|
327
|
+
'ISO-8859-1'
|
|
328
|
+
when /Windows-?1258/i # Windows-1258 is similar to 1252
|
|
329
|
+
"Windows-1252"
|
|
330
|
+
else
|
|
331
|
+
charset
|
|
332
|
+
end
|
|
333
|
+
Mail::Utilities.pick_encoding(charset)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
class << self
|
|
338
|
+
attr_accessor :charset_encoder
|
|
339
|
+
end
|
|
340
|
+
self.charset_encoder = BestEffortCharsetEncoder.new
|
|
341
|
+
|
|
342
|
+
# Escapes any parenthesis in a string that are unescaped this uses
|
|
343
|
+
# a Ruby 1.9.1 regexp feature of negative look behind
|
|
344
|
+
def Utilities.escape_paren( str )
|
|
345
|
+
re = /(?<!\\)([\(\)])/ # Only match unescaped parens
|
|
346
|
+
str.gsub(re) { |s| '\\' + s }
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def Utilities.paren( str )
|
|
350
|
+
str = ::Mail::Utilities.unparen( str )
|
|
351
|
+
str = escape_paren( str )
|
|
352
|
+
'(' + str + ')'
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def Utilities.escape_bracket( str )
|
|
356
|
+
re = /(?<!\\)([\<\>])/ # Only match unescaped brackets
|
|
357
|
+
str.gsub(re) { |s| '\\' + s }
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def Utilities.bracket( str )
|
|
361
|
+
str = ::Mail::Utilities.unbracket( str )
|
|
362
|
+
str = escape_bracket( str )
|
|
363
|
+
'<' + str + '>'
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def Utilities.decode_base64(str)
|
|
367
|
+
if !str.end_with?("=") && str.length % 4 != 0
|
|
368
|
+
str = str.ljust((str.length + 3) & ~3, "=")
|
|
369
|
+
end
|
|
370
|
+
str.unpack1( 'm' )
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def Utilities.encode_base64(str)
|
|
374
|
+
[str].pack( 'm' )
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def Utilities.has_constant?(klass, string)
|
|
378
|
+
klass.const_defined?( string, false )
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def Utilities.get_constant(klass, string)
|
|
382
|
+
klass.const_get( string )
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def Utilities.transcode_charset(str, from_encoding, to_encoding = Encoding::UTF_8)
|
|
386
|
+
to_encoding = Encoding.find(to_encoding)
|
|
387
|
+
replacement_char = to_encoding == Encoding::UTF_8 ? '�' : '?'
|
|
388
|
+
charset_encoder.encode(str.dup, from_encoding).encode(to_encoding, :undef => :replace, :invalid => :replace, :replace => replacement_char)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# From Ruby stdlib Net::IMAP
|
|
392
|
+
def Utilities.encode_utf7(string)
|
|
393
|
+
string.gsub(/(&)|[^\x20-\x7e]+/) do
|
|
394
|
+
if $1
|
|
395
|
+
"&-"
|
|
396
|
+
else
|
|
397
|
+
base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
|
|
398
|
+
"&" + base64.delete("=").tr("/", ",") + "-"
|
|
399
|
+
end
|
|
400
|
+
end.force_encoding(Encoding::ASCII_8BIT)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def Utilities.decode_utf7(utf7)
|
|
404
|
+
utf7.gsub(/&([^-]+)?-/n) do
|
|
405
|
+
if $1
|
|
406
|
+
($1.tr(",", "/") + "===").unpack1("m").encode(Encoding::UTF_8, Encoding::UTF_16BE)
|
|
407
|
+
else
|
|
408
|
+
"&"
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def Utilities.b_value_encode(str, encoding = nil)
|
|
414
|
+
encoding = str.encoding.to_s
|
|
415
|
+
[Utilities.encode_base64(str), encoding]
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def Utilities.b_value_decode(str)
|
|
419
|
+
b_decoded = str.gsub(/=\?([^?]+)\?[Bb]\?([^?]*)\?=/m) do |match|
|
|
420
|
+
charset = $1
|
|
421
|
+
|
|
422
|
+
string = Utilities.decode_base64($2)
|
|
423
|
+
|
|
424
|
+
begin
|
|
425
|
+
charset_encoder.encode(string, charset)
|
|
426
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError, Encoding::InvalidByteSequenceError
|
|
427
|
+
warn "WARNING: Encoding conversion failed #{$!}"
|
|
428
|
+
string.dup.force_encoding(Encoding::UTF_8)
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
transcode_to_scrubbed_utf8(b_decoded)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def Utilities.q_value_encode(str, encoding = nil)
|
|
436
|
+
encoding = str.encoding.to_s
|
|
437
|
+
[Encodings::QuotedPrintable.encode(str), encoding]
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def Utilities.q_value_decode(str)
|
|
441
|
+
q_decoded = str.gsub(/=\?([^?]+)\?[Qq]\?((?:[^?]|\?(?!\=))*)\?=/m) do |_match|
|
|
442
|
+
charset = $1
|
|
443
|
+
string = $2
|
|
444
|
+
.gsub(/_/, '=20')
|
|
445
|
+
.sub(/\=$/, '') # Remove trailing = if it exists in a Q encoding
|
|
446
|
+
|
|
447
|
+
unquoted_printable = Encodings::QuotedPrintable.decode(string)
|
|
448
|
+
decoded = begin
|
|
449
|
+
charset_encoder.encode(unquoted_printable, charset)
|
|
450
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
|
451
|
+
warn "WARNING: Encoding conversion failed #{$!}"
|
|
452
|
+
unquoted_printable.dup.force_encoding(Encoding::UTF_8)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# We assume that binary strings hold utf-8 directly to work around
|
|
456
|
+
# jruby/jruby#829 which subtly changes String#encode semantics.
|
|
457
|
+
decoded.force_encoding(Encoding::UTF_8) if decoded.encoding == Encoding::ASCII_8BIT
|
|
458
|
+
|
|
459
|
+
decoded
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
transcode_to_scrubbed_utf8(q_decoded)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def Utilities.param_decode(str, encoding)
|
|
466
|
+
str = uri_parser.unescape(str)
|
|
467
|
+
str = charset_encoder.encode(str, encoding) if encoding
|
|
468
|
+
transcode_to_scrubbed_utf8(str)
|
|
469
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
|
470
|
+
warn "WARNING: Encoding conversion failed #{$!}"
|
|
471
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def Utilities.param_encode(str)
|
|
475
|
+
encoding = str.encoding.to_s.downcase
|
|
476
|
+
language = Configuration.instance.param_encode_language
|
|
477
|
+
"#{encoding}'#{language}'#{uri_parser.escape(str)}"
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def Utilities.uri_parser
|
|
481
|
+
defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# Pick a Ruby encoding corresponding to the message charset. Most
|
|
485
|
+
# charsets have a Ruby encoding, but some need manual aliasing here.
|
|
486
|
+
#
|
|
487
|
+
# TODO: add this as a test somewhere:
|
|
488
|
+
# Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
|
|
489
|
+
# Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
|
|
490
|
+
def Utilities.pick_encoding(charset)
|
|
491
|
+
charset = charset.to_s
|
|
492
|
+
encoding = case charset.downcase
|
|
493
|
+
|
|
494
|
+
# ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
|
|
495
|
+
when /^iso[-_]?8859-(\d+)(-i)?$/
|
|
496
|
+
"ISO-8859-#{$1}"
|
|
497
|
+
|
|
498
|
+
# ISO-8859-15, ISO-2022-JP and alike
|
|
499
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})$/
|
|
500
|
+
"ISO-#{$1}-#{$2}"
|
|
501
|
+
|
|
502
|
+
# "ISO-2022-JP-KDDI" and alike
|
|
503
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})-?(\w*)$/
|
|
504
|
+
"ISO-#{$1}-#{$2}-#{$3}"
|
|
505
|
+
|
|
506
|
+
# UTF-8, UTF-32BE and alike
|
|
507
|
+
when /^utf[\-_]?(\d{1,2})?(\w{1,2})$/
|
|
508
|
+
"UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
|
|
509
|
+
|
|
510
|
+
# Windows-1252 and alike
|
|
511
|
+
when /^windows-?(.*)$/
|
|
512
|
+
"Windows-#{$1}"
|
|
513
|
+
|
|
514
|
+
when '8bit'
|
|
515
|
+
Encoding::ASCII_8BIT
|
|
516
|
+
|
|
517
|
+
# alternatives/misspellings of us-ascii seen in the wild
|
|
518
|
+
when /^iso[-_]?646(-us)?$/, 'us=ascii'
|
|
519
|
+
Encoding::ASCII
|
|
520
|
+
|
|
521
|
+
# Microsoft-specific alias for MACROMAN
|
|
522
|
+
when 'macintosh'
|
|
523
|
+
Encoding::MACROMAN
|
|
524
|
+
|
|
525
|
+
# Microsoft-specific alias for CP949 (Korean)
|
|
526
|
+
when 'ks_c_5601-1987'
|
|
527
|
+
Encoding::CP949
|
|
528
|
+
|
|
529
|
+
# Wrongly written Shift_JIS (Japanese)
|
|
530
|
+
when 'shift-jis'
|
|
531
|
+
Encoding::Shift_JIS
|
|
532
|
+
|
|
533
|
+
# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
|
|
534
|
+
when 'gb2312'
|
|
535
|
+
Encoding::GB18030
|
|
536
|
+
|
|
537
|
+
when 'cp-850'
|
|
538
|
+
Encoding::CP850
|
|
539
|
+
|
|
540
|
+
when 'latin2'
|
|
541
|
+
Encoding::ISO_8859_2
|
|
542
|
+
|
|
543
|
+
else
|
|
544
|
+
charset
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
convert_to_encoding(encoding)
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def Utilities.string_byteslice(str, *args)
|
|
551
|
+
str.byteslice(*args)
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
class << self
|
|
555
|
+
private
|
|
556
|
+
|
|
557
|
+
def convert_to_encoding(encoding)
|
|
558
|
+
if encoding.is_a?(Encoding)
|
|
559
|
+
encoding
|
|
560
|
+
else
|
|
561
|
+
# Fall back to ASCII for charsets that Ruby doesn't recognize
|
|
562
|
+
begin
|
|
563
|
+
Encoding.find(encoding)
|
|
564
|
+
rescue ArgumentError
|
|
565
|
+
Encoding::BINARY
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def transcode_to_scrubbed_utf8(str)
|
|
571
|
+
decoded = str.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => "�")
|
|
572
|
+
decoded.valid_encoding? ? decoded : decoded.encode(Encoding::UTF_16LE, :invalid => :replace, :replace => "�").encode(Encoding::UTF_8)
|
|
573
|
+
end
|
|
574
|
+
end
|
|
323
575
|
end
|
|
324
576
|
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'
|
|
@@ -66,9 +48,7 @@ module Mail # :doc:
|
|
|
66
48
|
require 'mail/field'
|
|
67
49
|
require 'mail/field_list'
|
|
68
50
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
register_autoload :Parsers, "mail/parsers"
|
|
51
|
+
register_autoload :Envelope, 'mail/envelope'
|
|
72
52
|
|
|
73
53
|
# Autoload header field elements and transfer encodings.
|
|
74
54
|
require 'mail/elements'
|
|
@@ -78,7 +58,7 @@ module Mail # :doc:
|
|
|
78
58
|
require 'mail/encodings/unix_to_unix'
|
|
79
59
|
|
|
80
60
|
require 'mail/matchers/has_sent_mail'
|
|
81
|
-
require 'mail/matchers/attachment_matchers
|
|
61
|
+
require 'mail/matchers/attachment_matchers'
|
|
82
62
|
|
|
83
63
|
# Finally... require all the Mail.methods
|
|
84
64
|
require 'mail/mail'
|