net-imap 0.4.21 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of net-imap might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab1b84ea6eac7b0d57793127bc365c79b63eb9564386427eb66cca91cf5df294
4
- data.tar.gz: 3040f462d83f7a364fed076a730d5a9ba0512ea52ab0368765befb0e2d700dd4
3
+ metadata.gz: 2c89d97e842bce303df112c3fbc0f048f20a2ff1dbf3b85bb2314ea0d2e207c5
4
+ data.tar.gz: 11d6ec196e1e768f61a17b0ce7f385a11eb03e3e92af8ab327ba9a90699ee940
5
5
  SHA512:
6
- metadata.gz: a7565d3002323cdaad204bbd5eceb0b64d265e61daa41d9e3c61b8ea12ff3bbbb5f5e62298138f48a96c32553b1fcf3c6dcab7cae5776a22a521abe77b6e153c
7
- data.tar.gz: 73b9338a55d33e703c86217ee80ee7486e92dbf67b232c152d4115a89a76c4f384c39e34888d24ec8cf389af1d67ca1d1d84e3edc676ecc501495cf6b66d7dfe
6
+ metadata.gz: ca70e20c3c70f4ca57822f70936546adb3129dd7c0771ef2d054b0356ed4f6a994b7453803cc06bd25ebb37dc0ed10f60b5541bc56749dee38a6126264344599
7
+ data.tar.gz: 2921a79dbdab7bd0476d883c18db50150388d587c711270faac99ad4505574a23e6532fe61a4fb48f51c8bddea6641d96f81d9dcbe245b4d4f4542fcc7418566
data/Gemfile CHANGED
@@ -13,4 +13,10 @@ gem "rdoc"
13
13
  gem "test-unit"
14
14
  gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
15
15
 
16
- gem "benchmark-driver"
16
+ gem "benchmark-driver", require: false
17
+
18
+ group :test do
19
+ gem "simplecov", require: false
20
+ gem "simplecov-html", require: false
21
+ gem "simplecov-json", require: false
22
+ end
@@ -9,7 +9,7 @@ module Net::IMAP::Authenticators
9
9
  "%s.%s is deprecated. Use %s.%s instead." % [
10
10
  Net::IMAP, __method__, Net::IMAP::SASL, __method__
11
11
  ],
12
- uplevel: 1
12
+ uplevel: 1, category: :deprecated
13
13
  )
14
14
  Net::IMAP::SASL.add_authenticator(...)
15
15
  end
@@ -20,7 +20,7 @@ module Net::IMAP::Authenticators
20
20
  "%s.%s is deprecated. Use %s.%s instead." % [
21
21
  Net::IMAP, __method__, Net::IMAP::SASL, __method__
22
22
  ],
23
- uplevel: 1
23
+ uplevel: 1, category: :deprecated
24
24
  )
25
25
  Net::IMAP::SASL.authenticator(...)
26
26
  end
@@ -179,6 +179,7 @@ module Net
179
179
  end
180
180
  end
181
181
 
182
+ # *DEPRECATED*. Replaced by SequenceSet.
182
183
  class MessageSet # :nodoc:
183
184
  def send_data(imap, tag)
184
185
  imap.__send__(:put_string, format_internal(@data))
@@ -192,6 +193,16 @@ module Net
192
193
 
193
194
  def initialize(data)
194
195
  @data = data
196
+ warn("DEPRECATED: #{MessageSet} should be replaced with #{SequenceSet}.",
197
+ uplevel: 1, category: :deprecated)
198
+ begin
199
+ # to ensure the input works with SequenceSet, too
200
+ SequenceSet.new(data)
201
+ rescue
202
+ warn "MessageSet input is incompatible with SequenceSet: [%s] %s" % [
203
+ $!.class, $!.message
204
+ ]
205
+ end
195
206
  end
196
207
 
197
208
  def format_internal(data)
@@ -18,8 +18,6 @@ module Net
18
18
  super(attr)
19
19
  AttrTypeCoercion.attr_accessor(attr, type: type)
20
20
  end
21
-
22
- module_function def Integer?; NilOrInteger end
23
21
  end
24
22
  private_constant :Macros
25
23
 
@@ -28,36 +26,34 @@ module Net
28
26
  end
29
27
  private_class_method :included
30
28
 
31
- if defined?(Ractor.make_shareable)
32
- def self.safe(...) Ractor.make_shareable nil.instance_eval(...).freeze end
33
- else
34
- def self.safe(...) nil.instance_eval(...).freeze end
29
+ def self.attr_accessor(attr, type: nil)
30
+ return unless type
31
+ if :boolean == type then boolean attr
32
+ elsif Integer == type then integer attr
33
+ elsif Array === type then enum attr, type
34
+ else raise ArgumentError, "unknown type coercion %p" % [type]
35
+ end
35
36
  end
36
- private_class_method :safe
37
37
 
38
- Types = Hash.new do |h, type|
39
- type.nil? || Proc === type or raise TypeError, "type not nil or Proc"
40
- safe{type}
38
+ def self.boolean(attr)
39
+ define_method :"#{attr}=" do |val| super !!val end
40
+ define_method :"#{attr}?" do send attr end
41
41
  end
42
- Types[:boolean] = Boolean = safe{-> {!!_1}}
43
- Types[Integer] = safe{->{Integer(_1)}}
44
42
 
45
- def self.attr_accessor(attr, type: nil)
46
- type = Types[type] or return
47
- define_method :"#{attr}=" do |val| super type[val] end
48
- define_method :"#{attr}?" do send attr end if type == Boolean
43
+ def self.integer(attr)
44
+ define_method :"#{attr}=" do |val| super Integer val end
49
45
  end
50
46
 
51
- NilOrInteger = safe{->val { Integer val unless val.nil? }}
52
-
53
- Enum = ->(*enum) {
54
- enum = safe{enum}
47
+ def self.enum(attr, enum)
48
+ enum = enum.dup.freeze
55
49
  expected = -"one of #{enum.map(&:inspect).join(", ")}"
56
- safe{->val {
57
- return val if enum.include?(val)
58
- raise ArgumentError, "expected %s, got %p" % [expected, val]
59
- }}
60
- }
50
+ define_method :"#{attr}=" do |val|
51
+ unless enum.include?(val)
52
+ raise ArgumentError, "expected %s, got %p" % [expected, val]
53
+ end
54
+ super val
55
+ end
56
+ end
61
57
 
62
58
  end
63
59
  end
@@ -129,25 +129,8 @@ module Net
129
129
  def self.global; @global if defined?(@global) end
130
130
 
131
131
  # A hash of hard-coded configurations, indexed by version number or name.
132
- # Values can be accessed with any object that responds to +to_sym+ or
133
- # +to_r+/+to_f+ with a non-zero number.
134
- #
135
- # Config::[] gets named or numbered versions from this hash.
136
- #
137
- # For example:
138
- # Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
139
- # Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
140
- # Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
141
- # Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
142
132
  def self.version_defaults; @version_defaults end
143
- @version_defaults = Hash.new {|h, k|
144
- # NOTE: String responds to both so the order is significant.
145
- # And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
146
- (h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
147
- (h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
148
- (h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
149
- (h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
150
- }
133
+ @version_defaults = {}
151
134
 
152
135
  # :call-seq:
153
136
  # Net::IMAP::Config[number] -> versioned config
@@ -170,17 +153,18 @@ module Net
170
153
  elsif config.nil? && global.nil? then nil
171
154
  elsif config.respond_to?(:to_hash) then new(global, **config).freeze
172
155
  else
173
- version_defaults[config] or
156
+ version_defaults.fetch(config) do
174
157
  case config
175
158
  when Numeric
176
159
  raise RangeError, "unknown config version: %p" % [config]
177
- when String, Symbol
160
+ when Symbol
178
161
  raise KeyError, "unknown config name: %p" % [config]
179
162
  else
180
163
  raise TypeError, "no implicit conversion of %s to %s" % [
181
164
  config.class, Config
182
165
  ]
183
166
  end
167
+ end
184
168
  end
185
169
  end
186
170
 
@@ -207,13 +191,10 @@ module Net
207
191
 
208
192
  # Seconds to wait until a connection is opened.
209
193
  #
210
- # Applied separately for establishing TCP connection and starting a TLS
211
- # connection.
212
- #
213
194
  # If the IMAP object cannot open a connection within this time,
214
195
  # it raises a Net::OpenTimeout exception.
215
196
  #
216
- # See Net::IMAP.new and Net::IMAP#starttls.
197
+ # See Net::IMAP.new.
217
198
  #
218
199
  # The default value is +30+ seconds.
219
200
  attr_accessor :open_timeout, type: Integer
@@ -242,40 +223,29 @@ module Net
242
223
  # Use +SASL-IR+ when it is supported by the server and the mechanism.
243
224
  attr_accessor :sasl_ir, type: :boolean
244
225
 
245
- # The maximum allowed server response size. When +nil+, there is no limit
246
- # on response size.
226
+ # Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
227
+ # capability is present. When enforced, Net::IMAP will raise a
228
+ # LoginDisabledError when that capability is present.
247
229
  #
248
- # The default value (512 MiB, since +v0.5.7+) is <em>very high</em> and
249
- # unlikely to be reached. To use a lower limit, fetch message bodies in
250
- # chunks rather than all at once. A _much_ lower value should be used
251
- # with untrusted servers (for example, when connecting to a user-provided
252
- # hostname).
230
+ # <em>(Support for +LOGINDISABLED+ was added in +v0.5.0+.)</em>
253
231
  #
254
- # <em>Please Note:</em> this only limits the size per response. It does
255
- # not prevent a flood of individual responses and it does not limit how
256
- # many unhandled responses may be stored on the responses hash. See
257
- # Net::IMAP@Unbounded+memory+use.
258
- #
259
- # Socket reads are limited to the maximum remaining bytes for the current
260
- # response: max_response_size minus the bytes that have already been read.
261
- # When the limit is reached, or reading a +literal+ _would_ go over the
262
- # limit, ResponseTooLargeError is raised and the connection is closed.
263
- # See also #socket_read_limit.
232
+ # ==== Valid options
264
233
  #
265
- # Note that changes will not take effect immediately, because the receiver
266
- # thread may already be waiting for the next response using the previous
267
- # value. Net::IMAP#noop can force a response and enforce the new setting
268
- # immediately.
234
+ # [+false+ <em>(original behavior, before support was added)</em>]
235
+ # Send the +LOGIN+ command without checking for +LOGINDISABLED+.
269
236
  #
270
- # ==== Versioned Defaults
237
+ # [+:when_capabilities_cached+]
238
+ # Enforce the requirement when Net::IMAP#capabilities_cached? is true,
239
+ # but do not send a +CAPABILITY+ command to discover the capabilities.
271
240
  #
272
- # Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
273
- # attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to this
274
- # config attribute.</em>
241
+ # [+true+ <em>(default since +v0.5+)</em>]
242
+ # Only send the +LOGIN+ command if the +LOGINDISABLED+ capability is not
243
+ # present. When capabilities are unknown, Net::IMAP will automatically
244
+ # send a +CAPABILITY+ command first before sending +LOGIN+.
275
245
  #
276
- # * original: +nil+ <em>(no limit)</em>
277
- # * +0.5+: 512 MiB
278
- attr_accessor :max_response_size, type: Integer?
246
+ attr_accessor :enforce_logindisabled, type: [
247
+ false, :when_capabilities_cached, true
248
+ ]
279
249
 
280
250
  # Controls the behavior of Net::IMAP#responses when called without any
281
251
  # arguments (+type+ or +block+).
@@ -303,7 +273,7 @@ module Net
303
273
  # Raise an ArgumentError with the deprecation warning.
304
274
  #
305
275
  # Note: #responses_without_args is an alias for #responses_without_block.
306
- attr_accessor :responses_without_block, type: Enum[
276
+ attr_accessor :responses_without_block, type: [
307
277
  :silence_deprecation_warning, :warn, :frozen_dup, :raise,
308
278
  ]
309
279
 
@@ -315,67 +285,6 @@ module Net
315
285
  #
316
286
  # Alias for responses_without_block
317
287
 
318
- # Whether ResponseParser should use the deprecated UIDPlusData or
319
- # CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
320
- # AppendUIDData for +APPENDUID+ response codes.
321
- #
322
- # UIDPlusData stores its data in arrays of numbers, which is vulnerable to
323
- # a memory exhaustion denial of service attack from an untrusted or
324
- # compromised server. Set this option to +false+ to completely block this
325
- # vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
326
- # mitigates this vulnerability.
327
- #
328
- # AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
329
- # UIDPlusData. Most applications should be able to upgrade with little
330
- # or no changes.
331
- #
332
- # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
333
- #
334
- # <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
335
- #
336
- # <em>UIDPlusData will be removed in +v0.6+ and this config setting will
337
- # be ignored.</em>
338
- #
339
- # ==== Valid options
340
- #
341
- # [+true+ <em>(original default)</em>]
342
- # ResponseParser only uses UIDPlusData.
343
- #
344
- # [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
345
- # ResponseParser uses UIDPlusData when the +uid-set+ size is below
346
- # parser_max_deprecated_uidplus_data_size. Above that size,
347
- # ResponseParser uses AppendUIDData or CopyUIDData.
348
- #
349
- # [+false+ <em>(planned default for +v0.6+)</em>]
350
- # ResponseParser _only_ uses AppendUIDData and CopyUIDData.
351
- attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
352
- true, :up_to_max_size, false
353
- ]
354
-
355
- # The maximum +uid-set+ size that ResponseParser will parse into
356
- # deprecated UIDPlusData. This limit only applies when
357
- # parser_use_deprecated_uidplus_data is not +false+.
358
- #
359
- # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
360
- #
361
- # <em>Support for limiting UIDPlusData to a maximum size was added in
362
- # +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
363
- #
364
- # <em>UIDPlusData will be removed in +v0.6+.</em>
365
- #
366
- # ==== Versioned Defaults
367
- #
368
- # Because this limit guards against a remote server causing catastrophic
369
- # memory exhaustion, the versioned default (used by #load_defaults) also
370
- # applies to versions without the feature.
371
- #
372
- # * +0.3+ and prior: <tt>10,000</tt>
373
- # * +0.4+: <tt>1,000</tt>
374
- # * +0.5+: <tt>100</tt>
375
- # * +0.6+: <tt>0</tt>
376
- #
377
- attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
378
-
379
288
  # Creates a new config object and initialize its attribute with +attrs+.
380
289
  #
381
290
  # If +parent+ is not given, the global config is used by default.
@@ -454,71 +363,38 @@ module Net
454
363
  open_timeout: 30,
455
364
  idle_response_timeout: 5,
456
365
  sasl_ir: true,
457
- max_response_size: nil,
458
- responses_without_block: :silence_deprecation_warning,
459
- parser_use_deprecated_uidplus_data: true,
460
- parser_max_deprecated_uidplus_data_size: 1000,
366
+ enforce_logindisabled: true,
367
+ responses_without_block: :warn,
461
368
  ).freeze
462
369
 
463
370
  @global = default.new
464
371
 
465
372
  version_defaults[:default] = Config[default.send(:defaults_hash)]
373
+ version_defaults[:current] = Config[:default]
466
374
 
467
- version_defaults[0r] = Config[:default].dup.update(
375
+ version_defaults[0] = Config[:current].dup.update(
468
376
  sasl_ir: false,
469
- max_response_size: nil,
470
- parser_use_deprecated_uidplus_data: true,
471
- parser_max_deprecated_uidplus_data_size: 10_000,
377
+ responses_without_block: :silence_deprecation_warning,
378
+ enforce_logindisabled: false,
472
379
  ).freeze
473
- version_defaults[0.0r] = Config[0r]
474
- version_defaults[0.1r] = Config[0r]
475
- version_defaults[0.2r] = Config[0r]
476
- version_defaults[0.3r] = Config[0r]
380
+ version_defaults[0.0] = Config[0]
381
+ version_defaults[0.1] = Config[0]
382
+ version_defaults[0.2] = Config[0]
383
+ version_defaults[0.3] = Config[0]
477
384
 
478
- version_defaults[0.4r] = Config[0.3r].dup.update(
385
+ version_defaults[0.4] = Config[0.3].dup.update(
479
386
  sasl_ir: true,
480
- parser_max_deprecated_uidplus_data_size: 1000,
481
387
  ).freeze
482
388
 
483
- version_defaults[0.5r] = Config[0.4r].dup.update(
484
- max_response_size: 512 << 20, # 512 MiB
485
- responses_without_block: :warn,
486
- parser_use_deprecated_uidplus_data: :up_to_max_size,
487
- parser_max_deprecated_uidplus_data_size: 100,
488
- ).freeze
389
+ version_defaults[0.5] = Config[:current]
489
390
 
490
- version_defaults[0.6r] = Config[0.5r].dup.update(
391
+ version_defaults[0.6] = Config[0.5].dup.update(
491
392
  responses_without_block: :frozen_dup,
492
- parser_use_deprecated_uidplus_data: false,
493
- parser_max_deprecated_uidplus_data_size: 0,
494
- ).freeze
495
-
496
- version_defaults[0.7r] = Config[0.6r].dup.update(
497
393
  ).freeze
498
-
499
- # Safe conversions one way only:
500
- # 0.6r.to_f == 0.6 # => true
501
- # 0.6 .to_r == 0.6r # => false
502
- version_defaults.to_a.each do |k, v|
503
- next unless k.is_a? Rational
504
- version_defaults[k.to_f] = v
505
- end
506
-
507
- current = VERSION.to_r
508
- version_defaults[:original] = Config[0]
509
- version_defaults[:current] = Config[current]
510
- version_defaults[:next] = Config[current + 0.1r]
511
-
512
- version_defaults[:future] = Config[0.7r]
394
+ version_defaults[:next] = Config[0.6]
395
+ version_defaults[:future] = Config[:next]
513
396
 
514
397
  version_defaults.freeze
515
-
516
- if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h
517
- warn "Misconfigured Net::IMAP::Config[:current] => %p,\n" \
518
- " not equal to Net::IMAP::Config[:default] => %p" % [
519
- self[:current].to_h, self[:default].to_h
520
- ]
521
- end
522
398
  end
523
399
  end
524
400
  end
@@ -186,7 +186,7 @@ module Net
186
186
 
187
187
  # Ensure argument is 'number' or raise DataFormatError
188
188
  def ensure_number(num)
189
- return if valid_number?(num)
189
+ return num if valid_number?(num)
190
190
 
191
191
  msg = "number must be unsigned 32-bit integer: #{num}"
192
192
  raise DataFormatError, msg
@@ -194,7 +194,7 @@ module Net
194
194
 
195
195
  # Ensure argument is 'nz_number' or raise DataFormatError
196
196
  def ensure_nz_number(num)
197
- return if valid_nz_number?(num)
197
+ return num if valid_nz_number?(num)
198
198
 
199
199
  msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
200
200
  raise DataFormatError, msg
@@ -202,7 +202,7 @@ module Net
202
202
 
203
203
  # Ensure argument is 'mod_sequence_value' or raise DataFormatError
204
204
  def ensure_mod_sequence_value(num)
205
- return if valid_mod_sequence_value?(num)
205
+ return num if valid_mod_sequence_value?(num)
206
206
 
207
207
  msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
208
208
  raise DataFormatError, msg
@@ -83,10 +83,12 @@ module Net
83
83
  elsif deprecated.empty?
84
84
  super host, port: port_or_options
85
85
  elsif deprecated.shift
86
- warn "DEPRECATED: Call Net::IMAP.new with keyword options", uplevel: 1
86
+ warn("DEPRECATED: Call Net::IMAP.new with keyword options",
87
+ uplevel: 1, category: :deprecated)
87
88
  super host, port: port_or_options, ssl: create_ssl_params(*deprecated)
88
89
  else
89
- warn "DEPRECATED: Call Net::IMAP.new with keyword options", uplevel: 1
90
+ warn("DEPRECATED: Call Net::IMAP.new with keyword options",
91
+ uplevel: 1, category: :deprecated)
90
92
  super host, port: port_or_options, ssl: false
91
93
  end
92
94
  end
@@ -113,7 +115,8 @@ module Net
113
115
  elsif deprecated.first.respond_to?(:to_hash)
114
116
  super(**Hash.try_convert(deprecated.first))
115
117
  else
116
- warn "DEPRECATED: Call Net::IMAP#starttls with keyword options", uplevel: 1
118
+ warn("DEPRECATED: Call Net::IMAP#starttls with keyword options",
119
+ uplevel: 1, category: :deprecated)
117
120
  super(**create_ssl_params(*deprecated))
118
121
  end
119
122
  end
@@ -7,41 +7,14 @@ module Net
7
7
  class Error < StandardError
8
8
  end
9
9
 
10
- # Error raised when data is in the incorrect format.
11
- class DataFormatError < Error
12
- end
13
-
14
- # Error raised when the socket cannot be read, due to a Config limit.
15
- class ResponseReadError < Error
16
- end
17
-
18
- # Error raised when a response is larger than IMAP#max_response_size.
19
- class ResponseTooLargeError < ResponseReadError
20
- attr_reader :bytes_read, :literal_size
21
- attr_reader :max_response_size
22
-
23
- def initialize(msg = nil, *args,
24
- bytes_read: nil,
25
- literal_size: nil,
26
- max_response_size: nil,
27
- **kwargs)
28
- @bytes_read = bytes_read
29
- @literal_size = literal_size
30
- @max_response_size = max_response_size
31
- msg ||= [
32
- "Response size", response_size_msg, "exceeds max_response_size",
33
- max_response_size && "(#{max_response_size}B)",
34
- ].compact.join(" ")
35
- super(msg, *args, **kwargs)
10
+ class LoginDisabledError < Error
11
+ def initialize(msg = "Remote server has disabled the LOGIN command", ...)
12
+ super
36
13
  end
14
+ end
37
15
 
38
- private
39
-
40
- def response_size_msg
41
- if bytes_read && literal_size
42
- "(#{bytes_read}B read + #{literal_size}B literal)"
43
- end
44
- end
16
+ # Error raised when data is in the incorrect format.
17
+ class DataFormatError < Error
45
18
  end
46
19
 
47
20
  # Error raised when a response from the server is non-parsable.