mail 2.5.4 → 2.6.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 +7 -0
- data/CHANGELOG.rdoc +7 -2
- data/CONTRIBUTING.md +13 -0
- data/Dependencies.txt +0 -1
- data/Gemfile +9 -7
- data/README.md +41 -21
- data/Rakefile +10 -2
- data/VERSION +4 -0
- data/lib/mail/body.rb +2 -2
- data/lib/mail/core_extensions/string.rb +12 -2
- data/lib/mail/elements/address.rb +38 -82
- data/lib/mail/elements/address_list.rb +19 -42
- data/lib/mail/elements/content_disposition_element.rb +3 -7
- data/lib/mail/elements/content_location_element.rb +2 -6
- data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
- data/lib/mail/elements/content_type_element.rb +4 -8
- data/lib/mail/elements/date_time_element.rb +3 -7
- data/lib/mail/elements/envelope_from_element.rb +3 -11
- data/lib/mail/elements/message_ids_element.rb +1 -6
- data/lib/mail/elements/mime_version_element.rb +3 -7
- data/lib/mail/elements/phrase_list.rb +2 -7
- data/lib/mail/elements/received_element.rb +3 -7
- data/lib/mail/encodings.rb +0 -1
- data/lib/mail/envelope.rb +0 -5
- data/lib/mail/field.rb +53 -17
- data/lib/mail/field_list.rb +18 -18
- data/lib/mail/fields/common/common_address.rb +15 -20
- data/lib/mail/fields/common/common_date.rb +0 -7
- data/lib/mail/fields/common/common_field.rb +1 -1
- data/lib/mail/fields/content_transfer_encoding_field.rb +0 -6
- data/lib/mail/fields/resent_sender_field.rb +1 -1
- data/lib/mail/fields/sender_field.rb +1 -1
- data/lib/mail/fields/unstructured_field.rb +7 -1
- data/lib/mail/header.rb +8 -22
- data/lib/mail/mail.rb +12 -0
- data/lib/mail/matchers/has_sent_mail.rb +34 -1
- data/lib/mail/message.rb +18 -11
- data/lib/mail/multibyte/unicode.rb +1 -1
- data/lib/mail/network/delivery_methods/sendmail.rb +1 -1
- data/lib/mail/network/delivery_methods/smtp.rb +5 -9
- data/lib/mail/network/retriever_methods/imap.rb +18 -13
- data/lib/mail/parsers/address_lists_parser.rb +132 -0
- data/lib/mail/parsers/content_disposition_parser.rb +67 -0
- data/lib/mail/parsers/content_location_parser.rb +35 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
- data/lib/mail/parsers/content_type_parser.rb +64 -0
- data/lib/mail/parsers/date_time_parser.rb +36 -0
- data/lib/mail/parsers/envelope_from_parser.rb +45 -0
- data/lib/mail/parsers/message_ids_parser.rb +39 -0
- data/lib/mail/parsers/mime_version_parser.rb +41 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
- data/lib/mail/parsers/ragel/common.rl +184 -0
- data/lib/mail/parsers/ragel/date_time.rl +30 -0
- data/lib/mail/parsers/ragel/parser_info.rb +61 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
- data/lib/mail/parsers/ragel/ruby.rb +29 -0
- data/lib/mail/parsers/ragel.rb +17 -0
- data/lib/mail/parsers/received_parser.rb +47 -0
- data/lib/mail/parsers.rb +26 -0
- data/lib/mail/parts_list.rb +5 -3
- data/lib/mail/patterns.rb +3 -1
- data/lib/mail/utilities.rb +3 -1
- data/lib/mail/version.rb +1 -1
- data/lib/mail/version_specific/ruby_1_8.rb +1 -1
- data/lib/mail/version_specific/ruby_1_9.rb +16 -1
- data/lib/mail.rb +1 -1
- metadata +77 -87
- data/lib/VERSION +0 -4
- data/lib/load_parsers.rb +0 -35
- 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/tasks/corpus.rake +0 -125
- data/lib/tasks/treetop.rake +0 -10
|
@@ -5,17 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@date_time = ::DateTime.parse("#{tree.ctime_date.text_value}")
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(EnvelopeFromElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def tree
|
|
18
|
-
@tree
|
|
8
|
+
@envelope_from = Mail::Parsers::EnvelopeFromParser.new.parse(string)
|
|
9
|
+
@address = @envelope_from.address
|
|
10
|
+
@date_time = ::DateTime.parse(@envelope_from.ctime_date)
|
|
19
11
|
end
|
|
20
12
|
|
|
21
13
|
def date_time
|
|
@@ -5,12 +5,7 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize(string)
|
|
8
|
-
|
|
9
|
-
if tree = parser.parse(string)
|
|
10
|
-
@message_ids = tree.message_ids.map { |msg_id| clean_msg_id(msg_id.text_value) }
|
|
11
|
-
else
|
|
12
|
-
raise Mail::Field::ParseError.new(MessageIdsElement, string, parser.failure_reason)
|
|
13
|
-
end
|
|
8
|
+
@message_ids = Mail::Parsers::MessageIdsParser.new.parse(string).message_ids.map { |msg_id| clean_msg_id(msg_id) }
|
|
14
9
|
end
|
|
15
10
|
|
|
16
11
|
def message_ids
|
|
@@ -5,13 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@minor = tree.minor.text_value
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(MimeVersionElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
8
|
+
mime_version = Mail::Parsers::MimeVersionParser.new.parse(string)
|
|
9
|
+
@major = mime_version.major
|
|
10
|
+
@minor = mime_version.minor
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def major
|
|
@@ -5,16 +5,11 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize(string)
|
|
8
|
-
|
|
9
|
-
if tree = parser.parse(string)
|
|
10
|
-
@phrases = tree.phrases
|
|
11
|
-
else
|
|
12
|
-
raise Mail::Field::ParseError.new(PhraseList, string, parser.failure_reason)
|
|
13
|
-
end
|
|
8
|
+
@phrase_lists = Mail::Parsers::PhraseListsParser.new.parse(string)
|
|
14
9
|
end
|
|
15
10
|
|
|
16
11
|
def phrases
|
|
17
|
-
@phrases.map { |p| unquote(p
|
|
12
|
+
@phrase_lists.phrases.map { |p| unquote(p) }
|
|
18
13
|
end
|
|
19
14
|
|
|
20
15
|
end
|
|
@@ -5,13 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@info = tree.name_val_list.text_value
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(ReceivedElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
8
|
+
received = Mail::Parsers::ReceivedParser.new.parse(string)
|
|
9
|
+
@date_time = ::DateTime.parse("#{received.date} #{received.time}")
|
|
10
|
+
@info = received.info
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def date_time
|
data/lib/mail/encodings.rb
CHANGED
data/lib/mail/envelope.rb
CHANGED
|
@@ -14,11 +14,6 @@ module Mail
|
|
|
14
14
|
super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def tree
|
|
18
|
-
@element ||= Mail::EnvelopeFromElement.new(value)
|
|
19
|
-
@tree ||= @element.tree
|
|
20
|
-
end
|
|
21
|
-
|
|
22
17
|
def element
|
|
23
18
|
@element ||= Mail::EnvelopeFromElement.new(value)
|
|
24
19
|
end
|
data/lib/mail/field.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Mail
|
|
|
22
22
|
#
|
|
23
23
|
class Field
|
|
24
24
|
|
|
25
|
-
include
|
|
25
|
+
include Utilities
|
|
26
26
|
include Comparable
|
|
27
27
|
|
|
28
28
|
STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
|
|
@@ -67,6 +67,10 @@ module Mail
|
|
|
67
67
|
"content-location" => ContentLocationField,
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
FIELD_NAME_MAP = FIELDS_MAP.inject({}) do |map, (field, field_klass)|
|
|
71
|
+
map.update(field => field_klass::CAPITALIZED_FIELD)
|
|
72
|
+
end
|
|
73
|
+
|
|
70
74
|
# Generic Field Exception
|
|
71
75
|
class FieldError < StandardError
|
|
72
76
|
end
|
|
@@ -110,15 +114,22 @@ module Mail
|
|
|
110
114
|
def initialize(name, value = nil, charset = 'utf-8')
|
|
111
115
|
case
|
|
112
116
|
when name =~ /:/ # Field.new("field-name: field data")
|
|
113
|
-
charset = value
|
|
114
|
-
name
|
|
115
|
-
|
|
117
|
+
@charset = value.blank? ? charset : value
|
|
118
|
+
@name = name[FIELD_PREFIX]
|
|
119
|
+
@raw_value = name
|
|
120
|
+
@value = nil
|
|
116
121
|
when name !~ /:/ && value.blank? # Field.new("field-name")
|
|
117
|
-
|
|
122
|
+
@name = name
|
|
123
|
+
@value = nil
|
|
124
|
+
@raw_value = nil
|
|
125
|
+
@charset = charset
|
|
118
126
|
else # Field.new("field-name", "value")
|
|
119
|
-
|
|
127
|
+
@name = name
|
|
128
|
+
@value = value
|
|
129
|
+
@raw_value = nil
|
|
130
|
+
@charset = charset
|
|
120
131
|
end
|
|
121
|
-
|
|
132
|
+
@name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
|
|
122
133
|
end
|
|
123
134
|
|
|
124
135
|
def field=(value)
|
|
@@ -126,11 +137,12 @@ module Mail
|
|
|
126
137
|
end
|
|
127
138
|
|
|
128
139
|
def field
|
|
129
|
-
@
|
|
140
|
+
_, @value = split(@raw_value) if @raw_value && !@value
|
|
141
|
+
@field ||= create_field(@name, @value, @charset)
|
|
130
142
|
end
|
|
131
143
|
|
|
132
144
|
def name
|
|
133
|
-
|
|
145
|
+
@name
|
|
134
146
|
end
|
|
135
147
|
|
|
136
148
|
def value
|
|
@@ -138,19 +150,29 @@ module Mail
|
|
|
138
150
|
end
|
|
139
151
|
|
|
140
152
|
def value=(val)
|
|
141
|
-
create_field(name, val, charset)
|
|
153
|
+
@field = create_field(name, val, @charset)
|
|
142
154
|
end
|
|
143
155
|
|
|
144
156
|
def to_s
|
|
145
157
|
field.to_s
|
|
146
158
|
end
|
|
147
159
|
|
|
160
|
+
def inspect
|
|
161
|
+
"#<#{self.class.name} 0x#{(object_id * 2).to_s(16)} #{instance_variables.map do |ivar|
|
|
162
|
+
"#{ivar}=#{instance_variable_get(ivar).inspect}"
|
|
163
|
+
end.join(" ")}>"
|
|
164
|
+
end
|
|
165
|
+
|
|
148
166
|
def update(name, value)
|
|
149
|
-
create_field(name, value, charset)
|
|
167
|
+
@field = create_field(name, value, @charset)
|
|
150
168
|
end
|
|
151
169
|
|
|
152
170
|
def same( other )
|
|
153
|
-
match_to_s(other.name,
|
|
171
|
+
match_to_s(other.name, self.name)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def responsible_for?( val )
|
|
175
|
+
name.to_s.casecmp(val.to_s) == 0
|
|
154
176
|
end
|
|
155
177
|
|
|
156
178
|
alias_method :==, :same
|
|
@@ -182,18 +204,32 @@ module Mail
|
|
|
182
204
|
|
|
183
205
|
def split(raw_field)
|
|
184
206
|
match_data = raw_field.mb_chars.match(FIELD_SPLIT)
|
|
185
|
-
[match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip]
|
|
207
|
+
[match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip.to_s]
|
|
186
208
|
rescue
|
|
187
209
|
STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
|
|
188
210
|
end
|
|
189
211
|
|
|
212
|
+
# 2.2.3. Long Header Fields
|
|
213
|
+
#
|
|
214
|
+
# The process of moving from this folded multiple-line representation
|
|
215
|
+
# of a header field to its single line representation is called
|
|
216
|
+
# "unfolding". Unfolding is accomplished by simply removing any CRLF
|
|
217
|
+
# that is immediately followed by WSP. Each header field should be
|
|
218
|
+
# treated in its unfolded form for further syntactic and semantic
|
|
219
|
+
# evaluation.
|
|
220
|
+
def unfold(string)
|
|
221
|
+
string.gsub(/[\r\n \t]+/m, ' ')
|
|
222
|
+
end
|
|
223
|
+
|
|
190
224
|
def create_field(name, value, charset)
|
|
225
|
+
value = unfold(value) if value.is_a?(String)
|
|
226
|
+
|
|
191
227
|
begin
|
|
192
|
-
|
|
228
|
+
new_field(name, value, charset)
|
|
193
229
|
rescue Mail::Field::ParseError => e
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
230
|
+
field = Mail::UnstructuredField.new(name, value)
|
|
231
|
+
field.errors << [name, value, e]
|
|
232
|
+
field
|
|
197
233
|
end
|
|
198
234
|
end
|
|
199
235
|
|
data/lib/mail/field_list.rb
CHANGED
|
@@ -8,26 +8,26 @@ module Mail
|
|
|
8
8
|
|
|
9
9
|
include Enumerable
|
|
10
10
|
|
|
11
|
+
# Insert the field in sorted order.
|
|
12
|
+
#
|
|
13
|
+
# Heavily based on bisect.insort from Python, which is:
|
|
14
|
+
# Copyright (C) 2001-2013 Python Software Foundation.
|
|
15
|
+
# Licensed under <http://docs.python.org/license.html>
|
|
16
|
+
# From <http://hg.python.org/cpython/file/2.7/Lib/bisect.py>
|
|
11
17
|
def <<( new_field )
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
when 0
|
|
22
|
-
next
|
|
23
|
-
when 1
|
|
24
|
-
insert_idx = idx
|
|
25
|
-
break
|
|
26
|
-
end
|
|
18
|
+
lo = 0
|
|
19
|
+
hi = size
|
|
20
|
+
|
|
21
|
+
while lo < hi
|
|
22
|
+
mid = (lo + hi) / 2
|
|
23
|
+
if new_field < self[mid]
|
|
24
|
+
hi = mid
|
|
25
|
+
else
|
|
26
|
+
lo = mid + 1
|
|
27
27
|
end
|
|
28
|
-
insert(insert_idx, new_field)
|
|
29
28
|
end
|
|
29
|
+
|
|
30
|
+
insert(lo, new_field)
|
|
30
31
|
end
|
|
31
|
-
|
|
32
32
|
end
|
|
33
|
-
end
|
|
33
|
+
end
|
|
@@ -6,7 +6,7 @@ module Mail
|
|
|
6
6
|
|
|
7
7
|
def parse(val = value)
|
|
8
8
|
unless val.blank?
|
|
9
|
-
@
|
|
9
|
+
@address_list = AddressList.new(encode_if_needed(val))
|
|
10
10
|
else
|
|
11
11
|
nil
|
|
12
12
|
end
|
|
@@ -20,44 +20,40 @@ module Mail
|
|
|
20
20
|
Encodings.address_encode(val, charset)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# Allows you to iterate through each address object in the
|
|
23
|
+
# Allows you to iterate through each address object in the address_list
|
|
24
24
|
def each
|
|
25
|
-
|
|
25
|
+
address_list.addresses.each do |address|
|
|
26
26
|
yield(address)
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Returns the address string of all the addresses in the address list
|
|
31
31
|
def addresses
|
|
32
|
-
list =
|
|
32
|
+
list = address_list.addresses.map { |a| a.address }
|
|
33
33
|
Mail::AddressContainer.new(self, list)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Returns the formatted string of all the addresses in the address list
|
|
37
37
|
def formatted
|
|
38
|
-
list =
|
|
38
|
+
list = address_list.addresses.map { |a| a.format }
|
|
39
39
|
Mail::AddressContainer.new(self, list)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Returns the display name of all the addresses in the address list
|
|
43
43
|
def display_names
|
|
44
|
-
list =
|
|
44
|
+
list = address_list.addresses.map { |a| a.display_name }
|
|
45
45
|
Mail::AddressContainer.new(self, list)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# Returns the actual address objects in the address list
|
|
49
49
|
def addrs
|
|
50
|
-
list =
|
|
50
|
+
list = address_list.addresses
|
|
51
51
|
Mail::AddressContainer.new(self, list)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Returns a hash of group name => address strings for the address list
|
|
55
55
|
def groups
|
|
56
|
-
|
|
57
|
-
tree.group_recipients.each do |group|
|
|
58
|
-
@groups[group.group_name.text_value.to_str] = get_group_addresses(group.group_list)
|
|
59
|
-
end
|
|
60
|
-
@groups
|
|
56
|
+
address_list.addresses_grouped_by_group
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
# Returns the addresses that are part of groups
|
|
@@ -77,7 +73,7 @@ module Mail
|
|
|
77
73
|
|
|
78
74
|
# Returns the name of all the groups in a string
|
|
79
75
|
def group_names # :nodoc:
|
|
80
|
-
|
|
76
|
+
address_list.group_names
|
|
81
77
|
end
|
|
82
78
|
|
|
83
79
|
def default
|
|
@@ -104,7 +100,7 @@ module Mail
|
|
|
104
100
|
|
|
105
101
|
def do_encode(field_name)
|
|
106
102
|
return '' if value.blank?
|
|
107
|
-
address_array =
|
|
103
|
+
address_array = address_list.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
|
|
108
104
|
address_text = address_array.join(", \r\n\s")
|
|
109
105
|
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
|
|
110
106
|
group_text = group_array.join(" \r\n\s")
|
|
@@ -114,7 +110,7 @@ module Mail
|
|
|
114
110
|
|
|
115
111
|
def do_decode
|
|
116
112
|
return nil if value.blank?
|
|
117
|
-
address_array =
|
|
113
|
+
address_array = address_list.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
|
|
118
114
|
address_text = address_array.join(", ")
|
|
119
115
|
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
|
|
120
116
|
group_text = group_array.join(" ")
|
|
@@ -122,15 +118,14 @@ module Mail
|
|
|
122
118
|
return_array.join(", ")
|
|
123
119
|
end
|
|
124
120
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
@tree ||= AddressList.new(value)
|
|
121
|
+
def address_list # :nodoc:
|
|
122
|
+
@address_list ||= AddressList.new(value)
|
|
128
123
|
end
|
|
129
124
|
|
|
130
125
|
def get_group_addresses(group_list)
|
|
131
126
|
if group_list.respond_to?(:addresses)
|
|
132
|
-
group_list.addresses.map do |
|
|
133
|
-
Mail::Address.new(
|
|
127
|
+
group_list.addresses.map do |address|
|
|
128
|
+
Mail::Address.new(address)
|
|
134
129
|
end
|
|
135
130
|
else
|
|
136
131
|
[]
|
|
@@ -13,7 +13,6 @@ module Mail
|
|
|
13
13
|
def parse(val = value)
|
|
14
14
|
unless val.blank?
|
|
15
15
|
@element = Mail::DateTimeElement.new(val)
|
|
16
|
-
@tree = @element.tree
|
|
17
16
|
else
|
|
18
17
|
nil
|
|
19
18
|
end
|
|
@@ -32,11 +31,5 @@ module Mail
|
|
|
32
31
|
def element
|
|
33
32
|
@element ||= Mail::DateTimeElement.new(value)
|
|
34
33
|
end
|
|
35
|
-
|
|
36
|
-
# Returns the syntax tree of the Date
|
|
37
|
-
def tree
|
|
38
|
-
@tree ||= element.tree
|
|
39
|
-
end
|
|
40
|
-
|
|
41
34
|
end
|
|
42
35
|
end
|
|
@@ -23,12 +23,6 @@ module Mail
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def tree
|
|
27
|
-
STDERR.puts("tree is deprecated. Please use encoding to get parse result\n#{caller}")
|
|
28
|
-
@element ||= Mail::ContentTransferEncodingElement.new(value)
|
|
29
|
-
@tree ||= @element.tree
|
|
30
|
-
end
|
|
31
|
-
|
|
32
26
|
def element
|
|
33
27
|
@element ||= Mail::ContentTransferEncodingElement.new(value)
|
|
34
28
|
end
|
|
@@ -144,6 +144,7 @@ module Mail
|
|
|
144
144
|
limit = 78 - prepend
|
|
145
145
|
limit = limit - 7 - encoding.length if should_encode
|
|
146
146
|
line = ""
|
|
147
|
+
first_word = true
|
|
147
148
|
while !words.empty?
|
|
148
149
|
break unless word = words.first.dup
|
|
149
150
|
word.encode!(charset) if charset && word.respond_to?(:encode!)
|
|
@@ -158,7 +159,12 @@ module Mail
|
|
|
158
159
|
# Remove the word from the queue ...
|
|
159
160
|
words.shift
|
|
160
161
|
# Add word separator
|
|
161
|
-
|
|
162
|
+
if first_word
|
|
163
|
+
first_word = false
|
|
164
|
+
else
|
|
165
|
+
line << " " if !should_encode
|
|
166
|
+
end
|
|
167
|
+
|
|
162
168
|
# ... add it in encoded form to the current line
|
|
163
169
|
line << word
|
|
164
170
|
end
|
data/lib/mail/header.rb
CHANGED
|
@@ -48,11 +48,15 @@ module Mail
|
|
|
48
48
|
# these cases, please make a patch and send it in, or at the least, send
|
|
49
49
|
# me the example so we can fix it.
|
|
50
50
|
def initialize(header_text = nil, charset = nil)
|
|
51
|
-
@errors = []
|
|
52
51
|
@charset = charset
|
|
53
52
|
self.raw_source = header_text.to_crlf.lstrip
|
|
54
53
|
split_header if header_text
|
|
55
54
|
end
|
|
55
|
+
|
|
56
|
+
def initialize_copy(original)
|
|
57
|
+
super
|
|
58
|
+
@fields = @fields.dup
|
|
59
|
+
end
|
|
56
60
|
|
|
57
61
|
# The preserved raw source of the header as you passed it in, untouched
|
|
58
62
|
# for your Regexing glory.
|
|
@@ -91,7 +95,6 @@ module Mail
|
|
|
91
95
|
unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
|
|
92
96
|
|
|
93
97
|
field = Field.new(field, nil, charset)
|
|
94
|
-
field.errors.each { |error| self.errors << error }
|
|
95
98
|
if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
|
|
96
99
|
selected.first.update(field.name, field.value)
|
|
97
100
|
else
|
|
@@ -102,7 +105,7 @@ module Mail
|
|
|
102
105
|
end
|
|
103
106
|
|
|
104
107
|
def errors
|
|
105
|
-
@errors
|
|
108
|
+
@fields.map(&:errors).flatten(1)
|
|
106
109
|
end
|
|
107
110
|
|
|
108
111
|
# 3.6. Field definitions
|
|
@@ -177,7 +180,7 @@ module Mail
|
|
|
177
180
|
if dasherize(fn) == "content-type"
|
|
178
181
|
# Update charset if specified in Content-Type
|
|
179
182
|
params = self[:content_type].parameters rescue nil
|
|
180
|
-
@charset = params && params[:charset]
|
|
183
|
+
@charset = params[:charset] if params && params[:charset]
|
|
181
184
|
end
|
|
182
185
|
end
|
|
183
186
|
|
|
@@ -246,27 +249,10 @@ module Mail
|
|
|
246
249
|
@raw_source = val
|
|
247
250
|
end
|
|
248
251
|
|
|
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
|
-
|
|
266
252
|
# Splits an unfolded and line break cleaned header into individual field
|
|
267
253
|
# strings.
|
|
268
254
|
def split_header
|
|
269
|
-
self.fields =
|
|
255
|
+
self.fields = raw_source.split(HEADER_SPLIT)
|
|
270
256
|
end
|
|
271
257
|
|
|
272
258
|
def select_field_for(name)
|
data/lib/mail/mail.rb
CHANGED
|
@@ -206,6 +206,12 @@ module Mail
|
|
|
206
206
|
end
|
|
207
207
|
end
|
|
208
208
|
|
|
209
|
+
# Unregister the given observer, allowing mail to resume operations
|
|
210
|
+
# without it.
|
|
211
|
+
def self.unregister_observer(observer)
|
|
212
|
+
@@delivery_notification_observers.delete(observer)
|
|
213
|
+
end
|
|
214
|
+
|
|
209
215
|
# You can register an object to be given every mail object that will be sent,
|
|
210
216
|
# before it is sent. So if you want to add special headers or modify any
|
|
211
217
|
# email that gets sent through the Mail library, you can do so.
|
|
@@ -219,6 +225,12 @@ module Mail
|
|
|
219
225
|
end
|
|
220
226
|
end
|
|
221
227
|
|
|
228
|
+
# Unregister the given interceptor, allowing mail to resume operations
|
|
229
|
+
# without it.
|
|
230
|
+
def self.unregister_interceptor(interceptor)
|
|
231
|
+
@@delivery_interceptors.delete(interceptor)
|
|
232
|
+
end
|
|
233
|
+
|
|
222
234
|
def self.inform_observers(mail)
|
|
223
235
|
@@delivery_notification_observers.each do |observer|
|
|
224
236
|
observer.delivered_email(mail)
|
|
@@ -29,6 +29,29 @@ module Mail
|
|
|
29
29
|
self
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def cc(recipient_or_list)
|
|
33
|
+
@copy_recipients ||= []
|
|
34
|
+
|
|
35
|
+
if recipient_or_list.kind_of?(Array)
|
|
36
|
+
@copy_recipients += recipient_or_list
|
|
37
|
+
else
|
|
38
|
+
@copy_recipients << recipient_or_list
|
|
39
|
+
end
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def bcc(recipient_or_list)
|
|
44
|
+
@blind_copy_recipients ||= []
|
|
45
|
+
|
|
46
|
+
if recipient_or_list.kind_of?(Array)
|
|
47
|
+
@blind_copy_recipients += recipient_or_list
|
|
48
|
+
else
|
|
49
|
+
@blind_copy_recipients << recipient_or_list
|
|
50
|
+
end
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
32
55
|
def with_subject(subject)
|
|
33
56
|
@subject = subject
|
|
34
57
|
self
|
|
@@ -73,7 +96,7 @@ module Mail
|
|
|
73
96
|
def filter_matched_deliveries(deliveries)
|
|
74
97
|
candidate_deliveries = deliveries
|
|
75
98
|
|
|
76
|
-
%w(sender recipients subject subject_matcher body body_matcher).each do |modifier_name|
|
|
99
|
+
%w(sender recipients copy_recipients blind_copy_recipients subject subject_matcher body body_matcher).each do |modifier_name|
|
|
77
100
|
next unless instance_variable_defined?("@#{modifier_name}")
|
|
78
101
|
candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
|
|
79
102
|
end
|
|
@@ -89,6 +112,14 @@ module Mail
|
|
|
89
112
|
@recipients.all? {|recipient| delivery.to.include?(recipient) }
|
|
90
113
|
end
|
|
91
114
|
|
|
115
|
+
def matches_on_copy_recipients?(delivery)
|
|
116
|
+
@copy_recipients.all? {|recipient| delivery.cc.include?(recipient) }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def matches_on_blind_copy_recipients?(delivery)
|
|
120
|
+
@blind_copy_recipients.all? {|recipient| delivery.bcc.include?(recipient) }
|
|
121
|
+
end
|
|
122
|
+
|
|
92
123
|
def matches_on_subject?(delivery)
|
|
93
124
|
delivery.subject == @subject
|
|
94
125
|
end
|
|
@@ -109,6 +140,8 @@ module Mail
|
|
|
109
140
|
result = ''
|
|
110
141
|
result += "from #{@sender} " if instance_variable_defined?('@sender')
|
|
111
142
|
result += "to #{@recipients.inspect} " if instance_variable_defined?('@recipients')
|
|
143
|
+
result += "cc #{@copy_recipients.inspect} " if instance_variable_defined?('@copy_recipients')
|
|
144
|
+
result += "bcc #{@blind_copy_recipients.inspect} " if instance_variable_defined?('@blind_copy_recipients')
|
|
112
145
|
result += "with subject \"#{@subject}\" " if instance_variable_defined?('@subject')
|
|
113
146
|
result += "with subject matching \"#{@subject_matcher}\" " if instance_variable_defined?('@subject_matcher')
|
|
114
147
|
result += "with body \"#{@body}\" " if instance_variable_defined?('@body')
|