net-imap 0.4.22 → 0.6.3

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -2
  3. data/README.md +10 -4
  4. data/docs/styles.css +75 -14
  5. data/lib/net/imap/authenticators.rb +2 -2
  6. data/lib/net/imap/command_data.rb +40 -95
  7. data/lib/net/imap/config/attr_accessors.rb +8 -9
  8. data/lib/net/imap/config/attr_inheritance.rb +64 -1
  9. data/lib/net/imap/config/attr_type_coercion.rb +22 -10
  10. data/lib/net/imap/config/attr_version_defaults.rb +90 -0
  11. data/lib/net/imap/config.rb +241 -125
  12. data/lib/net/imap/connection_state.rb +48 -0
  13. data/lib/net/imap/data_encoding.rb +80 -31
  14. data/lib/net/imap/deprecated_client_options.rb +6 -3
  15. data/lib/net/imap/errors.rb +158 -0
  16. data/lib/net/imap/esearch_result.rb +225 -0
  17. data/lib/net/imap/fetch_data.rb +126 -47
  18. data/lib/net/imap/flags.rb +1 -1
  19. data/lib/net/imap/response_data.rb +123 -187
  20. data/lib/net/imap/response_parser/parser_utils.rb +19 -23
  21. data/lib/net/imap/response_parser.rb +182 -38
  22. data/lib/net/imap/response_reader.rb +10 -12
  23. data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
  24. data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
  25. data/lib/net/imap/sasl/authenticators.rb +8 -4
  26. data/lib/net/imap/sasl/client_adapter.rb +77 -26
  27. data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
  28. data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
  29. data/lib/net/imap/sasl/external_authenticator.rb +2 -2
  30. data/lib/net/imap/sasl/gs2_header.rb +7 -7
  31. data/lib/net/imap/sasl/login_authenticator.rb +4 -3
  32. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
  33. data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
  34. data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
  35. data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
  36. data/lib/net/imap/sasl.rb +7 -4
  37. data/lib/net/imap/sasl_adapter.rb +0 -1
  38. data/lib/net/imap/search_result.rb +10 -5
  39. data/lib/net/imap/sequence_set.rb +1104 -421
  40. data/lib/net/imap/stringprep/nameprep.rb +1 -1
  41. data/lib/net/imap/stringprep/trace.rb +4 -4
  42. data/lib/net/imap/uidplus_data.rb +4 -147
  43. data/lib/net/imap/vanished_data.rb +65 -0
  44. data/lib/net/imap.rb +1002 -313
  45. data/net-imap.gemspec +1 -1
  46. data/rakelib/rfcs.rake +2 -0
  47. data/rakelib/string_prep_tables_generator.rb +6 -2
  48. metadata +7 -3
@@ -3,6 +3,7 @@
3
3
  require_relative "config/attr_accessors"
4
4
  require_relative "config/attr_inheritance"
5
5
  require_relative "config/attr_type_coercion"
6
+ require_relative "config/attr_version_defaults"
6
7
 
7
8
  module Net
8
9
  class IMAP
@@ -75,7 +76,7 @@ module Net
75
76
  #
76
77
  # client = Net::IMAP.new(hostname, config: :future)
77
78
  # client.config.sasl_ir # => true
78
- # client.config.responses_without_block # => :raise
79
+ # client.config.responses_without_block # => :frozen_dup
79
80
  #
80
81
  # The versioned default configs inherit certain specific config options from
81
82
  # Config.global, for example #debug:
@@ -109,9 +110,11 @@ module Net
109
110
  # [+:future+]
110
111
  # The _planned_ eventual config for some future +x.y+ version.
111
112
  #
112
- # For example, to raise exceptions for all current deprecations:
113
+ # For example, to disable all currently deprecated behavior:
113
114
  # client = Net::IMAP.new(hostname, config: :future)
114
- # client.responses # raises an ArgumentError
115
+ # client.config.response_without_args # => :frozen_dup
116
+ # client.responses.frozen? # => true
117
+ # client.responses.values.all?(&:frozen?) # => true
115
118
  #
116
119
  # == Thread Safety
117
120
  #
@@ -139,15 +142,7 @@ module Net
139
142
  # Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
140
143
  # Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
141
144
  # Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
142
- 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
- }
145
+ def self.version_defaults; AttrVersionDefaults.version_defaults end
151
146
 
152
147
  # :call-seq:
153
148
  # Net::IMAP::Config[number] -> versioned config
@@ -187,6 +182,7 @@ module Net
187
182
  include AttrAccessors
188
183
  include AttrInheritance
189
184
  include AttrTypeCoercion
185
+ extend AttrVersionDefaults
190
186
 
191
187
  # The debug mode (boolean). The default value is +false+.
192
188
  #
@@ -198,7 +194,7 @@ module Net
198
194
  #
199
195
  # *NOTE:* Versioned default configs inherit #debug from Config.global, and
200
196
  # #load_defaults will not override #debug.
201
- attr_accessor :debug, type: :boolean
197
+ attr_accessor :debug, type: :boolean, default: false
202
198
 
203
199
  # method: debug?
204
200
  # :call-seq: debug? -> boolean
@@ -216,7 +212,7 @@ module Net
216
212
  # See Net::IMAP.new and Net::IMAP#starttls.
217
213
  #
218
214
  # The default value is +30+ seconds.
219
- attr_accessor :open_timeout, type: Integer
215
+ attr_accessor :open_timeout, type: Integer, default: 30
220
216
 
221
217
  # Seconds to wait until an IDLE response is received, after
222
218
  # the client asks to leave the IDLE state.
@@ -224,7 +220,7 @@ module Net
224
220
  # See Net::IMAP#idle and Net::IMAP#idle_done.
225
221
  #
226
222
  # The default value is +5+ seconds.
227
- attr_accessor :idle_response_timeout, type: Integer
223
+ attr_accessor :idle_response_timeout, type: Integer, default: 5
228
224
 
229
225
  # Whether to use the +SASL-IR+ extension when the server and \SASL
230
226
  # mechanism both support it. Can be overridden by the +sasl_ir+ keyword
@@ -238,18 +234,61 @@ module Net
238
234
  # Do not use +SASL-IR+, even when it is supported by the server and the
239
235
  # mechanism.
240
236
  #
237
+ # [+:when_capabilities_cached+]
238
+ # Use +SASL-IR+ when Net::IMAP#capabilities_cached? is +true+ and it is
239
+ # supported by the server and the mechanism, but do not send a
240
+ # +CAPABILITY+ command to discover the server capabilities.
241
+ #
242
+ # <em>(+:when_capabilities_cached+ option was added by +v0.6.0+)</em>
243
+ #
241
244
  # [+true+ <em>(default since +v0.4+)</em>]
242
245
  # Use +SASL-IR+ when it is supported by the server and the mechanism.
243
- attr_accessor :sasl_ir, type: :boolean
246
+ attr_accessor :sasl_ir, type: Enum[
247
+ false, :when_capabilities_cached, true
248
+ ], defaults: {
249
+ 0.0r => false,
250
+ 0.4r => true,
251
+ }
252
+
253
+ # :stopdoc:
254
+ alias sasl_ir? sasl_ir
255
+ # :startdoc:
256
+
257
+ # Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
258
+ # capability is present. When enforced, Net::IMAP will raise a
259
+ # LoginDisabledError when that capability is present.
260
+ #
261
+ # <em>(Support for +LOGINDISABLED+ was added in +v0.5.0+.)</em>
262
+ #
263
+ # ==== Valid options
264
+ #
265
+ # [+false+ <em>(original behavior, before support was added)</em>]
266
+ # Send the +LOGIN+ command without checking for +LOGINDISABLED+.
267
+ #
268
+ # [+:when_capabilities_cached+]
269
+ # Enforce the requirement when Net::IMAP#capabilities_cached? is true,
270
+ # but do not send a +CAPABILITY+ command to discover the capabilities.
271
+ #
272
+ # [+true+ <em>(default since +v0.5+)</em>]
273
+ # Only send the +LOGIN+ command if the +LOGINDISABLED+ capability is not
274
+ # present. When capabilities are unknown, Net::IMAP will automatically
275
+ # send a +CAPABILITY+ command first before sending +LOGIN+.
276
+ #
277
+ attr_accessor :enforce_logindisabled, type: Enum[
278
+ false, :when_capabilities_cached, true
279
+ ], defaults: {
280
+ 0.0r => false,
281
+ 0.5r => true,
282
+ }
244
283
 
245
284
  # The maximum allowed server response size. When +nil+, there is no limit
246
285
  # on response size.
247
286
  #
248
287
  # 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).
288
+ # unlikely to be reached. A _much_ lower value should be used with
289
+ # untrusted servers (for example, when connecting to a user-provided
290
+ # hostname). When using a lower limit, message bodies should be fetched
291
+ # in chunks rather than all at once.
253
292
  #
254
293
  # <em>Please Note:</em> this only limits the size per response. It does
255
294
  # not prevent a flood of individual responses and it does not limit how
@@ -260,7 +299,6 @@ module Net
260
299
  # response: max_response_size minus the bytes that have already been read.
261
300
  # When the limit is reached, or reading a +literal+ _would_ go over the
262
301
  # limit, ResponseTooLargeError is raised and the connection is closed.
263
- # See also #socket_read_limit.
264
302
  #
265
303
  # Note that changes will not take effect immediately, because the receiver
266
304
  # thread may already be waiting for the next response using the previous
@@ -275,7 +313,10 @@ module Net
275
313
  #
276
314
  # * original: +nil+ <em>(no limit)</em>
277
315
  # * +0.5+: 512 MiB
278
- attr_accessor :max_response_size, type: Integer?
316
+ attr_accessor :max_response_size, type: Integer?, defaults: {
317
+ 0.0r => nil,
318
+ 0.5r => 512 << 20, # 512 MiB
319
+ }
279
320
 
280
321
  # Controls the behavior of Net::IMAP#responses when called without any
281
322
  # arguments (+type+ or +block+).
@@ -305,7 +346,11 @@ module Net
305
346
  # Note: #responses_without_args is an alias for #responses_without_block.
306
347
  attr_accessor :responses_without_block, type: Enum[
307
348
  :silence_deprecation_warning, :warn, :frozen_dup, :raise,
308
- ]
349
+ ], defaults: {
350
+ 0.0r => :silence_deprecation_warning,
351
+ 0.5r => :warn,
352
+ 0.6r => :frozen_dup,
353
+ }
309
354
 
310
355
  alias responses_without_args responses_without_block # :nodoc:
311
356
  alias responses_without_args= responses_without_block= # :nodoc:
@@ -315,66 +360,69 @@ module Net
315
360
  #
316
361
  # Alias for responses_without_block
317
362
 
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.
363
+ # **NOTE:** <em>+UIDPlusData+ has been removed since +v0.6.0+, and this
364
+ # config option only affects deprecation warnings.
365
+ # This config option will be **removed** in +v0.7.0+.</em>
327
366
  #
328
- # AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
329
- # UIDPlusData. Most applications should be able to upgrade with little
330
- # or no changes.
367
+ # ResponseParser always returns CopyUIDData for +COPYUID+ response codes,
368
+ # and AppendUIDData for +APPENDUID+ response codes. Previously, this
369
+ # option determined when UIDPlusData would be returned instead.
331
370
  #
332
- # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
371
+ # Parser support for +UIDPLUS+ added in +v0.3.2+.
333
372
  #
334
- # <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
373
+ # Config option added in +v0.4.19+ and +v0.5.6+.
335
374
  #
336
- # <em>UIDPlusData will be removed in +v0.6+ and this config setting will
337
- # be ignored.</em>
375
+ # <em>UIDPlusData removed in +v0.6.0+.</em>
338
376
  #
339
- # ==== Valid options
377
+ # ==== Options
340
378
  #
341
379
  # [+true+ <em>(original default)</em>]
342
- # ResponseParser only uses UIDPlusData.
380
+ # <em>Since v0.6.0:</em>
381
+ # Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
343
382
  #
344
383
  # [+: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.
384
+ # <em>Since v0.6.0:</em>
385
+ # Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
348
386
  #
349
- # [+false+ <em>(planned default for +v0.6+)</em>]
350
- # ResponseParser _only_ uses AppendUIDData and CopyUIDData.
387
+ # [+false+ <em>(default since +v0.6.0+)</em>]
388
+ # This is the only supported option <em>(since v0.6.0)</em>.
351
389
  attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
352
390
  true, :up_to_max_size, false
353
- ]
391
+ ], defaults: {
392
+ 0.0r => true,
393
+ 0.5r => :up_to_max_size,
394
+ 0.6r => false,
395
+ }
354
396
 
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+.
397
+ # **NOTE:** <em>+UIDPlusData+ has been removed since +v0.6.0+, and this
398
+ # config option is ignored.
399
+ # This config option will be **removed** in +v0.7.0+.</em>
358
400
  #
359
- # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
401
+ # ResponseParser always returns CopyUIDData for +COPYUID+ response codes,
402
+ # and AppendUIDData for +APPENDUID+ response codes. Previously, this
403
+ # option determined when UIDPlusData would be returned instead.
360
404
  #
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>
405
+ # Parser support for +UIDPLUS+ added in +v0.3.2+.
363
406
  #
364
- # <em>UIDPlusData will be removed in +v0.6+.</em>
407
+ # Support for limiting UIDPlusData to a maximum size was added in
408
+ # +v0.3.8+, +v0.4.19+, and +v0.5.6+.
365
409
  #
366
- # ==== Versioned Defaults
410
+ # <em>UIDPlusData was removed in +v0.6.0+.</em>
367
411
  #
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.
412
+ # ==== Versioned Defaults
371
413
  #
372
414
  # * +0.3+ and prior: <tt>10,000</tt>
373
415
  # * +0.4+: <tt>1,000</tt>
374
416
  # * +0.5+: <tt>100</tt>
375
417
  # * +0.6+: <tt>0</tt>
376
418
  #
377
- attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
419
+ attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer,
420
+ defaults: {
421
+ 0.0r => 10_000,
422
+ 0.4r => 1_000,
423
+ 0.5r => 100,
424
+ 0.6r => 0,
425
+ }
378
426
 
379
427
  # Creates a new config object and initialize its attribute with +attrs+.
380
428
  #
@@ -443,82 +491,150 @@ module Net
443
491
  # Returns all config attributes in a hash.
444
492
  def to_h; data.members.to_h { [_1, send(_1)] } end
445
493
 
494
+ # Returns a string representation of overriden config attributes and the
495
+ # inheritance chain.
496
+ #
497
+ # Attributes overridden by ancestors are also inspected, recursively.
498
+ # Attributes that are inherited from default configs are not shown (see
499
+ # Config@Versioned+defaults and Config@Named+defaults).
500
+ #
501
+ # # (Line breaks have been added to the example output for legibility.)
502
+ #
503
+ # Net::IMAP::Config.new(0.4)
504
+ # .new(open_timeout: 10, enforce_logindisabled: true)
505
+ # .inspect
506
+ # #=> "#<Net::IMAP::Config:0x0000745871125410 open_timeout=10 enforce_logindisabled=true
507
+ # # inherits from Net::IMAP::Config[0.4]
508
+ # # inherits from Net::IMAP::Config.global
509
+ # # inherits from Net::IMAP::Config.default>"
510
+ #
511
+ # Non-default attributes are listed after the ancestor config from which
512
+ # they are inherited.
513
+ #
514
+ # # (Line breaks have been added to the example output for legibility.)
515
+ #
516
+ # config = Net::IMAP::Config.global
517
+ # .new(open_timeout: 10, idle_response_timeout: 2)
518
+ # .new(enforce_logindisabled: :when_capabilities_cached, sasl_ir: false)
519
+ # config.inspect
520
+ # #=> "#<Net::IMAP::Config:0x00007ce2a1e20e40 sasl_ir=false enforce_logindisabled=:when_capabilities_cached
521
+ # # inherits from Net::IMAP::Config:0x00007ce2a1e20f80 open_timeout=10 idle_response_timeout=2
522
+ # # inherits from Net::IMAP::Config.global
523
+ # # inherits from Net::IMAP::Config.default>"
524
+ #
525
+ # Net::IMAP.debug = true
526
+ # config.inspect
527
+ # #=> "#<Net::IMAP::Config:0x00007ce2a1e20e40 sasl_ir=false enforce_logindisabled=:when_capabilities_cached
528
+ # # inherits from Net::IMAP::Config:0x00007ce2a1e20f80 open_timeout=10 idle_response_timeout=2
529
+ # # inherits from Net::IMAP::Config.global debug=true
530
+ # # inherits from Net::IMAP::Config.default>"
531
+ #
532
+ # Use +pp+ (see #pretty_print) to inspect _all_ config attributes,
533
+ # including default values.
534
+ #
535
+ # Use #to_h to inspect all config attributes ignoring inheritance.
536
+ def inspect;
537
+ "#<#{inspect_recursive}>"
538
+ end
539
+ alias to_s inspect
540
+
541
+ # Used by PP[https://docs.ruby-lang.org/en/master/PP.html] to create a
542
+ # string representation of all config attributes and the inheritance
543
+ # chain. Inherited attributes are listed with the ancestor config from
544
+ # which they are inherited.
545
+ #
546
+ # pp Config.new[0.4].new(open_timeout: 10, idle_response_timeout: 10)
547
+ # # #<Net::IMAP::Config:0x0000745871125410
548
+ # # open_timeout=10
549
+ # # idle_response_timeout=10
550
+ # # inherits from Net::IMAP::Config[0.4]
551
+ # # responses_without_block=:silence_deprecation_warning
552
+ # # max_response_size=nil
553
+ # # sasl_ir=true
554
+ # # enforce_logindisabled=false
555
+ # # parser_use_deprecated_uidplus_data=true
556
+ # # parser_max_deprecated_uidplus_data_size=1000
557
+ # # inherits from Net::IMAP::Config.global
558
+ # # inherits from Net::IMAP::Config.default
559
+ # # debug=false>
560
+ #
561
+ # Related: #inspect, #to_h.
562
+ def pretty_print(pp)
563
+ pp.group(2, "#<", ">") do
564
+ pretty_print_recursive(pp)
565
+ end
566
+ end
567
+
568
+ # :stopdoc:
569
+
446
570
  protected
447
571
 
448
- def defaults_hash
449
- to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
572
+ def named_default?
573
+ equal?(Config.default) ||
574
+ AttrVersionDefaults::VERSIONS.any? { equal? Config[_1] }
450
575
  end
451
576
 
452
- @default = new(
453
- debug: false,
454
- open_timeout: 30,
455
- idle_response_timeout: 5,
456
- 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,
461
- ).freeze
462
-
463
- @global = default.new
464
-
465
- version_defaults[:default] = Config[default.send(:defaults_hash)]
466
-
467
- version_defaults[0r] = Config[:default].dup.update(
468
- sasl_ir: false,
469
- max_response_size: nil,
470
- parser_use_deprecated_uidplus_data: true,
471
- parser_max_deprecated_uidplus_data_size: 10_000,
472
- ).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]
477
-
478
- version_defaults[0.4r] = Config[0.3r].dup.update(
479
- sasl_ir: true,
480
- parser_max_deprecated_uidplus_data_size: 1000,
481
- ).freeze
482
-
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
489
-
490
- version_defaults[0.6r] = Config[0.5r].dup.update(
491
- 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
- ).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
577
+ def name
578
+ if equal? Config.default then "#{Config}.default"
579
+ elsif equal? Config.global then "#{Config}.global"
580
+ elsif equal? Config[0.0r] then "#{Config}[:original]"
581
+ elsif (v = AttrVersionDefaults::VERSIONS.find { equal? Config[_1] })
582
+ "%s[%0.1f]" % [Config, v]
583
+ else
584
+ Kernel.instance_method(:to_s).bind_call(self).delete("<#>")
585
+ end
505
586
  end
506
587
 
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]
588
+ def inspect_recursive(attrs = AttrAccessors.struct.members)
589
+ strings = [name]
590
+ assigned = assigned_attrs_hash(attrs)
591
+ strings.concat assigned.map { "%s=%p" % _1 }
592
+ if parent
593
+ if parent.equal?(Config.default)
594
+ inherited_overrides = []
595
+ elsif parent
596
+ inherited_overrides = attrs - assigned.keys
597
+ inherited_overrides &= DEFAULT_TO_INHERIT if parent.named_default?
598
+ end
599
+ strings << "inherits from #{parent.inspect_recursive(inherited_overrides)}"
600
+ end
601
+ strings.join " "
602
+ end
511
603
 
512
- version_defaults[:future] = Config[0.7r]
604
+ def pretty_print_recursive(pp, attrs = AttrAccessors.struct.members)
605
+ pp.text name
606
+ assigned = assigned_attrs_hash(attrs)
607
+ pp.breakable
608
+ pp.seplist(assigned, ->{pp.breakable}) do |key, val|
609
+ pp.text key.to_s
610
+ pp.text "="
611
+ pp.pp val
612
+ end
613
+ if parent
614
+ pp.breakable if assigned.any?
615
+ pp.nest(2) do
616
+ pp.text "inherits from "
617
+ parent.pretty_print_recursive(pp, attrs - assigned.keys)
618
+ end
619
+ elsif assigned.empty?
620
+ pp.text "(overridden)"
621
+ end
622
+ end
513
623
 
514
- version_defaults.freeze
624
+ def assigned_attrs_hash(attrs)
625
+ own_attrs = attrs.reject { inherited?(_1) }
626
+ own_attrs.to_h { [_1, data[_1]] }
627
+ end
515
628
 
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
- ]
629
+ def defaults_hash
630
+ to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
521
631
  end
632
+
633
+ Struct = AttrAccessors.struct
634
+ @default = AttrVersionDefaults.compile_default!
635
+ @global = default.new
636
+ AttrVersionDefaults.compile_version_defaults!
637
+
522
638
  end
523
639
  end
524
640
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ class ConnectionState < Data # :nodoc:
6
+ def self.define(symbol, *attrs)
7
+ symbol => Symbol
8
+ state = super(*attrs)
9
+ state.const_set :NAME, symbol
10
+ state
11
+ end
12
+
13
+ def symbol; self.class::NAME end
14
+ def name; self.class::NAME.name end
15
+ alias to_sym symbol
16
+
17
+ def deconstruct; [symbol, *super] end
18
+
19
+ def deconstruct_keys(names)
20
+ hash = super
21
+ hash[:symbol] = symbol if names.nil? || names.include?(:symbol)
22
+ hash[:name] = name if names.nil? || names.include?(:name)
23
+ hash
24
+ end
25
+
26
+ def to_h(&block)
27
+ hash = deconstruct_keys(nil)
28
+ block ? hash.to_h(&block) : hash
29
+ end
30
+
31
+ def not_authenticated?; to_sym == :not_authenticated end
32
+ def authenticated?; to_sym == :authenticated end
33
+ def selected?; to_sym == :selected end
34
+ def logout?; to_sym == :logout end
35
+
36
+ NotAuthenticated = define(:not_authenticated)
37
+ Authenticated = define(:authenticated)
38
+ Selected = define(:selected)
39
+ Logout = define(:logout)
40
+
41
+ class << self
42
+ undef :define
43
+ end
44
+ freeze
45
+ end
46
+
47
+ end
48
+ end