mail 2.6.4 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +208 -156
- data/lib/mail/attachments_list.rb +13 -10
- data/lib/mail/body.rb +96 -107
- data/lib/mail/configuration.rb +2 -0
- data/lib/mail/constants.rb +27 -5
- data/lib/mail/elements/address.rb +61 -50
- data/lib/mail/elements/address_list.rb +11 -19
- data/lib/mail/elements/content_disposition_element.rb +9 -16
- data/lib/mail/elements/content_location_element.rb +6 -11
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
- data/lib/mail/elements/content_type_element.rb +16 -23
- data/lib/mail/elements/date_time_element.rb +7 -15
- data/lib/mail/elements/envelope_from_element.rb +22 -23
- data/lib/mail/elements/message_ids_element.rb +18 -13
- data/lib/mail/elements/mime_version_element.rb +7 -15
- data/lib/mail/elements/phrase_list.rb +12 -10
- data/lib/mail/elements/received_element.rb +27 -19
- data/lib/mail/encodings/7bit.rb +9 -14
- data/lib/mail/encodings/8bit.rb +2 -21
- data/lib/mail/encodings/base64.rb +11 -12
- data/lib/mail/encodings/binary.rb +3 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +6 -6
- data/lib/mail/encodings/transfer_encoding.rb +38 -29
- data/lib/mail/encodings/unix_to_unix.rb +4 -2
- data/lib/mail/encodings.rb +83 -56
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +181 -130
- 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 +53 -84
- data/lib/mail/fields/date_field.rb +22 -52
- 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 +9 -8
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
- 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 -30
- 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 +31 -47
- data/lib/mail/fields.rb +9 -0
- data/lib/mail/header.rb +71 -110
- data/lib/mail/mail.rb +34 -37
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/matchers/has_sent_mail.rb +21 -1
- data/lib/mail/message.rb +126 -127
- data/lib/mail/multibyte/chars.rb +24 -181
- data/lib/mail/multibyte/unicode.rb +11 -11
- data/lib/mail/multibyte/utils.rb +26 -43
- data/lib/mail/multibyte.rb +55 -16
- data/lib/mail/network/delivery_methods/exim.rb +8 -11
- data/lib/mail/network/delivery_methods/file_delivery.rb +15 -18
- data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +32 -35
- data/lib/mail/network/delivery_methods/smtp.rb +125 -68
- data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -16
- data/lib/mail/network/delivery_methods/test_mailer.rb +12 -13
- data/lib/mail/network/retriever_methods/base.rb +13 -13
- data/lib/mail/network/retriever_methods/imap.rb +25 -9
- data/lib/mail/network/retriever_methods/pop3.rb +25 -23
- data/lib/mail/network/retriever_methods/test_retriever.rb +3 -2
- data/lib/mail/network.rb +1 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33228 -116
- data/lib/mail/parsers/address_lists_parser.rl +183 -0
- data/lib/mail/parsers/content_disposition_parser.rb +885 -49
- data/lib/mail/parsers/content_disposition_parser.rl +93 -0
- data/lib/mail/parsers/content_location_parser.rb +812 -23
- data/lib/mail/parsers/content_location_parser.rl +82 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +512 -21
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
- data/lib/mail/parsers/content_type_parser.rb +1039 -55
- data/lib/mail/parsers/content_type_parser.rl +94 -0
- data/lib/mail/parsers/date_time_parser.rb +880 -25
- data/lib/mail/parsers/date_time_parser.rl +73 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3672 -40
- data/lib/mail/parsers/envelope_from_parser.rl +93 -0
- data/lib/mail/parsers/message_ids_parser.rb +5149 -25
- data/lib/mail/parsers/message_ids_parser.rl +97 -0
- data/lib/mail/parsers/mime_version_parser.rb +500 -26
- data/lib/mail/parsers/mime_version_parser.rl +72 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +873 -22
- data/lib/mail/parsers/phrase_lists_parser.rl +94 -0
- data/lib/mail/parsers/received_parser.rb +8779 -43
- data/lib/mail/parsers/received_parser.rl +95 -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 +74 -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 +11 -25
- data/lib/mail/part.rb +25 -29
- data/lib/mail/parts_list.rb +62 -6
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +361 -74
- data/lib/mail/version.rb +2 -2
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +4 -37
- metadata +125 -67
- data/CHANGELOG.rdoc +0 -787
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -11
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/lib/mail/check_delivery_params.rb +0 -21
- data/lib/mail/core_extensions/smtp.rb +0 -25
- data/lib/mail/core_extensions/string/access.rb +0 -146
- data/lib/mail/core_extensions/string/multibyte.rb +0 -79
- data/lib/mail/core_extensions/string.rb +0 -21
- data/lib/mail/fields/common/address_container.rb +0 -17
- data/lib/mail/fields/common/common_address.rb +0 -136
- data/lib/mail/fields/common/common_date.rb +0 -36
- data/lib/mail/fields/common/common_field.rb +0 -61
- data/lib/mail/fields/common/common_message_id.rb +0 -49
- data/lib/mail/multibyte/exceptions.rb +0 -9
- 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 -18
- data/lib/mail/version_specific/ruby_1_8.rb +0 -126
- data/lib/mail/version_specific/ruby_1_9.rb +0 -223
data/lib/mail/utilities.rb
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# frozen_string_literal: true
|
|
3
|
+
require 'mail/constants'
|
|
4
|
+
require 'socket'
|
|
5
|
+
|
|
3
6
|
module Mail
|
|
4
7
|
module Utilities
|
|
5
|
-
|
|
6
|
-
LF = "\n"
|
|
7
|
-
CRLF = "\r\n"
|
|
8
|
-
|
|
9
|
-
include Constants
|
|
8
|
+
extend self
|
|
10
9
|
|
|
11
10
|
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
|
12
11
|
def atom_safe?( str )
|
|
13
|
-
not ATOM_UNSAFE === str
|
|
12
|
+
not Constants::ATOM_UNSAFE === str
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
|
@@ -22,28 +21,38 @@ module Mail
|
|
|
22
21
|
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
|
23
22
|
# in double quotes, otherwise returns the string unmodified
|
|
24
23
|
def quote_phrase( str )
|
|
25
|
-
if
|
|
24
|
+
if str.respond_to?(:force_encoding)
|
|
26
25
|
original_encoding = str.encoding
|
|
27
|
-
ascii_str = str.dup.force_encoding('ASCII-8BIT')
|
|
28
|
-
if
|
|
26
|
+
ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
|
|
27
|
+
if Constants::PHRASE_UNSAFE === ascii_str
|
|
29
28
|
dquote(ascii_str).force_encoding(original_encoding)
|
|
30
29
|
else
|
|
31
30
|
str
|
|
32
31
|
end
|
|
33
32
|
else
|
|
34
|
-
|
|
33
|
+
Constants::PHRASE_UNSAFE === str ? dquote(str) : str
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
# Returns true if the string supplied is free from characters not allowed as a TOKEN
|
|
39
38
|
def token_safe?( str )
|
|
40
|
-
not TOKEN_UNSAFE === str
|
|
39
|
+
not Constants::TOKEN_UNSAFE === str
|
|
41
40
|
end
|
|
42
41
|
|
|
43
42
|
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
|
44
43
|
# in double quotes, otherwise returns the string unmodified
|
|
45
44
|
def quote_token( str )
|
|
46
|
-
|
|
45
|
+
if str.respond_to?(:force_encoding)
|
|
46
|
+
original_encoding = str.encoding
|
|
47
|
+
ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
|
|
48
|
+
if token_safe?( ascii_str )
|
|
49
|
+
str
|
|
50
|
+
else
|
|
51
|
+
dquote(ascii_str).force_encoding(original_encoding)
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
token_safe?( str ) ? str : dquote(str)
|
|
55
|
+
end
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
# Wraps supplied string in double quotes and applies \-escaping as necessary,
|
|
@@ -72,19 +81,32 @@ module Mail
|
|
|
72
81
|
# unqoute(string) #=> 'This is "a string"'
|
|
73
82
|
def unquote( str )
|
|
74
83
|
if str =~ /^"(.*?)"$/
|
|
75
|
-
$1
|
|
84
|
+
unescape($1)
|
|
76
85
|
else
|
|
77
86
|
str
|
|
78
87
|
end
|
|
79
88
|
end
|
|
80
89
|
|
|
90
|
+
# Removes any \-escaping.
|
|
91
|
+
#
|
|
92
|
+
# Example:
|
|
93
|
+
#
|
|
94
|
+
# string = 'This is \"a string\"'
|
|
95
|
+
# unescape(string) #=> 'This is "a string"'
|
|
96
|
+
#
|
|
97
|
+
# string = '"This is \"a string\""'
|
|
98
|
+
# unescape(string) #=> '"This is "a string""'
|
|
99
|
+
def unescape( str )
|
|
100
|
+
str.gsub(/\\(.)/, '\1')
|
|
101
|
+
end
|
|
102
|
+
|
|
81
103
|
# Wraps a string in parenthesis and escapes any that are in the string itself.
|
|
82
104
|
#
|
|
83
105
|
# Example:
|
|
84
106
|
#
|
|
85
107
|
# paren( 'This is a string' ) #=> '(This is a string)'
|
|
86
108
|
def paren( str )
|
|
87
|
-
|
|
109
|
+
Utilities.paren( str )
|
|
88
110
|
end
|
|
89
111
|
|
|
90
112
|
# Unwraps a string from being wrapped in parenthesis
|
|
@@ -94,8 +116,11 @@ module Mail
|
|
|
94
116
|
# str = '(This is a string)'
|
|
95
117
|
# unparen( str ) #=> 'This is a string'
|
|
96
118
|
def unparen( str )
|
|
97
|
-
|
|
98
|
-
|
|
119
|
+
if str.start_with?('(') && str.end_with?(')')
|
|
120
|
+
str.slice(1..-2)
|
|
121
|
+
else
|
|
122
|
+
str
|
|
123
|
+
end
|
|
99
124
|
end
|
|
100
125
|
|
|
101
126
|
# Wraps a string in angle brackets and escapes any that are in the string itself
|
|
@@ -104,7 +129,7 @@ module Mail
|
|
|
104
129
|
#
|
|
105
130
|
# bracket( 'This is a string' ) #=> '<This is a string>'
|
|
106
131
|
def bracket( str )
|
|
107
|
-
|
|
132
|
+
Utilities.bracket( str )
|
|
108
133
|
end
|
|
109
134
|
|
|
110
135
|
# Unwraps a string from being wrapped in parenthesis
|
|
@@ -114,8 +139,11 @@ module Mail
|
|
|
114
139
|
# str = '<This is a string>'
|
|
115
140
|
# unbracket( str ) #=> 'This is a string'
|
|
116
141
|
def unbracket( str )
|
|
117
|
-
|
|
118
|
-
|
|
142
|
+
if str.start_with?('<') && str.end_with?('>')
|
|
143
|
+
str.slice(1..-2)
|
|
144
|
+
else
|
|
145
|
+
str
|
|
146
|
+
end
|
|
119
147
|
end
|
|
120
148
|
|
|
121
149
|
# Escape parenthesies in a string
|
|
@@ -125,7 +153,7 @@ module Mail
|
|
|
125
153
|
# str = 'This is (a) string'
|
|
126
154
|
# escape_paren( str ) #=> 'This is \(a\) string'
|
|
127
155
|
def escape_paren( str )
|
|
128
|
-
|
|
156
|
+
Utilities.escape_paren( str )
|
|
129
157
|
end
|
|
130
158
|
|
|
131
159
|
def uri_escape( str )
|
|
@@ -137,7 +165,11 @@ module Mail
|
|
|
137
165
|
end
|
|
138
166
|
|
|
139
167
|
def uri_parser
|
|
140
|
-
@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
|
|
141
173
|
end
|
|
142
174
|
|
|
143
175
|
# Matches two objects with their to_s values case insensitively
|
|
@@ -178,9 +210,9 @@ module Mail
|
|
|
178
210
|
# Example:
|
|
179
211
|
#
|
|
180
212
|
# string = :resent_from_field
|
|
181
|
-
# dasherize
|
|
213
|
+
# dasherize( string ) #=> 'resent-from-field'
|
|
182
214
|
def dasherize( str )
|
|
183
|
-
str.to_s.tr(UNDERSCORE, HYPHEN)
|
|
215
|
+
str.to_s.tr(Constants::UNDERSCORE, Constants::HYPHEN)
|
|
184
216
|
end
|
|
185
217
|
|
|
186
218
|
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
|
|
@@ -191,89 +223,344 @@ module Mail
|
|
|
191
223
|
# string = :resent_from_field
|
|
192
224
|
# underscoreize ( string ) #=> 'resent_from_field'
|
|
193
225
|
def underscoreize( str )
|
|
194
|
-
str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
|
|
226
|
+
str.to_s.downcase.tr(Constants::HYPHEN, Constants::UNDERSCORE)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def map_lines( str, &block )
|
|
230
|
+
str.each_line.map(&block)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def map_with_index( enum, &block )
|
|
234
|
+
enum.each_with_index.map(&block)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def self.binary_unsafe_to_lf(string) #:nodoc:
|
|
238
|
+
string.gsub(/\r\n|\r/, Constants::LF)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
TO_CRLF_REGEX =
|
|
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)")
|
|
246
|
+
|
|
247
|
+
def self.binary_unsafe_to_crlf(string) #:nodoc:
|
|
248
|
+
string.gsub(TO_CRLF_REGEX, Constants::CRLF)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def self.safe_for_line_ending_conversion?(string) #:nodoc:
|
|
252
|
+
if string.encoding == Encoding::BINARY
|
|
253
|
+
string.ascii_only?
|
|
254
|
+
else
|
|
255
|
+
string.valid_encoding?
|
|
256
|
+
end
|
|
195
257
|
end
|
|
196
258
|
|
|
197
|
-
|
|
259
|
+
# Convert line endings to \n unless the string is binary. Used for
|
|
260
|
+
# sendmail delivery and for decoding 8bit Content-Transfer-Encoding.
|
|
261
|
+
def self.to_lf(string)
|
|
262
|
+
string = string.to_s
|
|
263
|
+
if safe_for_line_ending_conversion? string
|
|
264
|
+
binary_unsafe_to_lf string
|
|
265
|
+
else
|
|
266
|
+
string
|
|
267
|
+
end
|
|
268
|
+
end
|
|
198
269
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
270
|
+
# Convert line endings to \r\n unless the string is binary. Used for
|
|
271
|
+
# encoding 8bit and base64 Content-Transfer-Encoding and for convenience
|
|
272
|
+
# when parsing emails with \n line endings instead of the required \r\n.
|
|
273
|
+
def self.to_crlf(string)
|
|
274
|
+
string = string.to_s
|
|
275
|
+
if safe_for_line_ending_conversion? string
|
|
276
|
+
binary_unsafe_to_crlf string
|
|
277
|
+
else
|
|
278
|
+
string
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Returns true if the object is considered blank.
|
|
283
|
+
# A blank includes things like '', ' ', nil,
|
|
284
|
+
# and arrays and hashes that have nothing in them.
|
|
285
|
+
#
|
|
286
|
+
# This logic is mostly shared with ActiveSupport's blank?
|
|
287
|
+
def blank?(value)
|
|
288
|
+
if value.kind_of?(NilClass)
|
|
289
|
+
true
|
|
290
|
+
elsif value.kind_of?(String)
|
|
291
|
+
value !~ /\S/
|
|
292
|
+
else
|
|
293
|
+
value.respond_to?(:empty?) ? value.empty? : !value
|
|
294
|
+
end
|
|
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))
|
|
203
308
|
end
|
|
204
|
-
results
|
|
205
309
|
end
|
|
310
|
+
end
|
|
206
311
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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))
|
|
211
319
|
end
|
|
212
|
-
results
|
|
213
320
|
end
|
|
214
321
|
|
|
215
|
-
|
|
322
|
+
private
|
|
216
323
|
|
|
217
|
-
def
|
|
218
|
-
|
|
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)
|
|
219
334
|
end
|
|
335
|
+
end
|
|
220
336
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
337
|
+
class << self
|
|
338
|
+
attr_accessor :charset_encoder
|
|
339
|
+
end
|
|
340
|
+
self.charset_encoder = BestEffortCharsetEncoder.new
|
|
224
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 }
|
|
225
347
|
end
|
|
226
348
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
# Using String#encode is better performing than Regexp
|
|
349
|
+
def Utilities.paren( str )
|
|
350
|
+
str = ::Mail::Utilities.unparen( str )
|
|
351
|
+
str = escape_paren( str )
|
|
352
|
+
'(' + str + ')'
|
|
353
|
+
end
|
|
233
354
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
355
|
+
def Utilities.escape_bracket( str )
|
|
356
|
+
re = /(?<!\\)([\<\>])/ # Only match unescaped brackets
|
|
357
|
+
str.gsub(re) { |s| '\\' + s }
|
|
358
|
+
end
|
|
237
359
|
|
|
238
|
-
|
|
239
|
-
|
|
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, "=")
|
|
240
369
|
end
|
|
370
|
+
str.unpack1( 'm' )
|
|
371
|
+
end
|
|
241
372
|
|
|
242
|
-
|
|
373
|
+
def Utilities.encode_base64(str)
|
|
374
|
+
[str].pack( 'm' )
|
|
375
|
+
end
|
|
243
376
|
|
|
244
|
-
|
|
245
|
-
|
|
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
|
|
246
410
|
end
|
|
411
|
+
end
|
|
247
412
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
+
match = str.match(/\=\?(.+)?\?[Bb]\?(.*)\?\=/m)
|
|
420
|
+
if match
|
|
421
|
+
charset = match[1]
|
|
422
|
+
str = Utilities.decode_base64(match[2])
|
|
423
|
+
str = charset_encoder.encode(str, charset)
|
|
255
424
|
end
|
|
425
|
+
transcode_to_scrubbed_utf8(str)
|
|
426
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError, Encoding::InvalidByteSequenceError
|
|
427
|
+
warn "WARNING: Encoding conversion failed #{$!}"
|
|
428
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
|
429
|
+
end
|
|
256
430
|
|
|
257
|
-
|
|
258
|
-
|
|
431
|
+
def Utilities.q_value_encode(str, encoding = nil)
|
|
432
|
+
encoding = str.encoding.to_s
|
|
433
|
+
[Encodings::QuotedPrintable.encode(str), encoding]
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def Utilities.q_value_decode(str)
|
|
437
|
+
match = str.match(/\=\?(.+)?\?[Qq]\?(.*)\?\=/m)
|
|
438
|
+
if match
|
|
439
|
+
charset = match[1]
|
|
440
|
+
string = match[2].gsub(/_/, '=20')
|
|
441
|
+
# Remove trailing = if it exists in a Q encoding
|
|
442
|
+
string = string.sub(/\=$/, '')
|
|
443
|
+
str = Encodings::QuotedPrintable.decode(string)
|
|
444
|
+
str = charset_encoder.encode(str, charset)
|
|
445
|
+
# We assume that binary strings hold utf-8 directly to work around
|
|
446
|
+
# jruby/jruby#829 which subtly changes String#encode semantics.
|
|
447
|
+
str.force_encoding(Encoding::UTF_8) if str.encoding == Encoding::ASCII_8BIT
|
|
259
448
|
end
|
|
449
|
+
transcode_to_scrubbed_utf8(str)
|
|
450
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
|
451
|
+
warn "WARNING: Encoding conversion failed #{$!}"
|
|
452
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
|
453
|
+
end
|
|
260
454
|
|
|
455
|
+
def Utilities.param_decode(str, encoding)
|
|
456
|
+
str = uri_parser.unescape(str)
|
|
457
|
+
str = charset_encoder.encode(str, encoding) if encoding
|
|
458
|
+
transcode_to_scrubbed_utf8(str)
|
|
459
|
+
rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
|
|
460
|
+
warn "WARNING: Encoding conversion failed #{$!}"
|
|
461
|
+
str.dup.force_encoding(Encoding::UTF_8)
|
|
261
462
|
end
|
|
262
463
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
464
|
+
def Utilities.param_encode(str)
|
|
465
|
+
encoding = str.encoding.to_s.downcase
|
|
466
|
+
language = Configuration.instance.param_encode_language
|
|
467
|
+
"#{encoding}'#{language}'#{uri_parser.escape(str)}"
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def Utilities.uri_parser
|
|
471
|
+
defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# Pick a Ruby encoding corresponding to the message charset. Most
|
|
475
|
+
# charsets have a Ruby encoding, but some need manual aliasing here.
|
|
266
476
|
#
|
|
267
|
-
#
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
477
|
+
# TODO: add this as a test somewhere:
|
|
478
|
+
# Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
|
|
479
|
+
# Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
|
|
480
|
+
def Utilities.pick_encoding(charset)
|
|
481
|
+
charset = charset.to_s
|
|
482
|
+
encoding = case charset.downcase
|
|
483
|
+
|
|
484
|
+
# ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
|
|
485
|
+
when /^iso[-_]?8859-(\d+)(-i)?$/
|
|
486
|
+
"ISO-8859-#{$1}"
|
|
487
|
+
|
|
488
|
+
# ISO-8859-15, ISO-2022-JP and alike
|
|
489
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})$/
|
|
490
|
+
"ISO-#{$1}-#{$2}"
|
|
491
|
+
|
|
492
|
+
# "ISO-2022-JP-KDDI" and alike
|
|
493
|
+
when /^iso[-_]?(\d{4})-?(\w{1,2})-?(\w*)$/
|
|
494
|
+
"ISO-#{$1}-#{$2}-#{$3}"
|
|
495
|
+
|
|
496
|
+
# UTF-8, UTF-32BE and alike
|
|
497
|
+
when /^utf[\-_]?(\d{1,2})?(\w{1,2})$/
|
|
498
|
+
"UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
|
|
499
|
+
|
|
500
|
+
# Windows-1252 and alike
|
|
501
|
+
when /^windows-?(.*)$/
|
|
502
|
+
"Windows-#{$1}"
|
|
503
|
+
|
|
504
|
+
when '8bit'
|
|
505
|
+
Encoding::ASCII_8BIT
|
|
506
|
+
|
|
507
|
+
# alternatives/misspellings of us-ascii seen in the wild
|
|
508
|
+
when /^iso[-_]?646(-us)?$/, 'us=ascii'
|
|
509
|
+
Encoding::ASCII
|
|
510
|
+
|
|
511
|
+
# Microsoft-specific alias for MACROMAN
|
|
512
|
+
when 'macintosh'
|
|
513
|
+
Encoding::MACROMAN
|
|
514
|
+
|
|
515
|
+
# Microsoft-specific alias for CP949 (Korean)
|
|
516
|
+
when 'ks_c_5601-1987'
|
|
517
|
+
Encoding::CP949
|
|
518
|
+
|
|
519
|
+
# Wrongly written Shift_JIS (Japanese)
|
|
520
|
+
when 'shift-jis'
|
|
521
|
+
Encoding::Shift_JIS
|
|
522
|
+
|
|
523
|
+
# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
|
|
524
|
+
when 'gb2312'
|
|
525
|
+
Encoding::GB18030
|
|
526
|
+
|
|
527
|
+
when 'cp-850'
|
|
528
|
+
Encoding::CP850
|
|
529
|
+
|
|
530
|
+
when 'latin2'
|
|
531
|
+
Encoding::ISO_8859_2
|
|
532
|
+
|
|
273
533
|
else
|
|
274
|
-
|
|
534
|
+
charset
|
|
275
535
|
end
|
|
536
|
+
|
|
537
|
+
convert_to_encoding(encoding)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def Utilities.string_byteslice(str, *args)
|
|
541
|
+
str.byteslice(*args)
|
|
276
542
|
end
|
|
277
543
|
|
|
544
|
+
class << self
|
|
545
|
+
private
|
|
546
|
+
|
|
547
|
+
def convert_to_encoding(encoding)
|
|
548
|
+
if encoding.is_a?(Encoding)
|
|
549
|
+
encoding
|
|
550
|
+
else
|
|
551
|
+
# Fall back to ASCII for charsets that Ruby doesn't recognize
|
|
552
|
+
begin
|
|
553
|
+
Encoding.find(encoding)
|
|
554
|
+
rescue ArgumentError
|
|
555
|
+
Encoding::BINARY
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def transcode_to_scrubbed_utf8(str)
|
|
561
|
+
decoded = str.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => "�")
|
|
562
|
+
decoded.valid_encoding? ? decoded : decoded.encode(Encoding::UTF_16LE, :invalid => :replace, :replace => "�").encode(Encoding::UTF_8)
|
|
563
|
+
end
|
|
564
|
+
end
|
|
278
565
|
end
|
|
279
566
|
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
|
@@ -7,44 +7,13 @@ module Mail # :doc:
|
|
|
7
7
|
|
|
8
8
|
require 'uri'
|
|
9
9
|
require 'net/smtp'
|
|
10
|
-
|
|
11
|
-
begin
|
|
12
|
-
# Use mime/types/columnar if available, for reduced memory usage
|
|
13
|
-
require 'mime/types/columnar'
|
|
14
|
-
rescue LoadError
|
|
15
|
-
require 'mime/types'
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
if RUBY_VERSION <= '1.8.6'
|
|
19
|
-
begin
|
|
20
|
-
require 'tlsmail'
|
|
21
|
-
rescue LoadError
|
|
22
|
-
raise "You need to install tlsmail if you are using ruby <= 1.8.6"
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
if RUBY_VERSION >= "1.9.0"
|
|
27
|
-
require 'mail/version_specific/ruby_1_9'
|
|
28
|
-
RubyVer = Ruby19
|
|
29
|
-
else
|
|
30
|
-
require 'mail/version_specific/ruby_1_8'
|
|
31
|
-
RubyVer = Ruby18
|
|
32
|
-
end
|
|
10
|
+
require 'mini_mime'
|
|
33
11
|
|
|
34
12
|
require 'mail/version'
|
|
35
13
|
|
|
36
|
-
require 'mail/core_extensions/string'
|
|
37
|
-
require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
|
|
38
14
|
require 'mail/indifferent_hash'
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
if defined?(ActiveSupport)
|
|
42
|
-
require 'active_support/inflector'
|
|
43
|
-
else
|
|
44
|
-
require 'mail/core_extensions/string/access'
|
|
45
|
-
require 'mail/core_extensions/string/multibyte'
|
|
46
|
-
require 'mail/multibyte'
|
|
47
|
-
end
|
|
16
|
+
require 'mail/multibyte'
|
|
48
17
|
|
|
49
18
|
require 'mail/constants'
|
|
50
19
|
require 'mail/utilities'
|
|
@@ -79,9 +48,7 @@ module Mail # :doc:
|
|
|
79
48
|
require 'mail/field'
|
|
80
49
|
require 'mail/field_list'
|
|
81
50
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
register_autoload :Parsers, "mail/parsers"
|
|
51
|
+
register_autoload :Envelope, 'mail/envelope'
|
|
85
52
|
|
|
86
53
|
# Autoload header field elements and transfer encodings.
|
|
87
54
|
require 'mail/elements'
|
|
@@ -91,7 +58,7 @@ module Mail # :doc:
|
|
|
91
58
|
require 'mail/encodings/unix_to_unix'
|
|
92
59
|
|
|
93
60
|
require 'mail/matchers/has_sent_mail'
|
|
94
|
-
require 'mail/matchers/attachment_matchers
|
|
61
|
+
require 'mail/matchers/attachment_matchers'
|
|
95
62
|
|
|
96
63
|
# Finally... require all the Mail.methods
|
|
97
64
|
require 'mail/mail'
|