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.
Files changed (163) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +156 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +52 -32
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +2 -2
  10. data/lib/mail/body.rb +33 -9
  11. data/lib/mail/check_delivery_params.rb +20 -0
  12. data/lib/mail/configuration.rb +1 -1
  13. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  14. data/lib/mail/core_extensions/nil.rb +4 -2
  15. data/lib/mail/core_extensions/object.rb +8 -8
  16. data/lib/mail/core_extensions/smtp.rb +12 -13
  17. data/lib/mail/core_extensions/string.rb +19 -6
  18. data/lib/mail/elements/address.rb +82 -141
  19. data/lib/mail/elements/address_list.rb +9 -42
  20. data/lib/mail/elements/content_disposition_element.rb +3 -7
  21. data/lib/mail/elements/content_location_element.rb +2 -6
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  23. data/lib/mail/elements/content_type_element.rb +4 -8
  24. data/lib/mail/elements/date_time_element.rb +3 -7
  25. data/lib/mail/elements/envelope_from_element.rb +18 -13
  26. data/lib/mail/elements/message_ids_element.rb +1 -6
  27. data/lib/mail/elements/mime_version_element.rb +3 -7
  28. data/lib/mail/elements/phrase_list.rb +2 -7
  29. data/lib/mail/elements/received_element.rb +3 -7
  30. data/lib/mail/elements.rb +12 -12
  31. data/lib/mail/encodings/quoted_printable.rb +4 -3
  32. data/lib/mail/encodings/unix_to_unix.rb +17 -0
  33. data/lib/mail/encodings.rb +53 -38
  34. data/lib/mail/envelope.rb +0 -5
  35. data/lib/mail/field.rb +131 -118
  36. data/lib/mail/field_list.rb +18 -18
  37. data/lib/mail/fields/bcc_field.rb +2 -2
  38. data/lib/mail/fields/cc_field.rb +2 -2
  39. data/lib/mail/fields/comments_field.rb +1 -1
  40. data/lib/mail/fields/common/common_address.rb +32 -22
  41. data/lib/mail/fields/common/common_date.rb +0 -7
  42. data/lib/mail/fields/common/common_field.rb +10 -3
  43. data/lib/mail/fields/common/common_message_id.rb +9 -5
  44. data/lib/mail/fields/common/parameter_hash.rb +3 -3
  45. data/lib/mail/fields/content_disposition_field.rb +1 -0
  46. data/lib/mail/fields/content_id_field.rb +1 -2
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  48. data/lib/mail/fields/content_type_field.rb +5 -2
  49. data/lib/mail/fields/date_field.rb +14 -14
  50. data/lib/mail/fields/from_field.rb +2 -2
  51. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  52. data/lib/mail/fields/keywords_field.rb +1 -1
  53. data/lib/mail/fields/message_id_field.rb +2 -3
  54. data/lib/mail/fields/references_field.rb +2 -1
  55. data/lib/mail/fields/reply_to_field.rb +2 -2
  56. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  57. data/lib/mail/fields/resent_cc_field.rb +2 -2
  58. data/lib/mail/fields/resent_from_field.rb +2 -2
  59. data/lib/mail/fields/resent_sender_field.rb +3 -3
  60. data/lib/mail/fields/resent_to_field.rb +2 -2
  61. data/lib/mail/fields/sender_field.rb +8 -8
  62. data/lib/mail/fields/to_field.rb +2 -2
  63. data/lib/mail/fields/unstructured_field.rb +44 -31
  64. data/lib/mail/fields.rb +32 -32
  65. data/lib/mail/header.rb +47 -37
  66. data/lib/mail/mail.rb +15 -1
  67. data/lib/mail/matchers/has_sent_mail.rb +36 -3
  68. data/lib/mail/message.rb +164 -57
  69. data/lib/mail/multibyte/chars.rb +4 -4
  70. data/lib/mail/multibyte/unicode.rb +9 -1
  71. data/lib/mail/network/delivery_methods/exim.rb +2 -3
  72. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  73. data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
  74. data/lib/mail/network/delivery_methods/smtp.rb +34 -45
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
  77. data/lib/mail/network/retriever_methods/imap.rb +32 -19
  78. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  79. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  80. data/lib/mail/network.rb +9 -9
  81. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  82. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  83. data/lib/mail/parsers/content_location_parser.rb +35 -0
  84. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  85. data/lib/mail/parsers/content_type_parser.rb +66 -0
  86. data/lib/mail/parsers/date_time_parser.rb +36 -0
  87. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  88. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  89. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  90. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  91. data/lib/mail/parsers/ragel/common.rl +185 -0
  92. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  93. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  105. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  106. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  107. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  108. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  109. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  110. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  111. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  112. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  113. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  114. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  115. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  117. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  118. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  119. data/lib/mail/parsers/ragel.rb +17 -0
  120. data/lib/mail/parsers/received_parser.rb +47 -0
  121. data/lib/mail/parsers.rb +26 -0
  122. data/lib/mail/part.rb +6 -2
  123. data/lib/mail/parts_list.rb +5 -3
  124. data/lib/mail/utilities.rb +70 -60
  125. data/lib/mail/values/unicode_tables.dat +0 -0
  126. data/lib/mail/version.rb +7 -15
  127. data/lib/mail/version_specific/ruby_1_8.rb +25 -4
  128. data/lib/mail/version_specific/ruby_1_9.rb +110 -30
  129. data/lib/mail.rb +20 -19
  130. metadata +129 -73
  131. data/Gemfile.lock +0 -36
  132. data/lib/VERSION +0 -4
  133. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  134. data/lib/mail/parsers/address_lists.rb +0 -64
  135. data/lib/mail/parsers/address_lists.treetop +0 -19
  136. data/lib/mail/parsers/content_disposition.rb +0 -535
  137. data/lib/mail/parsers/content_disposition.treetop +0 -46
  138. data/lib/mail/parsers/content_location.rb +0 -139
  139. data/lib/mail/parsers/content_location.treetop +0 -20
  140. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  141. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  142. data/lib/mail/parsers/content_type.rb +0 -967
  143. data/lib/mail/parsers/content_type.treetop +0 -68
  144. data/lib/mail/parsers/date_time.rb +0 -114
  145. data/lib/mail/parsers/date_time.treetop +0 -11
  146. data/lib/mail/parsers/envelope_from.rb +0 -194
  147. data/lib/mail/parsers/envelope_from.treetop +0 -32
  148. data/lib/mail/parsers/message_ids.rb +0 -45
  149. data/lib/mail/parsers/message_ids.treetop +0 -15
  150. data/lib/mail/parsers/mime_version.rb +0 -144
  151. data/lib/mail/parsers/mime_version.treetop +0 -19
  152. data/lib/mail/parsers/phrase_lists.rb +0 -45
  153. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  154. data/lib/mail/parsers/received.rb +0 -71
  155. data/lib/mail/parsers/received.treetop +0 -11
  156. data/lib/mail/parsers/rfc2045.rb +0 -464
  157. data/lib/mail/parsers/rfc2045.treetop +0 -36
  158. data/lib/mail/parsers/rfc2822.rb +0 -5341
  159. data/lib/mail/parsers/rfc2822.treetop +0 -410
  160. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  161. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  162. data/lib/tasks/corpus.rake +0 -125
  163. 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 Patterns
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 =~ /:/ # Field.new("field-name: field data")
81
- charset = value unless value.blank?
82
- name, value = split(name)
83
- create_field(name, value, charset)
84
- when name !~ /:/ && value.blank? # Field.new("field-name")
85
- create_field(name, nil, charset)
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
- create_field(name, value, charset)
127
+ @name = name
128
+ @value = value
129
+ @raw_value = nil
130
+ @charset = charset
88
131
  end
89
- return self
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
- @field
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
- field.name
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, field.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
- self_order = FIELD_ORDER.rindex(self.name.to_s.downcase) || 100
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(/^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/)
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 ignorning) '#{raw_field}'"
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
- self.field = new_field(name, value, charset)
228
+ new_field(name, value, charset)
157
229
  rescue Mail::Field::ParseError => e
158
- self.field = Mail::UnstructuredField.new(name, value)
159
- self.field.errors << [name, value, e]
160
- self.field
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
- # Could do this with constantize and make it "as DRY as", but a simple case
166
- # statement is, well, simpler...
167
- case name.to_s.downcase
168
- when /^to$/i
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
@@ -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
- current_entry = self.rindex(new_field)
13
- if current_entry
14
- self.insert((current_entry + 1), new_field)
15
- else
16
- insert_idx = -1
17
- self.each_with_index do |item, idx|
18
- case item <=> new_field
19
- when -1
20
- next
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 it's field type. This includes all Mail::CommonAddress
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 #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
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
@@ -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 it's field type. This includes all Mail::CommonAddress
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 #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
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 it's field type.
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
- @tree = AddressList.new(encode_if_needed(val))
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 syntax tree
23
+ # Allows you to iterate through each address object in the address_list
24
24
  def each
25
- tree.addresses.each do |address|
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 = tree.addresses.map { |a| a.address }
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 = tree.addresses.map { |a| a.format }
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 = tree.addresses.map { |a| a.display_name }
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 = tree.addresses
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
- @groups = Hash.new
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
- groups.map { |k,v| v.map { |a| a.format } }.flatten
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
- tree.group_names
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
- parse((formatted + [val]).join(", "))
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 = tree.addresses.reject { |a| group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
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 = tree.addresses.reject { |a| group_addresses.include?(a.decoded) }.map { |a| a.decoded }
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
- # Returns the syntax tree of the Addresses
111
- def tree # :nodoc:
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 |address_tree|
118
- Mail::Address.new(address_tree)
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.downcase == val.to_s.downcase
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.gsub(/#{field_name}:\s+/i, '')
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}: #{do_decode}\r\n}
36
+ %Q{#{field_name}: #{formated_message_ids("\r\n ")}\r\n}
37
37
  end
38
-
38
+
39
39
  def do_decode
40
- "#{message_ids.map { |m| "<#{m}>" }.join(' ')}" if message_ids
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.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
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.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
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