mail 2.7.1 → 2.8.0.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 +45 -28
- data/lib/mail/attachments_list.rb +2 -5
- data/lib/mail/body.rb +24 -47
- data/lib/mail/check_delivery_params.rb +21 -16
- 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.rb +30 -59
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +37 -53
- data/lib/mail/field_list.rb +60 -7
- data/lib/mail/fields/bcc_field.rb +34 -52
- data/lib/mail/fields/cc_field.rb +28 -49
- data/lib/mail/fields/comments_field.rb +27 -37
- data/lib/mail/fields/common_address_field.rb +170 -0
- data/lib/mail/fields/common_date_field.rb +58 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +42 -0
- data/lib/mail/fields/content_description_field.rb +7 -14
- data/lib/mail/fields/content_disposition_field.rb +13 -38
- data/lib/mail/fields/content_id_field.rb +24 -51
- data/lib/mail/fields/content_location_field.rb +11 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
- data/lib/mail/fields/content_type_field.rb +46 -71
- data/lib/mail/fields/date_field.rb +23 -51
- data/lib/mail/fields/from_field.rb +28 -49
- data/lib/mail/fields/in_reply_to_field.rb +38 -49
- data/lib/mail/fields/keywords_field.rb +18 -31
- data/lib/mail/fields/message_id_field.rb +25 -71
- data/lib/mail/fields/mime_version_field.rb +19 -30
- data/lib/mail/fields/named_structured_field.rb +11 -0
- data/lib/mail/fields/named_unstructured_field.rb +11 -0
- data/lib/mail/fields/optional_field.rb +5 -6
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
- data/lib/mail/fields/received_field.rb +43 -57
- data/lib/mail/fields/references_field.rb +35 -49
- data/lib/mail/fields/reply_to_field.rb +28 -49
- data/lib/mail/fields/resent_bcc_field.rb +28 -49
- data/lib/mail/fields/resent_cc_field.rb +28 -49
- data/lib/mail/fields/resent_date_field.rb +5 -29
- data/lib/mail/fields/resent_from_field.rb +28 -49
- data/lib/mail/fields/resent_message_id_field.rb +5 -29
- data/lib/mail/fields/resent_sender_field.rb +27 -56
- data/lib/mail/fields/resent_to_field.rb +28 -49
- data/lib/mail/fields/return_path_field.rb +50 -54
- data/lib/mail/fields/sender_field.rb +34 -55
- data/lib/mail/fields/structured_field.rb +3 -30
- data/lib/mail/fields/subject_field.rb +9 -11
- data/lib/mail/fields/to_field.rb +28 -49
- data/lib/mail/fields/unstructured_field.rb +16 -48
- data/lib/mail/header.rb +69 -110
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/message.rb +52 -66
- data/lib/mail/multibyte/chars.rb +8 -166
- 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 +11 -10
- 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 +25 -9
- data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -12
- data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
- data/lib/mail/network/retriever_methods/base.rb +8 -8
- data/lib/mail/network/retriever_methods/imap.rb +2 -2
- data/lib/mail/network/retriever_methods/pop3.rb +2 -2
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/parsers/address_lists_parser.rb +33070 -33064
- data/lib/mail/parsers/address_lists_parser.rl +7 -0
- data/lib/mail/parsers/content_disposition_parser.rb +833 -827
- data/lib/mail/parsers/content_disposition_parser.rl +7 -0
- data/lib/mail/parsers/content_location_parser.rb +770 -764
- data/lib/mail/parsers/content_location_parser.rl +7 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +474 -468
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +7 -0
- data/lib/mail/parsers/content_type_parser.rb +971 -965
- data/lib/mail/parsers/content_type_parser.rl +7 -0
- data/lib/mail/parsers/date_time_parser.rb +838 -832
- data/lib/mail/parsers/date_time_parser.rl +7 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3623 -3529
- data/lib/mail/parsers/envelope_from_parser.rl +7 -0
- data/lib/mail/parsers/message_ids_parser.rb +5107 -2800
- data/lib/mail/parsers/message_ids_parser.rl +12 -1
- data/lib/mail/parsers/mime_version_parser.rb +463 -457
- data/lib/mail/parsers/mime_version_parser.rl +7 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +836 -830
- data/lib/mail/parsers/phrase_lists_parser.rl +8 -1
- data/lib/mail/parsers/received_parser.rb +8688 -8682
- data/lib/mail/parsers/received_parser.rl +7 -0
- data/lib/mail/parsers/rfc5322.rl +28 -13
- data/lib/mail/parsers.rb +11 -17
- data/lib/mail/part.rb +5 -9
- data/lib/mail/parts_list.rb +57 -0
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +307 -69
- data/lib/mail/version.rb +3 -3
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +3 -20
- metadata +72 -18
- 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/multibyte/chars.rb
CHANGED
@@ -38,16 +38,10 @@ module Mail #:nodoc:
|
|
38
38
|
alias to_s wrapped_string
|
39
39
|
alias to_str wrapped_string
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
46
|
-
end
|
47
|
-
else
|
48
|
-
def initialize(string) #:nodoc:
|
49
|
-
@wrapped_string = string
|
50
|
-
end
|
41
|
+
# Creates a new Chars instance by wrapping _string_.
|
42
|
+
def initialize(string)
|
43
|
+
@wrapped_string = string.dup
|
44
|
+
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
51
45
|
end
|
52
46
|
|
53
47
|
# Forward all undefined methods to the wrapped string.
|
@@ -72,15 +66,6 @@ module Mail #:nodoc:
|
|
72
66
|
true
|
73
67
|
end
|
74
68
|
|
75
|
-
# Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
|
76
|
-
def self.consumes?(string)
|
77
|
-
# Unpack is a little bit faster than regular expressions.
|
78
|
-
string.unpack('U*')
|
79
|
-
true
|
80
|
-
rescue ArgumentError
|
81
|
-
false
|
82
|
-
end
|
83
|
-
|
84
69
|
include Comparable
|
85
70
|
|
86
71
|
# Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
|
@@ -94,151 +79,8 @@ module Mail #:nodoc:
|
|
94
79
|
@wrapped_string <=> other.to_s
|
95
80
|
end
|
96
81
|
|
97
|
-
|
98
|
-
|
99
|
-
# +false+ otherwise.
|
100
|
-
def self.wants?(string)
|
101
|
-
$KCODE == 'UTF8' && consumes?(string)
|
102
|
-
end
|
103
|
-
|
104
|
-
# Returns a new Chars object containing the _other_ object concatenated to the string.
|
105
|
-
#
|
106
|
-
# Example:
|
107
|
-
# (Mail::Multibyte.mb_chars('Café') + ' périferôl').to_s # => "Café périferôl"
|
108
|
-
def +(other)
|
109
|
-
chars(@wrapped_string + other)
|
110
|
-
end
|
111
|
-
|
112
|
-
# Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
|
113
|
-
#
|
114
|
-
# Example:
|
115
|
-
# Mail::Multibyte.mb_chars('Café périferôl') =~ /ô/ # => 12
|
116
|
-
def =~(other)
|
117
|
-
translate_offset(@wrapped_string =~ other)
|
118
|
-
end
|
119
|
-
|
120
|
-
# Inserts the passed string at specified codepoint offsets.
|
121
|
-
#
|
122
|
-
# Example:
|
123
|
-
# Mail::Multibyte.mb_chars('Café').insert(4, ' périferôl').to_s # => "Café périferôl"
|
124
|
-
def insert(offset, fragment)
|
125
|
-
unpacked = Unicode.u_unpack(@wrapped_string)
|
126
|
-
unless offset > unpacked.length
|
127
|
-
@wrapped_string.replace(
|
128
|
-
Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
|
129
|
-
)
|
130
|
-
else
|
131
|
-
raise IndexError, "index #{offset} out of string"
|
132
|
-
end
|
133
|
-
self
|
134
|
-
end
|
135
|
-
|
136
|
-
# Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
|
137
|
-
#
|
138
|
-
# Example:
|
139
|
-
# Mail::Multibyte.mb_chars('Café').include?('é') # => true
|
140
|
-
def include?(other)
|
141
|
-
# We have to redefine this method because Enumerable defines it.
|
142
|
-
@wrapped_string.include?(other)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
|
146
|
-
#
|
147
|
-
# Example:
|
148
|
-
# Mail::Multibyte.mb_chars('Café périferôl').index('ô') # => 12
|
149
|
-
# Mail::Multibyte.mb_chars('Café périferôl').index(/\w/u) # => 0
|
150
|
-
def index(needle, offset=0)
|
151
|
-
wrapped_offset = first(offset).wrapped_string.length
|
152
|
-
index = @wrapped_string.index(needle, wrapped_offset)
|
153
|
-
index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
|
154
|
-
end
|
155
|
-
|
156
|
-
# Returns the position _needle_ in the string, counting in
|
157
|
-
# codepoints, searching backward from _offset_ or the end of the
|
158
|
-
# string. Returns +nil+ if _needle_ isn't found.
|
159
|
-
#
|
160
|
-
# Example:
|
161
|
-
# Mail::Multibyte.mb_chars('Café périferôl').rindex('é') # => 6
|
162
|
-
# Mail::Multibyte.mb_chars('Café périferôl').rindex(/\w/u) # => 13
|
163
|
-
def rindex(needle, offset=nil)
|
164
|
-
offset ||= length
|
165
|
-
wrapped_offset = first(offset).wrapped_string.length
|
166
|
-
index = @wrapped_string.rindex(needle, wrapped_offset)
|
167
|
-
index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
|
168
|
-
end
|
169
|
-
|
170
|
-
# Returns the number of codepoints in the string
|
171
|
-
def size
|
172
|
-
Unicode.u_unpack(@wrapped_string).size
|
173
|
-
end
|
174
|
-
alias_method :length, :size
|
175
|
-
|
176
|
-
# Strips entire range of Unicode whitespace from the right of the string.
|
177
|
-
def rstrip
|
178
|
-
chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
|
179
|
-
end
|
180
|
-
|
181
|
-
# Strips entire range of Unicode whitespace from the left of the string.
|
182
|
-
def lstrip
|
183
|
-
chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
|
184
|
-
end
|
185
|
-
|
186
|
-
# Strips entire range of Unicode whitespace from the right and left of the string.
|
187
|
-
def strip
|
188
|
-
rstrip.lstrip
|
189
|
-
end
|
190
|
-
|
191
|
-
# Returns the codepoint of the first character in the string.
|
192
|
-
#
|
193
|
-
# Example:
|
194
|
-
# Mail::Multibyte.mb_chars('こんにちは').ord # => 12371
|
195
|
-
def ord
|
196
|
-
Unicode.u_unpack(@wrapped_string)[0]
|
197
|
-
end
|
198
|
-
|
199
|
-
# Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
|
200
|
-
#
|
201
|
-
# Example:
|
202
|
-
#
|
203
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
204
|
-
# # => " ¾ cup"
|
205
|
-
#
|
206
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
207
|
-
# # => " ¾ cup"
|
208
|
-
def rjust(integer, padstr=' ')
|
209
|
-
justify(integer, :right, padstr)
|
210
|
-
end
|
211
|
-
|
212
|
-
# Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
|
213
|
-
#
|
214
|
-
# Example:
|
215
|
-
#
|
216
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
217
|
-
# # => "¾ cup "
|
218
|
-
#
|
219
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
220
|
-
# # => "¾ cup "
|
221
|
-
def ljust(integer, padstr=' ')
|
222
|
-
justify(integer, :left, padstr)
|
223
|
-
end
|
224
|
-
|
225
|
-
# Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
|
226
|
-
#
|
227
|
-
# Example:
|
228
|
-
#
|
229
|
-
# Mail::Multibyte.mb_chars("¾ cup").center(8).to_s
|
230
|
-
# # => " ¾ cup "
|
231
|
-
#
|
232
|
-
# Mail::Multibyte.mb_chars("¾ cup").center(8, " ").to_s # Use non-breaking whitespace
|
233
|
-
# # => " ¾ cup "
|
234
|
-
def center(integer, padstr=' ')
|
235
|
-
justify(integer, :center, padstr)
|
236
|
-
end
|
237
|
-
|
238
|
-
else
|
239
|
-
def =~(other)
|
240
|
-
@wrapped_string =~ other
|
241
|
-
end
|
82
|
+
def =~(other)
|
83
|
+
@wrapped_string =~ other
|
242
84
|
end
|
243
85
|
|
244
86
|
# Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
|
@@ -331,7 +173,7 @@ module Mail #:nodoc:
|
|
331
173
|
#
|
332
174
|
# Example:
|
333
175
|
# s = 'こんにちは'
|
334
|
-
# s.mb_chars.limit(7) # => "
|
176
|
+
# s.mb_chars.limit(7) # => "こん"
|
335
177
|
def limit(limit)
|
336
178
|
slice(0...translate_offset(limit))
|
337
179
|
end
|
@@ -419,7 +261,7 @@ module Mail #:nodoc:
|
|
419
261
|
# exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
|
420
262
|
if public_method_defined?(method)
|
421
263
|
define_method("#{method}!") do |*args|
|
422
|
-
@wrapped_string = send(
|
264
|
+
@wrapped_string = send(method, *args).to_s
|
423
265
|
self
|
424
266
|
end
|
425
267
|
end
|
data/lib/mail/multibyte/utils.rb
CHANGED
@@ -3,59 +3,42 @@
|
|
3
3
|
|
4
4
|
module Mail #:nodoc:
|
5
5
|
module Multibyte #:nodoc:
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
VALID_CHARACTER[Encoding.default_external.to_s]
|
10
|
-
end
|
11
|
-
else
|
12
|
-
def self.valid_character
|
13
|
-
case $KCODE
|
14
|
-
when 'UTF8'
|
15
|
-
VALID_CHARACTER['UTF-8']
|
16
|
-
when 'SJIS'
|
17
|
-
VALID_CHARACTER['Shift_JIS']
|
18
|
-
end
|
19
|
-
end
|
6
|
+
# Returns a regular expression that matches valid characters in the current encoding
|
7
|
+
def self.valid_character
|
8
|
+
VALID_CHARACTER[Encoding.default_external.to_s]
|
20
9
|
end
|
21
10
|
|
22
|
-
if
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
string.split(//).all? { |c| expression =~ c }
|
32
|
-
else
|
33
|
-
true
|
34
|
-
end
|
11
|
+
# Returns true if string has valid utf-8 encoding
|
12
|
+
def self.is_utf8?(string)
|
13
|
+
case string.encoding
|
14
|
+
when Encoding::UTF_8
|
15
|
+
verify(string)
|
16
|
+
when Encoding::ASCII_8BIT, Encoding::US_ASCII
|
17
|
+
verify(to_utf8(string))
|
18
|
+
else
|
19
|
+
false
|
35
20
|
end
|
36
21
|
end
|
37
22
|
|
23
|
+
# Verifies the encoding of a string
|
24
|
+
def self.verify(string)
|
25
|
+
string.valid_encoding?
|
26
|
+
end
|
27
|
+
|
38
28
|
# Verifies the encoding of the string and raises an exception when it's not valid
|
39
29
|
def self.verify!(string)
|
40
30
|
raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
|
41
31
|
end
|
42
32
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
if expression = valid_character
|
53
|
-
# Splits the string on character boundaries, which are determined based on $KCODE.
|
54
|
-
string.split(//).grep(expression).join
|
55
|
-
else
|
56
|
-
string
|
57
|
-
end
|
58
|
-
end
|
33
|
+
# Removes all invalid characters from the string.
|
34
|
+
#
|
35
|
+
# Note: this method is a no-op in Ruby 1.9
|
36
|
+
def self.clean(string)
|
37
|
+
string
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.to_utf8(string)
|
41
|
+
string.dup.force_encoding(Encoding::UTF_8)
|
59
42
|
end
|
60
43
|
end
|
61
44
|
end
|
data/lib/mail/multibyte.rb
CHANGED
@@ -19,7 +19,6 @@ module Mail #:nodoc:
|
|
19
19
|
|
20
20
|
self.proxy_class = Mail::Multibyte::Chars
|
21
21
|
|
22
|
-
if RUBY_VERSION >= "1.9"
|
23
22
|
# == Multibyte proxy
|
24
23
|
#
|
25
24
|
# +mb_chars+ is a multibyte safe proxy for string methods.
|
@@ -54,21 +53,12 @@ module Mail #:nodoc:
|
|
54
53
|
# For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
|
55
54
|
# information about how to change the default Multibyte behaviour see Mail::Multibyte.
|
56
55
|
def self.mb_chars(str)
|
57
|
-
if
|
56
|
+
if is_utf8?(str)
|
58
57
|
proxy_class.new(str)
|
59
58
|
else
|
60
59
|
str
|
61
60
|
end
|
62
61
|
end
|
63
|
-
else
|
64
|
-
def self.mb_chars(str)
|
65
|
-
if proxy_class.wants?(str)
|
66
|
-
proxy_class.new(str)
|
67
|
-
else
|
68
|
-
str
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
62
|
|
73
63
|
# Regular expressions that describe valid byte sequences for a character
|
74
64
|
VALID_CHARACTER = {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module Mail
|
3
2
|
|
3
|
+
module Mail
|
4
4
|
# A delivery method implementation which sends via exim.
|
5
5
|
#
|
6
6
|
# To use this, first find out where the exim binary is on your computer,
|
@@ -39,11 +39,12 @@ module Mail
|
|
39
39
|
class Exim < Sendmail
|
40
40
|
DEFAULTS = {
|
41
41
|
:location => '/usr/sbin/exim',
|
42
|
-
:arguments =>
|
42
|
+
:arguments => %w[ -i -t ]
|
43
43
|
}
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
# Uses -t option to extract recipients from the message.
|
46
|
+
def destinations_for(envelope)
|
47
|
+
nil
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'mail/
|
2
|
+
require 'mail/smtp_envelope'
|
3
3
|
|
4
4
|
module Mail
|
5
5
|
# FileDelivery class delivers emails into multiple files based on the destination
|
@@ -13,20 +13,16 @@ module Mail
|
|
13
13
|
# Make sure the path you specify with :location is writable by the Ruby process
|
14
14
|
# running Mail.
|
15
15
|
class FileDelivery
|
16
|
-
|
17
|
-
require 'fileutils'
|
18
|
-
else
|
19
|
-
require 'ftools'
|
20
|
-
end
|
16
|
+
require 'fileutils'
|
21
17
|
|
22
18
|
attr_accessor :settings
|
23
19
|
|
24
20
|
def initialize(values)
|
25
|
-
self.settings = { :location => './mails' }.merge!(values)
|
21
|
+
self.settings = { :location => './mails', :extension => '' }.merge!(values)
|
26
22
|
end
|
27
23
|
|
28
24
|
def deliver!(mail)
|
29
|
-
Mail::
|
25
|
+
envelope = Mail::SmtpEnvelope.new(mail)
|
30
26
|
|
31
27
|
if ::File.respond_to?(:makedirs)
|
32
28
|
::File.makedirs settings[:location]
|
@@ -34,8 +30,13 @@ module Mail
|
|
34
30
|
::FileUtils.mkdir_p settings[:location]
|
35
31
|
end
|
36
32
|
|
37
|
-
|
38
|
-
::File.
|
33
|
+
envelope.to.uniq.each do |to|
|
34
|
+
path = ::File.join(settings[:location], File.basename(to.to_s+settings[:extension]))
|
35
|
+
|
36
|
+
::File.open(path, 'a') do |f|
|
37
|
+
f.write envelope.message
|
38
|
+
f.write "\r\n\r\n"
|
39
|
+
end
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -1,9 +1,7 @@
|
|
1
|
-
require 'mail/
|
1
|
+
require 'mail/smtp_envelope'
|
2
2
|
|
3
3
|
module Mail
|
4
4
|
class LoggerDelivery
|
5
|
-
include Mail::CheckDeliveryParams
|
6
|
-
|
7
5
|
attr_reader :logger, :severity, :settings
|
8
6
|
|
9
7
|
def initialize(settings)
|
@@ -13,8 +11,7 @@ module Mail
|
|
13
11
|
end
|
14
12
|
|
15
13
|
def deliver!(mail)
|
16
|
-
Mail::
|
17
|
-
logger.log(severity) { mail.encoded }
|
14
|
+
logger.log(severity) { Mail::SmtpEnvelope.new(mail).message }
|
18
15
|
end
|
19
16
|
|
20
17
|
private
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'mail/
|
2
|
+
require 'mail/smtp_envelope'
|
3
3
|
|
4
4
|
module Mail
|
5
5
|
# A delivery method implementation which sends via sendmail.
|
@@ -40,56 +40,48 @@ module Mail
|
|
40
40
|
class Sendmail
|
41
41
|
DEFAULTS = {
|
42
42
|
:location => '/usr/sbin/sendmail',
|
43
|
-
:arguments =>
|
43
|
+
:arguments => %w[ -i ]
|
44
44
|
}
|
45
45
|
|
46
46
|
attr_accessor :settings
|
47
47
|
|
48
|
+
class DeliveryError < StandardError
|
49
|
+
end
|
50
|
+
|
48
51
|
def initialize(values)
|
49
52
|
self.settings = self.class::DEFAULTS.merge(values)
|
53
|
+
raise ArgumentError, ":arguments expected to be an Array of individual string args" if settings[:arguments].is_a?(String)
|
54
|
+
end
|
55
|
+
|
56
|
+
def destinations_for(envelope)
|
57
|
+
envelope.to
|
50
58
|
end
|
51
59
|
|
52
60
|
def deliver!(mail)
|
53
|
-
|
61
|
+
envelope = Mail::SmtpEnvelope.new(mail)
|
54
62
|
|
55
|
-
|
56
|
-
|
63
|
+
command = [settings[:location]]
|
64
|
+
command.concat Array(settings[:arguments])
|
65
|
+
command.concat [ '-f', envelope.from ] if envelope.from
|
57
66
|
|
58
|
-
|
59
|
-
|
60
|
-
|
67
|
+
if destinations = destinations_for(envelope)
|
68
|
+
command.push '--'
|
69
|
+
command.concat destinations
|
70
|
+
end
|
61
71
|
|
62
|
-
|
63
|
-
|
64
|
-
io.puts ::Mail::Utilities.binary_unsafe_to_lf(encoded_message)
|
72
|
+
popen(command) do |io|
|
73
|
+
io.puts ::Mail::Utilities.binary_unsafe_to_lf(envelope.message)
|
65
74
|
io.flush
|
66
75
|
end
|
67
76
|
end
|
68
77
|
|
69
|
-
|
70
|
-
def
|
71
|
-
IO.popen
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
78
|
+
private
|
79
|
+
def popen(command, &block)
|
80
|
+
IO.popen(command, 'w+', :err => :out, &block).tap do
|
81
|
+
if $?.exitstatus != 0
|
82
|
+
raise DeliveryError, "Delivery failed with exitstatus #{$?.exitstatus}: #{command.inspect}"
|
83
|
+
end
|
84
|
+
end
|
76
85
|
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# The following is an adaptation of ruby 1.9.2's shellwords.rb file,
|
80
|
-
# with the following modifications:
|
81
|
-
#
|
82
|
-
# - Wraps in double quotes
|
83
|
-
# - Allows '+' to accept email addresses with them
|
84
|
-
# - Allows '~' as it is not unescaped in double quotes
|
85
|
-
def self.shellquote(address)
|
86
|
-
# Process as a single byte sequence because not all shell
|
87
|
-
# implementations are multibyte aware.
|
88
|
-
#
|
89
|
-
# A LF cannot be escaped with a backslash because a backslash + LF
|
90
|
-
# combo is regarded as line continuation and simply ignored. Strip it.
|
91
|
-
escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@~])/n, "\\\\\\1").gsub("\n", '')
|
92
|
-
%("#{escaped}")
|
93
|
-
end
|
94
86
|
end
|
95
87
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'mail/
|
2
|
+
require 'mail/smtp_envelope'
|
3
3
|
|
4
4
|
module Mail
|
5
5
|
# == Sending Email with SMTP
|
@@ -88,8 +88,8 @@ module Mail
|
|
88
88
|
:openssl_verify_mode => nil,
|
89
89
|
:ssl => nil,
|
90
90
|
:tls => nil,
|
91
|
-
:open_timeout =>
|
92
|
-
:read_timeout =>
|
91
|
+
:open_timeout => 5,
|
92
|
+
:read_timeout => 5
|
93
93
|
}
|
94
94
|
|
95
95
|
def initialize(values)
|
@@ -111,17 +111,33 @@ module Mail
|
|
111
111
|
|
112
112
|
def build_smtp_session
|
113
113
|
Net::SMTP.new(settings[:address], settings[:port]).tap do |smtp|
|
114
|
-
|
115
|
-
|
114
|
+
tls = settings[:tls] || settings[:ssl]
|
115
|
+
if !tls.nil?
|
116
|
+
case tls
|
117
|
+
when true
|
116
118
|
smtp.enable_tls(ssl_context)
|
119
|
+
when false
|
120
|
+
smtp.disable_tls
|
121
|
+
else
|
122
|
+
raise ArgumentError, "Unrecognized :tls value #{settings[:tls].inspect}; expected true, false, or nil"
|
117
123
|
end
|
118
|
-
elsif settings[:enable_starttls]
|
119
|
-
|
124
|
+
elsif settings.include?(:enable_starttls) && !settings[:enable_starttls].nil?
|
125
|
+
case settings[:enable_starttls]
|
126
|
+
when true
|
120
127
|
smtp.enable_starttls(ssl_context)
|
128
|
+
when false
|
129
|
+
smtp.disable_starttls
|
130
|
+
else
|
131
|
+
raise ArgumentError, "Unrecognized :enable_starttls value #{settings[:enable_starttls].inspect}; expected true, false, or nil"
|
121
132
|
end
|
122
|
-
elsif settings[:enable_starttls_auto]
|
123
|
-
|
133
|
+
elsif settings.include?(:enable_starttls_auto) && !settings[:enable_starttls_auto].nil?
|
134
|
+
case settings[:enable_starttls_auto]
|
135
|
+
when true
|
124
136
|
smtp.enable_starttls_auto(ssl_context)
|
137
|
+
when false
|
138
|
+
smtp.disable_starttls_auto
|
139
|
+
else
|
140
|
+
raise ArgumentError, "Unrecognized :enable_starttls_auto value #{settings[:enable_starttls_auto].inspect}; expected true, false, or nil"
|
125
141
|
end
|
126
142
|
end
|
127
143
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'mail/
|
2
|
+
require 'mail/smtp_envelope'
|
3
3
|
|
4
4
|
module Mail
|
5
5
|
# == Sending Email with SMTP
|
@@ -49,18 +49,9 @@ module Mail
|
|
49
49
|
# Send the message via SMTP.
|
50
50
|
# The from and to attributes are optional. If not set, they are retrieve from the Message.
|
51
51
|
def deliver!(mail)
|
52
|
-
|
53
|
-
|
54
|
-
response = smtp.sendmail(dot_stuff(message), smtp_from, smtp_to)
|
55
|
-
|
52
|
+
envelope = Mail::SmtpEnvelope.new(mail)
|
53
|
+
response = smtp.sendmail(envelope.message, envelope.from, envelope.to)
|
56
54
|
settings[:return_response] ? response : self
|
57
55
|
end
|
58
|
-
|
59
|
-
private
|
60
|
-
# This is Net::SMTP's job, but before Ruby 2.x it does not dot-stuff
|
61
|
-
# an unterminated last line: https://bugs.ruby-lang.org/issues/9627
|
62
|
-
def dot_stuff(message)
|
63
|
-
message.gsub(/(\r\n\.)(\r\n|$)/, '\1.\2')
|
64
|
-
end
|
65
56
|
end
|
66
57
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'mail/
|
2
|
+
require 'mail/smtp_envelope'
|
3
3
|
|
4
4
|
module Mail
|
5
5
|
# The TestMailer is a bare bones mailer that does nothing. It is useful
|
@@ -35,7 +35,9 @@ module Mail
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def deliver!(mail)
|
38
|
-
|
38
|
+
# Create the envelope to validate it
|
39
|
+
Mail::SmtpEnvelope.new(mail)
|
40
|
+
|
39
41
|
Mail::TestMailer.deliveries << mail
|
40
42
|
end
|
41
43
|
end
|
@@ -11,8 +11,8 @@ module Mail
|
|
11
11
|
# count: number of emails to retrieve. The default value is 1.
|
12
12
|
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
|
13
13
|
#
|
14
|
-
def first(options =
|
15
|
-
options
|
14
|
+
def first(options = nil, &block)
|
15
|
+
options = options ? Hash[options] : {}
|
16
16
|
options[:what] = :first
|
17
17
|
options[:count] ||= 1
|
18
18
|
find(options, &block)
|
@@ -24,8 +24,8 @@ module Mail
|
|
24
24
|
# count: number of emails to retrieve. The default value is 1.
|
25
25
|
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
|
26
26
|
#
|
27
|
-
def last(options =
|
28
|
-
options
|
27
|
+
def last(options = nil, &block)
|
28
|
+
options = options ? Hash[options] : {}
|
29
29
|
options[:what] = :last
|
30
30
|
options[:count] ||= 1
|
31
31
|
find(options, &block)
|
@@ -36,8 +36,8 @@ module Mail
|
|
36
36
|
# Possible options:
|
37
37
|
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
|
38
38
|
#
|
39
|
-
def all(options =
|
40
|
-
options
|
39
|
+
def all(options = nil, &block)
|
40
|
+
options = options ? Hash[options] : {}
|
41
41
|
options[:count] = :all
|
42
42
|
find(options, &block)
|
43
43
|
end
|
@@ -53,8 +53,8 @@ module Mail
|
|
53
53
|
# delete_after_find: flag for whether to delete each retreived email after find. Default
|
54
54
|
# is true. Call #find if you would like this to default to false.
|
55
55
|
#
|
56
|
-
def find_and_delete(options =
|
57
|
-
options
|
56
|
+
def find_and_delete(options = nil, &block)
|
57
|
+
options = options ? Hash[options] : {}
|
58
58
|
options[:delete_after_find] ||= true
|
59
59
|
find(options, &block)
|
60
60
|
end
|
@@ -70,7 +70,7 @@ module Mail
|
|
70
70
|
# The default is 'ALL'
|
71
71
|
# search_charset: charset to pass to IMAP server search. Omitted by default. Example: 'UTF-8' or 'ASCII'.
|
72
72
|
#
|
73
|
-
def find(options=
|
73
|
+
def find(options=nil, &block)
|
74
74
|
options = validate_options(options)
|
75
75
|
|
76
76
|
start do |imap|
|
@@ -142,7 +142,7 @@ module Mail
|
|
142
142
|
|
143
143
|
# Set default options
|
144
144
|
def validate_options(options)
|
145
|
-
options
|
145
|
+
options = options ? Hash[options] : {}
|
146
146
|
options[:mailbox] ||= 'INBOX'
|
147
147
|
options[:count] ||= 10
|
148
148
|
options[:order] ||= :asc
|