mail 2.4.4 → 2.6.3
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 +156 -0
- data/CONTRIBUTING.md +21 -6
- data/Dependencies.txt +0 -1
- data/Gemfile +10 -21
- data/MIT-LICENSE +20 -0
- data/README.md +52 -32
- data/Rakefile +11 -22
- data/lib/mail/attachments_list.rb +2 -2
- data/lib/mail/body.rb +33 -9
- data/lib/mail/check_delivery_params.rb +20 -0
- data/lib/mail/configuration.rb +1 -1
- data/lib/mail/{patterns.rb → constants.rb} +26 -6
- data/lib/mail/core_extensions/nil.rb +4 -2
- data/lib/mail/core_extensions/object.rb +8 -8
- data/lib/mail/core_extensions/smtp.rb +12 -13
- data/lib/mail/core_extensions/string.rb +19 -6
- data/lib/mail/elements/address.rb +82 -141
- data/lib/mail/elements/address_list.rb +9 -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 +18 -13
- 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/elements.rb +12 -12
- data/lib/mail/encodings/quoted_printable.rb +4 -3
- data/lib/mail/encodings/unix_to_unix.rb +17 -0
- data/lib/mail/encodings.rb +53 -38
- data/lib/mail/envelope.rb +0 -5
- data/lib/mail/field.rb +131 -118
- data/lib/mail/field_list.rb +18 -18
- data/lib/mail/fields/bcc_field.rb +2 -2
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +1 -1
- data/lib/mail/fields/common/common_address.rb +32 -22
- data/lib/mail/fields/common/common_date.rb +0 -7
- data/lib/mail/fields/common/common_field.rb +10 -3
- data/lib/mail/fields/common/common_message_id.rb +9 -5
- data/lib/mail/fields/common/parameter_hash.rb +3 -3
- data/lib/mail/fields/content_disposition_field.rb +1 -0
- data/lib/mail/fields/content_id_field.rb +1 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
- data/lib/mail/fields/content_type_field.rb +5 -2
- data/lib/mail/fields/date_field.rb +14 -14
- data/lib/mail/fields/from_field.rb +2 -2
- data/lib/mail/fields/in_reply_to_field.rb +2 -1
- data/lib/mail/fields/keywords_field.rb +1 -1
- data/lib/mail/fields/message_id_field.rb +2 -3
- data/lib/mail/fields/references_field.rb +2 -1
- data/lib/mail/fields/reply_to_field.rb +2 -2
- data/lib/mail/fields/resent_bcc_field.rb +2 -2
- data/lib/mail/fields/resent_cc_field.rb +2 -2
- data/lib/mail/fields/resent_from_field.rb +2 -2
- data/lib/mail/fields/resent_sender_field.rb +3 -3
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +8 -8
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +44 -31
- data/lib/mail/fields.rb +32 -32
- data/lib/mail/header.rb +47 -37
- data/lib/mail/mail.rb +15 -1
- data/lib/mail/matchers/has_sent_mail.rb +36 -3
- data/lib/mail/message.rb +164 -57
- data/lib/mail/multibyte/chars.rb +4 -4
- data/lib/mail/multibyte/unicode.rb +9 -1
- data/lib/mail/network/delivery_methods/exim.rb +2 -3
- data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
- data/lib/mail/network/delivery_methods/smtp.rb +34 -45
- data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
- data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
- data/lib/mail/network/retriever_methods/imap.rb +32 -19
- data/lib/mail/network/retriever_methods/pop3.rb +2 -2
- data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
- data/lib/mail/network.rb +9 -9
- 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 +66 -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 +185 -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 +2149 -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 +40 -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/part.rb +6 -2
- data/lib/mail/parts_list.rb +5 -3
- data/lib/mail/utilities.rb +70 -60
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +7 -15
- data/lib/mail/version_specific/ruby_1_8.rb +25 -4
- data/lib/mail/version_specific/ruby_1_9.rb +110 -30
- data/lib/mail.rb +20 -19
- metadata +129 -73
- data/Gemfile.lock +0 -36
- data/lib/VERSION +0 -4
- data/lib/mail/core_extensions/shell_escape.rb +0 -56
- 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 -162
- data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
- data/lib/mail/parsers/content_type.rb +0 -967
- 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 -464
- data/lib/mail/parsers/rfc2045.treetop +0 -36
- data/lib/mail/parsers/rfc2822.rb +0 -5341
- data/lib/mail/parsers/rfc2822.treetop +0 -410
- 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
data/lib/mail/field.rb
CHANGED
|
@@ -5,11 +5,11 @@ module Mail
|
|
|
5
5
|
# Provides a single class to call to create a new structured or unstructured
|
|
6
6
|
# field. Works out per RFC what field of field it is being given and returns
|
|
7
7
|
# the correct field of class back on new.
|
|
8
|
-
#
|
|
8
|
+
#
|
|
9
9
|
# ===Per RFC 2822
|
|
10
|
-
#
|
|
10
|
+
#
|
|
11
11
|
# 2.2. Header Fields
|
|
12
|
-
#
|
|
12
|
+
#
|
|
13
13
|
# Header fields are lines composed of a field name, followed by a colon
|
|
14
14
|
# (":"), followed by a field body, and terminated by CRLF. A field
|
|
15
15
|
# name MUST be composed of printable US-ASCII characters (i.e.,
|
|
@@ -19,12 +19,12 @@ module Mail
|
|
|
19
19
|
# used in header "folding" and "unfolding" as described in section
|
|
20
20
|
# 2.2.3. All field bodies MUST conform to the syntax described in
|
|
21
21
|
# sections 3 and 4 of this standard.
|
|
22
|
-
#
|
|
22
|
+
#
|
|
23
23
|
class Field
|
|
24
|
-
|
|
25
|
-
include
|
|
24
|
+
|
|
25
|
+
include Utilities
|
|
26
26
|
include Comparable
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
|
|
29
29
|
content-id content-location content-transfer-encoding
|
|
30
30
|
content-type date from in-reply-to keywords message-id
|
|
@@ -34,7 +34,43 @@ module Mail
|
|
|
34
34
|
return-path sender to ]
|
|
35
35
|
|
|
36
36
|
KNOWN_FIELDS = STRUCTURED_FIELDS + ['comments', 'subject']
|
|
37
|
-
|
|
37
|
+
|
|
38
|
+
FIELDS_MAP = {
|
|
39
|
+
"to" => ToField,
|
|
40
|
+
"cc" => CcField,
|
|
41
|
+
"bcc" => BccField,
|
|
42
|
+
"message-id" => MessageIdField,
|
|
43
|
+
"in-reply-to" => InReplyToField,
|
|
44
|
+
"references" => ReferencesField,
|
|
45
|
+
"subject" => SubjectField,
|
|
46
|
+
"comments" => CommentsField,
|
|
47
|
+
"keywords" => KeywordsField,
|
|
48
|
+
"date" => DateField,
|
|
49
|
+
"from" => FromField,
|
|
50
|
+
"sender" => SenderField,
|
|
51
|
+
"reply-to" => ReplyToField,
|
|
52
|
+
"resent-date" => ResentDateField,
|
|
53
|
+
"resent-from" => ResentFromField,
|
|
54
|
+
"resent-sender" => ResentSenderField,
|
|
55
|
+
"resent-to" => ResentToField,
|
|
56
|
+
"resent-cc" => ResentCcField,
|
|
57
|
+
"resent-bcc" => ResentBccField,
|
|
58
|
+
"resent-message-id" => ResentMessageIdField,
|
|
59
|
+
"return-path" => ReturnPathField,
|
|
60
|
+
"received" => ReceivedField,
|
|
61
|
+
"mime-version" => MimeVersionField,
|
|
62
|
+
"content-transfer-encoding" => ContentTransferEncodingField,
|
|
63
|
+
"content-description" => ContentDescriptionField,
|
|
64
|
+
"content-disposition" => ContentDispositionField,
|
|
65
|
+
"content-type" => ContentTypeField,
|
|
66
|
+
"content-id" => ContentIdField,
|
|
67
|
+
"content-location" => ContentLocationField,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
FIELD_NAME_MAP = FIELDS_MAP.inject({}) do |map, (field, field_klass)|
|
|
71
|
+
map.update(field => field_klass::CAPITALIZED_FIELD)
|
|
72
|
+
end
|
|
73
|
+
|
|
38
74
|
# Generic Field Exception
|
|
39
75
|
class FieldError < StandardError
|
|
40
76
|
end
|
|
@@ -55,84 +91,104 @@ module Mail
|
|
|
55
91
|
# Raised when attempting to set a structured field's contents to an invalid syntax
|
|
56
92
|
class SyntaxError < FieldError #:nodoc:
|
|
57
93
|
end
|
|
58
|
-
|
|
94
|
+
|
|
59
95
|
# Accepts a string:
|
|
60
|
-
#
|
|
96
|
+
#
|
|
61
97
|
# Field.new("field-name: field data")
|
|
62
|
-
#
|
|
98
|
+
#
|
|
63
99
|
# Or name, value pair:
|
|
64
|
-
#
|
|
100
|
+
#
|
|
65
101
|
# Field.new("field-name", "value")
|
|
66
|
-
#
|
|
102
|
+
#
|
|
67
103
|
# Or a name by itself:
|
|
68
|
-
#
|
|
104
|
+
#
|
|
69
105
|
# Field.new("field-name")
|
|
70
|
-
#
|
|
106
|
+
#
|
|
71
107
|
# Note, does not want a terminating carriage return. Returns
|
|
72
108
|
# self appropriately parsed. If value is not a string, then
|
|
73
109
|
# it will be passed through as is, for example, content-type
|
|
74
|
-
# field can accept an array with the type and a hash of
|
|
110
|
+
# field can accept an array with the type and a hash of
|
|
75
111
|
# parameters:
|
|
76
|
-
#
|
|
112
|
+
#
|
|
77
113
|
# Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
|
|
78
114
|
def initialize(name, value = nil, charset = 'utf-8')
|
|
79
115
|
case
|
|
80
|
-
when name
|
|
81
|
-
charset = value
|
|
82
|
-
name
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
116
|
+
when name.index(COLON) # Field.new("field-name: field data")
|
|
117
|
+
@charset = value.blank? ? charset : value
|
|
118
|
+
@name = name[FIELD_PREFIX]
|
|
119
|
+
@raw_value = name
|
|
120
|
+
@value = nil
|
|
121
|
+
when value.blank? # Field.new("field-name")
|
|
122
|
+
@name = name
|
|
123
|
+
@value = nil
|
|
124
|
+
@raw_value = nil
|
|
125
|
+
@charset = charset
|
|
86
126
|
else # Field.new("field-name", "value")
|
|
87
|
-
|
|
127
|
+
@name = name
|
|
128
|
+
@value = value
|
|
129
|
+
@raw_value = nil
|
|
130
|
+
@charset = charset
|
|
88
131
|
end
|
|
89
|
-
|
|
132
|
+
@name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
|
|
90
133
|
end
|
|
91
134
|
|
|
92
135
|
def field=(value)
|
|
93
136
|
@field = value
|
|
94
137
|
end
|
|
95
|
-
|
|
138
|
+
|
|
96
139
|
def field
|
|
97
|
-
@
|
|
140
|
+
_, @value = split(@raw_value) if @raw_value && !@value
|
|
141
|
+
@field ||= create_field(@name, @value, @charset)
|
|
98
142
|
end
|
|
99
|
-
|
|
143
|
+
|
|
100
144
|
def name
|
|
101
|
-
|
|
145
|
+
@name
|
|
102
146
|
end
|
|
103
|
-
|
|
147
|
+
|
|
104
148
|
def value
|
|
105
149
|
field.value
|
|
106
150
|
end
|
|
107
|
-
|
|
151
|
+
|
|
108
152
|
def value=(val)
|
|
109
|
-
create_field(name, val, charset)
|
|
153
|
+
@field = create_field(name, val, @charset)
|
|
110
154
|
end
|
|
111
|
-
|
|
155
|
+
|
|
112
156
|
def to_s
|
|
113
157
|
field.to_s
|
|
114
158
|
end
|
|
115
|
-
|
|
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
|
+
|
|
116
166
|
def update(name, value)
|
|
117
|
-
create_field(name, value, charset)
|
|
167
|
+
@field = create_field(name, value, @charset)
|
|
118
168
|
end
|
|
119
|
-
|
|
169
|
+
|
|
120
170
|
def same( other )
|
|
121
|
-
match_to_s(other.name,
|
|
171
|
+
match_to_s(other.name, self.name)
|
|
122
172
|
end
|
|
123
|
-
|
|
173
|
+
|
|
174
|
+
def responsible_for?( val )
|
|
175
|
+
name.to_s.casecmp(val.to_s) == 0
|
|
176
|
+
end
|
|
177
|
+
|
|
124
178
|
alias_method :==, :same
|
|
125
|
-
|
|
179
|
+
|
|
126
180
|
def <=>( other )
|
|
127
|
-
|
|
128
|
-
other_order = FIELD_ORDER.rindex(other.name.to_s.downcase) || 100
|
|
129
|
-
self_order <=> other_order
|
|
181
|
+
self.field_order_id <=> other.field_order_id
|
|
130
182
|
end
|
|
131
|
-
|
|
183
|
+
|
|
184
|
+
def field_order_id
|
|
185
|
+
@field_order_id ||= (FIELD_ORDER_LOOKUP[self.name.to_s.downcase] || 100)
|
|
186
|
+
end
|
|
187
|
+
|
|
132
188
|
def method_missing(name, *args, &block)
|
|
133
189
|
field.send(name, *args, &block)
|
|
134
190
|
end
|
|
135
|
-
|
|
191
|
+
|
|
136
192
|
FIELD_ORDER = %w[ return-path received
|
|
137
193
|
resent-date resent-from resent-sender resent-to
|
|
138
194
|
resent-cc resent-bcc resent-message-id
|
|
@@ -141,94 +197,51 @@ module Mail
|
|
|
141
197
|
subject comments keywords
|
|
142
198
|
mime-version content-type content-transfer-encoding
|
|
143
199
|
content-location content-disposition content-description ]
|
|
144
|
-
|
|
200
|
+
|
|
201
|
+
FIELD_ORDER_LOOKUP = Hash[FIELD_ORDER.each_with_index.to_a]
|
|
202
|
+
|
|
145
203
|
private
|
|
146
|
-
|
|
204
|
+
|
|
147
205
|
def split(raw_field)
|
|
148
|
-
match_data = raw_field.mb_chars.match(
|
|
149
|
-
[match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip]
|
|
206
|
+
match_data = raw_field.mb_chars.match(FIELD_SPLIT)
|
|
207
|
+
[match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip.to_s]
|
|
150
208
|
rescue
|
|
151
|
-
STDERR.puts "WARNING: Could not parse (and so
|
|
209
|
+
STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
|
|
210
|
+
end
|
|
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, ' ')
|
|
152
222
|
end
|
|
153
|
-
|
|
223
|
+
|
|
154
224
|
def create_field(name, value, charset)
|
|
225
|
+
value = unfold(value) if value.is_a?(String)
|
|
226
|
+
|
|
155
227
|
begin
|
|
156
|
-
|
|
228
|
+
new_field(name, value, charset)
|
|
157
229
|
rescue Mail::Field::ParseError => e
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
230
|
+
field = Mail::UnstructuredField.new(name, value)
|
|
231
|
+
field.errors << [name, value, e]
|
|
232
|
+
field
|
|
161
233
|
end
|
|
162
234
|
end
|
|
163
235
|
|
|
164
236
|
def new_field(name, value, charset)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
ToField.new(value, charset)
|
|
170
|
-
when /^cc$/i
|
|
171
|
-
CcField.new(value, charset)
|
|
172
|
-
when /^bcc$/i
|
|
173
|
-
BccField.new(value, charset)
|
|
174
|
-
when /^message-id$/i
|
|
175
|
-
MessageIdField.new(value, charset)
|
|
176
|
-
when /^in-reply-to$/i
|
|
177
|
-
InReplyToField.new(value, charset)
|
|
178
|
-
when /^references$/i
|
|
179
|
-
ReferencesField.new(value, charset)
|
|
180
|
-
when /^subject$/i
|
|
181
|
-
SubjectField.new(value, charset)
|
|
182
|
-
when /^comments$/i
|
|
183
|
-
CommentsField.new(value, charset)
|
|
184
|
-
when /^keywords$/i
|
|
185
|
-
KeywordsField.new(value, charset)
|
|
186
|
-
when /^date$/i
|
|
187
|
-
DateField.new(value, charset)
|
|
188
|
-
when /^from$/i
|
|
189
|
-
FromField.new(value, charset)
|
|
190
|
-
when /^sender$/i
|
|
191
|
-
SenderField.new(value, charset)
|
|
192
|
-
when /^reply-to$/i
|
|
193
|
-
ReplyToField.new(value, charset)
|
|
194
|
-
when /^resent-date$/i
|
|
195
|
-
ResentDateField.new(value, charset)
|
|
196
|
-
when /^resent-from$/i
|
|
197
|
-
ResentFromField.new(value, charset)
|
|
198
|
-
when /^resent-sender$/i
|
|
199
|
-
ResentSenderField.new(value, charset)
|
|
200
|
-
when /^resent-to$/i
|
|
201
|
-
ResentToField.new(value, charset)
|
|
202
|
-
when /^resent-cc$/i
|
|
203
|
-
ResentCcField.new(value, charset)
|
|
204
|
-
when /^resent-bcc$/i
|
|
205
|
-
ResentBccField.new(value, charset)
|
|
206
|
-
when /^resent-message-id$/i
|
|
207
|
-
ResentMessageIdField.new(value, charset)
|
|
208
|
-
when /^return-path$/i
|
|
209
|
-
ReturnPathField.new(value, charset)
|
|
210
|
-
when /^received$/i
|
|
211
|
-
ReceivedField.new(value, charset)
|
|
212
|
-
when /^mime-version$/i
|
|
213
|
-
MimeVersionField.new(value, charset)
|
|
214
|
-
when /^content-transfer-encoding$/i
|
|
215
|
-
ContentTransferEncodingField.new(value, charset)
|
|
216
|
-
when /^content-description$/i
|
|
217
|
-
ContentDescriptionField.new(value, charset)
|
|
218
|
-
when /^content-disposition$/i
|
|
219
|
-
ContentDispositionField.new(value, charset)
|
|
220
|
-
when /^content-type$/i
|
|
221
|
-
ContentTypeField.new(value, charset)
|
|
222
|
-
when /^content-id$/i
|
|
223
|
-
ContentIdField.new(value, charset)
|
|
224
|
-
when /^content-location$/i
|
|
225
|
-
ContentLocationField.new(value, charset)
|
|
226
|
-
else
|
|
237
|
+
lower_case_name = name.to_s.downcase
|
|
238
|
+
if field_klass = FIELDS_MAP[lower_case_name]
|
|
239
|
+
field_klass.new(value, charset)
|
|
240
|
+
else
|
|
227
241
|
OptionalField.new(name, value, charset)
|
|
228
242
|
end
|
|
229
|
-
|
|
230
243
|
end
|
|
231
244
|
|
|
232
245
|
end
|
|
233
|
-
|
|
246
|
+
|
|
234
247
|
end
|
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).div(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 @@
|
|
|
6
6
|
# field in the email.
|
|
7
7
|
#
|
|
8
8
|
# Sending bcc to a mail message will instantiate a Mail::Field object that
|
|
9
|
-
# has a BccField as
|
|
9
|
+
# has a BccField as its field type. This includes all Mail::CommonAddress
|
|
10
10
|
# module instance metods.
|
|
11
11
|
#
|
|
12
12
|
# Only one Bcc field can appear in a header, though it can have multiple
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
#
|
|
17
17
|
# mail = Mail.new
|
|
18
18
|
# mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
|
19
|
-
# mail.bcc #=> ['
|
|
19
|
+
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
|
20
20
|
# mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
|
|
21
21
|
# mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
|
|
22
22
|
# mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
|
data/lib/mail/fields/cc_field.rb
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# field in the email.
|
|
7
7
|
#
|
|
8
8
|
# Sending cc to a mail message will instantiate a Mail::Field object that
|
|
9
|
-
# has a CcField as
|
|
9
|
+
# has a CcField as its field type. This includes all Mail::CommonAddress
|
|
10
10
|
# module instance metods.
|
|
11
11
|
#
|
|
12
12
|
# Only one Cc field can appear in a header, though it can have multiple
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
#
|
|
17
17
|
# mail = Mail.new
|
|
18
18
|
# mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
|
|
19
|
-
# mail.cc #=> ['
|
|
19
|
+
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
|
|
20
20
|
# mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
|
|
21
21
|
# mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
|
|
22
22
|
# mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# header field in the email.
|
|
7
7
|
#
|
|
8
8
|
# Sending comments to a mail message will instantiate a Mail::Field object that
|
|
9
|
-
# has a CommentsField as
|
|
9
|
+
# has a CommentsField as its field type.
|
|
10
10
|
#
|
|
11
11
|
# An email header can have as many comments fields as it wants. There is no upper
|
|
12
12
|
# limit, the comments field is also optional (that is, no comment is needed)
|
|
@@ -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,54 +20,60 @@ 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
|
|
64
60
|
def group_addresses
|
|
65
|
-
|
|
61
|
+
decoded_group_addresses
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns a list of decoded group addresses
|
|
65
|
+
def decoded_group_addresses
|
|
66
|
+
groups.map { |k,v| v.map { |a| a.decoded } }.flatten
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns a list of encoded group addresses
|
|
70
|
+
def encoded_group_addresses
|
|
71
|
+
groups.map { |k,v| v.map { |a| a.encoded } }.flatten
|
|
66
72
|
end
|
|
67
73
|
|
|
68
74
|
# Returns the name of all the groups in a string
|
|
69
75
|
def group_names # :nodoc:
|
|
70
|
-
|
|
76
|
+
address_list.group_names
|
|
71
77
|
end
|
|
72
78
|
|
|
73
79
|
def default
|
|
@@ -81,15 +87,20 @@ module Mail
|
|
|
81
87
|
when val.blank?
|
|
82
88
|
parse(encoded)
|
|
83
89
|
else
|
|
84
|
-
|
|
90
|
+
self.value = [self.value, val].reject {|a| a.blank? }.join(", ")
|
|
85
91
|
end
|
|
86
92
|
end
|
|
93
|
+
|
|
94
|
+
def value=(val)
|
|
95
|
+
super
|
|
96
|
+
parse(self.value)
|
|
97
|
+
end
|
|
87
98
|
|
|
88
99
|
private
|
|
89
100
|
|
|
90
101
|
def do_encode(field_name)
|
|
91
102
|
return '' if value.blank?
|
|
92
|
-
address_array =
|
|
103
|
+
address_array = address_list.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
|
|
93
104
|
address_text = address_array.join(", \r\n\s")
|
|
94
105
|
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
|
|
95
106
|
group_text = group_array.join(" \r\n\s")
|
|
@@ -99,7 +110,7 @@ module Mail
|
|
|
99
110
|
|
|
100
111
|
def do_decode
|
|
101
112
|
return nil if value.blank?
|
|
102
|
-
address_array =
|
|
113
|
+
address_array = address_list.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
|
|
103
114
|
address_text = address_array.join(", ")
|
|
104
115
|
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
|
|
105
116
|
group_text = group_array.join(" ")
|
|
@@ -107,15 +118,14 @@ module Mail
|
|
|
107
118
|
return_array.join(", ")
|
|
108
119
|
end
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
@tree ||= AddressList.new(value)
|
|
121
|
+
def address_list # :nodoc:
|
|
122
|
+
@address_list ||= AddressList.new(value)
|
|
113
123
|
end
|
|
114
124
|
|
|
115
125
|
def get_group_addresses(group_list)
|
|
116
126
|
if group_list.respond_to?(:addresses)
|
|
117
|
-
group_list.addresses.map do |
|
|
118
|
-
Mail::Address.new(
|
|
127
|
+
group_list.addresses.map do |address|
|
|
128
|
+
Mail::Address.new(address)
|
|
119
129
|
end
|
|
120
130
|
else
|
|
121
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
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
module Mail
|
|
3
3
|
module CommonField # :nodoc:
|
|
4
|
+
include Mail::Constants
|
|
4
5
|
|
|
5
6
|
def name=(value)
|
|
6
7
|
@name = value
|
|
@@ -22,7 +23,7 @@ module Mail
|
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def to_s
|
|
25
|
-
decoded
|
|
26
|
+
decoded.to_s
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def default
|
|
@@ -34,7 +35,7 @@ module Mail
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def responsible_for?( val )
|
|
37
|
-
name.to_s.
|
|
38
|
+
name.to_s.casecmp(val.to_s) == 0
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
private
|
|
@@ -43,9 +44,15 @@ module Mail
|
|
|
43
44
|
if value.is_a?(Array)
|
|
44
45
|
value
|
|
45
46
|
else
|
|
46
|
-
value.to_s.
|
|
47
|
+
value.to_s.sub(/#{field_name}:\s+/i, EMPTY)
|
|
47
48
|
end
|
|
48
49
|
end
|
|
49
50
|
|
|
51
|
+
FILENAME_RE = /\b(filename|name)=([^;"\r\n]+\s[^;"\r\n]+)/
|
|
52
|
+
def ensure_filename_quoted(value)
|
|
53
|
+
if value.is_a?(String)
|
|
54
|
+
value.sub! FILENAME_RE, '\1="\2"'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
50
57
|
end
|
|
51
58
|
end
|
|
@@ -31,14 +31,18 @@ module Mail
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
private
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def do_encode(field_name)
|
|
36
|
-
%Q{#{field_name}: #{
|
|
36
|
+
%Q{#{field_name}: #{formated_message_ids("\r\n ")}\r\n}
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
def do_decode
|
|
40
|
-
|
|
40
|
+
formated_message_ids(' ')
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
def formated_message_ids(join)
|
|
44
|
+
message_ids.map{ |m| "<#{m}>" }.join(join) if message_ids
|
|
45
|
+
end
|
|
46
|
+
|
|
43
47
|
end
|
|
44
48
|
end
|
|
@@ -18,7 +18,7 @@ module Mail
|
|
|
18
18
|
exact = nil
|
|
19
19
|
each do |k,v|
|
|
20
20
|
if k =~ /^#{key_pattern}(\*|$)/i
|
|
21
|
-
if $1 ==
|
|
21
|
+
if $1 == ASTERISK
|
|
22
22
|
pairs << [k, v]
|
|
23
23
|
else
|
|
24
24
|
exact = k
|
|
@@ -40,7 +40,7 @@ module Mail
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def encoded
|
|
43
|
-
map.
|
|
43
|
+
map.sort_by { |a| a.first.to_s }.map! do |key_name, value|
|
|
44
44
|
unless value.ascii_only?
|
|
45
45
|
value = Mail::Encodings.param_encode(value)
|
|
46
46
|
key_name = "#{key_name}*"
|
|
@@ -50,7 +50,7 @@ module Mail
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def decoded
|
|
53
|
-
map.
|
|
53
|
+
map.sort_by { |a| a.first.to_s }.map! do |key_name, value|
|
|
54
54
|
%Q{#{key_name}=#{quote_token(value)}}
|
|
55
55
|
end.join("; ")
|
|
56
56
|
end
|