mail 2.6.4 → 2.8.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 +111 -118
- data/lib/mail/attachments_list.rb +11 -10
- data/lib/mail/body.rb +73 -84
- data/lib/mail/check_delivery_params.rb +54 -10
- 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 +3 -1
- data/lib/mail/encodings.rb +81 -54
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +119 -98
- data/lib/mail/field_list.rb +60 -7
- data/lib/mail/fields/bcc_field.rb +34 -52
- data/lib/mail/fields/cc_field.rb +28 -49
- data/lib/mail/fields/comments_field.rb +27 -37
- data/lib/mail/fields/common_address_field.rb +170 -0
- data/lib/mail/fields/common_date_field.rb +58 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +42 -0
- data/lib/mail/fields/content_description_field.rb +7 -14
- data/lib/mail/fields/content_disposition_field.rb +13 -38
- data/lib/mail/fields/content_id_field.rb +24 -51
- data/lib/mail/fields/content_location_field.rb +11 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
- data/lib/mail/fields/content_type_field.rb +50 -80
- data/lib/mail/fields/date_field.rb +23 -52
- data/lib/mail/fields/from_field.rb +28 -49
- data/lib/mail/fields/in_reply_to_field.rb +38 -49
- data/lib/mail/fields/keywords_field.rb +18 -31
- data/lib/mail/fields/message_id_field.rb +25 -71
- data/lib/mail/fields/mime_version_field.rb +19 -30
- data/lib/mail/fields/named_structured_field.rb +11 -0
- data/lib/mail/fields/named_unstructured_field.rb +11 -0
- data/lib/mail/fields/optional_field.rb +9 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
- data/lib/mail/fields/received_field.rb +43 -57
- data/lib/mail/fields/references_field.rb +35 -49
- data/lib/mail/fields/reply_to_field.rb +28 -49
- data/lib/mail/fields/resent_bcc_field.rb +28 -49
- data/lib/mail/fields/resent_cc_field.rb +28 -49
- data/lib/mail/fields/resent_date_field.rb +5 -30
- data/lib/mail/fields/resent_from_field.rb +28 -49
- data/lib/mail/fields/resent_message_id_field.rb +5 -29
- data/lib/mail/fields/resent_sender_field.rb +27 -56
- data/lib/mail/fields/resent_to_field.rb +28 -49
- data/lib/mail/fields/return_path_field.rb +50 -54
- data/lib/mail/fields/sender_field.rb +34 -55
- data/lib/mail/fields/structured_field.rb +3 -30
- data/lib/mail/fields/subject_field.rb +9 -11
- data/lib/mail/fields/to_field.rb +28 -49
- data/lib/mail/fields/unstructured_field.rb +32 -47
- data/lib/mail/header.rb +71 -110
- data/lib/mail/mail.rb +2 -10
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/matchers/has_sent_mail.rb +21 -1
- data/lib/mail/message.rb +113 -117
- data/lib/mail/multibyte/chars.rb +23 -180
- data/lib/mail/multibyte/unicode.rb +10 -10
- 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 +13 -16
- 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 +76 -54
- data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -9
- data/lib/mail/network/delivery_methods/test_mailer.rb +8 -9
- data/lib/mail/network/retriever_methods/base.rb +8 -8
- data/lib/mail/network/retriever_methods/imap.rb +20 -7
- data/lib/mail/network/retriever_methods/pop3.rb +5 -3
- 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 +33225 -116
- data/lib/mail/parsers/address_lists_parser.rl +179 -0
- data/lib/mail/parsers/content_disposition_parser.rb +882 -49
- data/lib/mail/parsers/content_disposition_parser.rl +89 -0
- data/lib/mail/parsers/content_location_parser.rb +809 -23
- data/lib/mail/parsers/content_location_parser.rl +78 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
- data/lib/mail/parsers/content_type_parser.rb +1037 -56
- data/lib/mail/parsers/content_type_parser.rl +90 -0
- data/lib/mail/parsers/date_time_parser.rb +877 -25
- data/lib/mail/parsers/date_time_parser.rl +69 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
- data/lib/mail/parsers/envelope_from_parser.rl +89 -0
- data/lib/mail/parsers/message_ids_parser.rb +5146 -25
- data/lib/mail/parsers/message_ids_parser.rl +93 -0
- data/lib/mail/parsers/mime_version_parser.rb +497 -26
- data/lib/mail/parsers/mime_version_parser.rl +68 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
- data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
- data/lib/mail/parsers/received_parser.rb +8776 -43
- 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/{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 +6 -10
- data/lib/mail/parts_list.rb +62 -6
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +357 -74
- data/lib/mail/version.rb +2 -2
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +5 -35
- metadata +111 -66
- 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/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/fields/to_field.rb
CHANGED
@@ -1,55 +1,34 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# = To Field
|
5
|
-
#
|
6
|
-
# The To field inherits to StructuredField and handles the To: header
|
7
|
-
# field in the email.
|
8
|
-
#
|
9
|
-
# Sending to to a mail message will instantiate a Mail::Field object that
|
10
|
-
# has a ToField as its field type. This includes all Mail::CommonAddress
|
11
|
-
# module instance metods.
|
12
|
-
#
|
13
|
-
# Only one To field can appear in a header, though it can have multiple
|
14
|
-
# addresses and groups of addresses.
|
15
|
-
#
|
16
|
-
# == Examples:
|
17
|
-
#
|
18
|
-
# mail = Mail.new
|
19
|
-
# mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
20
|
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
21
|
-
# mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
22
|
-
# mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
23
|
-
# mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
|
24
|
-
#
|
25
|
-
# mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
|
26
|
-
# mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
27
|
-
# mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
28
|
-
# mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
|
29
|
-
#
|
30
|
-
require 'mail/fields/common/common_address'
|
3
|
+
require 'mail/fields/common_address_field'
|
31
4
|
|
32
5
|
module Mail
|
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,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
|
-
require 'mail/fields/
|
3
|
+
require 'mail/fields/common_field'
|
4
|
+
require 'mail/utilities'
|
4
5
|
|
5
6
|
module Mail
|
6
7
|
# Provides access to an unstructured header field
|
@@ -15,59 +16,39 @@ module Mail
|
|
15
16
|
# field bodies are simply to be treated as a single line of characters
|
16
17
|
# with no further processing (except for header "folding" and
|
17
18
|
# "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
|
-
|
19
|
+
class UnstructuredField < CommonField #:nodoc:
|
26
20
|
def initialize(name, value, charset = nil)
|
27
|
-
@errors = []
|
28
|
-
|
29
21
|
if value.is_a?(Array)
|
30
22
|
# Probably has arrived here from a failed parse of an AddressList Field
|
31
23
|
value = value.join(', ')
|
32
|
-
|
33
|
-
|
34
|
-
|
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?
|
35
29
|
end
|
36
30
|
|
37
|
-
|
38
|
-
self.charset = charset
|
39
|
-
else
|
31
|
+
charset ||=
|
40
32
|
if value.respond_to?(:encoding)
|
41
|
-
|
42
|
-
else
|
43
|
-
self.charset = $KCODE
|
33
|
+
value.encoding
|
44
34
|
end
|
45
|
-
end
|
46
|
-
self.name = name
|
47
|
-
self.value = value
|
48
|
-
self
|
49
|
-
end
|
50
35
|
|
51
|
-
|
52
|
-
do_encode
|
36
|
+
super name, value.to_s, charset
|
53
37
|
end
|
54
38
|
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
def default
|
60
|
-
decoded
|
61
|
-
end
|
62
|
-
|
63
|
-
def parse # An unstructured field does not parse
|
39
|
+
# An unstructured field does not parse
|
40
|
+
def parse
|
64
41
|
self
|
65
42
|
end
|
66
43
|
|
67
44
|
private
|
68
45
|
|
69
46
|
def do_encode
|
70
|
-
value.
|
47
|
+
if value && !value.empty?
|
48
|
+
"#{wrapped_value}\r\n"
|
49
|
+
else
|
50
|
+
''
|
51
|
+
end
|
71
52
|
end
|
72
53
|
|
73
54
|
def do_decode
|
@@ -121,7 +102,7 @@ module Mail
|
|
121
102
|
def fold(prepend = 0) # :nodoc:
|
122
103
|
encoding = normalized_encoding
|
123
104
|
decoded_string = decoded.to_s
|
124
|
-
should_encode = decoded_string.
|
105
|
+
should_encode = !decoded_string.ascii_only?
|
125
106
|
if should_encode
|
126
107
|
first = true
|
127
108
|
words = decoded_string.split(/[ \t]/).map do |word|
|
@@ -130,7 +111,7 @@ module Mail
|
|
130
111
|
else
|
131
112
|
word = " #{word}"
|
132
113
|
end
|
133
|
-
if word.
|
114
|
+
if !word.ascii_only?
|
134
115
|
word
|
135
116
|
else
|
136
117
|
word.scan(/.{7}|.+$/)
|
@@ -148,7 +129,14 @@ module Mail
|
|
148
129
|
first_word = true
|
149
130
|
while !words.empty?
|
150
131
|
break unless word = words.first.dup
|
151
|
-
|
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
|
+
|
152
140
|
word = encode(word) if should_encode
|
153
141
|
word = encode_crlf(word)
|
154
142
|
# Skip to next line if we're going to go past the limit
|
@@ -179,7 +167,7 @@ module Mail
|
|
179
167
|
end
|
180
168
|
|
181
169
|
def encode(value)
|
182
|
-
value = [value].pack(CAPITAL_M).gsub(EQUAL_LF, EMPTY)
|
170
|
+
value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
|
183
171
|
value.gsub!(/"/, '=22')
|
184
172
|
value.gsub!(/\(/, '=28')
|
185
173
|
value.gsub!(/\)/, '=29')
|
@@ -190,16 +178,13 @@ module Mail
|
|
190
178
|
end
|
191
179
|
|
192
180
|
def encode_crlf(value)
|
193
|
-
value.gsub!(CR, CR_ENCODED)
|
194
|
-
value.gsub!(LF, LF_ENCODED)
|
181
|
+
value.gsub!(Constants::CR, Constants::CR_ENCODED)
|
182
|
+
value.gsub!(Constants::LF, Constants::LF_ENCODED)
|
195
183
|
value
|
196
184
|
end
|
197
185
|
|
198
186
|
def normalized_encoding
|
199
|
-
|
200
|
-
encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
|
201
|
-
encoding
|
187
|
+
charset.to_s.upcase.gsub('_', '-')
|
202
188
|
end
|
203
|
-
|
204
189
|
end
|
205
190
|
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,29 +52,24 @@ 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
|
|
57
59
|
def initialize_copy(original)
|
58
60
|
super
|
59
61
|
@fields = @fields.dup
|
62
|
+
@fields.map!(&:dup)
|
60
63
|
end
|
61
|
-
|
62
|
-
# The preserved raw source of the header as you passed it in, untouched
|
63
|
-
# for your Regexing glory.
|
64
|
-
def raw_source
|
65
|
-
@raw_source
|
66
|
-
end
|
67
|
-
|
64
|
+
|
68
65
|
# Returns an array of all the fields in the header in order that they
|
69
66
|
# were read in.
|
70
67
|
def fields
|
71
68
|
@fields ||= FieldList.new
|
72
69
|
end
|
73
|
-
|
70
|
+
|
74
71
|
# 3.6. Field definitions
|
75
|
-
#
|
72
|
+
#
|
76
73
|
# It is important to note that the header fields are not guaranteed to
|
77
74
|
# be in a particular order. They may appear in any order, and they
|
78
75
|
# have been known to be reordered occasionally when transported over
|
@@ -82,35 +79,35 @@ module Mail
|
|
82
79
|
# header fields MUST NOT be reordered, and SHOULD be kept in blocks
|
83
80
|
# prepended to the message. See sections 3.6.6 and 3.6.7 for more
|
84
81
|
# information.
|
85
|
-
#
|
82
|
+
#
|
86
83
|
# Populates the fields container with Field objects in the order it
|
87
84
|
# receives them in.
|
88
85
|
#
|
89
86
|
# Acceps an array of field string values, for example:
|
90
|
-
#
|
87
|
+
#
|
91
88
|
# h = Header.new
|
92
89
|
# h.fields = ['From: mikel@me.com', 'To: bob@you.com']
|
93
90
|
def fields=(unfolded_fields)
|
94
91
|
@fields = Mail::FieldList.new
|
95
|
-
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
|
96
|
-
unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
|
97
92
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
else
|
102
|
-
@fields << field
|
103
|
-
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)
|
104
96
|
end
|
105
97
|
|
98
|
+
unfolded_fields.each do |field|
|
99
|
+
if field = Field.parse(field, charset)
|
100
|
+
@fields.add_field field
|
101
|
+
end
|
102
|
+
end
|
106
103
|
end
|
107
|
-
|
104
|
+
|
108
105
|
def errors
|
109
106
|
@fields.map(&:errors).flatten(1)
|
110
107
|
end
|
111
|
-
|
108
|
+
|
112
109
|
# 3.6. Field definitions
|
113
|
-
#
|
110
|
+
#
|
114
111
|
# The following table indicates limits on the number of times each
|
115
112
|
# field may occur in a message header as well as any special
|
116
113
|
# limitations on the use of those fields. An asterisk next to a value
|
@@ -120,35 +117,25 @@ module Mail
|
|
120
117
|
# <snip table from 3.6>
|
121
118
|
#
|
122
119
|
# As per RFC, many fields can appear more than once, we will return a string
|
123
|
-
# 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
|
124
121
|
# matching header, will return an array of values in order that they appear
|
125
122
|
# in the header ordered from top to bottom.
|
126
|
-
#
|
123
|
+
#
|
127
124
|
# Example:
|
128
|
-
#
|
125
|
+
#
|
129
126
|
# h = Header.new
|
130
127
|
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
|
131
128
|
# h['To'] #=> 'mikel@me.com'
|
132
129
|
# h['X-Mail-SPAM'] #=> ['15', '20']
|
133
130
|
def [](name)
|
134
|
-
|
135
|
-
name.downcase!
|
136
|
-
selected = select_field_for(name)
|
137
|
-
case
|
138
|
-
when selected.length > 1
|
139
|
-
selected.map { |f| f }
|
140
|
-
when !Utilities.blank?(selected)
|
141
|
-
selected.first
|
142
|
-
else
|
143
|
-
nil
|
144
|
-
end
|
131
|
+
fields.get_field(Utilities.dasherize(name))
|
145
132
|
end
|
146
|
-
|
133
|
+
|
147
134
|
# Sets the FIRST matching field in the header to passed value, or deletes
|
148
135
|
# the FIRST field matched from the header if passed nil
|
149
|
-
#
|
136
|
+
#
|
150
137
|
# Example:
|
151
|
-
#
|
138
|
+
#
|
152
139
|
# h = Header.new
|
153
140
|
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
|
154
141
|
# h['To'] = 'bob@you.com'
|
@@ -158,51 +145,39 @@ module Mail
|
|
158
145
|
# h['X-Mail-SPAM'] = nil
|
159
146
|
# h['X-Mail-SPAM'] # => nil
|
160
147
|
def []=(name, value)
|
161
|
-
name =
|
162
|
-
if name.include?(
|
148
|
+
name = name.to_s
|
149
|
+
if name.include?(Constants::COLON)
|
163
150
|
raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
|
164
151
|
end
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
fields.delete_if { |f| selected.include?(f) }
|
172
|
-
|
173
|
-
# User wants to change the field
|
174
|
-
when !Utilities.blank?(selected) && limited_field?(fn)
|
175
|
-
selected.first.update(fn, value)
|
176
|
-
|
177
|
-
# 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
|
178
158
|
else
|
179
|
-
|
180
|
-
|
181
|
-
end
|
182
|
-
if dasherize(fn) == "content-type"
|
159
|
+
fields.add_field Field.new(name.to_s, value, charset)
|
160
|
+
|
183
161
|
# Update charset if specified in Content-Type
|
184
|
-
|
185
|
-
|
162
|
+
if name == 'content-type'
|
163
|
+
params = self[:content_type].parameters rescue nil
|
164
|
+
@charset = params[:charset] if params && params[:charset]
|
165
|
+
end
|
186
166
|
end
|
187
167
|
end
|
188
|
-
|
189
|
-
def charset
|
190
|
-
@charset
|
191
|
-
end
|
192
|
-
|
168
|
+
|
193
169
|
def charset=(val)
|
194
170
|
params = self[:content_type].parameters rescue nil
|
195
171
|
if params
|
196
|
-
|
172
|
+
if val
|
173
|
+
params[:charset] = val
|
174
|
+
else
|
175
|
+
params.delete(:charset)
|
176
|
+
end
|
197
177
|
end
|
198
178
|
@charset = val
|
199
179
|
end
|
200
|
-
|
201
|
-
LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
|
202
|
-
message-id in-reply-to references subject
|
203
|
-
return-path content-type mime-version
|
204
|
-
content-transfer-encoding content-description
|
205
|
-
content-id content-disposition content-location]
|
180
|
+
|
206
181
|
|
207
182
|
def encoded
|
208
183
|
buffer = String.new
|
@@ -216,61 +191,47 @@ module Mail
|
|
216
191
|
def to_s
|
217
192
|
encoded
|
218
193
|
end
|
219
|
-
|
194
|
+
|
220
195
|
def decoded
|
221
|
-
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.'
|
222
197
|
end
|
223
198
|
|
224
199
|
def field_summary
|
225
|
-
fields.
|
200
|
+
fields.summary
|
226
201
|
end
|
227
202
|
|
228
203
|
# Returns true if the header has a Message-ID defined (empty or not)
|
229
204
|
def has_message_id?
|
230
|
-
|
205
|
+
fields.has_field? 'Message-ID'
|
231
206
|
end
|
232
207
|
|
233
208
|
# Returns true if the header has a Content-ID defined (empty or not)
|
234
209
|
def has_content_id?
|
235
|
-
|
210
|
+
fields.has_field? 'Content-ID'
|
236
211
|
end
|
237
212
|
|
238
213
|
# Returns true if the header has a Date defined (empty or not)
|
239
214
|
def has_date?
|
240
|
-
|
215
|
+
fields.has_field? 'Date'
|
241
216
|
end
|
242
217
|
|
243
218
|
# Returns true if the header has a MIME version defined (empty or not)
|
244
219
|
def has_mime_version?
|
245
|
-
|
220
|
+
fields.has_field? 'Mime-Version'
|
246
221
|
end
|
247
222
|
|
248
223
|
private
|
249
|
-
|
250
|
-
def raw_source=(val)
|
251
|
-
@raw_source = val
|
252
|
-
end
|
253
|
-
|
224
|
+
|
254
225
|
# Splits an unfolded and line break cleaned header into individual field
|
255
226
|
# strings.
|
256
227
|
def split_header
|
257
|
-
self.fields = raw_source.split(HEADER_SPLIT)
|
258
|
-
end
|
259
|
-
|
260
|
-
def select_field_for(name)
|
261
|
-
fields.select { |f| f.responsible_for?(name) }
|
262
|
-
end
|
263
|
-
|
264
|
-
def limited_field?(name)
|
265
|
-
LIMITED_FIELDS.include?(name.to_s.downcase)
|
228
|
+
self.fields = @raw_source.split(Constants::HEADER_SPLIT)
|
266
229
|
end
|
267
230
|
|
268
|
-
# Enumerable support; yield each field in order to the block if there is one,
|
269
|
-
# or return an Enumerator for them if there isn't.
|
270
|
-
def each( &block )
|
271
|
-
return self.fields.each( &block ) if block
|
272
|
-
self.fields.each
|
273
|
-
end
|
274
231
|
|
232
|
+
# Enumerable support. Yield each field in order.
|
233
|
+
def each(&block)
|
234
|
+
fields.each(&block)
|
235
|
+
end
|
275
236
|
end
|
276
237
|
end
|
data/lib/mail/mail.rb
CHANGED
@@ -90,19 +90,11 @@ module Mail
|
|
90
90
|
# Each mail object inherits the default set in Mail.delivery_method, however, on
|
91
91
|
# a per email basis, you can override the method:
|
92
92
|
#
|
93
|
-
# mail.delivery_method :
|
93
|
+
# mail.delivery_method :smtp
|
94
94
|
#
|
95
95
|
# Or you can override the method and pass in settings:
|
96
96
|
#
|
97
|
-
# mail.delivery_method :
|
98
|
-
#
|
99
|
-
# You can also just modify the settings:
|
100
|
-
#
|
101
|
-
# mail.delivery_settings = { :address => 'some.host' }
|
102
|
-
#
|
103
|
-
# The passed in hash is just merged against the defaults with +merge!+ and the result
|
104
|
-
# assigned the mail object. So the above example will change only the :address value
|
105
|
-
# of the global smtp_settings to be 'some.host', keeping all other values
|
97
|
+
# mail.delivery_method :smtp, :address => 'some.host'
|
106
98
|
def self.defaults(&block)
|
107
99
|
Configuration.instance.instance_eval(&block)
|
108
100
|
end
|
@@ -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
|
@@ -83,6 +83,16 @@ module Mail
|
|
83
83
|
self
|
84
84
|
end
|
85
85
|
|
86
|
+
def with_html(body)
|
87
|
+
@html_part_body = body
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def with_text(body)
|
92
|
+
@text_part_body = body
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
86
96
|
def description
|
87
97
|
result = "send a matching email"
|
88
98
|
result
|
@@ -108,7 +118,7 @@ module Mail
|
|
108
118
|
candidate_deliveries = deliveries
|
109
119
|
modifiers =
|
110
120
|
%w(sender recipients copy_recipients blind_copy_recipients subject
|
111
|
-
subject_matcher body body_matcher having_attachments attachments)
|
121
|
+
subject_matcher body body_matcher html_part_body text_part_body having_attachments attachments)
|
112
122
|
modifiers.each do |modifier_name|
|
113
123
|
next unless instance_variable_defined?("@#{modifier_name}")
|
114
124
|
candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
|
@@ -160,6 +170,14 @@ module Mail
|
|
160
170
|
@body_matcher.match delivery.body.raw_source
|
161
171
|
end
|
162
172
|
|
173
|
+
def matches_on_html_part_body?(delivery)
|
174
|
+
delivery.html_part.body == @html_part_body
|
175
|
+
end
|
176
|
+
|
177
|
+
def matches_on_text_part_body?(delivery)
|
178
|
+
delivery.text_part.body == @text_part_body
|
179
|
+
end
|
180
|
+
|
163
181
|
def explain_expectations
|
164
182
|
result = ''
|
165
183
|
result += "from #{@sender} " if instance_variable_defined?('@sender')
|
@@ -170,6 +188,8 @@ module Mail
|
|
170
188
|
result += "with subject matching \"#{@subject_matcher}\" " if instance_variable_defined?('@subject_matcher')
|
171
189
|
result += "with body \"#{@body}\" " if instance_variable_defined?('@body')
|
172
190
|
result += "with body matching \"#{@body_matcher}\" " if instance_variable_defined?('@body_matcher')
|
191
|
+
result += "with a text part matching \"#{@text_part_body}\" " if instance_variable_defined?('@text_part_body')
|
192
|
+
result += "with an HTML part matching \"#{@html_part_body}\" " if instance_variable_defined?('@html_part_body')
|
173
193
|
result
|
174
194
|
end
|
175
195
|
|