mail 2.5.2 → 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 (151) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +78 -0
  3. data/CONTRIBUTING.md +17 -4
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +50 -29
  8. data/Rakefile +11 -22
  9. data/VERSION +4 -0
  10. data/lib/mail/attachments_list.rb +2 -2
  11. data/lib/mail/body.rb +5 -5
  12. data/lib/mail/check_delivery_params.rb +12 -22
  13. data/lib/mail/core_extensions/object.rb +8 -8
  14. data/lib/mail/core_extensions/smtp.rb +12 -13
  15. data/lib/mail/core_extensions/string.rb +16 -6
  16. data/lib/mail/elements/address.rb +43 -79
  17. data/lib/mail/elements/address_list.rb +19 -42
  18. data/lib/mail/elements/content_disposition_element.rb +3 -7
  19. data/lib/mail/elements/content_location_element.rb +2 -6
  20. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  21. data/lib/mail/elements/content_type_element.rb +4 -8
  22. data/lib/mail/elements/date_time_element.rb +3 -7
  23. data/lib/mail/elements/envelope_from_element.rb +18 -13
  24. data/lib/mail/elements/message_ids_element.rb +1 -6
  25. data/lib/mail/elements/mime_version_element.rb +3 -7
  26. data/lib/mail/elements/phrase_list.rb +2 -7
  27. data/lib/mail/elements/received_element.rb +3 -7
  28. data/lib/mail/encodings/quoted_printable.rb +4 -3
  29. data/lib/mail/encodings.rb +60 -28
  30. data/lib/mail/envelope.rb +0 -5
  31. data/lib/mail/field.rb +63 -28
  32. data/lib/mail/field_list.rb +18 -18
  33. data/lib/mail/fields/bcc_field.rb +2 -2
  34. data/lib/mail/fields/cc_field.rb +2 -2
  35. data/lib/mail/fields/comments_field.rb +1 -1
  36. data/lib/mail/fields/common/common_address.rb +26 -21
  37. data/lib/mail/fields/common/common_date.rb +0 -7
  38. data/lib/mail/fields/common/common_field.rb +5 -5
  39. data/lib/mail/fields/content_id_field.rb +1 -2
  40. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  41. data/lib/mail/fields/content_type_field.rb +1 -1
  42. data/lib/mail/fields/date_field.rb +14 -14
  43. data/lib/mail/fields/from_field.rb +2 -2
  44. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  45. data/lib/mail/fields/message_id_field.rb +2 -3
  46. data/lib/mail/fields/references_field.rb +2 -1
  47. data/lib/mail/fields/reply_to_field.rb +2 -2
  48. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  49. data/lib/mail/fields/resent_cc_field.rb +2 -2
  50. data/lib/mail/fields/resent_from_field.rb +2 -2
  51. data/lib/mail/fields/resent_sender_field.rb +3 -3
  52. data/lib/mail/fields/resent_to_field.rb +2 -2
  53. data/lib/mail/fields/sender_field.rb +8 -8
  54. data/lib/mail/fields/to_field.rb +2 -2
  55. data/lib/mail/fields/unstructured_field.rb +8 -2
  56. data/lib/mail/header.rb +13 -23
  57. data/lib/mail/mail.rb +12 -0
  58. data/lib/mail/matchers/has_sent_mail.rb +34 -1
  59. data/lib/mail/message.rb +151 -48
  60. data/lib/mail/multibyte/chars.rb +2 -2
  61. data/lib/mail/multibyte/unicode.rb +6 -4
  62. data/lib/mail/network/delivery_methods/exim.rb +1 -6
  63. data/lib/mail/network/delivery_methods/file_delivery.rb +1 -1
  64. data/lib/mail/network/delivery_methods/sendmail.rb +32 -10
  65. data/lib/mail/network/delivery_methods/smtp.rb +31 -34
  66. data/lib/mail/network/delivery_methods/smtp_connection.rb +6 -6
  67. data/lib/mail/network/delivery_methods/test_mailer.rb +2 -2
  68. data/lib/mail/network/retriever_methods/imap.rb +18 -13
  69. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  70. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  71. data/lib/mail/parsers/content_location_parser.rb +35 -0
  72. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  73. data/lib/mail/parsers/content_type_parser.rb +64 -0
  74. data/lib/mail/parsers/date_time_parser.rb +36 -0
  75. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  76. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  77. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  78. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  79. data/lib/mail/parsers/ragel/common.rl +184 -0
  80. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  81. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  82. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  83. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  84. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  85. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  86. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  87. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  88. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  89. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  90. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  91. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  92. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  93. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  105. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  106. data/lib/mail/parsers/ragel/ruby.rb +29 -0
  107. data/lib/mail/parsers/ragel.rb +17 -0
  108. data/lib/mail/parsers/received_parser.rb +47 -0
  109. data/lib/mail/parsers.rb +26 -0
  110. data/lib/mail/part.rb +6 -2
  111. data/lib/mail/parts_list.rb +5 -3
  112. data/lib/mail/patterns.rb +4 -1
  113. data/lib/mail/utilities.rb +28 -18
  114. data/lib/mail/version.rb +1 -1
  115. data/lib/mail/version_specific/ruby_1_8.rb +6 -2
  116. data/lib/mail/version_specific/ruby_1_9.rb +61 -21
  117. data/lib/mail.rb +1 -2
  118. metadata +112 -73
  119. data/lib/VERSION +0 -4
  120. data/lib/load_parsers.rb +0 -41
  121. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  122. data/lib/mail/parsers/address_lists.rb +0 -64
  123. data/lib/mail/parsers/address_lists.treetop +0 -19
  124. data/lib/mail/parsers/content_disposition.rb +0 -535
  125. data/lib/mail/parsers/content_disposition.treetop +0 -46
  126. data/lib/mail/parsers/content_location.rb +0 -139
  127. data/lib/mail/parsers/content_location.treetop +0 -20
  128. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  129. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  130. data/lib/mail/parsers/content_type.rb +0 -967
  131. data/lib/mail/parsers/content_type.treetop +0 -68
  132. data/lib/mail/parsers/date_time.rb +0 -114
  133. data/lib/mail/parsers/date_time.treetop +0 -11
  134. data/lib/mail/parsers/envelope_from.rb +0 -194
  135. data/lib/mail/parsers/envelope_from.treetop +0 -32
  136. data/lib/mail/parsers/message_ids.rb +0 -45
  137. data/lib/mail/parsers/message_ids.treetop +0 -15
  138. data/lib/mail/parsers/mime_version.rb +0 -144
  139. data/lib/mail/parsers/mime_version.treetop +0 -19
  140. data/lib/mail/parsers/phrase_lists.rb +0 -45
  141. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  142. data/lib/mail/parsers/received.rb +0 -71
  143. data/lib/mail/parsers/received.treetop +0 -11
  144. data/lib/mail/parsers/rfc2045.rb +0 -464
  145. data/lib/mail/parsers/rfc2045.treetop +0 -36
  146. data/lib/mail/parsers/rfc2822.rb +0 -5397
  147. data/lib/mail/parsers/rfc2822.treetop +0 -410
  148. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  149. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  150. data/lib/tasks/corpus.rake +0 -125
  151. data/lib/tasks/treetop.rake +0 -10
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,27 +150,39 @@ 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
157
179
 
158
180
  def <=>( other )
159
- self_order = FIELD_ORDER.rindex(self.name.to_s.downcase) || 100
160
- other_order = FIELD_ORDER.rindex(other.name.to_s.downcase) || 100
161
- self_order <=> other_order
181
+ self.field_order_id <=> other.field_order_id
182
+ end
183
+
184
+ def field_order_id
185
+ @field_order_id ||= (FIELD_ORDER_LOOKUP[self.name.to_s.downcase] || 100)
162
186
  end
163
187
 
164
188
  def method_missing(name, *args, &block)
@@ -174,37 +198,48 @@ module Mail
174
198
  mime-version content-type content-transfer-encoding
175
199
  content-location content-disposition content-description ]
176
200
 
201
+ FIELD_ORDER_LOOKUP = Hash[FIELD_ORDER.each_with_index.to_a]
202
+
177
203
  private
178
204
 
179
205
  def split(raw_field)
180
- match_data = raw_field.mb_chars.match(/^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/)
181
- [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]
182
208
  rescue
183
209
  STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
184
210
  end
185
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
+
186
224
  def create_field(name, value, charset)
225
+ value = unfold(value) if value.is_a?(String)
226
+
187
227
  begin
188
- self.field = new_field(name, value, charset)
228
+ new_field(name, value, charset)
189
229
  rescue Mail::Field::ParseError => e
190
- self.field = Mail::UnstructuredField.new(name, value)
191
- self.field.errors << [name, value, e]
192
- self.field
230
+ field = Mail::UnstructuredField.new(name, value)
231
+ field.errors << [name, value, e]
232
+ field
193
233
  end
194
234
  end
195
235
 
196
236
  def new_field(name, value, charset)
197
237
  lower_case_name = name.to_s.downcase
198
- header_name = nil
199
- FIELDS_MAP.each do |field_name, _|
200
- header_name = field_name if lower_case_name == field_name
201
- end
202
- if header_name
203
- FIELDS_MAP[header_name].new(value, charset)
238
+ if field_klass = FIELDS_MAP[lower_case_name]
239
+ field_klass.new(value, charset)
204
240
  else
205
241
  OptionalField.new(name, value, charset)
206
242
  end
207
-
208
243
  end
209
244
 
210
245
  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) / 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
@@ -94,7 +100,7 @@ module Mail
94
100
 
95
101
  def do_encode(field_name)
96
102
  return '' if value.blank?
97
- 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 }
98
104
  address_text = address_array.join(", \r\n\s")
99
105
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
100
106
  group_text = group_array.join(" \r\n\s")
@@ -104,7 +110,7 @@ module Mail
104
110
 
105
111
  def do_decode
106
112
  return nil if value.blank?
107
- 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 }
108
114
  address_text = address_array.join(", ")
109
115
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
110
116
  group_text = group_array.join(" ")
@@ -112,15 +118,14 @@ module Mail
112
118
  return_array.join(", ")
113
119
  end
114
120
 
115
- # Returns the syntax tree of the Addresses
116
- def tree # :nodoc:
117
- @tree ||= AddressList.new(value)
121
+ def address_list # :nodoc:
122
+ @address_list ||= AddressList.new(value)
118
123
  end
119
124
 
120
125
  def get_group_addresses(group_list)
121
126
  if group_list.respond_to?(:addresses)
122
- group_list.addresses.map do |address_tree|
123
- Mail::Address.new(address_tree)
127
+ group_list.addresses.map do |address|
128
+ Mail::Address.new(address)
124
129
  end
125
130
  else
126
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
@@ -22,7 +22,7 @@ module Mail
22
22
  end
23
23
 
24
24
  def to_s
25
- decoded
25
+ decoded.to_s
26
26
  end
27
27
 
28
28
  def default
@@ -43,15 +43,15 @@ 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
 
50
+ FILENAME_RE = /\b(filename|name)=([^;"\r\n]+\s[^;"\r\n]+)/
50
51
  def ensure_filename_quoted(value)
51
- if !value.is_a?(Array) and /(.)*\s(filename|name)=[^"](.+\s)+[^"]/.match value
52
- value.gsub!(/[^=]+$/, '"\\0"')
52
+ if value.is_a?(String)
53
+ value.sub! FILENAME_RE, '\1="\2"'
53
54
  end
54
55
  end
55
-
56
56
  end
57
57
  end
@@ -55,8 +55,7 @@ module Mail
55
55
  private
56
56
 
57
57
  def generate_content_id
58
- fqdn = ::Socket.gethostname
59
- "<#{Mail.random_tag}@#{fqdn}.mail>"
58
+ "<#{Mail.random_tag}@#{::Socket.gethostname}.mail>"
60
59
  end
61
60
 
62
61
  end
@@ -10,8 +10,8 @@ module Mail
10
10
 
11
11
  def initialize(value = nil, charset = 'utf-8')
12
12
  self.charset = charset
13
- value = '7bit' if value.to_s =~ /7-bit/i
14
- value = '8bit' if value.to_s =~ /8-bit/i
13
+ value = '7bit' if value.to_s =~ /7-?bits?/i
14
+ value = '8bit' if value.to_s =~ /8-?bits?/i
15
15
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
16
16
  self.parse
17
17
  self
@@ -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
@@ -180,7 +180,7 @@ module Mail
180
180
  params = $2.to_s.split(/\s+/)
181
181
  params = params.map { |i| i.to_s.chomp.strip }
182
182
  params = params.map { |i| i.split(/\s*\=\s*/) }
183
- params = params.map { |i| "#{i[0]}=#{dquote(i[1].to_s)}" }.join('; ')
183
+ params = params.map { |i| "#{i[0]}=#{dquote(i[1].to_s.gsub(/;$/,""))}" }.join('; ')
184
184
  "#{type}; #{params}"
185
185
  when val =~ /^\s*$/
186
186
  'text/plain'
@@ -1,18 +1,18 @@
1
1
  # encoding: utf-8
2
- #
2
+ #
3
3
  # = Date Field
4
- #
4
+ #
5
5
  # The Date field inherits from StructuredField and handles the Date: header
6
6
  # field in the email.
7
- #
7
+ #
8
8
  # Sending date to a mail message will instantiate a Mail::Field object that
9
- # has a DateField as it's field type. This includes all Mail::CommonAddress
9
+ # has a DateField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance methods.
11
- #
11
+ #
12
12
  # There must be excatly one Date field in an RFC2822 email.
13
- #
13
+ #
14
14
  # == Examples:
15
- #
15
+ #
16
16
  # mail = Mail.new
17
17
  # mail.date = 'Mon, 24 Nov 1997 14:22:01 -0800'
18
18
  # mail.date #=> #<DateTime: 211747170121/86400,-1/3,2299161>
@@ -20,17 +20,17 @@
20
20
  # mail[:date] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
21
21
  # mail['date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
22
22
  # mail['Date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
23
- #
23
+ #
24
24
  require 'mail/fields/common/common_date'
25
25
 
26
26
  module Mail
27
27
  class DateField < StructuredField
28
-
28
+
29
29
  include Mail::CommonDate
30
-
30
+
31
31
  FIELD_NAME = 'date'
32
32
  CAPITALIZED_FIELD = "Date"
33
-
33
+
34
34
  def initialize(value = nil, charset = 'utf-8')
35
35
  self.charset = charset
36
36
  if value.blank?
@@ -44,14 +44,14 @@ module Mail
44
44
  rescue ArgumentError => e
45
45
  raise e unless "invalid date"==e.message
46
46
  end
47
-
47
+
48
48
  def encoded
49
49
  do_encode(CAPITALIZED_FIELD)
50
50
  end
51
-
51
+
52
52
  def decoded
53
53
  do_decode
54
54
  end
55
-
55
+
56
56
  end
57
57
  end
@@ -6,7 +6,7 @@
6
6
  # field in the email.
7
7
  #
8
8
  # Sending from to a mail message will instantiate a Mail::Field object that
9
- # has a FromField as it's field type. This includes all Mail::CommonAddress
9
+ # has a FromField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance metods.
11
11
  #
12
12
  # Only one From 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.from = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.from #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
19
+ # mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
20
  # mail[:from] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
21
21
  # mail['from'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
22
22
  # mail['From'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
@@ -6,7 +6,7 @@
6
6
  # In-Reply-To: header field in the email.
7
7
  #
8
8
  # Sending in_reply_to to a mail message will instantiate a Mail::Field object that
9
- # has a InReplyToField as it's field type. This includes all Mail::CommonMessageId
9
+ # has a InReplyToField as its field type. This includes all Mail::CommonMessageId
10
10
  # module instance metods.
11
11
  #
12
12
  # Note that, the #message_ids method will return an array of message IDs without the
@@ -38,6 +38,7 @@ module Mail
38
38
 
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
+ value = value.join("\r\n\s") if value.is_a?(Array)
41
42
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
43
  self.parse
43
44
  self
@@ -6,7 +6,7 @@
6
6
  # Message-ID: header field in the email.
7
7
  #
8
8
  # Sending message_id to a mail message will instantiate a Mail::Field object that
9
- # has a MessageIdField as it's field type. This includes all Mail::CommonMessageId
9
+ # has a MessageIdField as its field type. This includes all Mail::CommonMessageId
10
10
  # module instance metods.
11
11
  #
12
12
  # Only one MessageId field can appear in a header, and syntactically it can only have
@@ -75,8 +75,7 @@ module Mail
75
75
  private
76
76
 
77
77
  def generate_message_id
78
- fqdn = ::Socket.gethostname
79
- "<#{Mail.random_tag}@#{fqdn}.mail>"
78
+ "<#{Mail.random_tag}@#{::Socket.gethostname}.mail>"
80
79
  end
81
80
 
82
81
  end
@@ -6,7 +6,7 @@
6
6
  # field in the email.
7
7
  #
8
8
  # Sending references to a mail message will instantiate a Mail::Field object that
9
- # has a ReferencesField as it's field type. This includes all Mail::CommonAddress
9
+ # has a ReferencesField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance metods.
11
11
  #
12
12
  # Note that, the #message_ids method will return an array of message IDs without the
@@ -38,6 +38,7 @@ module Mail
38
38
 
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
+ value = value.join("\r\n\s") if value.is_a?(Array)
41
42
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
43
  self.parse
43
44
  self
@@ -6,7 +6,7 @@
6
6
  # field in the email.
7
7
  #
8
8
  # Sending reply_to to a mail message will instantiate a Mail::Field object that
9
- # has a ReplyToField as it's field type. This includes all Mail::CommonAddress
9
+ # has a ReplyToField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance metods.
11
11
  #
12
12
  # Only one Reply-To 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.reply_to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.reply_to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
19
+ # mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
20
  # mail[:reply_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
21
21
  # mail['reply-to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
22
22
  # mail['Reply-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
@@ -6,7 +6,7 @@
6
6
  # Resent-Bcc: header field in the email.
7
7
  #
8
8
  # Sending resent_bcc to a mail message will instantiate a Mail::Field object that
9
- # has a ResentBccField as it's field type. This includes all Mail::CommonAddress
9
+ # has a ResentBccField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance metods.
11
11
  #
12
12
  # Only one Resent-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.resent_bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.resent_bcc #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
19
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
20
  # mail[:resent_bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
21
21
  # mail['resent-bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
22
22
  # mail['Resent-Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4