mail 2.5.5 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +8 -9
  3. data/CONTRIBUTING.md +13 -0
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +7 -24
  6. data/README.md +36 -15
  7. data/Rakefile +10 -2
  8. data/VERSION +4 -0
  9. data/lib/mail.rb +1 -1
  10. data/lib/mail/body.rb +2 -2
  11. data/lib/mail/check_delivery_params.rb +10 -47
  12. data/lib/mail/core_extensions/string.rb +12 -2
  13. data/lib/mail/elements/address.rb +38 -82
  14. data/lib/mail/elements/address_list.rb +19 -42
  15. data/lib/mail/elements/content_disposition_element.rb +3 -7
  16. data/lib/mail/elements/content_location_element.rb +2 -6
  17. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  18. data/lib/mail/elements/content_type_element.rb +4 -8
  19. data/lib/mail/elements/date_time_element.rb +3 -7
  20. data/lib/mail/elements/envelope_from_element.rb +3 -11
  21. data/lib/mail/elements/message_ids_element.rb +1 -6
  22. data/lib/mail/elements/mime_version_element.rb +3 -7
  23. data/lib/mail/elements/phrase_list.rb +2 -7
  24. data/lib/mail/elements/received_element.rb +3 -7
  25. data/lib/mail/encodings.rb +0 -1
  26. data/lib/mail/envelope.rb +0 -5
  27. data/lib/mail/field.rb +53 -17
  28. data/lib/mail/field_list.rb +18 -18
  29. data/lib/mail/fields/common/common_address.rb +15 -20
  30. data/lib/mail/fields/common/common_date.rb +0 -7
  31. data/lib/mail/fields/common/common_field.rb +1 -1
  32. data/lib/mail/fields/content_transfer_encoding_field.rb +0 -6
  33. data/lib/mail/fields/resent_sender_field.rb +1 -1
  34. data/lib/mail/fields/sender_field.rb +1 -1
  35. data/lib/mail/fields/unstructured_field.rb +7 -1
  36. data/lib/mail/header.rb +8 -22
  37. data/lib/mail/mail.rb +12 -0
  38. data/lib/mail/matchers/has_sent_mail.rb +34 -1
  39. data/lib/mail/message.rb +18 -11
  40. data/lib/mail/multibyte/unicode.rb +1 -1
  41. data/lib/mail/network/delivery_methods/exim.rb +10 -6
  42. data/lib/mail/network/delivery_methods/file_delivery.rb +8 -4
  43. data/lib/mail/network/delivery_methods/sendmail.rb +7 -9
  44. data/lib/mail/network/delivery_methods/smtp.rb +5 -2
  45. data/lib/mail/network/delivery_methods/smtp_connection.rb +6 -2
  46. data/lib/mail/network/delivery_methods/test_mailer.rb +8 -5
  47. data/lib/mail/network/retriever_methods/imap.rb +18 -13
  48. data/lib/mail/parsers.rb +26 -0
  49. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  50. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  51. data/lib/mail/parsers/content_location_parser.rb +35 -0
  52. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  53. data/lib/mail/parsers/content_type_parser.rb +64 -0
  54. data/lib/mail/parsers/date_time_parser.rb +36 -0
  55. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  56. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  57. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  58. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  59. data/lib/mail/parsers/ragel.rb +17 -0
  60. data/lib/mail/parsers/ragel/common.rl +184 -0
  61. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  62. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  63. data/lib/mail/parsers/ragel/ruby.rb +29 -0
  64. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  65. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  66. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  67. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  68. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  69. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  70. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  71. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  72. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  73. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  74. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  75. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  76. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  77. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  78. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  79. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  80. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  81. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  82. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  83. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  84. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  85. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  86. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  87. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  88. data/lib/mail/parsers/received_parser.rb +47 -0
  89. data/lib/mail/parts_list.rb +4 -2
  90. data/lib/mail/patterns.rb +3 -1
  91. data/lib/mail/utilities.rb +3 -1
  92. data/lib/mail/version.rb +1 -1
  93. data/lib/mail/version_specific/ruby_1_8.rb +1 -1
  94. data/lib/mail/version_specific/ruby_1_9.rb +13 -1
  95. metadata +55 -51
  96. data/lib/VERSION +0 -4
  97. data/lib/load_parsers.rb +0 -35
  98. data/lib/mail/parsers/address_lists.rb +0 -64
  99. data/lib/mail/parsers/address_lists.treetop +0 -19
  100. data/lib/mail/parsers/content_disposition.rb +0 -535
  101. data/lib/mail/parsers/content_disposition.treetop +0 -46
  102. data/lib/mail/parsers/content_location.rb +0 -139
  103. data/lib/mail/parsers/content_location.treetop +0 -20
  104. data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
  105. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
  106. data/lib/mail/parsers/content_type.rb +0 -971
  107. data/lib/mail/parsers/content_type.treetop +0 -68
  108. data/lib/mail/parsers/date_time.rb +0 -114
  109. data/lib/mail/parsers/date_time.treetop +0 -11
  110. data/lib/mail/parsers/envelope_from.rb +0 -194
  111. data/lib/mail/parsers/envelope_from.treetop +0 -32
  112. data/lib/mail/parsers/message_ids.rb +0 -45
  113. data/lib/mail/parsers/message_ids.treetop +0 -15
  114. data/lib/mail/parsers/mime_version.rb +0 -144
  115. data/lib/mail/parsers/mime_version.treetop +0 -19
  116. data/lib/mail/parsers/phrase_lists.rb +0 -45
  117. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  118. data/lib/mail/parsers/received.rb +0 -71
  119. data/lib/mail/parsers/received.treetop +0 -11
  120. data/lib/mail/parsers/rfc2045.rb +0 -421
  121. data/lib/mail/parsers/rfc2045.treetop +0 -35
  122. data/lib/mail/parsers/rfc2822.rb +0 -5397
  123. data/lib/mail/parsers/rfc2822.treetop +0 -408
  124. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  125. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  126. data/lib/tasks/corpus.rake +0 -125
  127. data/lib/tasks/treetop.rake +0 -10
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  module Mail
3
3
  class AddressList # :nodoc:
4
-
4
+
5
5
  # Mail::AddressList is the class that parses To, From and other address fields from
6
6
  # emails passed into Mail.
7
7
  #
@@ -18,57 +18,34 @@ module Mail
18
18
  # a.addresses #=> [#<Mail::Address:14943130 Address: |ada@test.lindsaar.net...
19
19
  # a.group_names #=> ["My Group"]
20
20
  def initialize(string)
21
- if string.blank?
22
- @address_nodes = []
23
- return self
24
- end
25
- parser = Mail::AddressListsParser.new
26
- if tree = parser.parse(string)
27
- @address_nodes = tree.addresses
28
- else
29
- raise Mail::Field::ParseError.new(AddressListsParser, string, parser.failure_reason)
30
- end
21
+ @addresses_grouped_by_group = nil
22
+ @address_list = Parsers::AddressListsParser.new.parse(string)
31
23
  end
32
24
 
33
25
  # Returns a list of address objects from the parsed line
34
26
  def addresses
35
- @addresses ||= get_addresses.map do |address_tree|
36
- Mail::Address.new(address_tree)
27
+ @addresses ||= @address_list.addresses.map do |address_data|
28
+ Mail::Address.new(address_data)
37
29
  end
38
30
  end
39
-
40
- # Returns a list of all recipient syntax trees that are not part of a group
41
- def individual_recipients # :nodoc:
42
- @individual_recipients ||= @address_nodes - group_recipients
43
- end
44
-
45
- # Returns a list of all recipient syntax trees that are part of a group
46
- def group_recipients # :nodoc:
47
- @group_recipients ||= @address_nodes.select { |an| an.respond_to?(:group_name) }
31
+
32
+ def addresses_grouped_by_group
33
+ return @addresses_grouped_by_group if @addresses_grouped_by_group
34
+
35
+ @addresses_grouped_by_group = {}
36
+
37
+ @address_list.addresses.each do |address_data|
38
+ if group = address_data.group
39
+ @addresses_grouped_by_group[group] ||= []
40
+ @addresses_grouped_by_group[group] << Mail::Address.new(address_data)
41
+ end
42
+ end
43
+ @addresses_grouped_by_group
48
44
  end
49
45
 
50
46
  # Returns the names as an array of strings of all groups
51
47
  def group_names # :nodoc:
52
- group_recipients.map { |g| g.group_name.text_value }
53
- end
54
-
55
- # Returns a list of address syntax trees
56
- def address_nodes # :nodoc:
57
- @address_nodes
58
- end
59
-
60
- private
61
-
62
- def get_addresses
63
- (individual_recipients + group_recipients.map { |g| get_group_addresses(g) }).flatten
64
- end
65
-
66
- def get_group_addresses(g)
67
- if g.group_list.respond_to?(:addresses)
68
- g.group_list.addresses
69
- else
70
- []
71
- end
48
+ @address_list.group_names
72
49
  end
73
50
  end
74
51
  end
@@ -5,13 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::ContentDispositionParser.new
9
- if tree = parser.parse(cleaned(string))
10
- @disposition_type = tree.disposition_type.text_value.downcase
11
- @parameters = tree.parameters
12
- else
13
- raise Mail::Field::ParseError.new(ContentDispositionElement, string, parser.failure_reason)
14
- end
8
+ content_disposition = Mail::Parsers::ContentDispositionParser.new.parse(cleaned(string))
9
+ @disposition_type = content_disposition.disposition_type
10
+ @parameters = content_disposition.parameters
15
11
  end
16
12
 
17
13
  def disposition_type
@@ -5,12 +5,8 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::ContentLocationParser.new
9
- if tree = parser.parse(string)
10
- @location = tree.location.text_value
11
- else
12
- raise Mail::Field::ParseError.new(ContentLocationElement, string, parser.failure_reason)
13
- end
8
+ content_location = Mail::Parsers::ContentLocationParser.new.parse(string)
9
+ @location = content_location.location
14
10
  end
15
11
 
16
12
  def location
@@ -4,16 +4,9 @@ module Mail
4
4
 
5
5
  include Mail::Utilities
6
6
 
7
- def initialize( string )
8
- parser = Mail::ContentTransferEncodingParser.new
9
- case
10
- when string.blank?
11
- @encoding = ''
12
- when tree = parser.parse(string.to_s.downcase)
13
- @encoding = tree.encoding.text_value
14
- else
15
- raise Mail::Field::ParseError.new(ContentTransferEncodingElement, string, parser.failure_reason)
16
- end
7
+ def initialize(string)
8
+ content_transfer_encoding = Mail::Parsers::ContentTransferEncodingParser.new.parse(string)
9
+ @encoding = content_transfer_encoding.encoding
17
10
  end
18
11
 
19
12
  def encoding
@@ -5,14 +5,10 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::ContentTypeParser.new
9
- if tree = parser.parse(cleaned(string))
10
- @main_type = tree.main_type.text_value.downcase
11
- @sub_type = tree.sub_type.text_value.downcase
12
- @parameters = tree.parameters
13
- else
14
- raise Mail::Field::ParseError.new(ContentTypeElement, string, parser.failure_reason)
15
- end
8
+ content_type = Mail::Parsers::ContentTypeParser.new.parse(cleaned(string))
9
+ @main_type = content_type.main_type
10
+ @sub_type = content_type.sub_type
11
+ @parameters = content_type.parameters
16
12
  end
17
13
 
18
14
  def main_type
@@ -5,13 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::DateTimeParser.new
9
- if tree = parser.parse(string)
10
- @date_string = tree.date.text_value
11
- @time_string = tree.time.text_value
12
- else
13
- raise Mail::Field::ParseError.new(DateTimeElement, string, parser.failure_reason)
14
- end
8
+ date_time = Mail::Parsers::DateTimeParser.new.parse(string)
9
+ @date_string = date_time.date_string
10
+ @time_string = date_time.time_string
15
11
  end
16
12
 
17
13
  def date_string
@@ -5,17 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::EnvelopeFromParser.new
9
- if @tree = parser.parse(string)
10
- @address = tree.addr_spec.text_value.strip
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
- parser = Mail::MessageIdsParser.new
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
- parser = Mail::MimeVersionParser.new
9
- if tree = parser.parse(string)
10
- @major = tree.major.text_value
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
- parser = Mail::PhraseListsParser.new
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.text_value) }
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
- parser = Mail::ReceivedParser.new
9
- if tree = parser.parse(string)
10
- @date_time = ::DateTime.parse("#{tree.date_time.date.text_value} #{tree.date_time.time.text_value}")
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
@@ -292,7 +292,6 @@ module Mail
292
292
 
293
293
  if encoding == previous_encoding
294
294
  line = results.pop + line
295
- line.gsub!(/\?\=\=\?.+?\?[QqBb]\?/m, '')
296
295
  end
297
296
 
298
297
  previous_encoding = encoding
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 Patterns
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 unless value.blank?
114
- name, value = split(name)
115
- create_field(name, value, charset)
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
- create_field(name, nil, charset)
122
+ @name = name
123
+ @value = nil
124
+ @raw_value = nil
125
+ @charset = charset
118
126
  else # Field.new("field-name", "value")
119
- create_field(name, value, charset)
127
+ @name = name
128
+ @value = value
129
+ @raw_value = nil
130
+ @charset = charset
120
131
  end
121
- return self
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
- @field
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
- field.name
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, field.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
- self.field = new_field(name, value, charset)
228
+ new_field(name, value, charset)
193
229
  rescue Mail::Field::ParseError => e
194
- self.field = Mail::UnstructuredField.new(name, value)
195
- self.field.errors << [name, value, e]
196
- self.field
230
+ field = Mail::UnstructuredField.new(name, value)
231
+ field.errors << [name, value, e]
232
+ field
197
233
  end
198
234
  end
199
235
 
@@ -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) / 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