mail 2.6.6 → 2.8.1

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 (180) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +134 -119
  4. data/lib/mail/attachments_list.rb +10 -9
  5. data/lib/mail/body.rb +73 -84
  6. data/lib/mail/check_delivery_params.rb +28 -21
  7. data/lib/mail/configuration.rb +2 -0
  8. data/lib/mail/constants.rb +27 -5
  9. data/lib/mail/elements/address.rb +53 -47
  10. data/lib/mail/elements/address_list.rb +11 -19
  11. data/lib/mail/elements/content_disposition_element.rb +9 -16
  12. data/lib/mail/elements/content_location_element.rb +6 -11
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  14. data/lib/mail/elements/content_type_element.rb +16 -23
  15. data/lib/mail/elements/date_time_element.rb +7 -15
  16. data/lib/mail/elements/envelope_from_element.rb +22 -23
  17. data/lib/mail/elements/message_ids_element.rb +18 -13
  18. data/lib/mail/elements/mime_version_element.rb +7 -15
  19. data/lib/mail/elements/phrase_list.rb +12 -10
  20. data/lib/mail/elements/received_element.rb +27 -19
  21. data/lib/mail/encodings/7bit.rb +9 -14
  22. data/lib/mail/encodings/8bit.rb +2 -21
  23. data/lib/mail/encodings/base64.rb +11 -12
  24. data/lib/mail/encodings/binary.rb +3 -22
  25. data/lib/mail/encodings/identity.rb +24 -0
  26. data/lib/mail/encodings/quoted_printable.rb +6 -6
  27. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  28. data/lib/mail/encodings/unix_to_unix.rb +3 -1
  29. data/lib/mail/encodings.rb +81 -54
  30. data/lib/mail/envelope.rb +11 -14
  31. data/lib/mail/field.rb +119 -98
  32. data/lib/mail/field_list.rb +60 -7
  33. data/lib/mail/fields/bcc_field.rb +34 -52
  34. data/lib/mail/fields/cc_field.rb +28 -49
  35. data/lib/mail/fields/comments_field.rb +27 -37
  36. data/lib/mail/fields/common_address_field.rb +170 -0
  37. data/lib/mail/fields/common_date_field.rb +58 -0
  38. data/lib/mail/fields/common_field.rb +77 -0
  39. data/lib/mail/fields/common_message_id_field.rb +42 -0
  40. data/lib/mail/fields/content_description_field.rb +7 -14
  41. data/lib/mail/fields/content_disposition_field.rb +13 -38
  42. data/lib/mail/fields/content_id_field.rb +24 -51
  43. data/lib/mail/fields/content_location_field.rb +11 -25
  44. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  45. data/lib/mail/fields/content_type_field.rb +50 -80
  46. data/lib/mail/fields/date_field.rb +23 -52
  47. data/lib/mail/fields/from_field.rb +28 -49
  48. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  49. data/lib/mail/fields/keywords_field.rb +18 -31
  50. data/lib/mail/fields/message_id_field.rb +25 -71
  51. data/lib/mail/fields/mime_version_field.rb +19 -30
  52. data/lib/mail/fields/named_structured_field.rb +11 -0
  53. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  54. data/lib/mail/fields/optional_field.rb +9 -7
  55. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  56. data/lib/mail/fields/received_field.rb +43 -57
  57. data/lib/mail/fields/references_field.rb +35 -49
  58. data/lib/mail/fields/reply_to_field.rb +28 -49
  59. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  60. data/lib/mail/fields/resent_cc_field.rb +28 -49
  61. data/lib/mail/fields/resent_date_field.rb +5 -30
  62. data/lib/mail/fields/resent_from_field.rb +28 -49
  63. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  64. data/lib/mail/fields/resent_sender_field.rb +27 -56
  65. data/lib/mail/fields/resent_to_field.rb +28 -49
  66. data/lib/mail/fields/return_path_field.rb +50 -54
  67. data/lib/mail/fields/sender_field.rb +34 -55
  68. data/lib/mail/fields/structured_field.rb +3 -30
  69. data/lib/mail/fields/subject_field.rb +9 -11
  70. data/lib/mail/fields/to_field.rb +28 -49
  71. data/lib/mail/fields/unstructured_field.rb +32 -47
  72. data/lib/mail/header.rb +71 -110
  73. data/lib/mail/mail.rb +2 -10
  74. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  75. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  76. data/lib/mail/message.rb +113 -117
  77. data/lib/mail/multibyte/chars.rb +21 -178
  78. data/lib/mail/multibyte/unicode.rb +10 -10
  79. data/lib/mail/multibyte/utils.rb +26 -43
  80. data/lib/mail/multibyte.rb +55 -16
  81. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  82. data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
  83. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  84. data/lib/mail/network/delivery_methods/sendmail.rb +62 -21
  85. data/lib/mail/network/delivery_methods/smtp.rb +75 -50
  86. data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -4
  87. data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
  88. data/lib/mail/network/retriever_methods/base.rb +8 -8
  89. data/lib/mail/network/retriever_methods/imap.rb +20 -7
  90. data/lib/mail/network/retriever_methods/pop3.rb +5 -3
  91. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  92. data/lib/mail/network.rb +1 -0
  93. data/lib/mail/parser_tools.rb +15 -0
  94. data/lib/mail/parsers/address_lists_parser.rb +33225 -116
  95. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  96. data/lib/mail/parsers/content_disposition_parser.rb +882 -49
  97. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  98. data/lib/mail/parsers/content_location_parser.rb +809 -23
  99. data/lib/mail/parsers/content_location_parser.rl +78 -0
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
  101. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  102. data/lib/mail/parsers/content_type_parser.rb +1037 -56
  103. data/lib/mail/parsers/content_type_parser.rl +90 -0
  104. data/lib/mail/parsers/date_time_parser.rb +877 -25
  105. data/lib/mail/parsers/date_time_parser.rl +69 -0
  106. data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
  107. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  108. data/lib/mail/parsers/message_ids_parser.rb +5146 -25
  109. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  110. data/lib/mail/parsers/mime_version_parser.rb +497 -26
  111. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  112. data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
  113. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  114. data/lib/mail/parsers/received_parser.rb +8776 -43
  115. data/lib/mail/parsers/received_parser.rl +91 -0
  116. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  117. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  118. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  119. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  120. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  121. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  122. data/lib/mail/parsers/rfc5322.rl +74 -0
  123. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  124. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  125. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  126. data/lib/mail/parsers.rb +11 -25
  127. data/lib/mail/part.rb +6 -10
  128. data/lib/mail/parts_list.rb +62 -6
  129. data/lib/mail/smtp_envelope.rb +57 -0
  130. data/lib/mail/utilities.rb +343 -74
  131. data/lib/mail/version.rb +2 -2
  132. data/lib/mail/yaml.rb +30 -0
  133. data/lib/mail.rb +5 -35
  134. metadata +111 -66
  135. data/CHANGELOG.rdoc +0 -803
  136. data/CONTRIBUTING.md +0 -60
  137. data/Dependencies.txt +0 -2
  138. data/Gemfile +0 -14
  139. data/Rakefile +0 -29
  140. data/TODO.rdoc +0 -9
  141. data/lib/mail/core_extensions/smtp.rb +0 -25
  142. data/lib/mail/core_extensions/string/access.rb +0 -146
  143. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  144. data/lib/mail/core_extensions/string.rb +0 -21
  145. data/lib/mail/fields/common/address_container.rb +0 -17
  146. data/lib/mail/fields/common/common_address.rb +0 -136
  147. data/lib/mail/fields/common/common_date.rb +0 -36
  148. data/lib/mail/fields/common/common_field.rb +0 -61
  149. data/lib/mail/fields/common/common_message_id.rb +0 -49
  150. data/lib/mail/multibyte/exceptions.rb +0 -9
  151. data/lib/mail/parsers/ragel/common.rl +0 -185
  152. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  173. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  177. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  178. data/lib/mail/parsers/ragel.rb +0 -18
  179. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  180. data/lib/mail/version_specific/ruby_1_9.rb +0 -226
data/lib/mail/field.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'mail/fields'
3
+ require 'mail/constants'
3
4
 
4
5
  # encoding: utf-8
5
6
  module Mail
@@ -22,8 +23,6 @@ module Mail
22
23
  # sections 3 and 4 of this standard.
23
24
  #
24
25
  class Field
25
-
26
- include Utilities
27
26
  include Comparable
28
27
 
29
28
  STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
@@ -69,7 +68,7 @@ module Mail
69
68
  }
70
69
 
71
70
  FIELD_NAME_MAP = FIELDS_MAP.inject({}) do |map, (field, field_klass)|
72
- map.update(field => field_klass::CAPITALIZED_FIELD)
71
+ map.update(field => field_klass::NAME)
73
72
  end
74
73
 
75
74
  # Generic Field Exception
@@ -83,9 +82,31 @@ module Mail
83
82
 
84
83
  def initialize(element, value, reason)
85
84
  @element = element
86
- @value = value
87
- @reason = reason
88
- super("#{element} can not parse |#{value}|\nReason was: #{reason}")
85
+ @value = to_utf8(value)
86
+ @reason = to_utf8(reason)
87
+ super("#{@element} can not parse |#{@value}|: #{@reason}")
88
+ end
89
+
90
+ private
91
+ def to_utf8(text)
92
+ if text.respond_to?(:force_encoding)
93
+ text.dup.force_encoding(Encoding::UTF_8)
94
+ else
95
+ text
96
+ end
97
+ end
98
+ end
99
+
100
+ class NilParseError < ParseError #:nodoc:
101
+ def initialize(element)
102
+ super element, nil, 'nil is invalid'
103
+ end
104
+ end
105
+
106
+ class IncompleteParseError < ParseError #:nodoc:
107
+ def initialize(element, original_text, unparsed_index)
108
+ parsed_text = to_utf8(original_text[0...unparsed_index])
109
+ super element, original_text, "Only able to parse up to #{parsed_text.inspect}"
89
110
  end
90
111
  end
91
112
 
@@ -93,53 +114,77 @@ module Mail
93
114
  class SyntaxError < FieldError #:nodoc:
94
115
  end
95
116
 
96
- # Accepts a string:
97
- #
98
- # Field.new("field-name: field data")
99
- #
100
- # Or name, value pair:
101
- #
102
- # Field.new("field-name", "value")
117
+ class << self
118
+ # Parse a field from a raw header line:
119
+ #
120
+ # Mail::Field.parse("field-name: field data")
121
+ # # => #<Mail::Field …>
122
+ def parse(field, charset = 'utf-8')
123
+ name, value = split(field)
124
+ if name && value
125
+ new name, value, charset
126
+ end
127
+ end
128
+
129
+ def split(raw_field) #:nodoc:
130
+ if raw_field.index(Constants::COLON)
131
+ name, value = raw_field.split(Constants::COLON, 2)
132
+ name.rstrip!
133
+ if name =~ /\A#{Constants::FIELD_NAME}\z/
134
+ [ name.rstrip, value.strip ]
135
+ else
136
+ Kernel.warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: invalid header name syntax: #{name.inspect}"
137
+ nil
138
+ end
139
+ else
140
+ raw_field.strip
141
+ end
142
+ rescue => error
143
+ warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: #{error.class}: #{error.message}"
144
+ nil
145
+ end
146
+
147
+ def field_class_for(name) #:nodoc:
148
+ FIELDS_MAP[name.to_s.downcase]
149
+ end
150
+ end
151
+
152
+ attr_reader :unparsed_value
153
+
154
+ # Create a field by name and optional value:
103
155
  #
104
- # Or a name by itself:
156
+ # Mail::Field.new("field-name", "value")
157
+ # # => #<Mail::Field …>
105
158
  #
106
- # Field.new("field-name")
159
+ # Values that aren't strings or arrays are coerced to Strings with `#to_s`.
107
160
  #
108
- # Note, does not want a terminating carriage return. Returns
109
- # self appropriately parsed. If value is not a string, then
110
- # it will be passed through as is, for example, content-type
111
- # field can accept an array with the type and a hash of
112
- # parameters:
161
+ # Mail::Field.new("field-name", 1234)
162
+ # # => #<Mail::Field …>
113
163
  #
114
- # Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
164
+ # Mail::Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
165
+ # # => #<Mail::Field …>
115
166
  def initialize(name, value = nil, charset = 'utf-8')
116
167
  case
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")
168
+ when name.index(Constants::COLON)
169
+ raise ArgumentError, 'Passing an unparsed header field to Mail::Field.new is not supported in Mail 2.8.0+. Use Mail::Field.parse instead.'
170
+ when Utilities.blank?(value)
123
171
  @name = name
124
- @value = nil
125
- @raw_value = nil
172
+ @unparsed_value = nil
126
173
  @charset = charset
127
- else # Field.new("field-name", "value")
174
+ else
128
175
  @name = name
129
- @value = value
130
- @raw_value = nil
176
+ @unparsed_value = value
131
177
  @charset = charset
132
178
  end
133
179
  @name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
134
180
  end
135
181
 
136
- def field=(value)
137
- @field = value
182
+ def field=(field)
183
+ @field = field
138
184
  end
139
185
 
140
186
  def field
141
- _, @value = split(@raw_value) if @raw_value && !@value
142
- @field ||= create_field(@name, @value, @charset)
187
+ @field ||= create_field(@name, @unparsed_value, @charset)
143
188
  end
144
189
 
145
190
  def name
@@ -164,64 +209,63 @@ module Mail
164
209
  end.join(" ")}>"
165
210
  end
166
211
 
167
- def update(name, value)
168
- @field = create_field(name, value, @charset)
169
- end
170
-
171
- def same( other )
172
- return false unless other.kind_of?(self.class)
173
- match_to_s(other.name, self.name)
212
+ def same(other)
213
+ other.kind_of?(self.class) && Utilities.match_to_s(other.name, name)
174
214
  end
175
215
 
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)
216
+ def ==(other)
217
+ same(other) && Utilities.match_to_s(other.value, value)
179
218
  end
180
219
 
181
- def responsible_for?( val )
182
- name.to_s.casecmp(val.to_s) == 0
220
+ def responsible_for?(field_name)
221
+ name.to_s.casecmp(field_name.to_s) == 0
183
222
  end
184
223
 
185
- def <=>( other )
186
- self.field_order_id <=> other.field_order_id
224
+ def <=>(other)
225
+ field_order_id <=> other.field_order_id
187
226
  end
188
227
 
189
228
  def field_order_id
190
- @field_order_id ||= (FIELD_ORDER_LOOKUP[self.name.to_s.downcase] || 100)
229
+ @field_order_id ||= FIELD_ORDER_LOOKUP.fetch(self.name.to_s.downcase, 100)
191
230
  end
192
231
 
193
232
  def method_missing(name, *args, &block)
194
233
  field.send(name, *args, &block)
195
234
  end
196
235
 
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
236
+ def respond_to_missing?(method_name, include_private)
237
+ field.respond_to?(method_name, include_private) || super
205
238
  end
206
239
 
207
- FIELD_ORDER = %w[ return-path received
208
- resent-date resent-from resent-sender resent-to
209
- resent-cc resent-bcc resent-message-id
210
- date from sender reply-to to cc bcc
211
- message-id in-reply-to references
212
- subject comments keywords
213
- mime-version content-type content-transfer-encoding
214
- content-location content-disposition content-description ]
215
-
216
- FIELD_ORDER_LOOKUP = Hash[FIELD_ORDER.each_with_index.to_a]
240
+ FIELD_ORDER_LOOKUP = Hash[%w[
241
+ return-path received
242
+ resent-date resent-from resent-sender resent-to
243
+ resent-cc resent-bcc resent-message-id
244
+ date from sender reply-to to cc bcc
245
+ message-id in-reply-to references
246
+ subject comments keywords
247
+ mime-version content-type content-transfer-encoding
248
+ content-location content-disposition content-description
249
+ ].each_with_index.to_a]
217
250
 
218
251
  private
219
252
 
220
- def split(raw_field)
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]
223
- rescue
224
- STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
253
+ def create_field(name, value, charset)
254
+ parse_field(name, value, charset)
255
+ rescue Mail::Field::ParseError => e
256
+ field = Mail::UnstructuredField.new(name, value)
257
+ field.errors << [name, value, e]
258
+ field
259
+ end
260
+
261
+ def parse_field(name, value, charset)
262
+ value = unfold(value) if value.is_a?(String)
263
+
264
+ if klass = self.class.field_class_for(name)
265
+ klass.parse(value, charset)
266
+ else
267
+ OptionalField.parse(name, value, charset)
268
+ end
225
269
  end
226
270
 
227
271
  # 2.2.3. Long Header Fields
@@ -233,30 +277,7 @@ module Mail
233
277
  # treated in its unfolded form for further syntactic and semantic
234
278
  # evaluation.
235
279
  def unfold(string)
236
- string.gsub(/[\r\n \t]+/m, ' ')
237
- end
238
-
239
- def create_field(name, value, charset)
240
- value = unfold(value) if value.is_a?(String)
241
-
242
- begin
243
- new_field(name, value, charset)
244
- rescue Mail::Field::ParseError => e
245
- field = Mail::UnstructuredField.new(name, value)
246
- field.errors << [name, value, e]
247
- field
248
- end
280
+ string.gsub(Constants::UNFOLD_WS, '\1')
249
281
  end
250
-
251
- def new_field(name, value, charset)
252
- lower_case_name = name.to_s.downcase
253
- if field_klass = FIELDS_MAP[lower_case_name]
254
- field_klass.new(value, charset)
255
- else
256
- OptionalField.new(name, value, charset)
257
- end
258
- end
259
-
260
282
  end
261
-
262
283
  end
@@ -6,8 +6,36 @@ module Mail
6
6
  # email fields in order. And allows you to insert new fields without
7
7
  # having to worry about the order they will appear in.
8
8
  class FieldList < Array
9
+ def has_field?(field_name)
10
+ any? { |f| f.responsible_for? field_name }
11
+ end
12
+
13
+ def get_field(field_name)
14
+ fields = select_fields(field_name)
15
+ case fields.size
16
+ when 0; nil
17
+ when 1; fields.first
18
+ else fields
19
+ end
20
+ end
9
21
 
10
- include Enumerable
22
+ def add_field(field)
23
+ if field.singular?
24
+ replace_field field
25
+ else
26
+ insert_field field
27
+ end
28
+ end
29
+ alias_method :<<, :add_field
30
+
31
+ def replace_field(field)
32
+ if first_offset = index { |f| f.responsible_for? field.name }
33
+ delete_field field.name
34
+ insert first_offset, field
35
+ else
36
+ insert_field field
37
+ end
38
+ end
11
39
 
12
40
  # Insert the field in sorted order.
13
41
  #
@@ -15,20 +43,45 @@ module Mail
15
43
  # Copyright (C) 2001-2013 Python Software Foundation.
16
44
  # Licensed under <http://docs.python.org/license.html>
17
45
  # From <http://hg.python.org/cpython/file/2.7/Lib/bisect.py>
18
- def <<( new_field )
19
- lo = 0
20
- hi = size
21
-
46
+ def insert_field(field)
47
+ lo, hi = 0, size
22
48
  while lo < hi
23
49
  mid = (lo + hi).div(2)
24
- if new_field < self[mid]
50
+ if field < self[mid]
25
51
  hi = mid
26
52
  else
27
53
  lo = mid + 1
28
54
  end
29
55
  end
30
56
 
31
- insert(lo, new_field)
57
+ insert lo, field
58
+ end
59
+
60
+ def delete_field(name)
61
+ delete_if { |f| f.responsible_for? name }
62
+ end
63
+
64
+ def summary
65
+ map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
66
+ end
67
+
68
+ private
69
+
70
+ def select_fields(field_name)
71
+ fields = select { |f| f.responsible_for? field_name }
72
+ if fields.size > 1 && singular?(field_name)
73
+ Array(fields.detect { |f| f.errors.size == 0 } || fields.first)
74
+ else
75
+ fields
76
+ end
77
+ end
78
+
79
+ def singular?(field_name)
80
+ if klass = Mail::Field.field_class_for(field_name)
81
+ klass.singular?
82
+ else
83
+ false
84
+ end
32
85
  end
33
86
  end
34
87
  end
@@ -1,68 +1,50 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- #
4
- # = Blind Carbon Copy Field
5
- #
6
- # The Bcc field inherits from StructuredField and handles the Bcc: header
7
- # field in the email.
8
- #
9
- # Sending bcc to a mail message will instantiate a Mail::Field object that
10
- # has a BccField as its field type. This includes all Mail::CommonAddress
11
- # module instance metods.
12
- #
13
- # Only one Bcc field can appear in a header, though it can have multiple
14
- # addresses and groups of addresses.
15
- #
16
- # == Examples:
17
- #
18
- # mail = Mail.new
19
- # mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
20
- # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
21
- # mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
22
- # mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
23
- # mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
24
- #
25
- # mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
26
- # mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
27
- # mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
28
- # mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
29
- #
30
- require 'mail/fields/common/common_address'
3
+ require 'mail/fields/common_address_field'
31
4
 
32
5
  module Mail
33
- class BccField < StructuredField
34
-
35
- include Mail::CommonAddress
36
-
37
- FIELD_NAME = 'bcc'
38
- CAPITALIZED_FIELD = 'Bcc'
39
-
40
- def initialize(value = '', charset = 'utf-8')
41
- @charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
43
- self
44
- end
45
-
46
- def include_in_headers=(include_in_headers)
47
- @include_in_headers = include_in_headers
48
- end
6
+ # = Blind Carbon Copy Field
7
+ #
8
+ # The Bcc field inherits from StructuredField and handles the Bcc: header
9
+ # field in the email.
10
+ #
11
+ # Sending bcc to a mail message will instantiate a Mail::Field object that
12
+ # has a BccField as its field type. This includes all Mail::CommonAddress
13
+ # module instance metods.
14
+ #
15
+ # Only one Bcc field can appear in a header, though it can have multiple
16
+ # addresses and groups of addresses.
17
+ #
18
+ # == Examples:
19
+ #
20
+ # mail = Mail.new
21
+ # mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
22
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
23
+ # mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
24
+ # mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
25
+ # mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
26
+ #
27
+ # mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
28
+ # mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
29
+ # mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
30
+ # mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
31
+ class BccField < CommonAddressField #:nodoc:
32
+ NAME = 'Bcc'
49
33
 
50
- def include_in_headers
51
- defined?(@include_in_headers) ? @include_in_headers : self.include_in_headers = false
34
+ attr_accessor :include_in_headers
35
+
36
+ def initialize(value = nil, charset = nil)
37
+ super
38
+ self.include_in_headers = false
52
39
  end
53
40
 
54
41
  # Bcc field should not be :encoded by default
55
42
  def encoded
56
43
  if include_in_headers
57
- do_encode(CAPITALIZED_FIELD)
44
+ super
58
45
  else
59
46
  ''
60
47
  end
61
48
  end
62
-
63
- def decoded
64
- do_decode
65
- end
66
-
67
49
  end
68
50
  end
@@ -1,55 +1,34 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- #
4
- # = Carbon Copy Field
5
- #
6
- # The Cc field inherits from StructuredField and handles the Cc: header
7
- # field in the email.
8
- #
9
- # Sending cc to a mail message will instantiate a Mail::Field object that
10
- # has a CcField as its field type. This includes all Mail::CommonAddress
11
- # module instance metods.
12
- #
13
- # Only one Cc field can appear in a header, though it can have multiple
14
- # addresses and groups of addresses.
15
- #
16
- # == Examples:
17
- #
18
- # mail = Mail.new
19
- # mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
20
- # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
21
- # mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
22
- # mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
23
- # mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
24
- #
25
- # mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
26
- # mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
27
- # mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
28
- # mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
29
- #
30
- require 'mail/fields/common/common_address'
3
+ require 'mail/fields/common_address_field'
31
4
 
32
5
  module Mail
33
- class CcField < StructuredField
34
-
35
- include Mail::CommonAddress
36
-
37
- FIELD_NAME = 'cc'
38
- CAPITALIZED_FIELD = 'Cc'
39
-
40
- def initialize(value = nil, charset = 'utf-8')
41
- self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
43
- self
44
- end
45
-
46
- def encoded
47
- do_encode(CAPITALIZED_FIELD)
48
- end
49
-
50
- def decoded
51
- do_decode
52
- end
53
-
6
+ # = Carbon Copy Field
7
+ #
8
+ # The Cc field inherits from StructuredField and handles the Cc: header
9
+ # field in the email.
10
+ #
11
+ # Sending cc to a mail message will instantiate a Mail::Field object that
12
+ # has a CcField as its field type. This includes all Mail::CommonAddress
13
+ # module instance metods.
14
+ #
15
+ # Only one Cc field can appear in a header, though it can have multiple
16
+ # addresses and groups of addresses.
17
+ #
18
+ # == Examples:
19
+ #
20
+ # mail = Mail.new
21
+ # mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
22
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
23
+ # mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
24
+ # mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
25
+ # mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
26
+ #
27
+ # mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
28
+ # mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
29
+ # mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
30
+ # mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
31
+ class CcField < CommonAddressField #:nodoc:
32
+ NAME = 'Cc'
54
33
  end
55
34
  end
@@ -1,42 +1,32 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- #
4
- # = Comments Field
5
- #
6
- # The Comments field inherits from UnstructuredField and handles the Comments:
7
- # header field in the email.
8
- #
9
- # Sending comments to a mail message will instantiate a Mail::Field object that
10
- # has a CommentsField as its field type.
11
- #
12
- # An email header can have as many comments fields as it wants. There is no upper
13
- # limit, the comments field is also optional (that is, no comment is needed)
14
- #
15
- # == Examples:
16
- #
17
- # mail = Mail.new
18
- # mail.comments = 'This is a comment'
19
- # mail.comments #=> 'This is a comment'
20
- # mail[:comments] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
21
- # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
22
- # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
23
- #
24
- # mail.comments = "This is another comment"
25
- # mail[:comments].map { |c| c.to_s }
26
- # #=> ['This is a comment', "This is another comment"]
27
- #
3
+ require 'mail/fields/named_unstructured_field'
4
+
28
5
  module Mail
29
- class CommentsField < UnstructuredField
30
-
31
- FIELD_NAME = 'comments'
32
- CAPITALIZED_FIELD = 'Comments'
33
-
34
- def initialize(value = nil, charset = 'utf-8')
35
- @charset = charset
36
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value))
37
- self.parse
38
- self
39
- end
40
-
6
+ # = Comments Field
7
+ #
8
+ # The Comments field inherits from UnstructuredField and handles the Comments:
9
+ # header field in the email.
10
+ #
11
+ # Sending comments to a mail message will instantiate a Mail::Field object that
12
+ # has a CommentsField as its field type.
13
+ #
14
+ # An email header can have as many comments fields as it wants. There is no upper
15
+ # limit, the comments field is also optional (that is, no comment is needed)
16
+ #
17
+ # == Examples:
18
+ #
19
+ # mail = Mail.new
20
+ # mail.comments = 'This is a comment'
21
+ # mail.comments #=> 'This is a comment'
22
+ # mail[:comments] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
23
+ # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
24
+ # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
25
+ #
26
+ # mail.comments = "This is another comment"
27
+ # mail[:comments].map { |c| c.to_s }
28
+ # #=> ['This is a comment', "This is another comment"]
29
+ class CommentsField < NamedUnstructuredField #:nodoc:
30
+ NAME = 'Comments'
41
31
  end
42
32
  end