mail 2.5.4 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +7 -2
  3. data/CONTRIBUTING.md +13 -0
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +9 -7
  6. data/README.md +41 -21
  7. data/Rakefile +10 -2
  8. data/VERSION +4 -0
  9. data/lib/mail/body.rb +2 -2
  10. data/lib/mail/core_extensions/string.rb +12 -2
  11. data/lib/mail/elements/address.rb +38 -82
  12. data/lib/mail/elements/address_list.rb +19 -42
  13. data/lib/mail/elements/content_disposition_element.rb +3 -7
  14. data/lib/mail/elements/content_location_element.rb +2 -6
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  16. data/lib/mail/elements/content_type_element.rb +4 -8
  17. data/lib/mail/elements/date_time_element.rb +3 -7
  18. data/lib/mail/elements/envelope_from_element.rb +3 -11
  19. data/lib/mail/elements/message_ids_element.rb +1 -6
  20. data/lib/mail/elements/mime_version_element.rb +3 -7
  21. data/lib/mail/elements/phrase_list.rb +2 -7
  22. data/lib/mail/elements/received_element.rb +3 -7
  23. data/lib/mail/encodings.rb +0 -1
  24. data/lib/mail/envelope.rb +0 -5
  25. data/lib/mail/field.rb +53 -17
  26. data/lib/mail/field_list.rb +18 -18
  27. data/lib/mail/fields/common/common_address.rb +15 -20
  28. data/lib/mail/fields/common/common_date.rb +0 -7
  29. data/lib/mail/fields/common/common_field.rb +1 -1
  30. data/lib/mail/fields/content_transfer_encoding_field.rb +0 -6
  31. data/lib/mail/fields/resent_sender_field.rb +1 -1
  32. data/lib/mail/fields/sender_field.rb +1 -1
  33. data/lib/mail/fields/unstructured_field.rb +7 -1
  34. data/lib/mail/header.rb +8 -22
  35. data/lib/mail/mail.rb +12 -0
  36. data/lib/mail/matchers/has_sent_mail.rb +34 -1
  37. data/lib/mail/message.rb +18 -11
  38. data/lib/mail/multibyte/unicode.rb +1 -1
  39. data/lib/mail/network/delivery_methods/sendmail.rb +1 -1
  40. data/lib/mail/network/delivery_methods/smtp.rb +5 -9
  41. data/lib/mail/network/retriever_methods/imap.rb +18 -13
  42. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  43. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  44. data/lib/mail/parsers/content_location_parser.rb +35 -0
  45. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  46. data/lib/mail/parsers/content_type_parser.rb +64 -0
  47. data/lib/mail/parsers/date_time_parser.rb +36 -0
  48. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  49. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  50. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  51. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  52. data/lib/mail/parsers/ragel/common.rl +184 -0
  53. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  54. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  55. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  56. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  57. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  58. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  59. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  60. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  61. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  62. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  63. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  64. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  65. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  66. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  67. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  68. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  69. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  70. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  71. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  72. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  73. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  74. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  75. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  76. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  77. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  78. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  79. data/lib/mail/parsers/ragel/ruby.rb +29 -0
  80. data/lib/mail/parsers/ragel.rb +17 -0
  81. data/lib/mail/parsers/received_parser.rb +47 -0
  82. data/lib/mail/parsers.rb +26 -0
  83. data/lib/mail/parts_list.rb +5 -3
  84. data/lib/mail/patterns.rb +3 -1
  85. data/lib/mail/utilities.rb +3 -1
  86. data/lib/mail/version.rb +1 -1
  87. data/lib/mail/version_specific/ruby_1_8.rb +1 -1
  88. data/lib/mail/version_specific/ruby_1_9.rb +16 -1
  89. data/lib/mail.rb +1 -1
  90. metadata +77 -87
  91. data/lib/VERSION +0 -4
  92. data/lib/load_parsers.rb +0 -35
  93. data/lib/mail/parsers/address_lists.rb +0 -64
  94. data/lib/mail/parsers/address_lists.treetop +0 -19
  95. data/lib/mail/parsers/content_disposition.rb +0 -535
  96. data/lib/mail/parsers/content_disposition.treetop +0 -46
  97. data/lib/mail/parsers/content_location.rb +0 -139
  98. data/lib/mail/parsers/content_location.treetop +0 -20
  99. data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
  100. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
  101. data/lib/mail/parsers/content_type.rb +0 -971
  102. data/lib/mail/parsers/content_type.treetop +0 -68
  103. data/lib/mail/parsers/date_time.rb +0 -114
  104. data/lib/mail/parsers/date_time.treetop +0 -11
  105. data/lib/mail/parsers/envelope_from.rb +0 -194
  106. data/lib/mail/parsers/envelope_from.treetop +0 -32
  107. data/lib/mail/parsers/message_ids.rb +0 -45
  108. data/lib/mail/parsers/message_ids.treetop +0 -15
  109. data/lib/mail/parsers/mime_version.rb +0 -144
  110. data/lib/mail/parsers/mime_version.treetop +0 -19
  111. data/lib/mail/parsers/phrase_lists.rb +0 -45
  112. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  113. data/lib/mail/parsers/received.rb +0 -71
  114. data/lib/mail/parsers/received.treetop +0 -11
  115. data/lib/mail/parsers/rfc2045.rb +0 -421
  116. data/lib/mail/parsers/rfc2045.treetop +0 -35
  117. data/lib/mail/parsers/rfc2822.rb +0 -5397
  118. data/lib/mail/parsers/rfc2822.treetop +0 -408
  119. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  120. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  121. data/lib/tasks/corpus.rake +0 -125
  122. data/lib/tasks/treetop.rake +0 -10
@@ -5,17 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- 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
@@ -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,44 +20,40 @@ module Mail
20
20
  Encodings.address_encode(val, charset)
21
21
  end
22
22
 
23
- # Allows you to iterate through each address object in the 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
@@ -77,7 +73,7 @@ module Mail
77
73
 
78
74
  # Returns the name of all the groups in a string
79
75
  def group_names # :nodoc:
80
- tree.group_names
76
+ address_list.group_names
81
77
  end
82
78
 
83
79
  def default
@@ -104,7 +100,7 @@ module Mail
104
100
 
105
101
  def do_encode(field_name)
106
102
  return '' if value.blank?
107
- address_array = tree.addresses.reject { |a| encoded_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 }
108
104
  address_text = address_array.join(", \r\n\s")
109
105
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
110
106
  group_text = group_array.join(" \r\n\s")
@@ -114,7 +110,7 @@ module Mail
114
110
 
115
111
  def do_decode
116
112
  return nil if value.blank?
117
- address_array = tree.addresses.reject { |a| decoded_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 }
118
114
  address_text = address_array.join(", ")
119
115
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
120
116
  group_text = group_array.join(" ")
@@ -122,15 +118,14 @@ module Mail
122
118
  return_array.join(", ")
123
119
  end
124
120
 
125
- # Returns the syntax tree of the Addresses
126
- def tree # :nodoc:
127
- @tree ||= AddressList.new(value)
121
+ def address_list # :nodoc:
122
+ @address_list ||= AddressList.new(value)
128
123
  end
129
124
 
130
125
  def get_group_addresses(group_list)
131
126
  if group_list.respond_to?(:addresses)
132
- group_list.addresses.map do |address_tree|
133
- Mail::Address.new(address_tree)
127
+ group_list.addresses.map do |address|
128
+ Mail::Address.new(address)
134
129
  end
135
130
  else
136
131
  []
@@ -13,7 +13,6 @@ module Mail
13
13
  def parse(val = value)
14
14
  unless val.blank?
15
15
  @element = Mail::DateTimeElement.new(val)
16
- @tree = @element.tree
17
16
  else
18
17
  nil
19
18
  end
@@ -32,11 +31,5 @@ module Mail
32
31
  def element
33
32
  @element ||= Mail::DateTimeElement.new(value)
34
33
  end
35
-
36
- # Returns the syntax tree of the Date
37
- def tree
38
- @tree ||= element.tree
39
- end
40
-
41
34
  end
42
35
  end
@@ -43,7 +43,7 @@ module Mail
43
43
  if value.is_a?(Array)
44
44
  value
45
45
  else
46
- value.to_s.gsub(/#{field_name}:\s+/i, '')
46
+ value.to_s.sub(/#{field_name}:\s+/i, '')
47
47
  end
48
48
  end
49
49
 
@@ -23,12 +23,6 @@ module Mail
23
23
  end
24
24
  end
25
25
 
26
- def tree
27
- STDERR.puts("tree is deprecated. Please use encoding to get parse result\n#{caller}")
28
- @element ||= Mail::ContentTransferEncodingElement.new(value)
29
- @tree ||= @element.tree
30
- end
31
-
32
26
  def element
33
27
  @element ||= Mail::ContentTransferEncodingElement.new(value)
34
28
  end
@@ -47,7 +47,7 @@ module Mail
47
47
  end
48
48
 
49
49
  def address
50
- tree.addresses.first
50
+ address_list.addresses.first
51
51
  end
52
52
 
53
53
  def encoded
@@ -48,7 +48,7 @@ module Mail
48
48
  end
49
49
 
50
50
  def address
51
- tree.addresses.first
51
+ address_list.addresses.first
52
52
  end
53
53
 
54
54
  def encoded
@@ -144,6 +144,7 @@ module Mail
144
144
  limit = 78 - prepend
145
145
  limit = limit - 7 - encoding.length if should_encode
146
146
  line = ""
147
+ first_word = true
147
148
  while !words.empty?
148
149
  break unless word = words.first.dup
149
150
  word.encode!(charset) if charset && word.respond_to?(:encode!)
@@ -158,7 +159,12 @@ module Mail
158
159
  # Remove the word from the queue ...
159
160
  words.shift
160
161
  # Add word separator
161
- line << " " unless (line.empty? || should_encode)
162
+ if first_word
163
+ first_word = false
164
+ else
165
+ line << " " if !should_encode
166
+ end
167
+
162
168
  # ... add it in encoded form to the current line
163
169
  line << word
164
170
  end
data/lib/mail/header.rb CHANGED
@@ -48,11 +48,15 @@ module Mail
48
48
  # these cases, please make a patch and send it in, or at the least, send
49
49
  # me the example so we can fix it.
50
50
  def initialize(header_text = nil, charset = nil)
51
- @errors = []
52
51
  @charset = charset
53
52
  self.raw_source = header_text.to_crlf.lstrip
54
53
  split_header if header_text
55
54
  end
55
+
56
+ def initialize_copy(original)
57
+ super
58
+ @fields = @fields.dup
59
+ end
56
60
 
57
61
  # The preserved raw source of the header as you passed it in, untouched
58
62
  # for your Regexing glory.
@@ -91,7 +95,6 @@ module Mail
91
95
  unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
92
96
 
93
97
  field = Field.new(field, nil, charset)
94
- field.errors.each { |error| self.errors << error }
95
98
  if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
96
99
  selected.first.update(field.name, field.value)
97
100
  else
@@ -102,7 +105,7 @@ module Mail
102
105
  end
103
106
 
104
107
  def errors
105
- @errors
108
+ @fields.map(&:errors).flatten(1)
106
109
  end
107
110
 
108
111
  # 3.6. Field definitions
@@ -177,7 +180,7 @@ module Mail
177
180
  if dasherize(fn) == "content-type"
178
181
  # Update charset if specified in Content-Type
179
182
  params = self[:content_type].parameters rescue nil
180
- @charset = params && params[:charset]
183
+ @charset = params[:charset] if params && params[:charset]
181
184
  end
182
185
  end
183
186
 
@@ -246,27 +249,10 @@ module Mail
246
249
  @raw_source = val
247
250
  end
248
251
 
249
- # 2.2.3. Long Header Fields
250
- #
251
- # The process of moving from this folded multiple-line representation
252
- # of a header field to its single line representation is called
253
- # "unfolding". Unfolding is accomplished by simply removing any CRLF
254
- # that is immediately followed by WSP. Each header field should be
255
- # treated in its unfolded form for further syntactic and semantic
256
- # evaluation.
257
- def unfold(string)
258
- string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
259
- end
260
-
261
- # Returns the header with all the folds removed
262
- def unfolded_header
263
- @unfolded_header ||= unfold(raw_source)
264
- end
265
-
266
252
  # Splits an unfolded and line break cleaned header into individual field
267
253
  # strings.
268
254
  def split_header
269
- self.fields = unfolded_header.split(CRLF)
255
+ self.fields = raw_source.split(HEADER_SPLIT)
270
256
  end
271
257
 
272
258
  def select_field_for(name)
data/lib/mail/mail.rb CHANGED
@@ -206,6 +206,12 @@ module Mail
206
206
  end
207
207
  end
208
208
 
209
+ # Unregister the given observer, allowing mail to resume operations
210
+ # without it.
211
+ def self.unregister_observer(observer)
212
+ @@delivery_notification_observers.delete(observer)
213
+ end
214
+
209
215
  # You can register an object to be given every mail object that will be sent,
210
216
  # before it is sent. So if you want to add special headers or modify any
211
217
  # email that gets sent through the Mail library, you can do so.
@@ -219,6 +225,12 @@ module Mail
219
225
  end
220
226
  end
221
227
 
228
+ # Unregister the given interceptor, allowing mail to resume operations
229
+ # without it.
230
+ def self.unregister_interceptor(interceptor)
231
+ @@delivery_interceptors.delete(interceptor)
232
+ end
233
+
222
234
  def self.inform_observers(mail)
223
235
  @@delivery_notification_observers.each do |observer|
224
236
  observer.delivered_email(mail)
@@ -29,6 +29,29 @@ module Mail
29
29
  self
30
30
  end
31
31
 
32
+ def cc(recipient_or_list)
33
+ @copy_recipients ||= []
34
+
35
+ if recipient_or_list.kind_of?(Array)
36
+ @copy_recipients += recipient_or_list
37
+ else
38
+ @copy_recipients << recipient_or_list
39
+ end
40
+ self
41
+ end
42
+
43
+ def bcc(recipient_or_list)
44
+ @blind_copy_recipients ||= []
45
+
46
+ if recipient_or_list.kind_of?(Array)
47
+ @blind_copy_recipients += recipient_or_list
48
+ else
49
+ @blind_copy_recipients << recipient_or_list
50
+ end
51
+ self
52
+ end
53
+
54
+
32
55
  def with_subject(subject)
33
56
  @subject = subject
34
57
  self
@@ -73,7 +96,7 @@ module Mail
73
96
  def filter_matched_deliveries(deliveries)
74
97
  candidate_deliveries = deliveries
75
98
 
76
- %w(sender recipients subject subject_matcher body body_matcher).each do |modifier_name|
99
+ %w(sender recipients copy_recipients blind_copy_recipients subject subject_matcher body body_matcher).each do |modifier_name|
77
100
  next unless instance_variable_defined?("@#{modifier_name}")
78
101
  candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
79
102
  end
@@ -89,6 +112,14 @@ module Mail
89
112
  @recipients.all? {|recipient| delivery.to.include?(recipient) }
90
113
  end
91
114
 
115
+ def matches_on_copy_recipients?(delivery)
116
+ @copy_recipients.all? {|recipient| delivery.cc.include?(recipient) }
117
+ end
118
+
119
+ def matches_on_blind_copy_recipients?(delivery)
120
+ @blind_copy_recipients.all? {|recipient| delivery.bcc.include?(recipient) }
121
+ end
122
+
92
123
  def matches_on_subject?(delivery)
93
124
  delivery.subject == @subject
94
125
  end
@@ -109,6 +140,8 @@ module Mail
109
140
  result = ''
110
141
  result += "from #{@sender} " if instance_variable_defined?('@sender')
111
142
  result += "to #{@recipients.inspect} " if instance_variable_defined?('@recipients')
143
+ result += "cc #{@copy_recipients.inspect} " if instance_variable_defined?('@copy_recipients')
144
+ result += "bcc #{@blind_copy_recipients.inspect} " if instance_variable_defined?('@blind_copy_recipients')
112
145
  result += "with subject \"#{@subject}\" " if instance_variable_defined?('@subject')
113
146
  result += "with subject matching \"#{@subject_matcher}\" " if instance_variable_defined?('@subject_matcher')
114
147
  result += "with body \"#{@body}\" " if instance_variable_defined?('@body')