net-imap 0.5.15 → 0.6.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/net/imap/command_data.rb +22 -277
- data/lib/net/imap/config/attr_inheritance.rb +14 -1
- data/lib/net/imap/config/attr_version_defaults.rb +1 -1
- data/lib/net/imap/config.rb +175 -35
- data/lib/net/imap/connection_state.rb +1 -1
- data/lib/net/imap/data_encoding.rb +77 -28
- data/lib/net/imap/esearch_result.rb +6 -0
- data/lib/net/imap/response_data.rb +5 -23
- data/lib/net/imap/response_parser.rb +20 -33
- data/lib/net/imap/response_reader.rb +5 -24
- data/lib/net/imap/sasl/scram_authenticator.rb +0 -74
- data/lib/net/imap/search_result.rb +6 -0
- data/lib/net/imap/sequence_set.rb +622 -327
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap.rb +63 -161
- data/net-imap.gemspec +1 -1
- metadata +3 -4
- data/lib/net/imap/data_lite.rb +0 -226
data/lib/net/imap/config.rb
CHANGED
|
@@ -234,13 +234,26 @@ module Net
|
|
|
234
234
|
# Do not use +SASL-IR+, even when it is supported by the server and the
|
|
235
235
|
# mechanism.
|
|
236
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
|
+
#
|
|
237
244
|
# [+true+ <em>(default since +v0.4+)</em>]
|
|
238
245
|
# Use +SASL-IR+ when it is supported by the server and the mechanism.
|
|
239
|
-
attr_accessor :sasl_ir, type:
|
|
246
|
+
attr_accessor :sasl_ir, type: Enum[
|
|
247
|
+
false, :when_capabilities_cached, true
|
|
248
|
+
], defaults: {
|
|
240
249
|
0.0r => false,
|
|
241
250
|
0.4r => true,
|
|
242
251
|
}
|
|
243
252
|
|
|
253
|
+
# :stopdoc:
|
|
254
|
+
alias sasl_ir? sasl_ir
|
|
255
|
+
# :startdoc:
|
|
256
|
+
|
|
244
257
|
# Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
|
|
245
258
|
# capability is present. When enforced, Net::IMAP will raise a
|
|
246
259
|
# LoginDisabledError when that capability is present.
|
|
@@ -347,39 +360,32 @@ module Net
|
|
|
347
360
|
#
|
|
348
361
|
# Alias for responses_without_block
|
|
349
362
|
|
|
350
|
-
#
|
|
351
|
-
#
|
|
352
|
-
#
|
|
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>
|
|
353
366
|
#
|
|
354
|
-
#
|
|
355
|
-
#
|
|
356
|
-
#
|
|
357
|
-
# vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
|
|
358
|
-
# 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.
|
|
359
370
|
#
|
|
360
|
-
#
|
|
361
|
-
# UIDPlusData. Most applications should be able to upgrade with little
|
|
362
|
-
# or no changes.
|
|
371
|
+
# Parser support for +UIDPLUS+ added in +v0.3.2+.
|
|
363
372
|
#
|
|
364
|
-
#
|
|
373
|
+
# Config option added in +v0.4.19+ and +v0.5.6+.
|
|
365
374
|
#
|
|
366
|
-
# <em>
|
|
375
|
+
# <em>UIDPlusData removed in +v0.6.0+.</em>
|
|
367
376
|
#
|
|
368
|
-
#
|
|
369
|
-
# be ignored.</em>
|
|
370
|
-
#
|
|
371
|
-
# ==== Valid options
|
|
377
|
+
# ==== Options
|
|
372
378
|
#
|
|
373
379
|
# [+true+ <em>(original default)</em>]
|
|
374
|
-
#
|
|
380
|
+
# <em>Since v0.6.0:</em>
|
|
381
|
+
# Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
|
|
375
382
|
#
|
|
376
383
|
# [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
|
|
377
|
-
#
|
|
378
|
-
#
|
|
379
|
-
# ResponseParser uses AppendUIDData or CopyUIDData.
|
|
384
|
+
# <em>Since v0.6.0:</em>
|
|
385
|
+
# Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
|
|
380
386
|
#
|
|
381
|
-
# [+false+ <em>(
|
|
382
|
-
#
|
|
387
|
+
# [+false+ <em>(default since +v0.6.0+)</em>]
|
|
388
|
+
# This is the only supported option <em>(since v0.6.0)</em>.
|
|
383
389
|
attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
|
|
384
390
|
true, :up_to_max_size, false
|
|
385
391
|
], defaults: {
|
|
@@ -388,22 +394,22 @@ module Net
|
|
|
388
394
|
0.6r => false,
|
|
389
395
|
}
|
|
390
396
|
|
|
391
|
-
#
|
|
392
|
-
#
|
|
393
|
-
#
|
|
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>
|
|
394
400
|
#
|
|
395
|
-
#
|
|
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.
|
|
396
404
|
#
|
|
397
|
-
#
|
|
398
|
-
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
|
|
405
|
+
# Parser support for +UIDPLUS+ added in +v0.3.2+.
|
|
399
406
|
#
|
|
400
|
-
#
|
|
407
|
+
# Support for limiting UIDPlusData to a maximum size was added in
|
|
408
|
+
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.
|
|
401
409
|
#
|
|
402
|
-
#
|
|
410
|
+
# <em>UIDPlusData was removed in +v0.6.0+.</em>
|
|
403
411
|
#
|
|
404
|
-
#
|
|
405
|
-
# memory exhaustion, the versioned default (used by #load_defaults) also
|
|
406
|
-
# applies to versions without the feature.
|
|
412
|
+
# ==== Versioned Defaults
|
|
407
413
|
#
|
|
408
414
|
# * +0.3+ and prior: <tt>10,000</tt>
|
|
409
415
|
# * +0.4+: <tt>1,000</tt>
|
|
@@ -485,8 +491,142 @@ module Net
|
|
|
485
491
|
# Returns all config attributes in a hash.
|
|
486
492
|
def to_h; data.members.to_h { [_1, send(_1)] } end
|
|
487
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
|
+
|
|
488
570
|
protected
|
|
489
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 equal? Config[:default] then "#{Config}[:default]"
|
|
582
|
+
elsif (v = AttrVersionDefaults::VERSIONS.find { equal? Config[_1] })
|
|
583
|
+
"%s[%0.1f]" % [Config, v]
|
|
584
|
+
else
|
|
585
|
+
Kernel.instance_method(:to_s).bind_call(self).delete("<#>")
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def inspect_recursive(attrs = AttrAccessors.struct.members)
|
|
590
|
+
strings = [name]
|
|
591
|
+
assigned = assigned_attrs_hash(attrs)
|
|
592
|
+
strings.concat assigned.map { "%s=%p" % _1 }
|
|
593
|
+
if parent
|
|
594
|
+
if parent.equal?(Config.default)
|
|
595
|
+
inherited_overrides = []
|
|
596
|
+
elsif parent
|
|
597
|
+
inherited_overrides = attrs - assigned.keys
|
|
598
|
+
inherited_overrides &= DEFAULT_TO_INHERIT if parent.named_default?
|
|
599
|
+
end
|
|
600
|
+
strings << "inherits from #{parent.inspect_recursive(inherited_overrides)}"
|
|
601
|
+
end
|
|
602
|
+
strings.join " "
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def pretty_print_recursive(pp, attrs = AttrAccessors.struct.members)
|
|
606
|
+
pp.text name
|
|
607
|
+
assigned = assigned_attrs_hash(attrs)
|
|
608
|
+
pp.breakable
|
|
609
|
+
pp.seplist(assigned, ->{pp.breakable}) do |key, val|
|
|
610
|
+
pp.text key.to_s
|
|
611
|
+
pp.text "="
|
|
612
|
+
pp.pp val
|
|
613
|
+
end
|
|
614
|
+
if parent
|
|
615
|
+
pp.breakable if assigned.any?
|
|
616
|
+
pp.nest(2) do
|
|
617
|
+
pp.text "inherits from "
|
|
618
|
+
parent.pretty_print_recursive(pp, attrs - assigned.keys)
|
|
619
|
+
end
|
|
620
|
+
elsif assigned.empty?
|
|
621
|
+
pp.text "(overridden)"
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def assigned_attrs_hash(attrs)
|
|
626
|
+
own_attrs = attrs.reject { inherited?(_1) }
|
|
627
|
+
own_attrs.to_h { [_1, data[_1]] }
|
|
628
|
+
end
|
|
629
|
+
|
|
490
630
|
def defaults_hash
|
|
491
631
|
to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
|
|
492
632
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
208
|
-
|
|
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
|
|
@@ -25,6 +25,12 @@ module Net
|
|
|
25
25
|
# Some search extensions may result in the server sending ESearchResult
|
|
26
26
|
# responses after the initiating command has completed. Use
|
|
27
27
|
# IMAP#add_response_handler to handle these responses.
|
|
28
|
+
#
|
|
29
|
+
# ==== Compatibility with SearchResult
|
|
30
|
+
#
|
|
31
|
+
# Note that both SearchResult and ESearchResult implement +each+, +to_a+,
|
|
32
|
+
# and +to_sequence_set+. These methods can be used regardless of whether
|
|
33
|
+
# the server returns +SEARCH+ or +ESEARCH+ data (or no data).
|
|
28
34
|
class ESearchResult < Data.define(:tag, :uid, :data)
|
|
29
35
|
def initialize(tag: nil, uid: nil, data: nil)
|
|
30
36
|
tag => String | nil; tag = -tag if tag
|
|
@@ -6,7 +6,6 @@ module Net
|
|
|
6
6
|
autoload :FetchData, "#{__dir__}/fetch_data"
|
|
7
7
|
autoload :UIDFetchData, "#{__dir__}/fetch_data"
|
|
8
8
|
autoload :SearchResult, "#{__dir__}/search_result"
|
|
9
|
-
autoload :UIDPlusData, "#{__dir__}/uidplus_data"
|
|
10
9
|
autoload :AppendUIDData, "#{__dir__}/uidplus_data"
|
|
11
10
|
autoload :CopyUIDData, "#{__dir__}/uidplus_data"
|
|
12
11
|
autoload :VanishedData, "#{__dir__}/vanished_data"
|
|
@@ -260,8 +259,8 @@ module Net
|
|
|
260
259
|
#
|
|
261
260
|
# === +UIDPLUS+ extension
|
|
262
261
|
# See {[RFC4315 §3]}[https://www.rfc-editor.org/rfc/rfc4315#section-3].
|
|
263
|
-
# * +APPENDUID+, #data is
|
|
264
|
-
# * +COPYUID+, #data is
|
|
262
|
+
# * +APPENDUID+, #data is AppendUIDData. See IMAP#append.
|
|
263
|
+
# * +COPYUID+, #data is CopyUIDData. See IMAP#copy.
|
|
265
264
|
# * +UIDNOTSTICKY+, #data is +nil+. See IMAP#select.
|
|
266
265
|
#
|
|
267
266
|
# === +SEARCHRES+ extension
|
|
@@ -307,14 +306,6 @@ module Net
|
|
|
307
306
|
# because the server doesn't allow deletion of mailboxes with children.
|
|
308
307
|
# #data is +nil+.
|
|
309
308
|
#
|
|
310
|
-
# === <tt>QUOTA=RES-*</tt> response codes
|
|
311
|
-
# See {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208.html#section-4.3].
|
|
312
|
-
# * +OVERQUOTA+ (also in RFC5530[https://www.rfc-editor.org/rfc/rfc5530]),
|
|
313
|
-
# with a tagged +NO+ response to an +APPEND+/+COPY+/+MOVE+ command when
|
|
314
|
-
# the command would put the target mailbox over any quota, and with an
|
|
315
|
-
# untagged +NO+ when a mailbox exceeds a soft quota (which may be caused
|
|
316
|
-
# be external events). #data is +nil+.
|
|
317
|
-
#
|
|
318
309
|
# === +CONDSTORE+ extension
|
|
319
310
|
# See {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
|
320
311
|
# * +NOMODSEQ+, when selecting a mailbox that does not support
|
|
@@ -392,23 +383,14 @@ module Net
|
|
|
392
383
|
# and MailboxQuota objects.
|
|
393
384
|
#
|
|
394
385
|
# == Required capability
|
|
395
|
-
#
|
|
396
386
|
# Requires +QUOTA+ [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]]
|
|
397
|
-
#
|
|
398
|
-
# [RFC9208[https://www.rfc-editor.org/rfc/rfc9208]] capability.
|
|
387
|
+
# capability.
|
|
399
388
|
class MailboxQuota < Struct.new(:mailbox, :usage, :quota)
|
|
400
389
|
##
|
|
401
390
|
# method: mailbox
|
|
402
391
|
# :call-seq: mailbox -> string
|
|
403
392
|
#
|
|
404
|
-
# The
|
|
405
|
-
#
|
|
406
|
-
# NOTE: this was mistakenly named "mailbox". But the quota root's name may
|
|
407
|
-
# differ from the mailbox. A single quota root may cover multiple
|
|
408
|
-
# mailboxes, and a single mailbox may be governed by multiple quota roots.
|
|
409
|
-
|
|
410
|
-
# The quota root with the associated quota.
|
|
411
|
-
alias quota_root mailbox
|
|
393
|
+
# The mailbox with the associated quota.
|
|
412
394
|
|
|
413
395
|
##
|
|
414
396
|
# method: usage
|
|
@@ -420,7 +402,7 @@ module Net
|
|
|
420
402
|
# method: quota
|
|
421
403
|
# :call-seq: quota -> Integer
|
|
422
404
|
#
|
|
423
|
-
#
|
|
405
|
+
# Quota limit imposed on the mailbox.
|
|
424
406
|
#
|
|
425
407
|
end
|
|
426
408
|
|
|
@@ -2017,24 +2017,19 @@ module Net
|
|
|
2017
2017
|
CopyUID(validity, src_uids, dst_uids)
|
|
2018
2018
|
end
|
|
2019
2019
|
|
|
2020
|
-
def AppendUID(...) DeprecatedUIDPlus(...) || AppendUIDData.new(...) end
|
|
2021
|
-
def CopyUID(...) DeprecatedUIDPlus(...) || CopyUIDData.new(...) end
|
|
2022
|
-
|
|
2023
2020
|
# TODO: remove this code in the v0.6.0 release
|
|
2024
2021
|
def DeprecatedUIDPlus(validity, src_uids = nil, dst_uids)
|
|
2025
2022
|
return unless config.parser_use_deprecated_uidplus_data
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
dst_uids = dst_uids.each_ordered_number.to_a
|
|
2032
|
-
UIDPlusData.new(validity, src_uids, dst_uids)
|
|
2033
|
-
elsif config.parser_use_deprecated_uidplus_data != :up_to_max_size
|
|
2034
|
-
parse_error("uid-set is too large: %d > %d", count, max)
|
|
2035
|
-
end
|
|
2023
|
+
warn("#{Config}#parser_use_deprecated_uidplus_data is ignored " \
|
|
2024
|
+
"since v0.6.0. Disable this warning by setting " \
|
|
2025
|
+
"config.parser_use_deprecated_uidplus_data = false.",
|
|
2026
|
+
category: :deprecated, uplevel: 9)
|
|
2027
|
+
nil
|
|
2036
2028
|
end
|
|
2037
2029
|
|
|
2030
|
+
def AppendUID(...) DeprecatedUIDPlus(...) || AppendUIDData.new(...) end
|
|
2031
|
+
def CopyUID(...) DeprecatedUIDPlus(...) || CopyUIDData.new(...) end
|
|
2032
|
+
|
|
2038
2033
|
ADDRESS_REGEXP = /\G
|
|
2039
2034
|
\( (?: NIL | #{Patterns::QUOTED_rev2} ) # 1: NAME
|
|
2040
2035
|
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 2: ROUTE
|
|
@@ -2189,7 +2184,10 @@ module Net
|
|
|
2189
2184
|
if $1
|
|
2190
2185
|
return Token.new(T_SPACE, $+)
|
|
2191
2186
|
elsif $2
|
|
2192
|
-
|
|
2187
|
+
len = $+.to_i
|
|
2188
|
+
val = @str[@pos, len]
|
|
2189
|
+
@pos += len
|
|
2190
|
+
return Token.new(T_LITERAL8, val)
|
|
2193
2191
|
elsif $3 && $7
|
|
2194
2192
|
# greedily match ATOM, prefixed with NUMBER, NIL, or PLUS.
|
|
2195
2193
|
return Token.new(T_ATOM, $3)
|
|
@@ -2217,7 +2215,10 @@ module Net
|
|
|
2217
2215
|
elsif $15
|
|
2218
2216
|
return Token.new(T_RBRA, $+)
|
|
2219
2217
|
elsif $16
|
|
2220
|
-
|
|
2218
|
+
len = $+.to_i
|
|
2219
|
+
val = @str[@pos, len]
|
|
2220
|
+
@pos += len
|
|
2221
|
+
return Token.new(T_LITERAL, val)
|
|
2221
2222
|
elsif $17
|
|
2222
2223
|
return Token.new(T_PERCENT, $+)
|
|
2223
2224
|
elsif $18
|
|
@@ -2243,7 +2244,10 @@ module Net
|
|
|
2243
2244
|
elsif $4
|
|
2244
2245
|
return Token.new(T_QUOTED, Patterns.unescape_quoted($+))
|
|
2245
2246
|
elsif $5
|
|
2246
|
-
|
|
2247
|
+
len = $+.to_i
|
|
2248
|
+
val = @str[@pos, len]
|
|
2249
|
+
@pos += len
|
|
2250
|
+
return Token.new(T_LITERAL, val)
|
|
2247
2251
|
elsif $6
|
|
2248
2252
|
return Token.new(T_LPAR, $+)
|
|
2249
2253
|
elsif $7
|
|
@@ -2258,23 +2262,6 @@ module Net
|
|
|
2258
2262
|
else
|
|
2259
2263
|
parse_error("invalid @lex_state - %s", @lex_state.inspect)
|
|
2260
2264
|
end
|
|
2261
|
-
rescue DataFormatError => error
|
|
2262
|
-
parse_error error.message
|
|
2263
|
-
end
|
|
2264
|
-
|
|
2265
|
-
def literal_token(len, type = T_LITERAL)
|
|
2266
|
-
len = coerce_number64 len.to_i
|
|
2267
|
-
val = @str[@pos, len]
|
|
2268
|
-
@pos += len
|
|
2269
|
-
Token.new(type, val)
|
|
2270
|
-
end
|
|
2271
|
-
|
|
2272
|
-
# copied/adapted from NumValidator in v0.6
|
|
2273
|
-
def coerce_number64(num)
|
|
2274
|
-
int = num.to_i
|
|
2275
|
-
return int if 0 <= int && int <= 0x7fff_ffff_ffff_ffff
|
|
2276
|
-
raise DataFormatError,
|
|
2277
|
-
"number64 must be unsigned 63-bit integer: #{num}"
|
|
2278
2265
|
end
|
|
2279
2266
|
|
|
2280
2267
|
end
|
|
@@ -4,13 +4,10 @@ module Net
|
|
|
4
4
|
class IMAP
|
|
5
5
|
# See https://www.rfc-editor.org/rfc/rfc9051#section-2.2.2
|
|
6
6
|
class ResponseReader # :nodoc:
|
|
7
|
-
include NumValidator
|
|
8
|
-
|
|
9
7
|
attr_reader :client
|
|
10
8
|
|
|
11
9
|
def initialize(client, sock)
|
|
12
10
|
@client, @sock = client, sock
|
|
13
|
-
@buff = @literal_size = nil
|
|
14
11
|
end
|
|
15
12
|
|
|
16
13
|
def read_response_buffer
|
|
@@ -18,13 +15,13 @@ module Net
|
|
|
18
15
|
catch :eof do
|
|
19
16
|
while true
|
|
20
17
|
read_line
|
|
21
|
-
break unless literal_size
|
|
18
|
+
break unless (@literal_size = get_literal_size)
|
|
22
19
|
read_literal
|
|
23
20
|
end
|
|
24
21
|
end
|
|
25
22
|
buff
|
|
26
23
|
ensure
|
|
27
|
-
@buff =
|
|
24
|
+
@buff = nil
|
|
28
25
|
end
|
|
29
26
|
|
|
30
27
|
private
|
|
@@ -33,21 +30,13 @@ module Net
|
|
|
33
30
|
|
|
34
31
|
def bytes_read = buff.bytesize
|
|
35
32
|
def empty? = buff.empty?
|
|
36
|
-
def done? = line_done? && !
|
|
33
|
+
def done? = line_done? && !get_literal_size
|
|
37
34
|
def line_done? = buff.end_with?(CRLF)
|
|
38
|
-
|
|
39
|
-
def get_literal_size(buff)
|
|
40
|
-
buff.end_with?("}\r\n") && buff.rindex(/\{(\d+)\}\r\n\z/n) &&
|
|
41
|
-
coerce_number64($1)
|
|
42
|
-
rescue DataFormatError
|
|
43
|
-
raise DataFormatError, format("invalid response literal size (%s)", $1)
|
|
44
|
-
end
|
|
35
|
+
def get_literal_size = /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i
|
|
45
36
|
|
|
46
37
|
def read_line
|
|
47
|
-
|
|
48
|
-
buff << line
|
|
38
|
+
buff << (@sock.gets(CRLF, read_limit) or throw :eof)
|
|
49
39
|
max_response_remaining! unless line_done?
|
|
50
|
-
@literal_size = get_literal_size(line)
|
|
51
40
|
end
|
|
52
41
|
|
|
53
42
|
def read_literal
|
|
@@ -79,14 +68,6 @@ module Net
|
|
|
79
68
|
)
|
|
80
69
|
end
|
|
81
70
|
|
|
82
|
-
# copied/adapted from NumValidator in v0.6
|
|
83
|
-
def coerce_number64(num)
|
|
84
|
-
int = num.to_i
|
|
85
|
-
return int if 0 <= int && int <= 0x7fff_ffff_ffff_ffff
|
|
86
|
-
raise DataFormatError,
|
|
87
|
-
"number64 must be unsigned 63-bit integer: #{num}"
|
|
88
|
-
end
|
|
89
|
-
|
|
90
71
|
end
|
|
91
72
|
end
|
|
92
73
|
end
|