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/body.rb
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
module Mail
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
# = Body
|
|
6
|
-
#
|
|
6
|
+
#
|
|
7
7
|
# The body is where the text of the email is stored. Mail treats the body
|
|
8
8
|
# as a single object. The body itself has no information about boundaries
|
|
9
9
|
# used in the MIME standard, it just looks at its content as either a single
|
|
10
10
|
# block of text, or (if it is a multipart message) as an array of blocks of text.
|
|
11
|
-
#
|
|
11
|
+
#
|
|
12
12
|
# A body has to be told to split itself up into a multipart message by calling
|
|
13
13
|
# #split with the correct boundary. This is because the body object has no way
|
|
14
14
|
# of knowing what the correct boundary is for itself (there could be many
|
|
15
15
|
# boundaries in a body in the case of a nested MIME text).
|
|
16
|
-
#
|
|
16
|
+
#
|
|
17
17
|
# Once split is called, Mail::Body will slice itself up on this boundary,
|
|
18
18
|
# assigning anything that appears before the first part to the preamble, and
|
|
19
19
|
# anything that appears after the closing boundary to the epilogue, then
|
|
20
20
|
# each part gets initialized into a Mail::Part object.
|
|
21
|
-
#
|
|
21
|
+
#
|
|
22
22
|
# The boundary that is used to split up the Body is also stored in the Body
|
|
23
|
-
# object for use on encoding itself back out to a string. You can
|
|
23
|
+
# object for use on encoding itself back out to a string. You can
|
|
24
24
|
# overwrite this if it needs to be changed.
|
|
25
|
-
#
|
|
25
|
+
#
|
|
26
26
|
# On encoding, the body will return the preamble, then each part joined by
|
|
27
27
|
# the boundary, followed by a closing boundary string and then the epilogue.
|
|
28
28
|
class Body
|
|
@@ -32,7 +32,7 @@ module Mail
|
|
|
32
32
|
@preamble = nil
|
|
33
33
|
@epilogue = nil
|
|
34
34
|
@charset = nil
|
|
35
|
-
@part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
|
|
35
|
+
@part_sort_order = [ "text/plain", "text/enriched", "text/html", "multipart/alternative" ]
|
|
36
36
|
@parts = Mail::PartsList.new
|
|
37
37
|
if Utilities.blank?(string)
|
|
38
38
|
@raw_source = ''
|
|
@@ -46,21 +46,26 @@ module Mail
|
|
|
46
46
|
raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
|
-
@encoding =
|
|
49
|
+
@encoding = default_encoding
|
|
50
50
|
set_charset
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
def init_with(coder)
|
|
54
|
+
coder.map.each { |k, v| instance_variable_set(:"@#{k}", v) }
|
|
55
|
+
@parts = Mail::PartsList.new(coder['parts'])
|
|
56
|
+
end
|
|
57
|
+
|
|
53
58
|
# Matches this body with another body. Also matches the decoded value of this
|
|
54
59
|
# body with a string.
|
|
55
|
-
#
|
|
60
|
+
#
|
|
56
61
|
# Examples:
|
|
57
|
-
#
|
|
62
|
+
#
|
|
58
63
|
# body = Mail::Body.new('The body')
|
|
59
64
|
# body == body #=> true
|
|
60
|
-
#
|
|
65
|
+
#
|
|
61
66
|
# body = Mail::Body.new('The body')
|
|
62
67
|
# body == 'The body' #=> true
|
|
63
|
-
#
|
|
68
|
+
#
|
|
64
69
|
# body = Mail::Body.new("VGhlIGJvZHk=\n")
|
|
65
70
|
# body.encoding = 'base64'
|
|
66
71
|
# body == "The body" #=> true
|
|
@@ -71,28 +76,28 @@ module Mail
|
|
|
71
76
|
super
|
|
72
77
|
end
|
|
73
78
|
end
|
|
74
|
-
|
|
79
|
+
|
|
75
80
|
# Accepts a string and performs a regular expression against the decoded text
|
|
76
|
-
#
|
|
81
|
+
#
|
|
77
82
|
# Examples:
|
|
78
|
-
#
|
|
83
|
+
#
|
|
79
84
|
# body = Mail::Body.new('The body')
|
|
80
85
|
# body =~ /The/ #=> 0
|
|
81
|
-
#
|
|
86
|
+
#
|
|
82
87
|
# body = Mail::Body.new("VGhlIGJvZHk=\n")
|
|
83
88
|
# body.encoding = 'base64'
|
|
84
89
|
# body =~ /The/ #=> 0
|
|
85
90
|
def =~(regexp)
|
|
86
91
|
self.decoded =~ regexp
|
|
87
92
|
end
|
|
88
|
-
|
|
93
|
+
|
|
89
94
|
# Accepts a string and performs a regular expression against the decoded text
|
|
90
|
-
#
|
|
95
|
+
#
|
|
91
96
|
# Examples:
|
|
92
|
-
#
|
|
97
|
+
#
|
|
93
98
|
# body = Mail::Body.new('The body')
|
|
94
99
|
# body.match(/The/) #=> #<MatchData "The">
|
|
95
|
-
#
|
|
100
|
+
#
|
|
96
101
|
# body = Mail::Body.new("VGhlIGJvZHk=\n")
|
|
97
102
|
# body.encoding = 'base64'
|
|
98
103
|
# body.match(/The/) #=> #<MatchData "The">
|
|
@@ -115,12 +120,12 @@ module Mail
|
|
|
115
120
|
end
|
|
116
121
|
|
|
117
122
|
# Allows you to set the sort order of the parts, overriding the default sort order.
|
|
118
|
-
# Defaults to 'text/plain', then 'text/enriched', then 'text/html'
|
|
119
|
-
# type coming after.
|
|
123
|
+
# Defaults to 'text/plain', then 'text/enriched', then 'text/html', then 'multipart/alternative'
|
|
124
|
+
# with any other content type coming after.
|
|
120
125
|
def set_sort_order(order)
|
|
121
126
|
@part_sort_order = order
|
|
122
127
|
end
|
|
123
|
-
|
|
128
|
+
|
|
124
129
|
# Allows you to sort the parts according to the default sort order, or the sort order you
|
|
125
130
|
# set with :set_sort_order.
|
|
126
131
|
#
|
|
@@ -132,47 +137,45 @@ module Mail
|
|
|
132
137
|
end
|
|
133
138
|
@parts.sort!(@part_sort_order)
|
|
134
139
|
end
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def raw_source
|
|
139
|
-
@raw_source
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def get_best_encoding(target)
|
|
143
|
-
target_encoding = Mail::Encodings.get_encoding(target)
|
|
144
|
-
target_encoding.get_best_compatible(encoding, raw_source)
|
|
140
|
+
|
|
141
|
+
def negotiate_best_encoding(message_encoding, allowed_encodings = nil)
|
|
142
|
+
Mail::Encodings::TransferEncoding.negotiate(message_encoding, encoding, raw_source, allowed_encodings)
|
|
145
143
|
end
|
|
146
|
-
|
|
144
|
+
|
|
147
145
|
# Returns a body encoded using transfer_encoding. Multipart always uses an
|
|
148
146
|
# identiy encoding (i.e. no encoding).
|
|
149
147
|
# Calling this directly is not a good idea, but supported for compatibility
|
|
150
148
|
# TODO: Validate that preamble and epilogue are valid for requested encoding
|
|
151
|
-
def encoded(transfer_encoding =
|
|
149
|
+
def encoded(transfer_encoding = nil)
|
|
152
150
|
if multipart?
|
|
153
151
|
self.sort_parts!
|
|
154
152
|
encoded_parts = parts.map { |p| p.encoded }
|
|
155
153
|
([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
|
|
156
154
|
else
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
dec = Mail::Encodings.get_encoding(encoding)
|
|
156
|
+
enc =
|
|
157
|
+
if Utilities.blank?(transfer_encoding)
|
|
158
|
+
dec
|
|
159
|
+
else
|
|
160
|
+
negotiate_best_encoding(transfer_encoding)
|
|
161
|
+
end
|
|
162
|
+
|
|
160
163
|
if dec.nil?
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
# Cannot decode, so skip normalization
|
|
165
|
+
raw_source
|
|
163
166
|
else
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
# Decode then encode to normalize and allow transforming
|
|
168
|
+
# from base64 to Q-P and vice versa
|
|
169
|
+
decoded = dec.decode(raw_source)
|
|
170
|
+
if defined?(Encoding) && charset && charset != "US-ASCII"
|
|
171
|
+
decoded = decoded.encode(charset)
|
|
172
|
+
decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
|
|
173
|
+
end
|
|
174
|
+
enc.encode(decoded)
|
|
172
175
|
end
|
|
173
176
|
end
|
|
174
177
|
end
|
|
175
|
-
|
|
178
|
+
|
|
176
179
|
def decoded
|
|
177
180
|
if !Encodings.defined?(encoding)
|
|
178
181
|
raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
|
|
@@ -180,18 +183,10 @@ module Mail
|
|
|
180
183
|
Encodings.get_encoding(encoding).decode(raw_source)
|
|
181
184
|
end
|
|
182
185
|
end
|
|
183
|
-
|
|
186
|
+
|
|
184
187
|
def to_s
|
|
185
188
|
decoded
|
|
186
189
|
end
|
|
187
|
-
|
|
188
|
-
def charset
|
|
189
|
-
@charset
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def charset=( val )
|
|
193
|
-
@charset = val
|
|
194
|
-
end
|
|
195
190
|
|
|
196
191
|
def encoding(val = nil)
|
|
197
192
|
if val
|
|
@@ -200,54 +195,41 @@ module Mail
|
|
|
200
195
|
@encoding
|
|
201
196
|
end
|
|
202
197
|
end
|
|
203
|
-
|
|
198
|
+
|
|
204
199
|
def encoding=( val )
|
|
205
|
-
@encoding =
|
|
206
|
-
|
|
207
|
-
|
|
200
|
+
@encoding =
|
|
201
|
+
if val == "text" || Utilities.blank?(val)
|
|
202
|
+
default_encoding
|
|
203
|
+
else
|
|
208
204
|
val
|
|
209
|
-
|
|
205
|
+
end
|
|
210
206
|
end
|
|
211
207
|
|
|
212
|
-
# Returns the
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
208
|
+
# Returns the raw source that the body was initialized with, without
|
|
209
|
+
# any tampering
|
|
210
|
+
attr_reader :raw_source
|
|
211
|
+
|
|
212
|
+
# Returns parts of the body
|
|
213
|
+
attr_reader :parts
|
|
214
|
+
|
|
215
|
+
# Returns and sets the original character encoding
|
|
216
|
+
attr_accessor :charset
|
|
217
|
+
|
|
218
|
+
# Returns and sets the preamble as a string (any text that is before the first MIME boundary)
|
|
219
|
+
attr_accessor :preamble
|
|
220
|
+
|
|
221
|
+
# Returns and sets the epilogue as a string (any text that is after the last MIME boundary)
|
|
222
|
+
attr_accessor :epilogue
|
|
223
|
+
|
|
224
|
+
# Returns and sets the boundary used by the body
|
|
225
|
+
# Allows you to change the boundary of this Body object
|
|
226
|
+
attr_accessor :boundary
|
|
216
227
|
|
|
217
|
-
# Sets the preamble to a string (adds text before the first MIME boundary)
|
|
218
|
-
def preamble=( val )
|
|
219
|
-
@preamble = val
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# Returns the epilogue (any text that is after the last MIME boundary)
|
|
223
|
-
def epilogue
|
|
224
|
-
@epilogue
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# Sets the epilogue to a string (adds text after the last MIME boundary)
|
|
228
|
-
def epilogue=( val )
|
|
229
|
-
@epilogue = val
|
|
230
|
-
end
|
|
231
|
-
|
|
232
228
|
# Returns true if there are parts defined in the body
|
|
233
229
|
def multipart?
|
|
234
230
|
true unless parts.empty?
|
|
235
231
|
end
|
|
236
|
-
|
|
237
|
-
# Returns the boundary used by the body
|
|
238
|
-
def boundary
|
|
239
|
-
@boundary
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
# Allows you to change the boundary of this Body object
|
|
243
|
-
def boundary=( val )
|
|
244
|
-
@boundary = val
|
|
245
|
-
end
|
|
246
232
|
|
|
247
|
-
def parts
|
|
248
|
-
@parts
|
|
249
|
-
end
|
|
250
|
-
|
|
251
233
|
def <<( val )
|
|
252
234
|
if @parts
|
|
253
235
|
@parts << val
|
|
@@ -268,14 +250,21 @@ module Mail
|
|
|
268
250
|
self
|
|
269
251
|
end
|
|
270
252
|
|
|
271
|
-
def
|
|
272
|
-
|
|
253
|
+
def ascii_only?
|
|
254
|
+
unless defined? @ascii_only
|
|
255
|
+
@ascii_only = raw_source.ascii_only?
|
|
256
|
+
end
|
|
257
|
+
@ascii_only
|
|
273
258
|
end
|
|
274
|
-
|
|
259
|
+
|
|
275
260
|
def empty?
|
|
276
261
|
!!raw_source.to_s.empty?
|
|
277
262
|
end
|
|
278
|
-
|
|
263
|
+
|
|
264
|
+
def default_encoding
|
|
265
|
+
ascii_only? ? '7bit' : '8bit'
|
|
266
|
+
end
|
|
267
|
+
|
|
279
268
|
private
|
|
280
269
|
|
|
281
270
|
# split parts by boundary, ignore first part if empty, append final part when closing boundary was missing
|
|
@@ -283,7 +272,7 @@ module Mail
|
|
|
283
272
|
parts_regex = /
|
|
284
273
|
(?: # non-capturing group
|
|
285
274
|
\A | # start of string OR
|
|
286
|
-
\r
|
|
275
|
+
\r?\n # line break with optional CR
|
|
287
276
|
)
|
|
288
277
|
(
|
|
289
278
|
--#{Regexp.escape(boundary || "")} # boundary delimiter
|
|
@@ -300,17 +289,17 @@ module Mail
|
|
|
300
289
|
end
|
|
301
290
|
parts.map(&:first)
|
|
302
291
|
end
|
|
303
|
-
|
|
292
|
+
|
|
304
293
|
def crlf_boundary
|
|
305
294
|
"\r\n--#{boundary}\r\n"
|
|
306
295
|
end
|
|
307
|
-
|
|
296
|
+
|
|
308
297
|
def end_boundary
|
|
309
298
|
"\r\n--#{boundary}--\r\n"
|
|
310
299
|
end
|
|
311
|
-
|
|
300
|
+
|
|
312
301
|
def set_charset
|
|
313
|
-
|
|
302
|
+
@charset = ascii_only? ? 'US-ASCII' : nil
|
|
314
303
|
end
|
|
315
304
|
end
|
|
316
305
|
end
|
data/lib/mail/configuration.rb
CHANGED
data/lib/mail/constants.rb
CHANGED
|
@@ -16,9 +16,10 @@ module Mail
|
|
|
16
16
|
control = control.dup.force_encoding(Encoding::BINARY)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
LAX_CRLF = /\r?\n/
|
|
20
20
|
WSP = /[#{white_space}]/
|
|
21
|
-
FWS = /#{
|
|
21
|
+
FWS = /#{LAX_CRLF}#{WSP}*/
|
|
22
|
+
UNFOLD_WS = /#{LAX_CRLF}(#{WSP})/m
|
|
22
23
|
TEXT = /[#{text}]/ # + obs-text
|
|
23
24
|
FIELD_NAME = /[#{field_name}]+/
|
|
24
25
|
FIELD_PREFIX = /\A(#{FIELD_NAME})/
|
|
@@ -26,7 +27,7 @@ module Mail
|
|
|
26
27
|
FIELD_LINE = /^[#{field_name}]+:\s*.+$/
|
|
27
28
|
FIELD_SPLIT = /^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/
|
|
28
29
|
HEADER_LINE = /^([#{field_name}]+:\s*.+)$/
|
|
29
|
-
HEADER_SPLIT = /#{
|
|
30
|
+
HEADER_SPLIT = /#{LAX_CRLF}(?!#{WSP})/
|
|
30
31
|
|
|
31
32
|
QP_UNSAFE = /[^#{qp_safe}]/
|
|
32
33
|
QP_SAFE = /[#{qp_safe}]/
|
|
@@ -34,8 +35,28 @@ module Mail
|
|
|
34
35
|
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{sp}]/n
|
|
35
36
|
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
|
|
36
37
|
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{sp}]/n
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
ENCODED_VALUE = %r{
|
|
40
|
+
\=\? # literal =?
|
|
41
|
+
([^?]+) #
|
|
42
|
+
\? # literal ?
|
|
43
|
+
([QB]) # either a "Q" or a "B"
|
|
44
|
+
\? # literal ?
|
|
45
|
+
.*? # lazily match all characters
|
|
46
|
+
\?\= # literal ?=
|
|
47
|
+
}mix # m is multi-line, i is case-insensitive, x is free-spacing
|
|
48
|
+
|
|
49
|
+
FULL_ENCODED_VALUE = %r{ # Identical to ENCODED_VALUE but captures the whole rather than components of
|
|
50
|
+
(
|
|
51
|
+
\=\? # literal =?
|
|
52
|
+
[^?]+ #
|
|
53
|
+
\? # literal ?
|
|
54
|
+
[QB] # either a "Q" or a "B"
|
|
55
|
+
\? # literal ?
|
|
56
|
+
.*? # lazily match all characters
|
|
57
|
+
\?\= # literal ?=
|
|
58
|
+
)
|
|
59
|
+
}mix # m is multi-line, i is case-insensitive, x is free-spacing
|
|
39
60
|
|
|
40
61
|
EMPTY = ''
|
|
41
62
|
SPACE = ' '
|
|
@@ -43,6 +64,7 @@ module Mail
|
|
|
43
64
|
HYPHEN = '-'
|
|
44
65
|
COLON = ':'
|
|
45
66
|
ASTERISK = '*'
|
|
67
|
+
CRLF = "\r\n"
|
|
46
68
|
CR = "\r"
|
|
47
69
|
LF = "\n"
|
|
48
70
|
CR_ENCODED = "=0D"
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# frozen_string_literal: true
|
|
3
|
+
require 'mail/parsers/address_lists_parser'
|
|
4
|
+
require 'mail/constants'
|
|
5
|
+
require 'mail/utilities'
|
|
6
|
+
|
|
3
7
|
module Mail
|
|
8
|
+
# Mail::Address handles all email addresses in Mail. It takes an email address string
|
|
9
|
+
# and parses it, breaking it down into its component parts and allowing you to get the
|
|
10
|
+
# address, comments, display name, name, local part, domain part and fully formatted
|
|
11
|
+
# address.
|
|
12
|
+
#
|
|
13
|
+
# Mail::Address requires a correctly formatted email address per RFC2822 or RFC822. It
|
|
14
|
+
# handles all obsolete versions including obsolete domain routing on the local part.
|
|
15
|
+
#
|
|
16
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
17
|
+
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
18
|
+
# a.address #=> 'mikel@test.lindsaar.net'
|
|
19
|
+
# a.display_name #=> 'Mikel Lindsaar'
|
|
20
|
+
# a.local #=> 'mikel'
|
|
21
|
+
# a.domain #=> 'test.lindsaar.net'
|
|
22
|
+
# a.comments #=> ['My email address']
|
|
23
|
+
# a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
4
24
|
class Address
|
|
5
|
-
|
|
6
|
-
include Mail::Utilities
|
|
7
|
-
|
|
8
|
-
# Mail::Address handles all email addresses in Mail. It takes an email address string
|
|
9
|
-
# and parses it, breaking it down into its component parts and allowing you to get the
|
|
10
|
-
# address, comments, display name, name, local part, domain part and fully formatted
|
|
11
|
-
# address.
|
|
12
|
-
#
|
|
13
|
-
# Mail::Address requires a correctly formatted email address per RFC2822 or RFC822. It
|
|
14
|
-
# handles all obsolete versions including obsolete domain routing on the local part.
|
|
15
|
-
#
|
|
16
|
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
17
|
-
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
18
|
-
# a.address #=> 'mikel@test.lindsaar.net'
|
|
19
|
-
# a.display_name #=> 'Mikel Lindsaar'
|
|
20
|
-
# a.local #=> 'mikel'
|
|
21
|
-
# a.domain #=> 'test.lindsaar.net'
|
|
22
|
-
# a.comments #=> ['My email address']
|
|
23
|
-
# a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
24
25
|
def initialize(value = nil)
|
|
25
|
-
@output_type = :decode
|
|
26
26
|
if value.nil?
|
|
27
27
|
@parsed = false
|
|
28
28
|
@data = nil
|
|
@@ -44,14 +44,14 @@ module Mail
|
|
|
44
44
|
#
|
|
45
45
|
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
46
46
|
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
47
|
-
def format
|
|
47
|
+
def format(output_type = :decode)
|
|
48
48
|
parse unless @parsed
|
|
49
49
|
if @data.nil?
|
|
50
|
-
EMPTY
|
|
51
|
-
elsif display_name
|
|
52
|
-
[quote_phrase(
|
|
53
|
-
elsif address
|
|
54
|
-
[
|
|
50
|
+
Constants::EMPTY
|
|
51
|
+
elsif name = display_name(output_type)
|
|
52
|
+
[Utilities.quote_phrase(name), "<#{address(output_type)}>", format_comments].compact.join(Constants::SPACE)
|
|
53
|
+
elsif a = address(output_type)
|
|
54
|
+
[a, format_comments].compact.join(Constants::SPACE)
|
|
55
55
|
else
|
|
56
56
|
raw
|
|
57
57
|
end
|
|
@@ -62,9 +62,13 @@ module Mail
|
|
|
62
62
|
#
|
|
63
63
|
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
64
64
|
# a.address #=> 'mikel@test.lindsaar.net'
|
|
65
|
-
def address
|
|
65
|
+
def address(output_type = :decode)
|
|
66
66
|
parse unless @parsed
|
|
67
|
-
|
|
67
|
+
if d = domain(output_type)
|
|
68
|
+
"#{local(output_type)}@#{d}"
|
|
69
|
+
else
|
|
70
|
+
local(output_type)
|
|
71
|
+
end
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
# Provides a way to assign an address to an already made Mail::Address object.
|
|
@@ -80,10 +84,10 @@ module Mail
|
|
|
80
84
|
#
|
|
81
85
|
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
82
86
|
# a.display_name #=> 'Mikel Lindsaar'
|
|
83
|
-
def display_name
|
|
87
|
+
def display_name(output_type = :decode)
|
|
84
88
|
parse unless @parsed
|
|
85
89
|
@display_name ||= get_display_name
|
|
86
|
-
Encodings.decode_encode(@display_name.to_s,
|
|
90
|
+
Encodings.decode_encode(@display_name.to_s, output_type) if @display_name
|
|
87
91
|
end
|
|
88
92
|
|
|
89
93
|
# Provides a way to assign a display name to an already made Mail::Address object.
|
|
@@ -93,7 +97,7 @@ module Mail
|
|
|
93
97
|
# a.display_name = 'Mikel Lindsaar'
|
|
94
98
|
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
|
|
95
99
|
def display_name=( str )
|
|
96
|
-
@display_name = str.dup # in case frozen
|
|
100
|
+
@display_name = str.nil? ? nil : str.dup # in case frozen
|
|
97
101
|
end
|
|
98
102
|
|
|
99
103
|
# Returns the local part (the left hand side of the @ sign in the email address) of
|
|
@@ -101,9 +105,9 @@ module Mail
|
|
|
101
105
|
#
|
|
102
106
|
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
103
107
|
# a.local #=> 'mikel'
|
|
104
|
-
def local
|
|
108
|
+
def local(output_type = :decode)
|
|
105
109
|
parse unless @parsed
|
|
106
|
-
Encodings.decode_encode("#{@data.obs_domain_list}#{get_local.strip}",
|
|
110
|
+
Encodings.decode_encode("#{@data.obs_domain_list}#{get_local.strip}", output_type) if get_local
|
|
107
111
|
end
|
|
108
112
|
|
|
109
113
|
# Returns the domain part (the right hand side of the @ sign in the email address) of
|
|
@@ -111,19 +115,28 @@ module Mail
|
|
|
111
115
|
#
|
|
112
116
|
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
113
117
|
# a.domain #=> 'test.lindsaar.net'
|
|
114
|
-
def domain
|
|
118
|
+
def domain(output_type = :decode)
|
|
115
119
|
parse unless @parsed
|
|
116
|
-
Encodings.decode_encode(strip_all_comments(get_domain),
|
|
120
|
+
Encodings.decode_encode(strip_all_comments(get_domain), output_type) if get_domain
|
|
117
121
|
end
|
|
118
122
|
|
|
119
|
-
# Returns an array of comments that are in the email, or
|
|
123
|
+
# Returns an array of comments that are in the email, or nil if there
|
|
120
124
|
# are no comments
|
|
121
125
|
#
|
|
122
126
|
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
123
127
|
# a.comments #=> ['My email address']
|
|
128
|
+
#
|
|
129
|
+
# b = Address.new('Mikel Lindsaar <mikel@test.lindsaar.net>')
|
|
130
|
+
# b.comments #=> nil
|
|
131
|
+
|
|
124
132
|
def comments
|
|
125
133
|
parse unless @parsed
|
|
126
|
-
|
|
134
|
+
comments = get_comments
|
|
135
|
+
if comments.nil? || comments.none?
|
|
136
|
+
nil
|
|
137
|
+
else
|
|
138
|
+
comments.map { |c| c.squeeze(Constants::SPACE) }
|
|
139
|
+
end
|
|
127
140
|
end
|
|
128
141
|
|
|
129
142
|
# Sometimes an address will not have a display name, but might have the name
|
|
@@ -154,13 +167,11 @@ module Mail
|
|
|
154
167
|
end
|
|
155
168
|
|
|
156
169
|
def encoded
|
|
157
|
-
|
|
158
|
-
format
|
|
170
|
+
format :encode
|
|
159
171
|
end
|
|
160
172
|
|
|
161
173
|
def decoded
|
|
162
|
-
|
|
163
|
-
format
|
|
174
|
+
format :decode
|
|
164
175
|
end
|
|
165
176
|
|
|
166
177
|
def group
|
|
@@ -174,11 +185,11 @@ module Mail
|
|
|
174
185
|
@data = nil
|
|
175
186
|
|
|
176
187
|
case value
|
|
177
|
-
when Mail::Parsers::AddressStruct
|
|
188
|
+
when Mail::Parsers::AddressListsParser::AddressStruct
|
|
178
189
|
@data = value
|
|
179
190
|
when String
|
|
180
191
|
unless Utilities.blank?(value)
|
|
181
|
-
address_list = Mail::Parsers::AddressListsParser.
|
|
192
|
+
address_list = Mail::Parsers::AddressListsParser.parse(value)
|
|
182
193
|
@data = address_list.addresses.first
|
|
183
194
|
end
|
|
184
195
|
end
|
|
@@ -187,7 +198,7 @@ module Mail
|
|
|
187
198
|
def strip_all_comments(string)
|
|
188
199
|
unless Utilities.blank?(comments)
|
|
189
200
|
comments.each do |comment|
|
|
190
|
-
string = string.gsub("(#{comment})", EMPTY)
|
|
201
|
+
string = string.gsub("(#{comment})", Constants::EMPTY)
|
|
191
202
|
end
|
|
192
203
|
end
|
|
193
204
|
string.strip
|
|
@@ -197,7 +208,7 @@ module Mail
|
|
|
197
208
|
unless Utilities.blank?(comments)
|
|
198
209
|
comments.each do |comment|
|
|
199
210
|
if @data.domain && @data.domain.include?("(#{comment})")
|
|
200
|
-
value = value.gsub("(#{comment})", EMPTY)
|
|
211
|
+
value = value.gsub("(#{comment})", Constants::EMPTY)
|
|
201
212
|
end
|
|
202
213
|
end
|
|
203
214
|
end
|
|
@@ -205,9 +216,9 @@ module Mail
|
|
|
205
216
|
end
|
|
206
217
|
|
|
207
218
|
def get_display_name
|
|
208
|
-
if @data.display_name
|
|
219
|
+
if @data && @data.display_name
|
|
209
220
|
str = strip_all_comments(@data.display_name.to_s)
|
|
210
|
-
elsif @data.comments && @data.domain
|
|
221
|
+
elsif @data && @data.comments && @data.domain
|
|
211
222
|
str = strip_domain_comments(format_comments)
|
|
212
223
|
end
|
|
213
224
|
str unless Utilities.blank?(str)
|
|
@@ -217,15 +228,15 @@ module Mail
|
|
|
217
228
|
if display_name
|
|
218
229
|
str = display_name
|
|
219
230
|
elsif comments
|
|
220
|
-
str = "(#{comments.join(SPACE).squeeze(SPACE)})"
|
|
231
|
+
str = "(#{comments.join(Constants::SPACE).squeeze(Constants::SPACE)})"
|
|
221
232
|
end
|
|
222
233
|
|
|
223
|
-
unparen(str) unless Utilities.blank?(str)
|
|
234
|
+
Utilities.unparen(str) unless Utilities.blank?(str)
|
|
224
235
|
end
|
|
225
236
|
|
|
226
237
|
def format_comments
|
|
227
238
|
if comments
|
|
228
|
-
comment_text = comments.map {|c| escape_paren(c) }.join(SPACE).squeeze(SPACE)
|
|
239
|
+
comment_text = comments.map {|c| Utilities.escape_paren(c) }.join(Constants::SPACE).squeeze(Constants::SPACE)
|
|
229
240
|
@format_comments ||= "(#{comment_text})"
|
|
230
241
|
else
|
|
231
242
|
nil
|