mail 2.5.5 → 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.
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