net-imap 0.5.6 → 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.
@@ -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
@@ -131,8 +132,17 @@ module Net
131
132
  def self.global; @global if defined?(@global) end
132
133
 
133
134
  # A hash of hard-coded configurations, indexed by version number or name.
134
- def self.version_defaults; @version_defaults end
135
- @version_defaults = {}
135
+ # Values can be accessed with any object that responds to +to_sym+ or
136
+ # +to_r+/+to_f+ with a non-zero number.
137
+ #
138
+ # Config::[] gets named or numbered versions from this hash.
139
+ #
140
+ # For example:
141
+ # Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
142
+ # Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
143
+ # Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
144
+ # Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
145
+ def self.version_defaults; AttrVersionDefaults.version_defaults end
136
146
 
137
147
  # :call-seq:
138
148
  # Net::IMAP::Config[number] -> versioned config
@@ -155,24 +165,24 @@ module Net
155
165
  elsif config.nil? && global.nil? then nil
156
166
  elsif config.respond_to?(:to_hash) then new(global, **config).freeze
157
167
  else
158
- version_defaults.fetch(config) do
168
+ version_defaults[config] or
159
169
  case config
160
170
  when Numeric
161
171
  raise RangeError, "unknown config version: %p" % [config]
162
- when Symbol
172
+ when String, Symbol
163
173
  raise KeyError, "unknown config name: %p" % [config]
164
174
  else
165
175
  raise TypeError, "no implicit conversion of %s to %s" % [
166
176
  config.class, Config
167
177
  ]
168
178
  end
169
- end
170
179
  end
171
180
  end
172
181
 
173
182
  include AttrAccessors
174
183
  include AttrInheritance
175
184
  include AttrTypeCoercion
185
+ extend AttrVersionDefaults
176
186
 
177
187
  # The debug mode (boolean). The default value is +false+.
178
188
  #
@@ -184,7 +194,7 @@ module Net
184
194
  #
185
195
  # *NOTE:* Versioned default configs inherit #debug from Config.global, and
186
196
  # #load_defaults will not override #debug.
187
- attr_accessor :debug, type: :boolean
197
+ attr_accessor :debug, type: :boolean, default: false
188
198
 
189
199
  # method: debug?
190
200
  # :call-seq: debug? -> boolean
@@ -193,13 +203,16 @@ module Net
193
203
 
194
204
  # Seconds to wait until a connection is opened.
195
205
  #
206
+ # Applied separately for establishing TCP connection and starting a TLS
207
+ # connection.
208
+ #
196
209
  # If the IMAP object cannot open a connection within this time,
197
210
  # it raises a Net::OpenTimeout exception.
198
211
  #
199
- # See Net::IMAP.new.
212
+ # See Net::IMAP.new and Net::IMAP#starttls.
200
213
  #
201
214
  # The default value is +30+ seconds.
202
- attr_accessor :open_timeout, type: Integer
215
+ attr_accessor :open_timeout, type: Integer, default: 30
203
216
 
204
217
  # Seconds to wait until an IDLE response is received, after
205
218
  # the client asks to leave the IDLE state.
@@ -207,7 +220,7 @@ module Net
207
220
  # See Net::IMAP#idle and Net::IMAP#idle_done.
208
221
  #
209
222
  # The default value is +5+ seconds.
210
- attr_accessor :idle_response_timeout, type: Integer
223
+ attr_accessor :idle_response_timeout, type: Integer, default: 5
211
224
 
212
225
  # Whether to use the +SASL-IR+ extension when the server and \SASL
213
226
  # mechanism both support it. Can be overridden by the +sasl_ir+ keyword
@@ -221,9 +234,25 @@ module Net
221
234
  # Do not use +SASL-IR+, even when it is supported by the server and the
222
235
  # mechanism.
223
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
+ #
224
244
  # [+true+ <em>(default since +v0.4+)</em>]
225
245
  # Use +SASL-IR+ when it is supported by the server and the mechanism.
226
- 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:
227
256
 
228
257
  # Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
229
258
  # capability is present. When enforced, Net::IMAP will raise a
@@ -245,9 +274,49 @@ module Net
245
274
  # present. When capabilities are unknown, Net::IMAP will automatically
246
275
  # send a +CAPABILITY+ command first before sending +LOGIN+.
247
276
  #
248
- attr_accessor :enforce_logindisabled, type: [
277
+ attr_accessor :enforce_logindisabled, type: Enum[
249
278
  false, :when_capabilities_cached, true
250
- ]
279
+ ], defaults: {
280
+ 0.0r => false,
281
+ 0.5r => true,
282
+ }
283
+
284
+ # The maximum allowed server response size. When +nil+, there is no limit
285
+ # on response size.
286
+ #
287
+ # The default value (512 MiB, since +v0.5.7+) is <em>very high</em> and
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.
292
+ #
293
+ # <em>Please Note:</em> this only limits the size per response. It does
294
+ # not prevent a flood of individual responses and it does not limit how
295
+ # many unhandled responses may be stored on the responses hash. See
296
+ # Net::IMAP@Unbounded+memory+use.
297
+ #
298
+ # Socket reads are limited to the maximum remaining bytes for the current
299
+ # response: max_response_size minus the bytes that have already been read.
300
+ # When the limit is reached, or reading a +literal+ _would_ go over the
301
+ # limit, ResponseTooLargeError is raised and the connection is closed.
302
+ #
303
+ # Note that changes will not take effect immediately, because the receiver
304
+ # thread may already be waiting for the next response using the previous
305
+ # value. Net::IMAP#noop can force a response and enforce the new setting
306
+ # immediately.
307
+ #
308
+ # ==== Versioned Defaults
309
+ #
310
+ # Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
311
+ # attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to this
312
+ # config attribute.</em>
313
+ #
314
+ # * original: +nil+ <em>(no limit)</em>
315
+ # * +0.5+: 512 MiB
316
+ attr_accessor :max_response_size, type: Integer?, defaults: {
317
+ 0.0r => nil,
318
+ 0.5r => 512 << 20, # 512 MiB
319
+ }
251
320
 
252
321
  # Controls the behavior of Net::IMAP#responses when called without any
253
322
  # arguments (+type+ or +block+).
@@ -275,9 +344,13 @@ module Net
275
344
  # Raise an ArgumentError with the deprecation warning.
276
345
  #
277
346
  # Note: #responses_without_args is an alias for #responses_without_block.
278
- attr_accessor :responses_without_block, type: [
347
+ attr_accessor :responses_without_block, type: Enum[
279
348
  :silence_deprecation_warning, :warn, :frozen_dup, :raise,
280
- ]
349
+ ], defaults: {
350
+ 0.0r => :silence_deprecation_warning,
351
+ 0.5r => :warn,
352
+ 0.6r => :frozen_dup,
353
+ }
281
354
 
282
355
  alias responses_without_args responses_without_block # :nodoc:
283
356
  alias responses_without_args= responses_without_block= # :nodoc:
@@ -287,66 +360,69 @@ module Net
287
360
  #
288
361
  # Alias for responses_without_block
289
362
 
290
- # Whether ResponseParser should use the deprecated UIDPlusData or
291
- # CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
292
- # AppendUIDData for +APPENDUID+ response codes.
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>
293
366
  #
294
- # UIDPlusData stores its data in arrays of numbers, which is vulnerable to
295
- # a memory exhaustion denial of service attack from an untrusted or
296
- # compromised server. Set this option to +false+ to completely block this
297
- # vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
298
- # mitigates this vulnerability.
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.
299
370
  #
300
- # AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
301
- # UIDPlusData. Most applications should be able to upgrade with little
302
- # or no changes.
371
+ # Parser support for +UIDPLUS+ added in +v0.3.2+.
303
372
  #
304
- # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
373
+ # Config option added in +v0.4.19+ and +v0.5.6+.
305
374
  #
306
- # <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
375
+ # <em>UIDPlusData removed in +v0.6.0+.</em>
307
376
  #
308
- # <em>UIDPlusData will be removed in +v0.6+ and this config setting will
309
- # be ignored.</em>
310
- #
311
- # ==== Valid options
377
+ # ==== Options
312
378
  #
313
379
  # [+true+ <em>(original default)</em>]
314
- # ResponseParser only uses UIDPlusData.
380
+ # <em>Since v0.6.0:</em>
381
+ # Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
315
382
  #
316
383
  # [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
317
- # ResponseParser uses UIDPlusData when the +uid-set+ size is below
318
- # parser_max_deprecated_uidplus_data_size. Above that size,
319
- # ResponseParser uses AppendUIDData or CopyUIDData.
384
+ # <em>Since v0.6.0:</em>
385
+ # Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
320
386
  #
321
- # [+false+ <em>(planned default for +v0.6+)</em>]
322
- # ResponseParser _only_ uses AppendUIDData and CopyUIDData.
323
- attr_accessor :parser_use_deprecated_uidplus_data, type: [
387
+ # [+false+ <em>(default since +v0.6.0+)</em>]
388
+ # This is the only supported option <em>(since v0.6.0)</em>.
389
+ attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
324
390
  true, :up_to_max_size, false
325
- ]
391
+ ], defaults: {
392
+ 0.0r => true,
393
+ 0.5r => :up_to_max_size,
394
+ 0.6r => false,
395
+ }
326
396
 
327
- # The maximum +uid-set+ size that ResponseParser will parse into
328
- # deprecated UIDPlusData. This limit only applies when
329
- # 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>
330
400
  #
331
- # <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.
332
404
  #
333
- # <em>Support for limiting UIDPlusData to a maximum size was added in
334
- # +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
405
+ # Parser support for +UIDPLUS+ added in +v0.3.2+.
335
406
  #
336
- # <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+.
337
409
  #
338
- # ==== Versioned Defaults
410
+ # <em>UIDPlusData was removed in +v0.6.0+.</em>
339
411
  #
340
- # Because this limit guards against a remote server causing catastrophic
341
- # memory exhaustion, the versioned default (used by #load_defaults) also
342
- # applies to versions without the feature.
412
+ # ==== Versioned Defaults
343
413
  #
344
414
  # * +0.3+ and prior: <tt>10,000</tt>
345
415
  # * +0.4+: <tt>1,000</tt>
346
416
  # * +0.5+: <tt>100</tt>
347
417
  # * +0.6+: <tt>0</tt>
348
418
  #
349
- 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
+ }
350
426
 
351
427
  # Creates a new config object and initialize its attribute with +attrs+.
352
428
  #
@@ -415,56 +491,150 @@ module Net
415
491
  # Returns all config attributes in a hash.
416
492
  def to_h; data.members.to_h { [_1, send(_1)] } end
417
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
+
418
570
  protected
419
571
 
572
+ def named_default?
573
+ equal?(Config.default) ||
574
+ AttrVersionDefaults::VERSIONS.any? { equal? Config[_1] }
575
+ end
576
+
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
586
+ end
587
+
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
603
+
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
623
+
624
+ def assigned_attrs_hash(attrs)
625
+ own_attrs = attrs.reject { inherited?(_1) }
626
+ own_attrs.to_h { [_1, data[_1]] }
627
+ end
628
+
420
629
  def defaults_hash
421
630
  to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
422
631
  end
423
632
 
424
- @default = new(
425
- debug: false,
426
- open_timeout: 30,
427
- idle_response_timeout: 5,
428
- sasl_ir: true,
429
- enforce_logindisabled: true,
430
- responses_without_block: :warn,
431
- parser_use_deprecated_uidplus_data: :up_to_max_size,
432
- parser_max_deprecated_uidplus_data_size: 100,
433
- ).freeze
434
-
435
- @global = default.new
436
-
437
- version_defaults[:default] = Config[default.send(:defaults_hash)]
438
- version_defaults[:current] = Config[:default]
439
-
440
- version_defaults[0] = Config[:current].dup.update(
441
- sasl_ir: false,
442
- responses_without_block: :silence_deprecation_warning,
443
- enforce_logindisabled: false,
444
- parser_use_deprecated_uidplus_data: true,
445
- parser_max_deprecated_uidplus_data_size: 10_000,
446
- ).freeze
447
- version_defaults[0.0] = Config[0]
448
- version_defaults[0.1] = Config[0]
449
- version_defaults[0.2] = Config[0]
450
- version_defaults[0.3] = Config[0]
451
-
452
- version_defaults[0.4] = Config[0.3].dup.update(
453
- sasl_ir: true,
454
- parser_max_deprecated_uidplus_data_size: 1000,
455
- ).freeze
456
-
457
- version_defaults[0.5] = Config[:current]
458
-
459
- version_defaults[0.6] = Config[0.5].dup.update(
460
- responses_without_block: :frozen_dup,
461
- parser_use_deprecated_uidplus_data: false,
462
- parser_max_deprecated_uidplus_data_size: 0,
463
- ).freeze
464
- version_defaults[:next] = Config[0.6]
465
- version_defaults[:future] = Config[:next]
466
-
467
- version_defaults.freeze
633
+ Struct = AttrAccessors.struct
634
+ @default = AttrVersionDefaults.compile_default!
635
+ @global = default.new
636
+ AttrVersionDefaults.compile_version_defaults!
637
+
468
638
  end
469
639
  end
470
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
@@ -155,57 +155,106 @@ module Net
155
155
 
156
156
  # Common validators of number and nz_number types
157
157
  module NumValidator # :nodoc
158
+ NUMBER_RE = /\A(?:0|[1-9]\d*)\z/
158
159
  module_function
159
160
 
160
- # Check is passed argument valid 'number' in RFC 3501 terminology
161
+ # Check if argument is a valid 'number' according to RFC 3501
162
+ # number = 1*DIGIT
163
+ # ; Unsigned 32-bit integer
164
+ # ; (0 <= n < 4,294,967,296)
161
165
  def valid_number?(num)
162
- # [RFC 3501]
163
- # number = 1*DIGIT
164
- # ; Unsigned 32-bit integer
165
- # ; (0 <= n < 4,294,967,296)
166
- num >= 0 && num < 4294967296
166
+ 0 <= num && num <= 0xffff_ffff
167
167
  end
168
168
 
169
- # Check is passed argument valid 'nz_number' in RFC 3501 terminology
169
+ # Check if argument is a valid 'nz-number' according to RFC 3501
170
+ # nz-number = digit-nz *DIGIT
171
+ # ; Non-zero unsigned 32-bit integer
172
+ # ; (0 < n < 4,294,967,296)
170
173
  def valid_nz_number?(num)
171
- # [RFC 3501]
172
- # nz-number = digit-nz *DIGIT
173
- # ; Non-zero unsigned 32-bit integer
174
- # ; (0 < n < 4,294,967,296)
175
- num != 0 && valid_number?(num)
174
+ 0 < num && num <= 0xffff_ffff
176
175
  end
177
176
 
178
- # Check is passed argument valid 'mod_sequence_value' in RFC 4551 terminology
177
+ # Check if argument is a valid 'mod-sequence-value' according to RFC 4551
178
+ # mod-sequence-value = 1*DIGIT
179
+ # ; Positive unsigned 64-bit integer
180
+ # ; (mod-sequence)
181
+ # ; (1 <= n < 18,446,744,073,709,551,615)
179
182
  def valid_mod_sequence_value?(num)
180
- # mod-sequence-value = 1*DIGIT
181
- # ; Positive unsigned 64-bit integer
182
- # ; (mod-sequence)
183
- # ; (1 <= n < 18,446,744,073,709,551,615)
184
- num >= 1 && num < 18446744073709551615
183
+ 1 <= num && num < 0xffff_ffff_ffff_ffff
184
+ end
185
+
186
+ # Check if argument is a valid 'mod-sequence-valzer' according to RFC 4551
187
+ # mod-sequence-valzer = "0" / mod-sequence-value
188
+ def valid_mod_sequence_valzer?(num)
189
+ 0 <= num && num < 0xffff_ffff_ffff_ffff
185
190
  end
186
191
 
187
192
  # Ensure argument is 'number' or raise DataFormatError
188
193
  def ensure_number(num)
189
194
  return num if valid_number?(num)
190
-
191
- msg = "number must be unsigned 32-bit integer: #{num}"
192
- raise DataFormatError, msg
195
+ raise DataFormatError,
196
+ "number must be unsigned 32-bit integer: #{num}"
193
197
  end
194
198
 
195
- # Ensure argument is 'nz_number' or raise DataFormatError
199
+ # Ensure argument is 'nz-number' or raise DataFormatError
196
200
  def ensure_nz_number(num)
197
201
  return num if valid_nz_number?(num)
198
-
199
- msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
200
- raise DataFormatError, msg
202
+ raise DataFormatError,
203
+ "nz-number must be non-zero unsigned 32-bit integer: #{num}"
201
204
  end
202
205
 
203
- # Ensure argument is 'mod_sequence_value' or raise DataFormatError
206
+ # Ensure argument is 'mod-sequence-value' or raise DataFormatError
204
207
  def ensure_mod_sequence_value(num)
205
208
  return num if valid_mod_sequence_value?(num)
209
+ raise DataFormatError,
210
+ "mod-sequence-value must be non-zero unsigned 64-bit integer: #{num}"
211
+ end
212
+
213
+ # Ensure argument is 'mod-sequence-valzer' or raise DataFormatError
214
+ def ensure_mod_sequence_valzer(num)
215
+ return num if valid_mod_sequence_valzer?(num)
216
+ raise DataFormatError,
217
+ "mod-sequence-valzer must be unsigned 64-bit integer: #{num}"
218
+ end
219
+
220
+ # Like #ensure_number, but usable with numeric String input.
221
+ def coerce_number(num)
222
+ case num
223
+ when Integer then ensure_number num
224
+ when NUMBER_RE then ensure_number Integer num
225
+ else
226
+ raise DataFormatError, "%p is not a valid number" % [num]
227
+ end
228
+ end
206
229
 
207
- msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
208
- raise DataFormatError, msg
230
+ # Like #ensure_nz_number, but usable with numeric String input.
231
+ def coerce_nz_number(num)
232
+ case num
233
+ when Integer then ensure_nz_number num
234
+ when NUMBER_RE then ensure_nz_number Integer num
235
+ else
236
+ raise DataFormatError, "%p is not a valid nz-number" % [num]
237
+ end
238
+ end
239
+
240
+ # Like #ensure_mod_sequence_value, but usable with numeric String input.
241
+ def coerce_mod_sequence_value(num)
242
+ case num
243
+ when Integer then ensure_mod_sequence_value num
244
+ when NUMBER_RE then ensure_mod_sequence_value Integer num
245
+ else
246
+ raise DataFormatError, "%p is not a valid mod-sequence-value" % [num]
247
+ end
248
+ end
249
+
250
+ # Like #ensure_mod_sequence_valzer, but usable with numeric String input.
251
+ def coerce_mod_sequence_valzer(num)
252
+ case num
253
+ when Integer then ensure_mod_sequence_valzer num
254
+ when NUMBER_RE then ensure_mod_sequence_valzer Integer num
255
+ else
256
+ raise DataFormatError, "%p is not a valid mod-sequence-valzer" % [num]
257
+ end
209
258
  end
210
259
 
211
260
  end