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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# frozen_string_literal: true
|
|
3
|
-
require 'mail/
|
|
3
|
+
require 'mail/utilities'
|
|
4
4
|
|
|
5
5
|
module Mail
|
|
6
6
|
# Provides access to an unstructured header field
|
|
@@ -15,58 +15,28 @@ module Mail
|
|
|
15
15
|
# field bodies are simply to be treated as a single line of characters
|
|
16
16
|
# with no further processing (except for header "folding" and
|
|
17
17
|
# "unfolding" as described in section 2.2.3).
|
|
18
|
-
class UnstructuredField
|
|
19
|
-
|
|
20
|
-
include Mail::CommonField
|
|
21
|
-
include Mail::Utilities
|
|
22
|
-
|
|
23
|
-
attr_accessor :charset
|
|
24
|
-
attr_reader :errors
|
|
25
|
-
|
|
18
|
+
class UnstructuredField < CommonField #:nodoc:
|
|
26
19
|
def initialize(name, value, charset = nil)
|
|
27
|
-
@errors = []
|
|
28
|
-
|
|
29
20
|
if value.is_a?(Array)
|
|
30
21
|
# Probably has arrived here from a failed parse of an AddressList Field
|
|
31
22
|
value = value.join(', ')
|
|
32
|
-
else
|
|
33
|
-
# Ensure we are dealing with a string
|
|
34
|
-
value = value.to_s
|
|
35
23
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
end
|
|
24
|
+
# Mark UTF-8 strings parsed from ASCII-8BIT
|
|
25
|
+
elsif value.respond_to?(:force_encoding) && value.encoding == Encoding::ASCII_8BIT
|
|
26
|
+
utf8 = value.dup.force_encoding(Encoding::UTF_8)
|
|
27
|
+
value = utf8 if utf8.valid_encoding?
|
|
41
28
|
end
|
|
42
29
|
|
|
43
|
-
|
|
44
|
-
self.charset = charset
|
|
45
|
-
else
|
|
30
|
+
charset ||=
|
|
46
31
|
if value.respond_to?(:encoding)
|
|
47
|
-
|
|
48
|
-
else
|
|
49
|
-
self.charset = $KCODE
|
|
32
|
+
value.encoding
|
|
50
33
|
end
|
|
51
|
-
end
|
|
52
|
-
self.name = name
|
|
53
|
-
self.value = value
|
|
54
|
-
self
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def encoded
|
|
58
|
-
do_encode
|
|
59
|
-
end
|
|
60
34
|
|
|
61
|
-
|
|
62
|
-
do_decode
|
|
35
|
+
super name, value.to_s, charset
|
|
63
36
|
end
|
|
64
37
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def parse # An unstructured field does not parse
|
|
38
|
+
# An unstructured field does not parse
|
|
39
|
+
def parse
|
|
70
40
|
self
|
|
71
41
|
end
|
|
72
42
|
|
|
@@ -196,7 +166,7 @@ module Mail
|
|
|
196
166
|
end
|
|
197
167
|
|
|
198
168
|
def encode(value)
|
|
199
|
-
value = [value].pack(CAPITAL_M).gsub(EQUAL_LF, EMPTY)
|
|
169
|
+
value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
|
|
200
170
|
value.gsub!(/"/, '=22')
|
|
201
171
|
value.gsub!(/\(/, '=28')
|
|
202
172
|
value.gsub!(/\)/, '=29')
|
|
@@ -207,16 +177,13 @@ module Mail
|
|
|
207
177
|
end
|
|
208
178
|
|
|
209
179
|
def encode_crlf(value)
|
|
210
|
-
value.gsub!(CR, CR_ENCODED)
|
|
211
|
-
value.gsub!(LF, LF_ENCODED)
|
|
180
|
+
value.gsub!(Constants::CR, Constants::CR_ENCODED)
|
|
181
|
+
value.gsub!(Constants::LF, Constants::LF_ENCODED)
|
|
212
182
|
value
|
|
213
183
|
end
|
|
214
184
|
|
|
215
185
|
def normalized_encoding
|
|
216
|
-
|
|
217
|
-
encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
|
|
218
|
-
encoding
|
|
186
|
+
charset.to_s.upcase.gsub('_', '-')
|
|
219
187
|
end
|
|
220
|
-
|
|
221
188
|
end
|
|
222
189
|
end
|
data/lib/mail/fields.rb
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module Mail
|
|
3
|
+
register_autoload :CommonField, 'mail/fields/common_field'
|
|
4
|
+
register_autoload :CommonAddressField, 'mail/fields/common_address_field'
|
|
5
|
+
register_autoload :CommonMessageIdField, 'mail/fields/common_message_id_field'
|
|
6
|
+
register_autoload :CommonDateField, 'mail/fields/common_date_field'
|
|
7
|
+
|
|
3
8
|
register_autoload :UnstructuredField, 'mail/fields/unstructured_field'
|
|
9
|
+
register_autoload :NamedUnstructuredField, 'mail/fields/named_unstructured_field'
|
|
4
10
|
register_autoload :StructuredField, 'mail/fields/structured_field'
|
|
5
11
|
register_autoload :OptionalField, 'mail/fields/optional_field'
|
|
6
12
|
|
|
@@ -19,6 +25,7 @@ module Mail
|
|
|
19
25
|
register_autoload :KeywordsField, 'mail/fields/keywords_field'
|
|
20
26
|
register_autoload :MessageIdField, 'mail/fields/message_id_field'
|
|
21
27
|
register_autoload :MimeVersionField, 'mail/fields/mime_version_field'
|
|
28
|
+
register_autoload :NamedStructuredField, 'mail/fields/named_structured_field'
|
|
22
29
|
register_autoload :ReceivedField, 'mail/fields/received_field'
|
|
23
30
|
register_autoload :ReferencesField, 'mail/fields/references_field'
|
|
24
31
|
register_autoload :ReplyToField, 'mail/fields/reply_to_field'
|
|
@@ -33,4 +40,6 @@ module Mail
|
|
|
33
40
|
register_autoload :SenderField, 'mail/fields/sender_field'
|
|
34
41
|
register_autoload :SubjectField, 'mail/fields/subject_field'
|
|
35
42
|
register_autoload :ToField, 'mail/fields/to_field'
|
|
43
|
+
|
|
44
|
+
register_autoload :ParameterHash, 'mail/fields/parameter_hash'
|
|
36
45
|
end
|
data/lib/mail/header.rb
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# frozen_string_literal: true
|
|
3
|
+
require 'mail/constants'
|
|
4
|
+
require 'mail/utilities'
|
|
5
|
+
|
|
3
6
|
module Mail
|
|
4
|
-
|
|
5
7
|
# Provides access to a header object.
|
|
6
|
-
#
|
|
8
|
+
#
|
|
7
9
|
# ===Per RFC2822
|
|
8
|
-
#
|
|
10
|
+
#
|
|
9
11
|
# 2.2. Header Fields
|
|
10
|
-
#
|
|
12
|
+
#
|
|
11
13
|
# Header fields are lines composed of a field name, followed by a colon
|
|
12
14
|
# (":"), followed by a field body, and terminated by CRLF. A field
|
|
13
15
|
# name MUST be composed of printable US-ASCII characters (i.e.,
|
|
@@ -18,14 +20,12 @@ module Mail
|
|
|
18
20
|
# 2.2.3. All field bodies MUST conform to the syntax described in
|
|
19
21
|
# sections 3 and 4 of this standard.
|
|
20
22
|
class Header
|
|
21
|
-
include Constants
|
|
22
|
-
include Utilities
|
|
23
23
|
include Enumerable
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
@@maximum_amount = 1000
|
|
26
26
|
|
|
27
27
|
# Large amount of headers in Email might create extra high CPU load
|
|
28
|
-
# Use this parameter to limit number of headers that will be parsed by
|
|
28
|
+
# Use this parameter to limit number of headers that will be parsed by
|
|
29
29
|
# mail library.
|
|
30
30
|
# Default: 1000
|
|
31
31
|
def self.maximum_amount
|
|
@@ -36,12 +36,14 @@ module Mail
|
|
|
36
36
|
@@maximum_amount = value
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
attr_reader :raw_source, :charset
|
|
40
|
+
|
|
39
41
|
# Creates a new header object.
|
|
40
|
-
#
|
|
42
|
+
#
|
|
41
43
|
# Accepts raw text or nothing. If given raw text will attempt to parse
|
|
42
44
|
# it and split it into the various fields, instantiating each field as
|
|
43
45
|
# it goes.
|
|
44
|
-
#
|
|
46
|
+
#
|
|
45
47
|
# If it finds a field that should be a structured field (such as content
|
|
46
48
|
# type), but it fails to parse it, it will simply make it an unstructured
|
|
47
49
|
# field and leave it alone. This will mean that the data is preserved but
|
|
@@ -50,7 +52,7 @@ module Mail
|
|
|
50
52
|
# me the example so we can fix it.
|
|
51
53
|
def initialize(header_text = nil, charset = nil)
|
|
52
54
|
@charset = charset
|
|
53
|
-
|
|
55
|
+
@raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
|
|
54
56
|
split_header if header_text
|
|
55
57
|
end
|
|
56
58
|
|
|
@@ -59,21 +61,15 @@ module Mail
|
|
|
59
61
|
@fields = @fields.dup
|
|
60
62
|
@fields.map!(&:dup)
|
|
61
63
|
end
|
|
62
|
-
|
|
63
|
-
# The preserved raw source of the header as you passed it in, untouched
|
|
64
|
-
# for your Regexing glory.
|
|
65
|
-
def raw_source
|
|
66
|
-
@raw_source
|
|
67
|
-
end
|
|
68
|
-
|
|
64
|
+
|
|
69
65
|
# Returns an array of all the fields in the header in order that they
|
|
70
66
|
# were read in.
|
|
71
67
|
def fields
|
|
72
68
|
@fields ||= FieldList.new
|
|
73
69
|
end
|
|
74
|
-
|
|
70
|
+
|
|
75
71
|
# 3.6. Field definitions
|
|
76
|
-
#
|
|
72
|
+
#
|
|
77
73
|
# It is important to note that the header fields are not guaranteed to
|
|
78
74
|
# be in a particular order. They may appear in any order, and they
|
|
79
75
|
# have been known to be reordered occasionally when transported over
|
|
@@ -83,36 +79,35 @@ module Mail
|
|
|
83
79
|
# header fields MUST NOT be reordered, and SHOULD be kept in blocks
|
|
84
80
|
# prepended to the message. See sections 3.6.6 and 3.6.7 for more
|
|
85
81
|
# information.
|
|
86
|
-
#
|
|
82
|
+
#
|
|
87
83
|
# Populates the fields container with Field objects in the order it
|
|
88
84
|
# receives them in.
|
|
89
85
|
#
|
|
90
86
|
# Acceps an array of field string values, for example:
|
|
91
|
-
#
|
|
87
|
+
#
|
|
92
88
|
# h = Header.new
|
|
93
89
|
# h.fields = ['From: mikel@me.com', 'To: bob@you.com']
|
|
94
90
|
def fields=(unfolded_fields)
|
|
95
91
|
@fields = Mail::FieldList.new
|
|
96
|
-
Kernel.warn "WARNING: More than #{self.class.maximum_amount} header fields; only using the first #{self.class.maximum_amount} and ignoring the rest" if unfolded_fields.length > self.class.maximum_amount
|
|
97
|
-
unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
|
|
98
92
|
|
|
93
|
+
if unfolded_fields.size > self.class.maximum_amount
|
|
94
|
+
Kernel.warn "WARNING: More than #{self.class.maximum_amount} header fields; only using the first #{self.class.maximum_amount} and ignoring the rest"
|
|
95
|
+
unfolded_fields = unfolded_fields.slice(0...self.class.maximum_amount)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
unfolded_fields.each do |field|
|
|
99
99
|
if field = Field.parse(field, charset)
|
|
100
|
-
|
|
101
|
-
selected.first.update(field.name, field.value)
|
|
102
|
-
else
|
|
103
|
-
@fields << field
|
|
104
|
-
end
|
|
100
|
+
@fields.add_field field
|
|
105
101
|
end
|
|
106
102
|
end
|
|
107
|
-
|
|
108
103
|
end
|
|
109
|
-
|
|
104
|
+
|
|
110
105
|
def errors
|
|
111
106
|
@fields.map(&:errors).flatten(1)
|
|
112
107
|
end
|
|
113
|
-
|
|
108
|
+
|
|
114
109
|
# 3.6. Field definitions
|
|
115
|
-
#
|
|
110
|
+
#
|
|
116
111
|
# The following table indicates limits on the number of times each
|
|
117
112
|
# field may occur in a message header as well as any special
|
|
118
113
|
# limitations on the use of those fields. An asterisk next to a value
|
|
@@ -122,35 +117,25 @@ module Mail
|
|
|
122
117
|
# <snip table from 3.6>
|
|
123
118
|
#
|
|
124
119
|
# As per RFC, many fields can appear more than once, we will return a string
|
|
125
|
-
# of the value if there is only one header, or if there is more than one
|
|
120
|
+
# of the value if there is only one header, or if there is more than one
|
|
126
121
|
# matching header, will return an array of values in order that they appear
|
|
127
122
|
# in the header ordered from top to bottom.
|
|
128
|
-
#
|
|
123
|
+
#
|
|
129
124
|
# Example:
|
|
130
|
-
#
|
|
125
|
+
#
|
|
131
126
|
# h = Header.new
|
|
132
127
|
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
|
|
133
128
|
# h['To'] #=> 'mikel@me.com'
|
|
134
129
|
# h['X-Mail-SPAM'] #=> ['15', '20']
|
|
135
130
|
def [](name)
|
|
136
|
-
|
|
137
|
-
name.downcase!
|
|
138
|
-
selected = select_field_for(name)
|
|
139
|
-
case
|
|
140
|
-
when selected.length > 1
|
|
141
|
-
selected.map { |f| f }
|
|
142
|
-
when !Utilities.blank?(selected)
|
|
143
|
-
selected.first
|
|
144
|
-
else
|
|
145
|
-
nil
|
|
146
|
-
end
|
|
131
|
+
fields.get_field(Utilities.dasherize(name))
|
|
147
132
|
end
|
|
148
|
-
|
|
133
|
+
|
|
149
134
|
# Sets the FIRST matching field in the header to passed value, or deletes
|
|
150
135
|
# the FIRST field matched from the header if passed nil
|
|
151
|
-
#
|
|
136
|
+
#
|
|
152
137
|
# Example:
|
|
153
|
-
#
|
|
138
|
+
#
|
|
154
139
|
# h = Header.new
|
|
155
140
|
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
|
|
156
141
|
# h['To'] = 'bob@you.com'
|
|
@@ -160,51 +145,39 @@ module Mail
|
|
|
160
145
|
# h['X-Mail-SPAM'] = nil
|
|
161
146
|
# h['X-Mail-SPAM'] # => nil
|
|
162
147
|
def []=(name, value)
|
|
163
|
-
name =
|
|
164
|
-
if name.include?(
|
|
148
|
+
name = name.to_s
|
|
149
|
+
if name.include?(Constants::COLON)
|
|
165
150
|
raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
|
|
166
151
|
end
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
fields.delete_if { |f| selected.include?(f) }
|
|
174
|
-
|
|
175
|
-
# User wants to change the field
|
|
176
|
-
when !Utilities.blank?(selected) && limited_field?(fn)
|
|
177
|
-
selected.first.update(fn, value)
|
|
178
|
-
|
|
179
|
-
# User wants to create the field
|
|
152
|
+
|
|
153
|
+
name = Utilities.dasherize(name)
|
|
154
|
+
|
|
155
|
+
# Assign nil to delete the field
|
|
156
|
+
if value.nil?
|
|
157
|
+
fields.delete_field name
|
|
180
158
|
else
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
end
|
|
184
|
-
if dasherize(fn) == "content-type"
|
|
159
|
+
fields.add_field Field.new(name.to_s, value, charset)
|
|
160
|
+
|
|
185
161
|
# Update charset if specified in Content-Type
|
|
186
|
-
|
|
187
|
-
|
|
162
|
+
if name == 'content-type'
|
|
163
|
+
params = self[:content_type].parameters rescue nil
|
|
164
|
+
@charset = params[:charset] if params && params[:charset]
|
|
165
|
+
end
|
|
188
166
|
end
|
|
189
167
|
end
|
|
190
|
-
|
|
191
|
-
def charset
|
|
192
|
-
@charset
|
|
193
|
-
end
|
|
194
|
-
|
|
168
|
+
|
|
195
169
|
def charset=(val)
|
|
196
170
|
params = self[:content_type].parameters rescue nil
|
|
197
171
|
if params
|
|
198
|
-
|
|
172
|
+
if val
|
|
173
|
+
params[:charset] = val
|
|
174
|
+
else
|
|
175
|
+
params.delete(:charset)
|
|
176
|
+
end
|
|
199
177
|
end
|
|
200
178
|
@charset = val
|
|
201
179
|
end
|
|
202
|
-
|
|
203
|
-
LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
|
|
204
|
-
message-id in-reply-to references subject
|
|
205
|
-
return-path content-type mime-version
|
|
206
|
-
content-transfer-encoding content-description
|
|
207
|
-
content-id content-disposition content-location]
|
|
180
|
+
|
|
208
181
|
|
|
209
182
|
def encoded
|
|
210
183
|
buffer = String.new
|
|
@@ -218,61 +191,47 @@ module Mail
|
|
|
218
191
|
def to_s
|
|
219
192
|
encoded
|
|
220
193
|
end
|
|
221
|
-
|
|
194
|
+
|
|
222
195
|
def decoded
|
|
223
|
-
raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts
|
|
196
|
+
raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts. Try calling #decoded on the various fields.'
|
|
224
197
|
end
|
|
225
198
|
|
|
226
199
|
def field_summary
|
|
227
|
-
fields.
|
|
200
|
+
fields.summary
|
|
228
201
|
end
|
|
229
202
|
|
|
230
203
|
# Returns true if the header has a Message-ID defined (empty or not)
|
|
231
204
|
def has_message_id?
|
|
232
|
-
|
|
205
|
+
fields.has_field? 'Message-ID'
|
|
233
206
|
end
|
|
234
207
|
|
|
235
208
|
# Returns true if the header has a Content-ID defined (empty or not)
|
|
236
209
|
def has_content_id?
|
|
237
|
-
|
|
210
|
+
fields.has_field? 'Content-ID'
|
|
238
211
|
end
|
|
239
212
|
|
|
240
213
|
# Returns true if the header has a Date defined (empty or not)
|
|
241
214
|
def has_date?
|
|
242
|
-
|
|
215
|
+
fields.has_field? 'Date'
|
|
243
216
|
end
|
|
244
217
|
|
|
245
218
|
# Returns true if the header has a MIME version defined (empty or not)
|
|
246
219
|
def has_mime_version?
|
|
247
|
-
|
|
220
|
+
fields.has_field? 'MIME-Version'
|
|
248
221
|
end
|
|
249
222
|
|
|
250
223
|
private
|
|
251
|
-
|
|
252
|
-
def raw_source=(val)
|
|
253
|
-
@raw_source = ::Mail::Utilities.to_crlf(val).lstrip
|
|
254
|
-
end
|
|
255
|
-
|
|
224
|
+
|
|
256
225
|
# Splits an unfolded and line break cleaned header into individual field
|
|
257
226
|
# strings.
|
|
258
227
|
def split_header
|
|
259
|
-
self.fields = raw_source.split(HEADER_SPLIT)
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def select_field_for(name)
|
|
263
|
-
fields.select { |f| f.responsible_for?(name) }
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def limited_field?(name)
|
|
267
|
-
LIMITED_FIELDS.include?(name.to_s.downcase)
|
|
228
|
+
self.fields = @raw_source.split(Constants::HEADER_SPLIT)
|
|
268
229
|
end
|
|
269
230
|
|
|
270
|
-
# Enumerable support; yield each field in order to the block if there is one,
|
|
271
|
-
# or return an Enumerator for them if there isn't.
|
|
272
|
-
def each( &block )
|
|
273
|
-
return self.fields.each( &block ) if block
|
|
274
|
-
self.fields.each
|
|
275
|
-
end
|
|
276
231
|
|
|
232
|
+
# Enumerable support. Yield each field in order.
|
|
233
|
+
def each(&block)
|
|
234
|
+
fields.each(&block)
|
|
235
|
+
end
|
|
277
236
|
end
|
|
278
237
|
end
|
data/lib/mail/mail.rb
CHANGED
|
@@ -3,45 +3,45 @@
|
|
|
3
3
|
module Mail
|
|
4
4
|
|
|
5
5
|
# Allows you to create a new Mail::Message object.
|
|
6
|
-
#
|
|
6
|
+
#
|
|
7
7
|
# You can make an email via passing a string or passing a block.
|
|
8
|
-
#
|
|
8
|
+
#
|
|
9
9
|
# For example, the following two examples will create the same email
|
|
10
10
|
# message:
|
|
11
|
-
#
|
|
11
|
+
#
|
|
12
12
|
# Creating via a string:
|
|
13
|
-
#
|
|
13
|
+
#
|
|
14
14
|
# string = "To: mikel@test.lindsaar.net\r\n"
|
|
15
15
|
# string << "From: bob@test.lindsaar.net\r\n"
|
|
16
16
|
# string << "Subject: This is an email\r\n"
|
|
17
17
|
# string << "\r\n"
|
|
18
18
|
# string << "This is the body"
|
|
19
19
|
# Mail.new(string)
|
|
20
|
-
#
|
|
20
|
+
#
|
|
21
21
|
# Or creating via a block:
|
|
22
|
-
#
|
|
22
|
+
#
|
|
23
23
|
# message = Mail.new do
|
|
24
24
|
# to 'mikel@test.lindsaar.net'
|
|
25
25
|
# from 'bob@test.lindsaar.net'
|
|
26
26
|
# subject 'This is an email'
|
|
27
27
|
# body 'This is the body'
|
|
28
28
|
# end
|
|
29
|
-
#
|
|
29
|
+
#
|
|
30
30
|
# Or creating via a hash (or hash like object):
|
|
31
|
-
#
|
|
31
|
+
#
|
|
32
32
|
# message = Mail.new({:to => 'mikel@test.lindsaar.net',
|
|
33
33
|
# 'from' => 'bob@test.lindsaar.net',
|
|
34
34
|
# :subject => 'This is an email',
|
|
35
35
|
# :body => 'This is the body' })
|
|
36
|
-
#
|
|
36
|
+
#
|
|
37
37
|
# Note, the hash keys can be strings or symbols, the passed in object
|
|
38
38
|
# does not need to be a hash, it just needs to respond to :each_pair
|
|
39
39
|
# and yield each key value pair.
|
|
40
|
-
#
|
|
40
|
+
#
|
|
41
41
|
# As a side note, you can also create a new email through creating
|
|
42
42
|
# a Mail::Message object directly and then passing in values via string,
|
|
43
43
|
# symbol or direct method calls. See Mail::Message for more information.
|
|
44
|
-
#
|
|
44
|
+
#
|
|
45
45
|
# mail = Mail.new
|
|
46
46
|
# mail.to = 'mikel@test.lindsaar.net'
|
|
47
47
|
# mail[:from] = 'bob@test.lindsaar.net'
|
|
@@ -54,20 +54,20 @@ module Mail
|
|
|
54
54
|
# Sets the default delivery method and retriever method for all new Mail objects.
|
|
55
55
|
# The delivery_method and retriever_method default to :smtp and :pop3, with defaults
|
|
56
56
|
# set.
|
|
57
|
-
#
|
|
57
|
+
#
|
|
58
58
|
# So sending a new email, if you have an SMTP server running on localhost is
|
|
59
59
|
# as easy as:
|
|
60
|
-
#
|
|
60
|
+
#
|
|
61
61
|
# Mail.deliver do
|
|
62
62
|
# to 'mikel@test.lindsaar.net'
|
|
63
63
|
# from 'bob@test.lindsaar.net'
|
|
64
64
|
# subject 'hi there!'
|
|
65
65
|
# body 'this is a body'
|
|
66
66
|
# end
|
|
67
|
-
#
|
|
67
|
+
#
|
|
68
68
|
# If you do not specify anything, you will get the following equivalent code set in
|
|
69
69
|
# every new mail object:
|
|
70
|
-
#
|
|
70
|
+
#
|
|
71
71
|
# Mail.defaults do
|
|
72
72
|
# delivery_method :smtp, { :address => "localhost",
|
|
73
73
|
# :port => 25,
|
|
@@ -76,14 +76,14 @@ module Mail
|
|
|
76
76
|
# :password => nil,
|
|
77
77
|
# :authentication => nil,
|
|
78
78
|
# :enable_starttls_auto => true }
|
|
79
|
-
#
|
|
79
|
+
#
|
|
80
80
|
# retriever_method :pop3, { :address => "localhost",
|
|
81
81
|
# :port => 995,
|
|
82
82
|
# :user_name => nil,
|
|
83
83
|
# :password => nil,
|
|
84
84
|
# :enable_ssl => true }
|
|
85
85
|
# end
|
|
86
|
-
#
|
|
86
|
+
#
|
|
87
87
|
# Mail.delivery_method.new #=> Mail::SMTP instance
|
|
88
88
|
# Mail.retriever_method.new #=> Mail::POP3 instance
|
|
89
89
|
#
|
|
@@ -91,9 +91,9 @@ module Mail
|
|
|
91
91
|
# a per email basis, you can override the method:
|
|
92
92
|
#
|
|
93
93
|
# mail.delivery_method :smtp
|
|
94
|
-
#
|
|
94
|
+
#
|
|
95
95
|
# Or you can override the method and pass in settings:
|
|
96
|
-
#
|
|
96
|
+
#
|
|
97
97
|
# mail.delivery_method :smtp, :address => 'some.host'
|
|
98
98
|
def self.defaults(&block)
|
|
99
99
|
Configuration.instance.instance_eval(&block)
|
|
@@ -112,21 +112,21 @@ module Mail
|
|
|
112
112
|
# Send an email using the default configuration. You do need to set a default
|
|
113
113
|
# configuration first before you use self.deliver, if you don't, an appropriate
|
|
114
114
|
# error will be raised telling you to.
|
|
115
|
-
#
|
|
115
|
+
#
|
|
116
116
|
# If you do not specify a delivery type, SMTP will be used.
|
|
117
|
-
#
|
|
117
|
+
#
|
|
118
118
|
# Mail.deliver do
|
|
119
119
|
# to 'mikel@test.lindsaar.net'
|
|
120
120
|
# from 'ada@test.lindsaar.net'
|
|
121
121
|
# subject 'This is a test email'
|
|
122
122
|
# body 'Not much to say here'
|
|
123
123
|
# end
|
|
124
|
-
#
|
|
124
|
+
#
|
|
125
125
|
# You can also do:
|
|
126
|
-
#
|
|
126
|
+
#
|
|
127
127
|
# mail = Mail.read('email.eml')
|
|
128
128
|
# mail.deliver!
|
|
129
|
-
#
|
|
129
|
+
#
|
|
130
130
|
# And your email object will be created and sent.
|
|
131
131
|
def self.deliver(*args, &block)
|
|
132
132
|
mail = self.new(args, &block)
|
|
@@ -152,7 +152,7 @@ module Mail
|
|
|
152
152
|
retriever_method.first(*args, &block)
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
-
# Receive the
|
|
155
|
+
# Receive the last email(s) from the default retriever
|
|
156
156
|
# See Mail::Retriever for a complete documentation.
|
|
157
157
|
def self.last(*args, &block)
|
|
158
158
|
retriever_method.last(*args, &block)
|
|
@@ -190,7 +190,7 @@ module Mail
|
|
|
190
190
|
|
|
191
191
|
# You can register an object to be informed of every email that is sent through
|
|
192
192
|
# this method.
|
|
193
|
-
#
|
|
193
|
+
#
|
|
194
194
|
# Your object needs to respond to a single method #delivered_email(mail)
|
|
195
195
|
# which receives the email that is sent.
|
|
196
196
|
def self.register_observer(observer)
|
|
@@ -208,7 +208,7 @@ module Mail
|
|
|
208
208
|
# You can register an object to be given every mail object that will be sent,
|
|
209
209
|
# before it is sent. So if you want to add special headers or modify any
|
|
210
210
|
# email that gets sent through the Mail library, you can do so.
|
|
211
|
-
#
|
|
211
|
+
#
|
|
212
212
|
# Your object needs to respond to a single method #delivering_email(mail)
|
|
213
213
|
# which receives the email that is about to be sent. Make your modifications
|
|
214
214
|
# directly to this object.
|
|
@@ -236,6 +236,11 @@ module Mail
|
|
|
236
236
|
end
|
|
237
237
|
end
|
|
238
238
|
|
|
239
|
+
# Returns a list of registered delivery interceptors.
|
|
240
|
+
def self.delivery_interceptors
|
|
241
|
+
@@delivery_interceptors
|
|
242
|
+
end
|
|
243
|
+
|
|
239
244
|
protected
|
|
240
245
|
|
|
241
246
|
RANDOM_TAG='%x%x_%x%x%d%x'
|
|
@@ -9,6 +9,10 @@ module Mail
|
|
|
9
9
|
AttachmentFilenameMatcher.new(filename)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
def an_attachment_with_mime_type(filename)
|
|
13
|
+
AttachmentMimeTypeMatcher.new(filename)
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
class AnyAttachmentMatcher
|
|
13
17
|
def ===(other)
|
|
14
18
|
other.attachment?
|
|
@@ -25,5 +29,16 @@ module Mail
|
|
|
25
29
|
other.attachment? && other.filename == filename
|
|
26
30
|
end
|
|
27
31
|
end
|
|
32
|
+
|
|
33
|
+
class AttachmentMimeTypeMatcher
|
|
34
|
+
attr_reader :mime_type
|
|
35
|
+
def initialize(mime_type)
|
|
36
|
+
@mime_type = mime_type
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def ===(other)
|
|
40
|
+
other.attachment? && other.mime_type == mime_type
|
|
41
|
+
end
|
|
42
|
+
end
|
|
28
43
|
end
|
|
29
44
|
end
|