mail 2.5.5 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +170 -108
  4. data/lib/mail/attachments_list.rb +13 -10
  5. data/lib/mail/body.rb +105 -91
  6. data/lib/mail/check_delivery_params.rb +30 -22
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +79 -0
  9. data/lib/mail/elements/address.rb +118 -174
  10. data/lib/mail/elements/address_list.rb +16 -56
  11. data/lib/mail/elements/content_disposition_element.rb +12 -22
  12. data/lib/mail/elements/content_location_element.rb +9 -17
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +8 -19
  14. data/lib/mail/elements/content_type_element.rb +20 -30
  15. data/lib/mail/elements/date_time_element.rb +10 -21
  16. data/lib/mail/elements/envelope_from_element.rb +23 -31
  17. data/lib/mail/elements/message_ids_element.rb +22 -20
  18. data/lib/mail/elements/mime_version_element.rb +10 -21
  19. data/lib/mail/elements/phrase_list.rb +13 -15
  20. data/lib/mail/elements/received_element.rb +26 -21
  21. data/lib/mail/elements.rb +1 -0
  22. data/lib/mail/encodings/7bit.rb +10 -14
  23. data/lib/mail/encodings/8bit.rb +5 -18
  24. data/lib/mail/encodings/base64.rb +15 -10
  25. data/lib/mail/encodings/binary.rb +4 -22
  26. data/lib/mail/encodings/identity.rb +24 -0
  27. data/lib/mail/encodings/quoted_printable.rb +13 -7
  28. data/lib/mail/encodings/transfer_encoding.rb +47 -28
  29. data/lib/mail/encodings/unix_to_unix.rb +20 -0
  30. data/lib/mail/encodings.rb +102 -93
  31. data/lib/mail/envelope.rb +12 -19
  32. data/lib/mail/field.rb +143 -71
  33. data/lib/mail/field_list.rb +73 -19
  34. data/lib/mail/fields/bcc_field.rb +42 -48
  35. data/lib/mail/fields/cc_field.rb +29 -50
  36. data/lib/mail/fields/comments_field.rb +28 -37
  37. data/lib/mail/fields/common_address_field.rb +170 -0
  38. data/lib/mail/fields/common_date_field.rb +58 -0
  39. data/lib/mail/fields/common_field.rb +77 -0
  40. data/lib/mail/fields/common_message_id_field.rb +42 -0
  41. data/lib/mail/fields/content_description_field.rb +8 -14
  42. data/lib/mail/fields/content_disposition_field.rb +20 -44
  43. data/lib/mail/fields/content_id_field.rb +25 -51
  44. data/lib/mail/fields/content_location_field.rb +12 -25
  45. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -36
  46. data/lib/mail/fields/content_type_field.rb +51 -80
  47. data/lib/mail/fields/date_field.rb +24 -52
  48. data/lib/mail/fields/from_field.rb +29 -50
  49. data/lib/mail/fields/in_reply_to_field.rb +39 -49
  50. data/lib/mail/fields/keywords_field.rb +19 -32
  51. data/lib/mail/fields/message_id_field.rb +26 -71
  52. data/lib/mail/fields/mime_version_field.rb +20 -30
  53. data/lib/mail/fields/named_structured_field.rb +11 -0
  54. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  55. data/lib/mail/fields/optional_field.rb +10 -7
  56. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
  57. data/lib/mail/fields/received_field.rb +44 -57
  58. data/lib/mail/fields/references_field.rb +36 -49
  59. data/lib/mail/fields/reply_to_field.rb +29 -50
  60. data/lib/mail/fields/resent_bcc_field.rb +29 -50
  61. data/lib/mail/fields/resent_cc_field.rb +29 -50
  62. data/lib/mail/fields/resent_date_field.rb +6 -30
  63. data/lib/mail/fields/resent_from_field.rb +29 -50
  64. data/lib/mail/fields/resent_message_id_field.rb +6 -29
  65. data/lib/mail/fields/resent_sender_field.rb +28 -57
  66. data/lib/mail/fields/resent_to_field.rb +29 -50
  67. data/lib/mail/fields/return_path_field.rb +51 -55
  68. data/lib/mail/fields/sender_field.rb +35 -56
  69. data/lib/mail/fields/structured_field.rb +4 -30
  70. data/lib/mail/fields/subject_field.rb +10 -11
  71. data/lib/mail/fields/to_field.rb +29 -50
  72. data/lib/mail/fields/unstructured_field.rb +43 -51
  73. data/lib/mail/fields.rb +1 -0
  74. data/lib/mail/header.rb +78 -129
  75. data/lib/mail/indifferent_hash.rb +1 -0
  76. data/lib/mail/mail.rb +18 -11
  77. data/lib/mail/matchers/attachment_matchers.rb +44 -0
  78. data/lib/mail/matchers/has_sent_mail.rb +81 -4
  79. data/lib/mail/message.rb +142 -139
  80. data/lib/mail/multibyte/chars.rb +24 -180
  81. data/lib/mail/multibyte/unicode.rb +32 -27
  82. data/lib/mail/multibyte/utils.rb +27 -43
  83. data/lib/mail/multibyte.rb +56 -16
  84. data/lib/mail/network/delivery_methods/exim.rb +6 -4
  85. data/lib/mail/network/delivery_methods/file_delivery.rb +12 -10
  86. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  87. data/lib/mail/network/delivery_methods/sendmail.rb +63 -21
  88. data/lib/mail/network/delivery_methods/smtp.rb +76 -50
  89. data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -4
  90. data/lib/mail/network/delivery_methods/test_mailer.rb +5 -2
  91. data/lib/mail/network/retriever_methods/base.rb +9 -8
  92. data/lib/mail/network/retriever_methods/imap.rb +37 -18
  93. data/lib/mail/network/retriever_methods/pop3.rb +6 -3
  94. data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
  95. data/lib/mail/network.rb +2 -0
  96. data/lib/mail/parser_tools.rb +15 -0
  97. data/lib/mail/parsers/address_lists_parser.rb +33242 -0
  98. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  99. data/lib/mail/parsers/content_disposition_parser.rb +901 -0
  100. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  101. data/lib/mail/parsers/content_location_parser.rb +822 -0
  102. data/lib/mail/parsers/content_location_parser.rl +78 -0
  103. data/lib/mail/parsers/content_transfer_encoding_parser.rb +522 -0
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  105. data/lib/mail/parsers/content_type_parser.rb +1048 -0
  106. data/lib/mail/parsers/content_type_parser.rl +90 -0
  107. data/lib/mail/parsers/date_time_parser.rb +891 -0
  108. data/lib/mail/parsers/date_time_parser.rl +69 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +3675 -0
  110. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  111. data/lib/mail/parsers/message_ids_parser.rb +5161 -0
  112. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +513 -0
  114. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  115. data/lib/mail/parsers/phrase_lists_parser.rb +884 -0
  116. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  117. data/lib/mail/parsers/received_parser.rb +8782 -0
  118. data/lib/mail/parsers/received_parser.rl +91 -0
  119. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  120. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  121. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  122. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  123. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  124. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  125. data/lib/mail/parsers/rfc5322.rl +74 -0
  126. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  127. data/lib/mail/parsers/rfc5322_date_time.rl +37 -0
  128. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  129. data/lib/mail/parsers.rb +13 -0
  130. data/lib/mail/part.rb +11 -12
  131. data/lib/mail/parts_list.rb +90 -14
  132. data/lib/mail/smtp_envelope.rb +57 -0
  133. data/lib/mail/utilities.rb +415 -76
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +8 -15
  136. data/lib/mail/yaml.rb +30 -0
  137. data/lib/mail.rb +9 -32
  138. metadata +127 -79
  139. data/CHANGELOG.rdoc +0 -742
  140. data/CONTRIBUTING.md +0 -45
  141. data/Dependencies.txt +0 -3
  142. data/Gemfile +0 -32
  143. data/Rakefile +0 -21
  144. data/TODO.rdoc +0 -9
  145. data/lib/VERSION +0 -4
  146. data/lib/load_parsers.rb +0 -35
  147. data/lib/mail/core_extensions/nil.rb +0 -19
  148. data/lib/mail/core_extensions/object.rb +0 -13
  149. data/lib/mail/core_extensions/smtp.rb +0 -24
  150. data/lib/mail/core_extensions/string/access.rb +0 -145
  151. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  152. data/lib/mail/core_extensions/string.rb +0 -33
  153. data/lib/mail/fields/common/address_container.rb +0 -16
  154. data/lib/mail/fields/common/common_address.rb +0 -140
  155. data/lib/mail/fields/common/common_date.rb +0 -42
  156. data/lib/mail/fields/common/common_field.rb +0 -57
  157. data/lib/mail/fields/common/common_message_id.rb +0 -48
  158. data/lib/mail/multibyte/exceptions.rb +0 -8
  159. data/lib/mail/parsers/address_lists.rb +0 -64
  160. data/lib/mail/parsers/address_lists.treetop +0 -19
  161. data/lib/mail/parsers/content_disposition.rb +0 -535
  162. data/lib/mail/parsers/content_disposition.treetop +0 -46
  163. data/lib/mail/parsers/content_location.rb +0 -139
  164. data/lib/mail/parsers/content_location.treetop +0 -20
  165. data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
  166. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
  167. data/lib/mail/parsers/content_type.rb +0 -971
  168. data/lib/mail/parsers/content_type.treetop +0 -68
  169. data/lib/mail/parsers/date_time.rb +0 -114
  170. data/lib/mail/parsers/date_time.treetop +0 -11
  171. data/lib/mail/parsers/envelope_from.rb +0 -194
  172. data/lib/mail/parsers/envelope_from.treetop +0 -32
  173. data/lib/mail/parsers/message_ids.rb +0 -45
  174. data/lib/mail/parsers/message_ids.treetop +0 -15
  175. data/lib/mail/parsers/mime_version.rb +0 -144
  176. data/lib/mail/parsers/mime_version.treetop +0 -19
  177. data/lib/mail/parsers/phrase_lists.rb +0 -45
  178. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  179. data/lib/mail/parsers/received.rb +0 -71
  180. data/lib/mail/parsers/received.treetop +0 -11
  181. data/lib/mail/parsers/rfc2045.rb +0 -421
  182. data/lib/mail/parsers/rfc2045.treetop +0 -35
  183. data/lib/mail/parsers/rfc2822.rb +0 -5397
  184. data/lib/mail/parsers/rfc2822.treetop +0 -408
  185. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  186. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  187. data/lib/mail/patterns.rb +0 -35
  188. data/lib/mail/version_specific/ruby_1_8.rb +0 -119
  189. data/lib/mail/version_specific/ruby_1_9.rb +0 -147
  190. data/lib/tasks/corpus.rake +0 -125
  191. data/lib/tasks/treetop.rake +0 -10
data/lib/mail/field.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require 'mail/fields'
3
+ require 'mail/constants'
2
4
 
3
5
  # encoding: utf-8
4
6
  module Mail
@@ -21,8 +23,6 @@ module Mail
21
23
  # sections 3 and 4 of this standard.
22
24
  #
23
25
  class Field
24
-
25
- include Patterns
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::NAME)
72
+ end
73
+
70
74
  # Generic Field Exception
71
75
  class FieldError < StandardError
72
76
  end
@@ -78,9 +82,31 @@ module Mail
78
82
 
79
83
  def initialize(element, value, reason)
80
84
  @element = element
81
- @value = value
82
- @reason = reason
83
- 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}"
84
110
  end
85
111
  end
86
112
 
@@ -88,49 +114,81 @@ module Mail
88
114
  class SyntaxError < FieldError #:nodoc:
89
115
  end
90
116
 
91
- # Accepts a string:
92
- #
93
- # Field.new("field-name: field data")
94
- #
95
- # Or name, value pair:
96
- #
97
- # 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:
98
155
  #
99
- # Or a name by itself:
156
+ # Mail::Field.new("field-name", "value")
157
+ # # => #<Mail::Field …>
100
158
  #
101
- # Field.new("field-name")
159
+ # Values that aren't strings or arrays are coerced to Strings with `#to_s`.
102
160
  #
103
- # Note, does not want a terminating carriage return. Returns
104
- # self appropriately parsed. If value is not a string, then
105
- # it will be passed through as is, for example, content-type
106
- # field can accept an array with the type and a hash of
107
- # parameters:
161
+ # Mail::Field.new("field-name", 1234)
162
+ # # => #<Mail::Field …>
108
163
  #
109
- # Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
164
+ # Mail::Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
165
+ # # => #<Mail::Field …>
110
166
  def initialize(name, value = nil, charset = 'utf-8')
111
167
  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)
118
- else # Field.new("field-name", "value")
119
- create_field(name, value, charset)
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)
171
+ @name = name
172
+ @unparsed_value = nil
173
+ @charset = charset
174
+ else
175
+ @name = name
176
+ @unparsed_value = value
177
+ @charset = charset
120
178
  end
121
- return self
179
+ @name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
122
180
  end
123
181
 
124
- def field=(value)
125
- @field = value
182
+ def field=(field)
183
+ @field = field
126
184
  end
127
185
 
128
186
  def field
129
- @field
187
+ @field ||= create_field(@name, @unparsed_value, @charset)
130
188
  end
131
189
 
132
190
  def name
133
- field.name
191
+ @name
134
192
  end
135
193
 
136
194
  def value
@@ -138,74 +196,88 @@ module Mail
138
196
  end
139
197
 
140
198
  def value=(val)
141
- create_field(name, val, charset)
199
+ @field = create_field(name, val, @charset)
142
200
  end
143
201
 
144
202
  def to_s
145
203
  field.to_s
146
204
  end
147
205
 
148
- def update(name, value)
149
- create_field(name, value, charset)
206
+ def inspect
207
+ "#<#{self.class.name} 0x#{(object_id * 2).to_s(16)} #{instance_variables.map do |ivar|
208
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
209
+ end.join(" ")}>"
210
+ end
211
+
212
+ def same(other)
213
+ other.kind_of?(self.class) && Utilities.match_to_s(other.name, name)
150
214
  end
151
215
 
152
- def same( other )
153
- match_to_s(other.name, field.name)
216
+ def ==(other)
217
+ same(other) && Utilities.match_to_s(other.value, value)
154
218
  end
155
219
 
156
- alias_method :==, :same
220
+ def responsible_for?(field_name)
221
+ name.to_s.casecmp(field_name.to_s) == 0
222
+ end
157
223
 
158
- def <=>( other )
159
- self.field_order_id <=> other.field_order_id
224
+ def <=>(other)
225
+ field_order_id <=> other.field_order_id
160
226
  end
161
227
 
162
228
  def field_order_id
163
- @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)
164
230
  end
165
231
 
166
232
  def method_missing(name, *args, &block)
167
233
  field.send(name, *args, &block)
168
234
  end
169
235
 
170
- FIELD_ORDER = %w[ return-path received
171
- resent-date resent-from resent-sender resent-to
172
- resent-cc resent-bcc resent-message-id
173
- date from sender reply-to to cc bcc
174
- message-id in-reply-to references
175
- subject comments keywords
176
- mime-version content-type content-transfer-encoding
177
- content-location content-disposition content-description ]
236
+ def respond_to_missing?(method_name, include_private)
237
+ field.respond_to?(method_name, include_private) || super
238
+ end
178
239
 
179
- 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]
180
250
 
181
251
  private
182
252
 
183
- def split(raw_field)
184
- match_data = raw_field.mb_chars.match(FIELD_SPLIT)
185
- [match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip]
186
- rescue
187
- STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
188
- end
189
-
190
253
  def create_field(name, value, charset)
191
- begin
192
- self.field = new_field(name, value, charset)
193
- rescue Mail::Field::ParseError => e
194
- self.field = Mail::UnstructuredField.new(name, value)
195
- self.field.errors << [name, value, e]
196
- self.field
197
- end
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
198
259
  end
199
260
 
200
- def new_field(name, value, charset)
201
- lower_case_name = name.to_s.downcase
202
- if field_klass = FIELDS_MAP[lower_case_name]
203
- field_klass.new(value, charset)
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)
204
266
  else
205
- OptionalField.new(name, value, charset)
267
+ OptionalField.parse(name, value, charset)
206
268
  end
207
269
  end
208
270
 
271
+ # 2.2.3. Long Header Fields
272
+ #
273
+ # The process of moving from this folded multiple-line representation
274
+ # of a header field to its single line representation is called
275
+ # "unfolding". Unfolding is accomplished by simply removing any CRLF
276
+ # that is immediately followed by WSP. Each header field should be
277
+ # treated in its unfolded form for further syntactic and semantic
278
+ # evaluation.
279
+ def unfold(string)
280
+ string.gsub(Constants::UNFOLD_WS, '\1')
281
+ end
209
282
  end
210
-
211
283
  end
@@ -1,33 +1,87 @@
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
5
6
  # email fields in order. And allows you to insert new fields without
6
7
  # having to worry about the order they will appear in.
7
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
8
21
 
9
- 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
10
30
 
11
- def <<( new_field )
12
- current_entry = self.rindex(new_field)
13
- if current_entry
14
- self.insert((current_entry + 1), new_field)
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
15
35
  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
36
+ insert_field field
37
+ end
38
+ end
39
+
40
+ # Insert the field in sorted order.
41
+ #
42
+ # Heavily based on bisect.insort from Python, which is:
43
+ # Copyright (C) 2001-2013 Python Software Foundation.
44
+ # Licensed under <http://docs.python.org/license.html>
45
+ # From <http://hg.python.org/cpython/file/2.7/Lib/bisect.py>
46
+ def insert_field(field)
47
+ lo, hi = 0, size
48
+ while lo < hi
49
+ mid = (lo + hi).div(2)
50
+ if field < self[mid]
51
+ hi = mid
52
+ else
53
+ lo = mid + 1
27
54
  end
28
- insert(insert_idx, new_field)
55
+ end
56
+
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
29
84
  end
30
85
  end
31
-
32
86
  end
33
- end
87
+ end
@@ -1,56 +1,50 @@
1
1
  # encoding: utf-8
2
- #
3
- # = Blind Carbon Copy Field
4
- #
5
- # The Bcc field inherits from StructuredField and handles the Bcc: header
6
- # field in the email.
7
- #
8
- # Sending bcc to a mail message will instantiate a Mail::Field object that
9
- # has a BccField as its field type. This includes all Mail::CommonAddress
10
- # module instance metods.
11
- #
12
- # Only one Bcc field can appear in a header, though it can have multiple
13
- # addresses and groups of addresses.
14
- #
15
- # == Examples:
16
- #
17
- # mail = Mail.new
18
- # mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
- # mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
21
- # mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
22
- # mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
23
- #
24
- # mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
25
- # mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
- # mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
- # mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
28
- #
29
- require 'mail/fields/common/common_address'
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/common_address_field'
30
4
 
31
5
  module Mail
32
- class BccField < StructuredField
33
-
34
- include Mail::CommonAddress
35
-
36
- FIELD_NAME = 'bcc'
37
- CAPITALIZED_FIELD = 'Bcc'
38
-
39
- def initialize(value = '', charset = 'utf-8')
40
- @charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
- self
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'
33
+
34
+ attr_accessor :include_in_headers
35
+
36
+ def initialize(value = nil, charset = nil)
37
+ super
38
+ self.include_in_headers = false
44
39
  end
45
-
46
- # Bcc field should never be :encoded
40
+
41
+ # Bcc field should not be :encoded by default
47
42
  def encoded
48
- ''
49
- end
50
-
51
- def decoded
52
- do_decode
43
+ if include_in_headers
44
+ super
45
+ else
46
+ ''
47
+ end
53
48
  end
54
-
55
49
  end
56
50
  end
@@ -1,55 +1,34 @@
1
1
  # encoding: utf-8
2
- #
3
- # = Carbon Copy Field
4
- #
5
- # The Cc field inherits from StructuredField and handles the Cc: header
6
- # field in the email.
7
- #
8
- # Sending cc to a mail message will instantiate a Mail::Field object that
9
- # has a CcField as its field type. This includes all Mail::CommonAddress
10
- # module instance metods.
11
- #
12
- # Only one Cc field can appear in a header, though it can have multiple
13
- # addresses and groups of addresses.
14
- #
15
- # == Examples:
16
- #
17
- # mail = Mail.new
18
- # mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
- # mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
21
- # mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
22
- # mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
23
- #
24
- # mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
25
- # mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
- # mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
- # mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
28
- #
29
- require 'mail/fields/common/common_address'
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/common_address_field'
30
4
 
31
5
  module Mail
32
- class CcField < StructuredField
33
-
34
- include Mail::CommonAddress
35
-
36
- FIELD_NAME = 'cc'
37
- CAPITALIZED_FIELD = 'Cc'
38
-
39
- def initialize(value = nil, charset = 'utf-8')
40
- self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
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,41 +1,32 @@
1
1
  # encoding: utf-8
2
- #
3
- # = Comments Field
4
- #
5
- # The Comments field inherits from UnstructuredField and handles the Comments:
6
- # header field in the email.
7
- #
8
- # Sending comments to a mail message will instantiate a Mail::Field object that
9
- # has a CommentsField as its field type.
10
- #
11
- # An email header can have as many comments fields as it wants. There is no upper
12
- # limit, the comments field is also optional (that is, no comment is needed)
13
- #
14
- # == Examples:
15
- #
16
- # mail = Mail.new
17
- # mail.comments = 'This is a comment'
18
- # mail.comments #=> 'This is a comment'
19
- # mail[:comments] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
20
- # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
21
- # mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
22
- #
23
- # mail.comments = "This is another comment"
24
- # mail[:comments].map { |c| c.to_s }
25
- # #=> ['This is a comment', "This is another comment"]
26
- #
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/named_unstructured_field'
4
+
27
5
  module Mail
28
- class CommentsField < UnstructuredField
29
-
30
- FIELD_NAME = 'comments'
31
- CAPITALIZED_FIELD = 'Comments'
32
-
33
- def initialize(value = nil, charset = 'utf-8')
34
- @charset = charset
35
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value))
36
- self.parse
37
- self
38
- end
39
-
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'
40
31
  end
41
32
  end