mail 2.5.2 → 2.6.4

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 (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +124 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +7 -22
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +86 -31
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +4 -2
  10. data/lib/mail/body.rb +36 -11
  11. data/lib/mail/check_delivery_params.rb +13 -22
  12. data/lib/mail/configuration.rb +1 -0
  13. data/lib/mail/{patterns.rb → constants.rb} +29 -6
  14. data/lib/mail/core_extensions/smtp.rb +13 -13
  15. data/lib/mail/core_extensions/string/access.rb +1 -0
  16. data/lib/mail/core_extensions/string/multibyte.rb +1 -0
  17. data/lib/mail/core_extensions/string.rb +5 -17
  18. data/lib/mail/elements/address.rb +86 -145
  19. data/lib/mail/elements/address_list.rb +10 -42
  20. data/lib/mail/elements/content_disposition_element.rb +4 -7
  21. data/lib/mail/elements/content_location_element.rb +8 -11
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +4 -10
  23. data/lib/mail/elements/content_type_element.rb +5 -8
  24. data/lib/mail/elements/date_time_element.rb +4 -7
  25. data/lib/mail/elements/envelope_from_element.rb +19 -13
  26. data/lib/mail/elements/message_ids_element.rb +8 -11
  27. data/lib/mail/elements/mime_version_element.rb +4 -7
  28. data/lib/mail/elements/phrase_list.rb +3 -7
  29. data/lib/mail/elements/received_element.rb +4 -7
  30. data/lib/mail/elements.rb +1 -0
  31. data/lib/mail/encodings/7bit.rb +1 -0
  32. data/lib/mail/encodings/8bit.rb +8 -2
  33. data/lib/mail/encodings/base64.rb +7 -1
  34. data/lib/mail/encodings/binary.rb +1 -0
  35. data/lib/mail/encodings/quoted_printable.rb +11 -4
  36. data/lib/mail/encodings/transfer_encoding.rb +27 -17
  37. data/lib/mail/encodings/unix_to_unix.rb +18 -0
  38. data/lib/mail/encodings.rb +54 -39
  39. data/lib/mail/envelope.rb +1 -5
  40. data/lib/mail/field.rb +81 -31
  41. data/lib/mail/field_list.rb +19 -18
  42. data/lib/mail/fields/bcc_field.rb +17 -5
  43. data/lib/mail/fields/cc_field.rb +3 -3
  44. data/lib/mail/fields/comments_field.rb +2 -1
  45. data/lib/mail/fields/common/address_container.rb +1 -0
  46. data/lib/mail/fields/common/common_address.rb +34 -28
  47. data/lib/mail/fields/common/common_date.rb +2 -8
  48. data/lib/mail/fields/common/common_field.rb +9 -5
  49. data/lib/mail/fields/common/common_message_id.rb +3 -2
  50. data/lib/mail/fields/common/parameter_hash.rb +4 -3
  51. data/lib/mail/fields/content_description_field.rb +1 -0
  52. data/lib/mail/fields/content_disposition_field.rb +13 -12
  53. data/lib/mail/fields/content_id_field.rb +4 -4
  54. data/lib/mail/fields/content_location_field.rb +2 -1
  55. data/lib/mail/fields/content_transfer_encoding_field.rb +4 -9
  56. data/lib/mail/fields/content_type_field.rb +4 -3
  57. data/lib/mail/fields/date_field.rb +16 -15
  58. data/lib/mail/fields/from_field.rb +3 -3
  59. data/lib/mail/fields/in_reply_to_field.rb +3 -1
  60. data/lib/mail/fields/keywords_field.rb +2 -2
  61. data/lib/mail/fields/message_id_field.rb +4 -4
  62. data/lib/mail/fields/mime_version_field.rb +3 -2
  63. data/lib/mail/fields/optional_field.rb +1 -0
  64. data/lib/mail/fields/received_field.rb +4 -3
  65. data/lib/mail/fields/references_field.rb +3 -1
  66. data/lib/mail/fields/reply_to_field.rb +3 -3
  67. data/lib/mail/fields/resent_bcc_field.rb +3 -3
  68. data/lib/mail/fields/resent_cc_field.rb +3 -3
  69. data/lib/mail/fields/resent_date_field.rb +2 -1
  70. data/lib/mail/fields/resent_from_field.rb +3 -3
  71. data/lib/mail/fields/resent_message_id_field.rb +1 -0
  72. data/lib/mail/fields/resent_sender_field.rb +4 -4
  73. data/lib/mail/fields/resent_to_field.rb +3 -3
  74. data/lib/mail/fields/return_path_field.rb +1 -1
  75. data/lib/mail/fields/sender_field.rb +9 -9
  76. data/lib/mail/fields/structured_field.rb +1 -0
  77. data/lib/mail/fields/subject_field.rb +1 -0
  78. data/lib/mail/fields/to_field.rb +3 -3
  79. data/lib/mail/fields/unstructured_field.rb +15 -8
  80. data/lib/mail/fields.rb +1 -0
  81. data/lib/mail/header.rb +21 -29
  82. data/lib/mail/indifferent_hash.rb +1 -0
  83. data/lib/mail/mail.rb +16 -1
  84. data/lib/mail/matchers/attachment_matchers.rb +29 -0
  85. data/lib/mail/matchers/has_sent_mail.rb +61 -4
  86. data/lib/mail/message.rb +175 -72
  87. data/lib/mail/multibyte/chars.rb +3 -2
  88. data/lib/mail/multibyte/exceptions.rb +1 -0
  89. data/lib/mail/multibyte/unicode.rb +22 -15
  90. data/lib/mail/multibyte/utils.rb +1 -0
  91. data/lib/mail/multibyte.rb +1 -0
  92. data/lib/mail/network/delivery_methods/exim.rb +3 -7
  93. data/lib/mail/network/delivery_methods/file_delivery.rb +2 -1
  94. data/lib/mail/network/delivery_methods/sendmail.rb +33 -10
  95. data/lib/mail/network/delivery_methods/smtp.rb +32 -34
  96. data/lib/mail/network/delivery_methods/smtp_connection.rb +7 -6
  97. data/lib/mail/network/delivery_methods/test_mailer.rb +3 -2
  98. data/lib/mail/network/retriever_methods/base.rb +1 -0
  99. data/lib/mail/network/retriever_methods/imap.rb +19 -13
  100. data/lib/mail/network/retriever_methods/pop3.rb +1 -0
  101. data/lib/mail/network/retriever_methods/test_retriever.rb +1 -0
  102. data/lib/mail/network.rb +1 -0
  103. data/lib/mail/parsers/address_lists_parser.rb +133 -0
  104. data/lib/mail/parsers/content_disposition_parser.rb +68 -0
  105. data/lib/mail/parsers/content_location_parser.rb +36 -0
  106. data/lib/mail/parsers/content_transfer_encoding_parser.rb +34 -0
  107. data/lib/mail/parsers/content_type_parser.rb +67 -0
  108. data/lib/mail/parsers/date_time_parser.rb +39 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +46 -0
  110. data/lib/mail/parsers/message_ids_parser.rb +40 -0
  111. data/lib/mail/parsers/mime_version_parser.rb +42 -0
  112. data/lib/mail/parsers/phrase_lists_parser.rb +36 -0
  113. data/lib/mail/parsers/ragel/common.rl +185 -0
  114. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  115. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  117. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  118. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  119. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  120. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  121. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  122. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  123. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  124. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  125. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  126. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  127. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  128. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  129. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  130. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  131. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  132. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  133. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  134. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  135. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  136. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  137. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  138. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  139. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  140. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  141. data/lib/mail/parsers/ragel.rb +18 -0
  142. data/lib/mail/parsers/received_parser.rb +49 -0
  143. data/lib/mail/parsers.rb +27 -0
  144. data/lib/mail/part.rb +11 -4
  145. data/lib/mail/parts_list.rb +30 -10
  146. data/lib/mail/utilities.rb +127 -63
  147. data/lib/mail/values/unicode_tables.dat +0 -0
  148. data/lib/mail/version.rb +8 -15
  149. data/lib/mail/version_specific/ruby_1_8.rb +18 -7
  150. data/lib/mail/version_specific/ruby_1_9.rb +136 -32
  151. data/lib/mail.rb +12 -6
  152. metadata +115 -77
  153. data/lib/VERSION +0 -4
  154. data/lib/load_parsers.rb +0 -41
  155. data/lib/mail/core_extensions/nil.rb +0 -19
  156. data/lib/mail/core_extensions/object.rb +0 -13
  157. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  158. data/lib/mail/parsers/address_lists.rb +0 -64
  159. data/lib/mail/parsers/address_lists.treetop +0 -19
  160. data/lib/mail/parsers/content_disposition.rb +0 -535
  161. data/lib/mail/parsers/content_disposition.treetop +0 -46
  162. data/lib/mail/parsers/content_location.rb +0 -139
  163. data/lib/mail/parsers/content_location.treetop +0 -20
  164. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  165. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  166. data/lib/mail/parsers/content_type.rb +0 -967
  167. data/lib/mail/parsers/content_type.treetop +0 -68
  168. data/lib/mail/parsers/date_time.rb +0 -114
  169. data/lib/mail/parsers/date_time.treetop +0 -11
  170. data/lib/mail/parsers/envelope_from.rb +0 -194
  171. data/lib/mail/parsers/envelope_from.treetop +0 -32
  172. data/lib/mail/parsers/message_ids.rb +0 -45
  173. data/lib/mail/parsers/message_ids.treetop +0 -15
  174. data/lib/mail/parsers/mime_version.rb +0 -144
  175. data/lib/mail/parsers/mime_version.treetop +0 -19
  176. data/lib/mail/parsers/phrase_lists.rb +0 -45
  177. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  178. data/lib/mail/parsers/received.rb +0 -71
  179. data/lib/mail/parsers/received.treetop +0 -11
  180. data/lib/mail/parsers/rfc2045.rb +0 -464
  181. data/lib/mail/parsers/rfc2045.treetop +0 -36
  182. data/lib/mail/parsers/rfc2822.rb +0 -5397
  183. data/lib/mail/parsers/rfc2822.treetop +0 -410
  184. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  185. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  186. data/lib/tasks/corpus.rake +0 -125
  187. data/lib/tasks/treetop.rake +0 -10
data/lib/mail/field.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'mail/fields'
2
3
 
3
4
  # encoding: utf-8
@@ -22,7 +23,7 @@ module Mail
22
23
  #
23
24
  class Field
24
25
 
25
- include Patterns
26
+ include Utilities
26
27
  include Comparable
27
28
 
28
29
  STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
@@ -67,6 +68,10 @@ module Mail
67
68
  "content-location" => ContentLocationField,
68
69
  }
69
70
 
71
+ FIELD_NAME_MAP = FIELDS_MAP.inject({}) do |map, (field, field_klass)|
72
+ map.update(field => field_klass::CAPITALIZED_FIELD)
73
+ end
74
+
70
75
  # Generic Field Exception
71
76
  class FieldError < StandardError
72
77
  end
@@ -109,16 +114,23 @@ module Mail
109
114
  # Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
110
115
  def initialize(name, value = nil, charset = 'utf-8')
111
116
  case
112
- 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)
116
- when name !~ /:/ && value.blank? # Field.new("field-name")
117
- create_field(name, nil, charset)
117
+ when name.index(COLON) # Field.new("field-name: field data")
118
+ @charset = Utilities.blank?(value) ? charset : value
119
+ @name = name[FIELD_PREFIX]
120
+ @raw_value = name
121
+ @value = nil
122
+ when Utilities.blank?(value) # Field.new("field-name")
123
+ @name = name
124
+ @value = nil
125
+ @raw_value = nil
126
+ @charset = charset
118
127
  else # Field.new("field-name", "value")
119
- create_field(name, value, charset)
128
+ @name = name
129
+ @value = value
130
+ @raw_value = nil
131
+ @charset = charset
120
132
  end
121
- return self
133
+ @name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
122
134
  end
123
135
 
124
136
  def field=(value)
@@ -126,11 +138,12 @@ module Mail
126
138
  end
127
139
 
128
140
  def field
129
- @field
141
+ _, @value = split(@raw_value) if @raw_value && !@value
142
+ @field ||= create_field(@name, @value, @charset)
130
143
  end
131
144
 
132
145
  def name
133
- field.name
146
+ @name
134
147
  end
135
148
 
136
149
  def value
@@ -138,33 +151,59 @@ module Mail
138
151
  end
139
152
 
140
153
  def value=(val)
141
- create_field(name, val, charset)
154
+ @field = create_field(name, val, @charset)
142
155
  end
143
156
 
144
157
  def to_s
145
158
  field.to_s
146
159
  end
147
160
 
161
+ def inspect
162
+ "#<#{self.class.name} 0x#{(object_id * 2).to_s(16)} #{instance_variables.map do |ivar|
163
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
164
+ end.join(" ")}>"
165
+ end
166
+
148
167
  def update(name, value)
149
- create_field(name, value, charset)
168
+ @field = create_field(name, value, @charset)
150
169
  end
151
170
 
152
171
  def same( other )
153
- match_to_s(other.name, field.name)
172
+ return false unless other.kind_of?(self.class)
173
+ match_to_s(other.name, self.name)
154
174
  end
155
175
 
156
- alias_method :==, :same
176
+ def ==( other )
177
+ return false unless other.kind_of?(self.class)
178
+ match_to_s(other.name, self.name) && match_to_s(other.value, self.value)
179
+ end
180
+
181
+ def responsible_for?( val )
182
+ name.to_s.casecmp(val.to_s) == 0
183
+ end
157
184
 
158
185
  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
186
+ self.field_order_id <=> other.field_order_id
187
+ end
188
+
189
+ def field_order_id
190
+ @field_order_id ||= (FIELD_ORDER_LOOKUP[self.name.to_s.downcase] || 100)
162
191
  end
163
192
 
164
193
  def method_missing(name, *args, &block)
165
194
  field.send(name, *args, &block)
166
195
  end
167
196
 
197
+ if RUBY_VERSION >= '1.9.2'
198
+ def respond_to_missing?(method_name, include_private)
199
+ field.respond_to?(method_name, include_private) || super
200
+ end
201
+ else
202
+ def respond_to?(method_name, include_private = false)
203
+ field.respond_to?(method_name, include_private) || super
204
+ end
205
+ end
206
+
168
207
  FIELD_ORDER = %w[ return-path received
169
208
  resent-date resent-from resent-sender resent-to
170
209
  resent-cc resent-bcc resent-message-id
@@ -174,37 +213,48 @@ module Mail
174
213
  mime-version content-type content-transfer-encoding
175
214
  content-location content-disposition content-description ]
176
215
 
216
+ FIELD_ORDER_LOOKUP = Hash[FIELD_ORDER.each_with_index.to_a]
217
+
177
218
  private
178
219
 
179
220
  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]
221
+ match_data = raw_field.mb_chars.match(FIELD_SPLIT)
222
+ [match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip.to_s]
182
223
  rescue
183
224
  STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
184
225
  end
185
226
 
227
+ # 2.2.3. Long Header Fields
228
+ #
229
+ # The process of moving from this folded multiple-line representation
230
+ # of a header field to its single line representation is called
231
+ # "unfolding". Unfolding is accomplished by simply removing any CRLF
232
+ # that is immediately followed by WSP. Each header field should be
233
+ # treated in its unfolded form for further syntactic and semantic
234
+ # evaluation.
235
+ def unfold(string)
236
+ string.gsub(/[\r\n \t]+/m, ' ')
237
+ end
238
+
186
239
  def create_field(name, value, charset)
240
+ value = unfold(value) if value.is_a?(String)
241
+
187
242
  begin
188
- self.field = new_field(name, value, charset)
243
+ new_field(name, value, charset)
189
244
  rescue Mail::Field::ParseError => e
190
- self.field = Mail::UnstructuredField.new(name, value)
191
- self.field.errors << [name, value, e]
192
- self.field
245
+ field = Mail::UnstructuredField.new(name, value)
246
+ field.errors << [name, value, e]
247
+ field
193
248
  end
194
249
  end
195
250
 
196
251
  def new_field(name, value, charset)
197
252
  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)
253
+ if field_klass = FIELDS_MAP[lower_case_name]
254
+ field_klass.new(value, charset)
204
255
  else
205
256
  OptionalField.new(name, value, charset)
206
257
  end
207
-
208
258
  end
209
259
 
210
260
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # Field List class provides an enhanced array that keeps a list of
@@ -8,26 +9,26 @@ module Mail
8
9
 
9
10
  include Enumerable
10
11
 
12
+ # Insert the field in sorted order.
13
+ #
14
+ # Heavily based on bisect.insort from Python, which is:
15
+ # Copyright (C) 2001-2013 Python Software Foundation.
16
+ # Licensed under <http://docs.python.org/license.html>
17
+ # From <http://hg.python.org/cpython/file/2.7/Lib/bisect.py>
11
18
  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
19
+ lo = 0
20
+ hi = size
21
+
22
+ while lo < hi
23
+ mid = (lo + hi).div(2)
24
+ if new_field < self[mid]
25
+ hi = mid
26
+ else
27
+ lo = mid + 1
27
28
  end
28
- insert(insert_idx, new_field)
29
29
  end
30
+
31
+ insert(lo, new_field)
30
32
  end
31
-
32
33
  end
33
- end
34
+ end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Blind Carbon Copy Field
4
5
  #
@@ -6,7 +7,7 @@
6
7
  # field in the email.
7
8
  #
8
9
  # 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
10
+ # has a BccField as its field type. This includes all Mail::CommonAddress
10
11
  # module instance metods.
11
12
  #
12
13
  # Only one Bcc field can appear in a header, though it can have multiple
@@ -16,7 +17,7 @@
16
17
  #
17
18
  # mail = Mail.new
18
19
  # 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']
20
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
21
  # mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
21
22
  # mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
22
23
  # mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
@@ -39,13 +40,24 @@ module Mail
39
40
  def initialize(value = '', charset = 'utf-8')
40
41
  @charset = charset
41
42
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
43
  self
44
44
  end
45
45
 
46
- # Bcc field should never be :encoded
46
+ def include_in_headers=(include_in_headers)
47
+ @include_in_headers = include_in_headers
48
+ end
49
+
50
+ def include_in_headers
51
+ defined?(@include_in_headers) ? @include_in_headers : self.include_in_headers = false
52
+ end
53
+
54
+ # Bcc field should not be :encoded by default
47
55
  def encoded
48
- ''
56
+ if include_in_headers
57
+ do_encode(CAPITALIZED_FIELD)
58
+ else
59
+ ''
60
+ end
49
61
  end
50
62
 
51
63
  def decoded
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Carbon Copy Field
4
5
  #
@@ -6,7 +7,7 @@
6
7
  # field in the email.
7
8
  #
8
9
  # 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
10
+ # has a CcField as its field type. This includes all Mail::CommonAddress
10
11
  # module instance metods.
11
12
  #
12
13
  # Only one Cc field can appear in a header, though it can have multiple
@@ -16,7 +17,7 @@
16
17
  #
17
18
  # mail = Mail.new
18
19
  # 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']
20
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
21
  # mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
21
22
  # mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
22
23
  # mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
@@ -39,7 +40,6 @@ module Mail
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
42
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
43
  self
44
44
  end
45
45
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Comments Field
4
5
  #
@@ -6,7 +7,7 @@
6
7
  # header field in the email.
7
8
  #
8
9
  # Sending comments to a mail message will instantiate a Mail::Field object that
9
- # has a CommentsField as it's field type.
10
+ # has a CommentsField as its field type.
10
11
  #
11
12
  # An email header can have as many comments fields as it wants. There is no upper
12
13
  # limit, the comments field is also optional (that is, no comment is needed)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Mail
2
3
 
3
4
  class AddressContainer < Array
@@ -1,12 +1,13 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  require 'mail/fields/common/address_container'
3
4
 
4
5
  module Mail
5
6
  module CommonAddress # :nodoc:
6
7
 
7
8
  def parse(val = value)
8
- unless val.blank?
9
- @tree = AddressList.new(encode_if_needed(val))
9
+ unless Utilities.blank?(val)
10
+ @address_list = AddressList.new(encode_if_needed(val))
10
11
  else
11
12
  nil
12
13
  end
@@ -20,54 +21,60 @@ module Mail
20
21
  Encodings.address_encode(val, charset)
21
22
  end
22
23
 
23
- # Allows you to iterate through each address object in the syntax tree
24
+ # Allows you to iterate through each address object in the address_list
24
25
  def each
25
- tree.addresses.each do |address|
26
+ address_list.addresses.each do |address|
26
27
  yield(address)
27
28
  end
28
29
  end
29
30
 
30
31
  # Returns the address string of all the addresses in the address list
31
32
  def addresses
32
- list = tree.addresses.map { |a| a.address }
33
+ list = address_list.addresses.map { |a| a.address }
33
34
  Mail::AddressContainer.new(self, list)
34
35
  end
35
36
 
36
37
  # Returns the formatted string of all the addresses in the address list
37
38
  def formatted
38
- list = tree.addresses.map { |a| a.format }
39
+ list = address_list.addresses.map { |a| a.format }
39
40
  Mail::AddressContainer.new(self, list)
40
41
  end
41
42
 
42
43
  # Returns the display name of all the addresses in the address list
43
44
  def display_names
44
- list = tree.addresses.map { |a| a.display_name }
45
+ list = address_list.addresses.map { |a| a.display_name }
45
46
  Mail::AddressContainer.new(self, list)
46
47
  end
47
48
 
48
49
  # Returns the actual address objects in the address list
49
50
  def addrs
50
- list = tree.addresses
51
+ list = address_list.addresses
51
52
  Mail::AddressContainer.new(self, list)
52
53
  end
53
54
 
54
55
  # Returns a hash of group name => address strings for the address list
55
56
  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
57
+ address_list.addresses_grouped_by_group
61
58
  end
62
59
 
63
60
  # Returns the addresses that are part of groups
64
61
  def group_addresses
65
- groups.map { |k,v| v.map { |a| a.format } }.flatten
62
+ decoded_group_addresses
63
+ end
64
+
65
+ # Returns a list of decoded group addresses
66
+ def decoded_group_addresses
67
+ groups.map { |k,v| v.map { |a| a.decoded } }.flatten
68
+ end
69
+
70
+ # Returns a list of encoded group addresses
71
+ def encoded_group_addresses
72
+ groups.map { |k,v| v.map { |a| a.encoded } }.flatten
66
73
  end
67
74
 
68
75
  # Returns the name of all the groups in a string
69
76
  def group_names # :nodoc:
70
- tree.group_names
77
+ address_list.group_names
71
78
  end
72
79
 
73
80
  def default
@@ -78,10 +85,10 @@ module Mail
78
85
  case
79
86
  when val.nil?
80
87
  raise ArgumentError, "Need to pass an address to <<"
81
- when val.blank?
88
+ when Utilities.blank?(val)
82
89
  parse(encoded)
83
90
  else
84
- self.value = [self.value, val].reject {|a| a.blank? }.join(", ")
91
+ self.value = [self.value, val].reject {|a| Utilities.blank?(a) }.join(", ")
85
92
  end
86
93
  end
87
94
 
@@ -93,34 +100,33 @@ module Mail
93
100
  private
94
101
 
95
102
  def do_encode(field_name)
96
- return '' if value.blank?
97
- address_array = tree.addresses.reject { |a| group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
103
+ return '' if Utilities.blank?(value)
104
+ address_array = address_list.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
98
105
  address_text = address_array.join(", \r\n\s")
99
106
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
100
107
  group_text = group_array.join(" \r\n\s")
101
- return_array = [address_text, group_text].reject { |a| a.blank? }
108
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
102
109
  "#{field_name}: #{return_array.join(", \r\n\s")}\r\n"
103
110
  end
104
111
 
105
112
  def do_decode
106
- return nil if value.blank?
107
- address_array = tree.addresses.reject { |a| group_addresses.include?(a.decoded) }.map { |a| a.decoded }
113
+ return nil if Utilities.blank?(value)
114
+ address_array = address_list.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
108
115
  address_text = address_array.join(", ")
109
116
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
110
117
  group_text = group_array.join(" ")
111
- return_array = [address_text, group_text].reject { |a| a.blank? }
118
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
112
119
  return_array.join(", ")
113
120
  end
114
121
 
115
- # Returns the syntax tree of the Addresses
116
- def tree # :nodoc:
117
- @tree ||= AddressList.new(value)
122
+ def address_list # :nodoc:
123
+ @address_list ||= AddressList.new(value)
118
124
  end
119
125
 
120
126
  def get_group_addresses(group_list)
121
127
  if group_list.respond_to?(:addresses)
122
- group_list.addresses.map do |address_tree|
123
- Mail::Address.new(address_tree)
128
+ group_list.addresses.map do |address|
129
+ Mail::Address.new(address)
124
130
  end
125
131
  else
126
132
  []
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
  module CommonDate # :nodoc:
4
5
  # Returns a date time object of the parsed date
@@ -11,9 +12,8 @@ module Mail
11
12
  end
12
13
 
13
14
  def parse(val = value)
14
- unless val.blank?
15
+ unless Utilities.blank?(val)
15
16
  @element = Mail::DateTimeElement.new(val)
16
- @tree = @element.tree
17
17
  else
18
18
  nil
19
19
  end
@@ -32,11 +32,5 @@ module Mail
32
32
  def element
33
33
  @element ||= Mail::DateTimeElement.new(value)
34
34
  end
35
-
36
- # Returns the syntax tree of the Date
37
- def tree
38
- @tree ||= element.tree
39
- end
40
-
41
35
  end
42
36
  end
@@ -1,6 +1,8 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
  module CommonField # :nodoc:
5
+ include Mail::Constants
4
6
 
5
7
  def name=(value)
6
8
  @name = value
@@ -22,7 +24,7 @@ module Mail
22
24
  end
23
25
 
24
26
  def to_s
25
- decoded
27
+ decoded.to_s
26
28
  end
27
29
 
28
30
  def default
@@ -43,15 +45,17 @@ module Mail
43
45
  if value.is_a?(Array)
44
46
  value
45
47
  else
46
- value.to_s.gsub(/#{field_name}:\s+/i, '')
48
+ value.to_s.sub(/\A#{field_name}:\s+/i, EMPTY)
47
49
  end
48
50
  end
49
51
 
52
+ FILENAME_RE = /\b(filename|name)=([^;"\r\n]+\s[^;"\r\n]+)/
50
53
  def ensure_filename_quoted(value)
51
- if !value.is_a?(Array) and /(.)*\s(filename|name)=[^"](.+\s)+[^"]/.match value
52
- value.gsub!(/[^=]+$/, '"\\0"')
54
+ if value.is_a?(String)
55
+ value.sub FILENAME_RE, '\1="\2"'
56
+ else
57
+ value
53
58
  end
54
59
  end
55
-
56
60
  end
57
61
  end
@@ -1,12 +1,13 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
  module CommonMessageId # :nodoc:
4
5
  def element
5
- @element ||= Mail::MessageIdsElement.new(value) unless value.blank?
6
+ @element ||= Mail::MessageIdsElement.new(value) unless Utilities.blank?(value)
6
7
  end
7
8
 
8
9
  def parse(val = value)
9
- unless val.blank?
10
+ unless Utilities.blank?(val)
10
11
  @element = Mail::MessageIdsElement.new(val)
11
12
  else
12
13
  nil
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # ParameterHash is an intelligent Hash that allows you to add
@@ -18,7 +19,7 @@ module Mail
18
19
  exact = nil
19
20
  each do |k,v|
20
21
  if k =~ /^#{key_pattern}(\*|$)/i
21
- if $1 == '*'
22
+ if $1 == ASTERISK
22
23
  pairs << [k, v]
23
24
  else
24
25
  exact = k
@@ -40,7 +41,7 @@ module Mail
40
41
  end
41
42
 
42
43
  def encoded
43
- map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
44
+ map.sort_by { |a| a.first.to_s }.map! do |key_name, value|
44
45
  unless value.ascii_only?
45
46
  value = Mail::Encodings.param_encode(value)
46
47
  key_name = "#{key_name}*"
@@ -50,7 +51,7 @@ module Mail
50
51
  end
51
52
 
52
53
  def decoded
53
- map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
54
+ map.sort_by { |a| a.first.to_s }.map! do |key_name, value|
54
55
  %Q{#{key_name}=#{quote_token(value)}}
55
56
  end.join("; ")
56
57
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  #
4
5
  #