mail 2.5.5 → 2.8.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 +5 -5
- data/MIT-LICENSE +1 -1
- data/README.md +170 -108
- data/lib/mail/attachments_list.rb +13 -10
- data/lib/mail/body.rb +105 -91
- data/lib/mail/check_delivery_params.rb +30 -22
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/constants.rb +79 -0
- data/lib/mail/elements/address.rb +118 -174
- data/lib/mail/elements/address_list.rb +16 -56
- data/lib/mail/elements/content_disposition_element.rb +12 -22
- data/lib/mail/elements/content_location_element.rb +9 -17
- data/lib/mail/elements/content_transfer_encoding_element.rb +8 -19
- data/lib/mail/elements/content_type_element.rb +20 -30
- data/lib/mail/elements/date_time_element.rb +10 -21
- data/lib/mail/elements/envelope_from_element.rb +23 -31
- data/lib/mail/elements/message_ids_element.rb +22 -20
- data/lib/mail/elements/mime_version_element.rb +10 -21
- data/lib/mail/elements/phrase_list.rb +13 -15
- data/lib/mail/elements/received_element.rb +26 -21
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +10 -14
- data/lib/mail/encodings/8bit.rb +5 -18
- data/lib/mail/encodings/base64.rb +15 -10
- data/lib/mail/encodings/binary.rb +4 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +13 -7
- data/lib/mail/encodings/transfer_encoding.rb +47 -28
- data/lib/mail/encodings/unix_to_unix.rb +20 -0
- data/lib/mail/encodings.rb +102 -93
- data/lib/mail/envelope.rb +12 -19
- data/lib/mail/field.rb +143 -71
- data/lib/mail/field_list.rb +73 -19
- data/lib/mail/fields/bcc_field.rb +42 -48
- data/lib/mail/fields/cc_field.rb +29 -50
- data/lib/mail/fields/comments_field.rb +28 -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 +8 -14
- data/lib/mail/fields/content_disposition_field.rb +20 -44
- data/lib/mail/fields/content_id_field.rb +25 -51
- data/lib/mail/fields/content_location_field.rb +12 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +31 -36
- data/lib/mail/fields/content_type_field.rb +51 -80
- data/lib/mail/fields/date_field.rb +24 -52
- data/lib/mail/fields/from_field.rb +29 -50
- data/lib/mail/fields/in_reply_to_field.rb +39 -49
- data/lib/mail/fields/keywords_field.rb +19 -32
- data/lib/mail/fields/message_id_field.rb +26 -71
- data/lib/mail/fields/mime_version_field.rb +20 -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 +10 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
- data/lib/mail/fields/received_field.rb +44 -57
- data/lib/mail/fields/references_field.rb +36 -49
- data/lib/mail/fields/reply_to_field.rb +29 -50
- data/lib/mail/fields/resent_bcc_field.rb +29 -50
- data/lib/mail/fields/resent_cc_field.rb +29 -50
- data/lib/mail/fields/resent_date_field.rb +6 -30
- data/lib/mail/fields/resent_from_field.rb +29 -50
- data/lib/mail/fields/resent_message_id_field.rb +6 -29
- data/lib/mail/fields/resent_sender_field.rb +28 -57
- data/lib/mail/fields/resent_to_field.rb +29 -50
- data/lib/mail/fields/return_path_field.rb +51 -55
- data/lib/mail/fields/sender_field.rb +35 -56
- data/lib/mail/fields/structured_field.rb +4 -30
- data/lib/mail/fields/subject_field.rb +10 -11
- data/lib/mail/fields/to_field.rb +29 -50
- data/lib/mail/fields/unstructured_field.rb +43 -51
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +78 -129
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +18 -11
- data/lib/mail/matchers/attachment_matchers.rb +44 -0
- data/lib/mail/matchers/has_sent_mail.rb +81 -4
- data/lib/mail/message.rb +142 -139
- data/lib/mail/multibyte/chars.rb +24 -180
- data/lib/mail/multibyte/unicode.rb +32 -27
- data/lib/mail/multibyte/utils.rb +27 -43
- data/lib/mail/multibyte.rb +56 -16
- data/lib/mail/network/delivery_methods/exim.rb +6 -4
- data/lib/mail/network/delivery_methods/file_delivery.rb +12 -10
- data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +63 -21
- data/lib/mail/network/delivery_methods/smtp.rb +76 -50
- data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -4
- data/lib/mail/network/delivery_methods/test_mailer.rb +5 -2
- data/lib/mail/network/retriever_methods/base.rb +9 -8
- data/lib/mail/network/retriever_methods/imap.rb +37 -18
- data/lib/mail/network/retriever_methods/pop3.rb +6 -3
- data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
- data/lib/mail/network.rb +2 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33242 -0
- data/lib/mail/parsers/address_lists_parser.rl +179 -0
- data/lib/mail/parsers/content_disposition_parser.rb +901 -0
- data/lib/mail/parsers/content_disposition_parser.rl +89 -0
- data/lib/mail/parsers/content_location_parser.rb +822 -0
- data/lib/mail/parsers/content_location_parser.rl +78 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +522 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
- data/lib/mail/parsers/content_type_parser.rb +1048 -0
- data/lib/mail/parsers/content_type_parser.rl +90 -0
- data/lib/mail/parsers/date_time_parser.rb +891 -0
- data/lib/mail/parsers/date_time_parser.rl +69 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3675 -0
- data/lib/mail/parsers/envelope_from_parser.rl +89 -0
- data/lib/mail/parsers/message_ids_parser.rb +5161 -0
- data/lib/mail/parsers/message_ids_parser.rl +93 -0
- data/lib/mail/parsers/mime_version_parser.rb +513 -0
- data/lib/mail/parsers/mime_version_parser.rl +68 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +884 -0
- data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
- data/lib/mail/parsers/received_parser.rb +8782 -0
- data/lib/mail/parsers/received_parser.rl +91 -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/rfc5322_date_time.rl +37 -0
- data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/lib/mail/parsers.rb +13 -0
- data/lib/mail/part.rb +11 -12
- data/lib/mail/parts_list.rb +90 -14
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +415 -76
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +8 -15
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +9 -32
- metadata +127 -79
- data/CHANGELOG.rdoc +0 -742
- data/CONTRIBUTING.md +0 -45
- data/Dependencies.txt +0 -3
- data/Gemfile +0 -32
- data/Rakefile +0 -21
- data/TODO.rdoc +0 -9
- data/lib/VERSION +0 -4
- data/lib/load_parsers.rb +0 -35
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/smtp.rb +0 -24
- data/lib/mail/core_extensions/string/access.rb +0 -145
- data/lib/mail/core_extensions/string/multibyte.rb +0 -78
- data/lib/mail/core_extensions/string.rb +0 -33
- data/lib/mail/fields/common/address_container.rb +0 -16
- data/lib/mail/fields/common/common_address.rb +0 -140
- data/lib/mail/fields/common/common_date.rb +0 -42
- data/lib/mail/fields/common/common_field.rb +0 -57
- data/lib/mail/fields/common/common_message_id.rb +0 -48
- data/lib/mail/multibyte/exceptions.rb +0 -8
- data/lib/mail/parsers/address_lists.rb +0 -64
- data/lib/mail/parsers/address_lists.treetop +0 -19
- data/lib/mail/parsers/content_disposition.rb +0 -535
- data/lib/mail/parsers/content_disposition.treetop +0 -46
- data/lib/mail/parsers/content_location.rb +0 -139
- data/lib/mail/parsers/content_location.treetop +0 -20
- data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
- data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
- data/lib/mail/parsers/content_type.rb +0 -971
- data/lib/mail/parsers/content_type.treetop +0 -68
- data/lib/mail/parsers/date_time.rb +0 -114
- data/lib/mail/parsers/date_time.treetop +0 -11
- data/lib/mail/parsers/envelope_from.rb +0 -194
- data/lib/mail/parsers/envelope_from.treetop +0 -32
- data/lib/mail/parsers/message_ids.rb +0 -45
- data/lib/mail/parsers/message_ids.treetop +0 -15
- data/lib/mail/parsers/mime_version.rb +0 -144
- data/lib/mail/parsers/mime_version.treetop +0 -19
- data/lib/mail/parsers/phrase_lists.rb +0 -45
- data/lib/mail/parsers/phrase_lists.treetop +0 -15
- data/lib/mail/parsers/received.rb +0 -71
- data/lib/mail/parsers/received.treetop +0 -11
- data/lib/mail/parsers/rfc2045.rb +0 -421
- data/lib/mail/parsers/rfc2045.treetop +0 -35
- data/lib/mail/parsers/rfc2822.rb +0 -5397
- data/lib/mail/parsers/rfc2822.treetop +0 -408
- data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
- data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
- data/lib/mail/patterns.rb +0 -35
- data/lib/mail/version_specific/ruby_1_8.rb +0 -119
- data/lib/mail/version_specific/ruby_1_9.rb +0 -147
- data/lib/tasks/corpus.rake +0 -125
- data/lib/tasks/treetop.rake +0 -10
data/lib/mail/fields/to_field.rb
CHANGED
@@ -1,55 +1,34 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
#
|
3
|
-
|
4
|
-
#
|
5
|
-
# The To field inherits to StructuredField and handles the To: header
|
6
|
-
# field in the email.
|
7
|
-
#
|
8
|
-
# Sending to to a mail message will instantiate a Mail::Field object that
|
9
|
-
# has a ToField as its field type. This includes all Mail::CommonAddress
|
10
|
-
# module instance metods.
|
11
|
-
#
|
12
|
-
# Only one To field can appear in a header, though it can have multiple
|
13
|
-
# addresses and groups of addresses.
|
14
|
-
#
|
15
|
-
# == Examples:
|
16
|
-
#
|
17
|
-
# mail = Mail.new
|
18
|
-
# mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
19
|
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
20
|
-
# mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
21
|
-
# mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
22
|
-
# mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
23
|
-
#
|
24
|
-
# mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
|
25
|
-
# mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
26
|
-
# mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
27
|
-
# mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
|
28
|
-
#
|
29
|
-
require 'mail/fields/common/common_address'
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/fields/common_address_field'
|
30
4
|
|
31
5
|
module Mail
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
6
|
+
# = To Field
|
7
|
+
#
|
8
|
+
# The To field inherits to StructuredField and handles the To: header
|
9
|
+
# field in the email.
|
10
|
+
#
|
11
|
+
# Sending to to a mail message will instantiate a Mail::Field object that
|
12
|
+
# has a ToField as its field type. This includes all Mail::CommonAddress
|
13
|
+
# module instance metods.
|
14
|
+
#
|
15
|
+
# Only one To field can appear in a header, though it can have multiple
|
16
|
+
# addresses and groups of addresses.
|
17
|
+
#
|
18
|
+
# == Examples:
|
19
|
+
#
|
20
|
+
# mail = Mail.new
|
21
|
+
# mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
22
|
+
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
23
|
+
# mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
24
|
+
# mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
25
|
+
# mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
26
|
+
#
|
27
|
+
# mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
|
28
|
+
# mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
29
|
+
# mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
30
|
+
# mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
|
31
|
+
class ToField < CommonAddressField #:nodoc:
|
32
|
+
NAME = 'To'
|
54
33
|
end
|
55
34
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/fields/common_field'
|
4
|
+
require 'mail/utilities'
|
3
5
|
|
4
6
|
module Mail
|
5
7
|
# Provides access to an unstructured header field
|
@@ -14,63 +16,43 @@ module Mail
|
|
14
16
|
# field bodies are simply to be treated as a single line of characters
|
15
17
|
# with no further processing (except for header "folding" and
|
16
18
|
# "unfolding" as described in section 2.2.3).
|
17
|
-
class UnstructuredField
|
18
|
-
|
19
|
-
include Mail::CommonField
|
20
|
-
include Mail::Utilities
|
21
|
-
|
22
|
-
attr_accessor :charset
|
23
|
-
attr_reader :errors
|
24
|
-
|
19
|
+
class UnstructuredField < CommonField #:nodoc:
|
25
20
|
def initialize(name, value, charset = nil)
|
26
|
-
@errors = []
|
27
|
-
|
28
21
|
if value.is_a?(Array)
|
29
22
|
# Probably has arrived here from a failed parse of an AddressList Field
|
30
23
|
value = value.join(', ')
|
31
|
-
|
32
|
-
|
33
|
-
|
24
|
+
|
25
|
+
# Mark UTF-8 strings parsed from ASCII-8BIT
|
26
|
+
elsif value.respond_to?(:force_encoding) && value.encoding == Encoding::ASCII_8BIT
|
27
|
+
utf8 = value.dup.force_encoding(Encoding::UTF_8)
|
28
|
+
value = utf8 if utf8.valid_encoding?
|
34
29
|
end
|
35
30
|
|
36
|
-
|
37
|
-
self.charset = charset
|
38
|
-
else
|
31
|
+
charset ||=
|
39
32
|
if value.respond_to?(:encoding)
|
40
|
-
|
41
|
-
else
|
42
|
-
self.charset = $KCODE
|
33
|
+
value.encoding
|
43
34
|
end
|
44
|
-
end
|
45
|
-
self.name = name
|
46
|
-
self.value = value
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
def encoded
|
51
|
-
do_encode
|
52
|
-
end
|
53
35
|
|
54
|
-
|
55
|
-
do_decode
|
36
|
+
super name, value.to_s, charset
|
56
37
|
end
|
57
38
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
def parse # An unstructured field does not parse
|
39
|
+
# An unstructured field does not parse
|
40
|
+
def parse
|
63
41
|
self
|
64
42
|
end
|
65
43
|
|
66
44
|
private
|
67
45
|
|
68
46
|
def do_encode
|
69
|
-
value.
|
47
|
+
if value && !value.empty?
|
48
|
+
"#{wrapped_value}\r\n"
|
49
|
+
else
|
50
|
+
''
|
51
|
+
end
|
70
52
|
end
|
71
53
|
|
72
54
|
def do_decode
|
73
|
-
|
55
|
+
Utilities.blank?(value) ? nil : Encodings.decode_encode(value, :decode)
|
74
56
|
end
|
75
57
|
|
76
58
|
# 2.2.3. Long Header Fields
|
@@ -120,16 +102,16 @@ module Mail
|
|
120
102
|
def fold(prepend = 0) # :nodoc:
|
121
103
|
encoding = normalized_encoding
|
122
104
|
decoded_string = decoded.to_s
|
123
|
-
should_encode = decoded_string.
|
105
|
+
should_encode = !decoded_string.ascii_only?
|
124
106
|
if should_encode
|
125
107
|
first = true
|
126
108
|
words = decoded_string.split(/[ \t]/).map do |word|
|
127
109
|
if first
|
128
110
|
first = !first
|
129
111
|
else
|
130
|
-
word = " "
|
112
|
+
word = " #{word}"
|
131
113
|
end
|
132
|
-
if word.
|
114
|
+
if !word.ascii_only?
|
133
115
|
word
|
134
116
|
else
|
135
117
|
word.scan(/.{7}|.+$/)
|
@@ -143,10 +125,18 @@ module Mail
|
|
143
125
|
while !words.empty?
|
144
126
|
limit = 78 - prepend
|
145
127
|
limit = limit - 7 - encoding.length if should_encode
|
146
|
-
line =
|
128
|
+
line = String.new
|
129
|
+
first_word = true
|
147
130
|
while !words.empty?
|
148
131
|
break unless word = words.first.dup
|
149
|
-
|
132
|
+
|
133
|
+
# Convert on 1.9+ only since we aren't sure of the current
|
134
|
+
# charset encoding on 1.8. We'd need to track internal/external
|
135
|
+
# charset on each field.
|
136
|
+
if charset && word.respond_to?(:encoding)
|
137
|
+
word = Encodings.transcode_charset(word, word.encoding, charset)
|
138
|
+
end
|
139
|
+
|
150
140
|
word = encode(word) if should_encode
|
151
141
|
word = encode_crlf(word)
|
152
142
|
# Skip to next line if we're going to go past the limit
|
@@ -158,7 +148,12 @@ module Mail
|
|
158
148
|
# Remove the word from the queue ...
|
159
149
|
words.shift
|
160
150
|
# Add word separator
|
161
|
-
|
151
|
+
if first_word
|
152
|
+
first_word = false
|
153
|
+
else
|
154
|
+
line << " " if !should_encode
|
155
|
+
end
|
156
|
+
|
162
157
|
# ... add it in encoded form to the current line
|
163
158
|
line << word
|
164
159
|
end
|
@@ -172,7 +167,7 @@ module Mail
|
|
172
167
|
end
|
173
168
|
|
174
169
|
def encode(value)
|
175
|
-
value = [value].pack(
|
170
|
+
value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
|
176
171
|
value.gsub!(/"/, '=22')
|
177
172
|
value.gsub!(/\(/, '=28')
|
178
173
|
value.gsub!(/\)/, '=29')
|
@@ -183,16 +178,13 @@ module Mail
|
|
183
178
|
end
|
184
179
|
|
185
180
|
def encode_crlf(value)
|
186
|
-
value.gsub!(
|
187
|
-
value.gsub!(
|
181
|
+
value.gsub!(Constants::CR, Constants::CR_ENCODED)
|
182
|
+
value.gsub!(Constants::LF, Constants::LF_ENCODED)
|
188
183
|
value
|
189
184
|
end
|
190
185
|
|
191
186
|
def normalized_encoding
|
192
|
-
|
193
|
-
encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
|
194
|
-
encoding
|
187
|
+
charset.to_s.upcase.gsub('_', '-')
|
195
188
|
end
|
196
|
-
|
197
189
|
end
|
198
190
|
end
|
data/lib/mail/fields.rb
CHANGED
data/lib/mail/header.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/constants'
|
4
|
+
require 'mail/utilities'
|
5
|
+
|
2
6
|
module Mail
|
3
|
-
|
4
7
|
# Provides access to a header object.
|
5
|
-
#
|
8
|
+
#
|
6
9
|
# ===Per RFC2822
|
7
|
-
#
|
10
|
+
#
|
8
11
|
# 2.2. Header Fields
|
9
|
-
#
|
12
|
+
#
|
10
13
|
# Header fields are lines composed of a field name, followed by a colon
|
11
14
|
# (":"), followed by a field body, and terminated by CRLF. A field
|
12
15
|
# name MUST be composed of printable US-ASCII characters (i.e.,
|
@@ -17,14 +20,12 @@ module Mail
|
|
17
20
|
# 2.2.3. All field bodies MUST conform to the syntax described in
|
18
21
|
# sections 3 and 4 of this standard.
|
19
22
|
class Header
|
20
|
-
include Patterns
|
21
|
-
include Utilities
|
22
23
|
include Enumerable
|
23
|
-
|
24
|
+
|
24
25
|
@@maximum_amount = 1000
|
25
26
|
|
26
27
|
# Large amount of headers in Email might create extra high CPU load
|
27
|
-
# 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
|
28
29
|
# mail library.
|
29
30
|
# Default: 1000
|
30
31
|
def self.maximum_amount
|
@@ -35,12 +36,14 @@ module Mail
|
|
35
36
|
@@maximum_amount = value
|
36
37
|
end
|
37
38
|
|
39
|
+
attr_reader :raw_source, :charset
|
40
|
+
|
38
41
|
# Creates a new header object.
|
39
|
-
#
|
42
|
+
#
|
40
43
|
# Accepts raw text or nothing. If given raw text will attempt to parse
|
41
44
|
# it and split it into the various fields, instantiating each field as
|
42
45
|
# it goes.
|
43
|
-
#
|
46
|
+
#
|
44
47
|
# If it finds a field that should be a structured field (such as content
|
45
48
|
# type), but it fails to parse it, it will simply make it an unstructured
|
46
49
|
# field and leave it alone. This will mean that the data is preserved but
|
@@ -48,26 +51,25 @@ module Mail
|
|
48
51
|
# these cases, please make a patch and send it in, or at the least, send
|
49
52
|
# me the example so we can fix it.
|
50
53
|
def initialize(header_text = nil, charset = nil)
|
51
|
-
@errors = []
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@
|
58
|
+
|
59
|
+
def initialize_copy(original)
|
60
|
+
super
|
61
|
+
@fields = @fields.dup
|
62
|
+
@fields.map!(&:dup)
|
61
63
|
end
|
62
|
-
|
64
|
+
|
63
65
|
# Returns an array of all the fields in the header in order that they
|
64
66
|
# were read in.
|
65
67
|
def fields
|
66
68
|
@fields ||= FieldList.new
|
67
69
|
end
|
68
|
-
|
70
|
+
|
69
71
|
# 3.6. Field definitions
|
70
|
-
#
|
72
|
+
#
|
71
73
|
# It is important to note that the header fields are not guaranteed to
|
72
74
|
# be in a particular order. They may appear in any order, and they
|
73
75
|
# have been known to be reordered occasionally when transported over
|
@@ -77,36 +79,35 @@ module Mail
|
|
77
79
|
# header fields MUST NOT be reordered, and SHOULD be kept in blocks
|
78
80
|
# prepended to the message. See sections 3.6.6 and 3.6.7 for more
|
79
81
|
# information.
|
80
|
-
#
|
82
|
+
#
|
81
83
|
# Populates the fields container with Field objects in the order it
|
82
84
|
# receives them in.
|
83
85
|
#
|
84
86
|
# Acceps an array of field string values, for example:
|
85
|
-
#
|
87
|
+
#
|
86
88
|
# h = Header.new
|
87
89
|
# h.fields = ['From: mikel@me.com', 'To: bob@you.com']
|
88
90
|
def fields=(unfolded_fields)
|
89
91
|
@fields = Mail::FieldList.new
|
90
|
-
warn "Warning: more than #{self.class.maximum_amount} header fields only using the first #{self.class.maximum_amount}" if unfolded_fields.length > self.class.maximum_amount
|
91
|
-
unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
|
92
92
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
selected.first.update(field.name, field.value)
|
97
|
-
else
|
98
|
-
@fields << field
|
99
|
-
end
|
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)
|
100
96
|
end
|
101
97
|
|
98
|
+
unfolded_fields.each do |field|
|
99
|
+
if field = Field.parse(field, charset)
|
100
|
+
@fields.add_field field
|
101
|
+
end
|
102
|
+
end
|
102
103
|
end
|
103
|
-
|
104
|
+
|
104
105
|
def errors
|
105
|
-
@errors
|
106
|
+
@fields.map(&:errors).flatten(1)
|
106
107
|
end
|
107
|
-
|
108
|
+
|
108
109
|
# 3.6. Field definitions
|
109
|
-
#
|
110
|
+
#
|
110
111
|
# The following table indicates limits on the number of times each
|
111
112
|
# field may occur in a message header as well as any special
|
112
113
|
# limitations on the use of those fields. An asterisk next to a value
|
@@ -116,34 +117,25 @@ module Mail
|
|
116
117
|
# <snip table from 3.6>
|
117
118
|
#
|
118
119
|
# As per RFC, many fields can appear more than once, we will return a string
|
119
|
-
# 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
|
120
121
|
# matching header, will return an array of values in order that they appear
|
121
122
|
# in the header ordered from top to bottom.
|
122
|
-
#
|
123
|
+
#
|
123
124
|
# Example:
|
124
|
-
#
|
125
|
+
#
|
125
126
|
# h = Header.new
|
126
127
|
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
|
127
128
|
# h['To'] #=> 'mikel@me.com'
|
128
129
|
# h['X-Mail-SPAM'] #=> ['15', '20']
|
129
130
|
def [](name)
|
130
|
-
|
131
|
-
selected = select_field_for(name)
|
132
|
-
case
|
133
|
-
when selected.length > 1
|
134
|
-
selected.map { |f| f }
|
135
|
-
when !selected.blank?
|
136
|
-
selected.first
|
137
|
-
else
|
138
|
-
nil
|
139
|
-
end
|
131
|
+
fields.get_field(Utilities.dasherize(name))
|
140
132
|
end
|
141
|
-
|
133
|
+
|
142
134
|
# Sets the FIRST matching field in the header to passed value, or deletes
|
143
135
|
# the FIRST field matched from the header if passed nil
|
144
|
-
#
|
136
|
+
#
|
145
137
|
# Example:
|
146
|
-
#
|
138
|
+
#
|
147
139
|
# h = Header.new
|
148
140
|
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
|
149
141
|
# h['To'] = 'bob@you.com'
|
@@ -153,54 +145,42 @@ module Mail
|
|
153
145
|
# h['X-Mail-SPAM'] = nil
|
154
146
|
# h['X-Mail-SPAM'] # => nil
|
155
147
|
def []=(name, value)
|
156
|
-
name =
|
157
|
-
if name.include?(
|
148
|
+
name = name.to_s
|
149
|
+
if name.include?(Constants::COLON)
|
158
150
|
raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
|
159
151
|
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
fields.delete_if { |f| selected.include?(f) }
|
167
|
-
|
168
|
-
# User wants to change the field
|
169
|
-
when !selected.blank? && limited_field?(fn)
|
170
|
-
selected.first.update(fn, value)
|
171
|
-
|
172
|
-
# 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
|
173
158
|
else
|
174
|
-
|
175
|
-
|
176
|
-
end
|
177
|
-
if dasherize(fn) == "content-type"
|
159
|
+
fields.add_field Field.new(name.to_s, value, charset)
|
160
|
+
|
178
161
|
# Update charset if specified in Content-Type
|
179
|
-
|
180
|
-
|
162
|
+
if name == 'content-type'
|
163
|
+
params = self[:content_type].parameters rescue nil
|
164
|
+
@charset = params[:charset] if params && params[:charset]
|
165
|
+
end
|
181
166
|
end
|
182
167
|
end
|
183
|
-
|
184
|
-
def charset
|
185
|
-
@charset
|
186
|
-
end
|
187
|
-
|
168
|
+
|
188
169
|
def charset=(val)
|
189
170
|
params = self[:content_type].parameters rescue nil
|
190
171
|
if params
|
191
|
-
|
172
|
+
if val
|
173
|
+
params[:charset] = val
|
174
|
+
else
|
175
|
+
params.delete(:charset)
|
176
|
+
end
|
192
177
|
end
|
193
178
|
@charset = val
|
194
179
|
end
|
195
|
-
|
196
|
-
LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
|
197
|
-
message-id in-reply-to references subject
|
198
|
-
return-path content-type mime-version
|
199
|
-
content-transfer-encoding content-description
|
200
|
-
content-id content-disposition content-location]
|
180
|
+
|
201
181
|
|
202
182
|
def encoded
|
203
|
-
buffer =
|
183
|
+
buffer = String.new
|
204
184
|
buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
|
205
185
|
fields.each do |field|
|
206
186
|
buffer << field.encoded
|
@@ -211,78 +191,47 @@ module Mail
|
|
211
191
|
def to_s
|
212
192
|
encoded
|
213
193
|
end
|
214
|
-
|
194
|
+
|
215
195
|
def decoded
|
216
|
-
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.'
|
217
197
|
end
|
218
198
|
|
219
199
|
def field_summary
|
220
|
-
fields.
|
200
|
+
fields.summary
|
221
201
|
end
|
222
202
|
|
223
203
|
# Returns true if the header has a Message-ID defined (empty or not)
|
224
204
|
def has_message_id?
|
225
|
-
|
205
|
+
fields.has_field? 'Message-ID'
|
226
206
|
end
|
227
207
|
|
228
208
|
# Returns true if the header has a Content-ID defined (empty or not)
|
229
209
|
def has_content_id?
|
230
|
-
|
210
|
+
fields.has_field? 'Content-ID'
|
231
211
|
end
|
232
212
|
|
233
213
|
# Returns true if the header has a Date defined (empty or not)
|
234
214
|
def has_date?
|
235
|
-
|
215
|
+
fields.has_field? 'Date'
|
236
216
|
end
|
237
217
|
|
238
218
|
# Returns true if the header has a MIME version defined (empty or not)
|
239
219
|
def has_mime_version?
|
240
|
-
|
220
|
+
fields.has_field? 'Mime-Version'
|
241
221
|
end
|
242
222
|
|
243
223
|
private
|
244
|
-
|
245
|
-
def raw_source=(val)
|
246
|
-
@raw_source = val
|
247
|
-
end
|
248
|
-
|
249
|
-
# 2.2.3. Long Header Fields
|
250
|
-
#
|
251
|
-
# The process of moving from this folded multiple-line representation
|
252
|
-
# of a header field to its single line representation is called
|
253
|
-
# "unfolding". Unfolding is accomplished by simply removing any CRLF
|
254
|
-
# that is immediately followed by WSP. Each header field should be
|
255
|
-
# treated in its unfolded form for further syntactic and semantic
|
256
|
-
# evaluation.
|
257
|
-
def unfold(string)
|
258
|
-
string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
|
259
|
-
end
|
260
|
-
|
261
|
-
# Returns the header with all the folds removed
|
262
|
-
def unfolded_header
|
263
|
-
@unfolded_header ||= unfold(raw_source)
|
264
|
-
end
|
265
|
-
|
224
|
+
|
266
225
|
# Splits an unfolded and line break cleaned header into individual field
|
267
226
|
# strings.
|
268
227
|
def split_header
|
269
|
-
self.fields =
|
270
|
-
end
|
271
|
-
|
272
|
-
def select_field_for(name)
|
273
|
-
fields.select { |f| f.responsible_for?(name) }
|
274
|
-
end
|
275
|
-
|
276
|
-
def limited_field?(name)
|
277
|
-
LIMITED_FIELDS.include?(name.to_s.downcase)
|
228
|
+
self.fields = @raw_source.split(Constants::HEADER_SPLIT)
|
278
229
|
end
|
279
230
|
|
280
|
-
# Enumerable support; yield each field in order to the block if there is one,
|
281
|
-
# or return an Enumerator for them if there isn't.
|
282
|
-
def each( &block )
|
283
|
-
return self.fields.each( &block ) if block
|
284
|
-
self.fields.each
|
285
|
-
end
|
286
231
|
|
232
|
+
# Enumerable support. Yield each field in order.
|
233
|
+
def each(&block)
|
234
|
+
fields.each(&block)
|
235
|
+
end
|
287
236
|
end
|
288
237
|
end
|
data/lib/mail/mail.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
module Mail
|
3
4
|
|
4
5
|
# Allows you to create a new Mail::Message object.
|
@@ -89,19 +90,11 @@ module Mail
|
|
89
90
|
# Each mail object inherits the default set in Mail.delivery_method, however, on
|
90
91
|
# a per email basis, you can override the method:
|
91
92
|
#
|
92
|
-
# mail.delivery_method :
|
93
|
+
# mail.delivery_method :smtp
|
93
94
|
#
|
94
95
|
# Or you can override the method and pass in settings:
|
95
96
|
#
|
96
|
-
# mail.delivery_method :
|
97
|
-
#
|
98
|
-
# You can also just modify the settings:
|
99
|
-
#
|
100
|
-
# mail.delivery_settings = { :address => 'some.host' }
|
101
|
-
#
|
102
|
-
# The passed in hash is just merged against the defaults with +merge!+ and the result
|
103
|
-
# assigned the mail object. So the above example will change only the :address value
|
104
|
-
# of the global smtp_settings to be 'some.host', keeping all other values
|
97
|
+
# mail.delivery_method :smtp, :address => 'some.host'
|
105
98
|
def self.defaults(&block)
|
106
99
|
Configuration.instance.instance_eval(&block)
|
107
100
|
end
|
@@ -206,6 +199,12 @@ module Mail
|
|
206
199
|
end
|
207
200
|
end
|
208
201
|
|
202
|
+
# Unregister the given observer, allowing mail to resume operations
|
203
|
+
# without it.
|
204
|
+
def self.unregister_observer(observer)
|
205
|
+
@@delivery_notification_observers.delete(observer)
|
206
|
+
end
|
207
|
+
|
209
208
|
# You can register an object to be given every mail object that will be sent,
|
210
209
|
# before it is sent. So if you want to add special headers or modify any
|
211
210
|
# email that gets sent through the Mail library, you can do so.
|
@@ -219,6 +218,12 @@ module Mail
|
|
219
218
|
end
|
220
219
|
end
|
221
220
|
|
221
|
+
# Unregister the given interceptor, allowing mail to resume operations
|
222
|
+
# without it.
|
223
|
+
def self.unregister_interceptor(interceptor)
|
224
|
+
@@delivery_interceptors.delete(interceptor)
|
225
|
+
end
|
226
|
+
|
222
227
|
def self.inform_observers(mail)
|
223
228
|
@@delivery_notification_observers.each do |observer|
|
224
229
|
observer.delivered_email(mail)
|
@@ -233,9 +238,11 @@ module Mail
|
|
233
238
|
|
234
239
|
protected
|
235
240
|
|
241
|
+
RANDOM_TAG='%x%x_%x%x%d%x'
|
242
|
+
|
236
243
|
def self.random_tag
|
237
244
|
t = Time.now
|
238
|
-
sprintf(
|
245
|
+
sprintf(RANDOM_TAG,
|
239
246
|
t.to_i, t.tv_usec,
|
240
247
|
$$, Thread.current.object_id.abs, self.uniq, rand(255))
|
241
248
|
end
|