net-imap 0.4.14 → 0.4.21
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/lib/net/imap/command_data.rb +2 -2
- data/lib/net/imap/config/attr_type_coercion.rb +25 -21
- data/lib/net/imap/config.rb +228 -43
- data/lib/net/imap/errors.rb +33 -0
- data/lib/net/imap/response_data.rb +3 -54
- data/lib/net/imap/response_parser.rb +30 -14
- data/lib/net/imap/response_reader.rb +75 -0
- data/lib/net/imap/sequence_set.rb +274 -123
- data/lib/net/imap/uidplus_data.rb +326 -0
- data/lib/net/imap.rb +220 -59
- metadata +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab1b84ea6eac7b0d57793127bc365c79b63eb9564386427eb66cca91cf5df294
|
4
|
+
data.tar.gz: 3040f462d83f7a364fed076a730d5a9ba0512ea52ab0368765befb0e2d700dd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7565d3002323cdaad204bbd5eceb0b64d265e61daa41d9e3c61b8ea12ff3bbbb5f5e62298138f48a96c32553b1fcf3c6dcab7cae5776a22a521abe77b6e153c
|
7
|
+
data.tar.gz: 73b9338a55d33e703c86217ee80ee7486e92dbf67b232c152d4115a89a76c4f384c39e34888d24ec8cf389af1d67ca1d1d84e3edc676ecc501495cf6b66d7dfe
|
@@ -40,10 +40,10 @@ module Net
|
|
40
40
|
send_number_data(data)
|
41
41
|
when Array
|
42
42
|
send_list_data(data, tag)
|
43
|
-
when Date
|
44
|
-
send_date_data(data)
|
45
43
|
when Time, DateTime
|
46
44
|
send_time_data(data)
|
45
|
+
when Date
|
46
|
+
send_date_data(data)
|
47
47
|
when Symbol
|
48
48
|
send_symbol_data(data)
|
49
49
|
else
|
@@ -18,6 +18,8 @@ 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
|
21
23
|
end
|
22
24
|
private_constant :Macros
|
23
25
|
|
@@ -26,34 +28,36 @@ module Net
|
|
26
28
|
end
|
27
29
|
private_class_method :included
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
elsif Array === type then enum attr, type
|
34
|
-
else raise ArgumentError, "unknown type coercion %p" % [type]
|
35
|
-
end
|
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
|
36
35
|
end
|
36
|
+
private_class_method :safe
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
Types = Hash.new do |h, type|
|
39
|
+
type.nil? || Proc === type or raise TypeError, "type not nil or Proc"
|
40
|
+
safe{type}
|
41
41
|
end
|
42
|
+
Types[:boolean] = Boolean = safe{-> {!!_1}}
|
43
|
+
Types[Integer] = safe{->{Integer(_1)}}
|
42
44
|
|
43
|
-
def self.
|
44
|
-
|
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
|
45
49
|
end
|
46
50
|
|
47
|
-
|
48
|
-
|
51
|
+
NilOrInteger = safe{->val { Integer val unless val.nil? }}
|
52
|
+
|
53
|
+
Enum = ->(*enum) {
|
54
|
+
enum = safe{enum}
|
49
55
|
expected = -"one of #{enum.map(&:inspect).join(", ")}"
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
56
|
-
end
|
56
|
+
safe{->val {
|
57
|
+
return val if enum.include?(val)
|
58
|
+
raise ArgumentError, "expected %s, got %p" % [expected, val]
|
59
|
+
}}
|
60
|
+
}
|
57
61
|
|
58
62
|
end
|
59
63
|
end
|
data/lib/net/imap/config.rb
CHANGED
@@ -7,10 +7,10 @@ require_relative "config/attr_type_coercion"
|
|
7
7
|
module Net
|
8
8
|
class IMAP
|
9
9
|
|
10
|
-
# Net::IMAP::Config
|
11
|
-
#
|
12
|
-
# Net::IMAP::Config.global, and the
|
13
|
-
# seen at Net::IMAP#config.
|
10
|
+
# Net::IMAP::Config <em>(available since +v0.4.13+)</em> stores
|
11
|
+
# configuration options for Net::IMAP clients. The global configuration can
|
12
|
+
# be seen at either Net::IMAP.config or Net::IMAP::Config.global, and the
|
13
|
+
# client-specific configuration can be seen at Net::IMAP#config.
|
14
14
|
#
|
15
15
|
# When creating a new client, all unhandled keyword arguments to
|
16
16
|
# Net::IMAP.new are delegated to Config.new. Every client has its own
|
@@ -128,9 +128,26 @@ module Net
|
|
128
128
|
# The global config object. Also available from Net::IMAP.config.
|
129
129
|
def self.global; @global if defined?(@global) end
|
130
130
|
|
131
|
-
# A hash of hard-coded configurations, indexed by version number.
|
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
|
132
142
|
def self.version_defaults; @version_defaults end
|
133
|
-
@version_defaults = {
|
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
|
+
}
|
134
151
|
|
135
152
|
# :call-seq:
|
136
153
|
# Net::IMAP::Config[number] -> versioned config
|
@@ -153,18 +170,17 @@ module Net
|
|
153
170
|
elsif config.nil? && global.nil? then nil
|
154
171
|
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
|
155
172
|
else
|
156
|
-
version_defaults
|
173
|
+
version_defaults[config] or
|
157
174
|
case config
|
158
175
|
when Numeric
|
159
176
|
raise RangeError, "unknown config version: %p" % [config]
|
160
|
-
when Symbol
|
177
|
+
when String, Symbol
|
161
178
|
raise KeyError, "unknown config name: %p" % [config]
|
162
179
|
else
|
163
180
|
raise TypeError, "no implicit conversion of %s to %s" % [
|
164
181
|
config.class, Config
|
165
182
|
]
|
166
183
|
end
|
167
|
-
end
|
168
184
|
end
|
169
185
|
end
|
170
186
|
|
@@ -172,9 +188,16 @@ module Net
|
|
172
188
|
include AttrInheritance
|
173
189
|
include AttrTypeCoercion
|
174
190
|
|
175
|
-
# The debug mode (boolean)
|
191
|
+
# The debug mode (boolean). The default value is +false+.
|
176
192
|
#
|
177
|
-
#
|
193
|
+
# When #debug is +true+:
|
194
|
+
# * Data sent to and received from the server will be logged.
|
195
|
+
# * ResponseParser will print warnings with extra detail for parse
|
196
|
+
# errors. _This may include recoverable errors._
|
197
|
+
# * ResponseParser makes extra assertions.
|
198
|
+
#
|
199
|
+
# *NOTE:* Versioned default configs inherit #debug from Config.global, and
|
200
|
+
# #load_defaults will not override #debug.
|
178
201
|
attr_accessor :debug, type: :boolean
|
179
202
|
|
180
203
|
# method: debug?
|
@@ -184,10 +207,13 @@ module Net
|
|
184
207
|
|
185
208
|
# Seconds to wait until a connection is opened.
|
186
209
|
#
|
210
|
+
# Applied separately for establishing TCP connection and starting a TLS
|
211
|
+
# connection.
|
212
|
+
#
|
187
213
|
# If the IMAP object cannot open a connection within this time,
|
188
214
|
# it raises a Net::OpenTimeout exception.
|
189
215
|
#
|
190
|
-
# See Net::IMAP.new.
|
216
|
+
# See Net::IMAP.new and Net::IMAP#starttls.
|
191
217
|
#
|
192
218
|
# The default value is +30+ seconds.
|
193
219
|
attr_accessor :open_timeout, type: Integer
|
@@ -200,34 +226,156 @@ module Net
|
|
200
226
|
# The default value is +5+ seconds.
|
201
227
|
attr_accessor :idle_response_timeout, type: Integer
|
202
228
|
|
203
|
-
# :markup: markdown
|
204
|
-
#
|
205
229
|
# Whether to use the +SASL-IR+ extension when the server and \SASL
|
206
|
-
# mechanism both support it.
|
230
|
+
# mechanism both support it. Can be overridden by the +sasl_ir+ keyword
|
231
|
+
# parameter to Net::IMAP#authenticate.
|
232
|
+
#
|
233
|
+
# <em>(Support for +SASL-IR+ was added in +v0.4.0+.)</em>
|
234
|
+
#
|
235
|
+
# ==== Valid options
|
207
236
|
#
|
208
|
-
#
|
237
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
238
|
+
# Do not use +SASL-IR+, even when it is supported by the server and the
|
239
|
+
# mechanism.
|
209
240
|
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
# | _original_ | +false+ <em>(extension unsupported)</em> |
|
213
|
-
# | v0.4 | +true+ <em>(support added)</em> |
|
241
|
+
# [+true+ <em>(default since +v0.4+)</em>]
|
242
|
+
# Use +SASL-IR+ when it is supported by the server and the mechanism.
|
214
243
|
attr_accessor :sasl_ir, type: :boolean
|
215
244
|
|
216
|
-
#
|
245
|
+
# The maximum allowed server response size. When +nil+, there is no limit
|
246
|
+
# on response size.
|
247
|
+
#
|
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).
|
253
|
+
#
|
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.
|
264
|
+
#
|
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.
|
269
|
+
#
|
270
|
+
# ==== Versioned Defaults
|
271
|
+
#
|
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>
|
275
|
+
#
|
276
|
+
# * original: +nil+ <em>(no limit)</em>
|
277
|
+
# * +0.5+: 512 MiB
|
278
|
+
attr_accessor :max_response_size, type: Integer?
|
279
|
+
|
280
|
+
# Controls the behavior of Net::IMAP#responses when called without any
|
281
|
+
# arguments (+type+ or +block+).
|
282
|
+
#
|
283
|
+
# ==== Valid options
|
284
|
+
#
|
285
|
+
# [+:silence_deprecation_warning+ <em>(original behavior)</em>]
|
286
|
+
# Returns the mutable responses hash (without any warnings).
|
287
|
+
# <em>This is not thread-safe.</em>
|
288
|
+
#
|
289
|
+
# [+:warn+ <em>(default since +v0.5+)</em>]
|
290
|
+
# Prints a warning and returns the mutable responses hash.
|
291
|
+
# <em>This is not thread-safe.</em>
|
292
|
+
#
|
293
|
+
# [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
|
294
|
+
# Returns a frozen copy of the unhandled responses hash, with frozen
|
295
|
+
# array values.
|
296
|
+
#
|
297
|
+
# Note that calling IMAP#responses with a +type+ and without a block is
|
298
|
+
# not configurable and always behaves like +:frozen_dup+.
|
299
|
+
#
|
300
|
+
# <em>(+:frozen_dup+ config option was added in +v0.4.17+)</em>
|
301
|
+
#
|
302
|
+
# [+:raise+]
|
303
|
+
# Raise an ArgumentError with the deprecation warning.
|
304
|
+
#
|
305
|
+
# Note: #responses_without_args is an alias for #responses_without_block.
|
306
|
+
attr_accessor :responses_without_block, type: Enum[
|
307
|
+
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
|
308
|
+
]
|
309
|
+
|
310
|
+
alias responses_without_args responses_without_block # :nodoc:
|
311
|
+
alias responses_without_args= responses_without_block= # :nodoc:
|
312
|
+
|
313
|
+
##
|
314
|
+
# :attr_accessor: responses_without_args
|
315
|
+
#
|
316
|
+
# Alias for responses_without_block
|
317
|
+
|
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
|
217
340
|
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
# `:silence_deprecation_warning`.
|
341
|
+
# [+true+ <em>(original default)</em>]
|
342
|
+
# ResponseParser only uses UIDPlusData.
|
221
343
|
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
|
228
|
-
|
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
|
229
353
|
]
|
230
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
|
+
|
231
379
|
# Creates a new config object and initialize its attribute with +attrs+.
|
232
380
|
#
|
233
381
|
# If +parent+ is not given, the global config is used by default.
|
@@ -306,34 +454,71 @@ module Net
|
|
306
454
|
open_timeout: 30,
|
307
455
|
idle_response_timeout: 5,
|
308
456
|
sasl_ir: true,
|
457
|
+
max_response_size: nil,
|
309
458
|
responses_without_block: :silence_deprecation_warning,
|
459
|
+
parser_use_deprecated_uidplus_data: true,
|
460
|
+
parser_max_deprecated_uidplus_data_size: 1000,
|
310
461
|
).freeze
|
311
462
|
|
312
463
|
@global = default.new
|
313
464
|
|
314
|
-
version_defaults[
|
465
|
+
version_defaults[:default] = Config[default.send(:defaults_hash)]
|
315
466
|
|
316
|
-
version_defaults[
|
467
|
+
version_defaults[0r] = Config[:default].dup.update(
|
317
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,
|
318
472
|
).freeze
|
319
|
-
version_defaults[0.
|
320
|
-
version_defaults[0.
|
321
|
-
version_defaults[0.
|
322
|
-
version_defaults[0.
|
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]
|
323
477
|
|
324
|
-
version_defaults[0.
|
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
|
325
485
|
responses_without_block: :warn,
|
486
|
+
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
487
|
+
parser_max_deprecated_uidplus_data_size: 100,
|
326
488
|
).freeze
|
327
489
|
|
328
|
-
version_defaults[
|
329
|
-
|
330
|
-
|
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
|
331
495
|
|
332
|
-
version_defaults[
|
333
|
-
responses_without_block: :raise,
|
496
|
+
version_defaults[0.7r] = Config[0.6r].dup.update(
|
334
497
|
).freeze
|
335
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]
|
513
|
+
|
336
514
|
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
|
337
522
|
end
|
338
523
|
end
|
339
524
|
end
|
data/lib/net/imap/errors.rb
CHANGED
@@ -11,6 +11,39 @@ module Net
|
|
11
11
|
class DataFormatError < Error
|
12
12
|
end
|
13
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)
|
36
|
+
end
|
37
|
+
|
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
|
45
|
+
end
|
46
|
+
|
14
47
|
# Error raised when a response from the server is non-parsable.
|
15
48
|
class ResponseParseError < Error
|
16
49
|
end
|
@@ -5,6 +5,9 @@ module Net
|
|
5
5
|
autoload :FetchData, "#{__dir__}/fetch_data"
|
6
6
|
autoload :SearchResult, "#{__dir__}/search_result"
|
7
7
|
autoload :SequenceSet, "#{__dir__}/sequence_set"
|
8
|
+
autoload :UIDPlusData, "#{__dir__}/uidplus_data"
|
9
|
+
autoload :AppendUIDData, "#{__dir__}/uidplus_data"
|
10
|
+
autoload :CopyUIDData, "#{__dir__}/uidplus_data"
|
8
11
|
|
9
12
|
# Net::IMAP::ContinuationRequest represents command continuation requests.
|
10
13
|
#
|
@@ -324,60 +327,6 @@ module Net
|
|
324
327
|
# code data can take.
|
325
328
|
end
|
326
329
|
|
327
|
-
# Net::IMAP::UIDPlusData represents the ResponseCode#data that accompanies
|
328
|
-
# the +APPENDUID+ and +COPYUID+ response codes.
|
329
|
-
#
|
330
|
-
# See [[UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
331
|
-
#
|
332
|
-
# ==== Capability requirement
|
333
|
-
#
|
334
|
-
# The +UIDPLUS+ capability[rdoc-ref:Net::IMAP#capability] must be supported.
|
335
|
-
# A server that supports +UIDPLUS+ should send a UIDPlusData object inside
|
336
|
-
# every TaggedResponse returned by the append[rdoc-ref:Net::IMAP#append],
|
337
|
-
# copy[rdoc-ref:Net::IMAP#copy], move[rdoc-ref:Net::IMAP#move], {uid
|
338
|
-
# copy}[rdoc-ref:Net::IMAP#uid_copy], and {uid
|
339
|
-
# move}[rdoc-ref:Net::IMAP#uid_move] commands---unless the destination
|
340
|
-
# mailbox reports +UIDNOTSTICKY+.
|
341
|
-
#
|
342
|
-
#--
|
343
|
-
# TODO: support MULTIAPPEND
|
344
|
-
#++
|
345
|
-
#
|
346
|
-
class UIDPlusData < Struct.new(:uidvalidity, :source_uids, :assigned_uids)
|
347
|
-
##
|
348
|
-
# method: uidvalidity
|
349
|
-
# :call-seq: uidvalidity -> nonzero uint32
|
350
|
-
#
|
351
|
-
# The UIDVALIDITY of the destination mailbox.
|
352
|
-
|
353
|
-
##
|
354
|
-
# method: source_uids
|
355
|
-
# :call-seq: source_uids -> nil or an array of nonzero uint32
|
356
|
-
#
|
357
|
-
# The UIDs of the copied or moved messages.
|
358
|
-
#
|
359
|
-
# Note:: Returns +nil+ for Net::IMAP#append.
|
360
|
-
|
361
|
-
##
|
362
|
-
# method: assigned_uids
|
363
|
-
# :call-seq: assigned_uids -> an array of nonzero uint32
|
364
|
-
#
|
365
|
-
# The newly assigned UIDs of the copied, moved, or appended messages.
|
366
|
-
#
|
367
|
-
# Note:: This always returns an array, even when it contains only one UID.
|
368
|
-
|
369
|
-
##
|
370
|
-
# :call-seq: uid_mapping -> nil or a hash
|
371
|
-
#
|
372
|
-
# Returns a hash mapping each source UID to the newly assigned destination
|
373
|
-
# UID.
|
374
|
-
#
|
375
|
-
# Note:: Returns +nil+ for Net::IMAP#append.
|
376
|
-
def uid_mapping
|
377
|
-
source_uids&.zip(assigned_uids)&.to_h
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
330
|
# Net::IMAP::MailboxList represents contents of the LIST response,
|
382
331
|
# representing a single mailbox path.
|
383
332
|
#
|
@@ -13,13 +13,17 @@ module Net
|
|
13
13
|
|
14
14
|
attr_reader :config
|
15
15
|
|
16
|
-
#
|
16
|
+
# Creates a new ResponseParser.
|
17
|
+
#
|
18
|
+
# When +config+ is frozen or global, the parser #config inherits from it.
|
19
|
+
# Otherwise, +config+ will be used directly.
|
17
20
|
def initialize(config: Config.global)
|
18
21
|
@str = nil
|
19
22
|
@pos = nil
|
20
23
|
@lex_state = nil
|
21
24
|
@token = nil
|
22
25
|
@config = Config[config]
|
26
|
+
@config = @config.new if @config == Config.global || @config.frozen?
|
23
27
|
end
|
24
28
|
|
25
29
|
# :call-seq:
|
@@ -1339,7 +1343,8 @@ module Net
|
|
1339
1343
|
assert_no_lookahead
|
1340
1344
|
start = @pos
|
1341
1345
|
astring
|
1342
|
-
@
|
1346
|
+
end_pos = @token ? @pos - 1 : @pos
|
1347
|
+
@str[start...end_pos]
|
1343
1348
|
end
|
1344
1349
|
|
1345
1350
|
# mailbox-data = "FLAGS" SP flag-list / "LIST" SP mailbox-list /
|
@@ -1862,11 +1867,10 @@ module Net
|
|
1862
1867
|
#
|
1863
1868
|
# n.b, uniqueid ⊂ uid-set. To avoid inconsistent return types, we always
|
1864
1869
|
# match uid_set even if that returns a single-member array.
|
1865
|
-
#
|
1866
1870
|
def resp_code_apnd__data
|
1867
1871
|
validity = number; SP!
|
1868
1872
|
dst_uids = uid_set # uniqueid ⊂ uid-set
|
1869
|
-
|
1873
|
+
AppendUID(validity, dst_uids)
|
1870
1874
|
end
|
1871
1875
|
|
1872
1876
|
# already matched: "COPYUID"
|
@@ -1876,7 +1880,25 @@ module Net
|
|
1876
1880
|
validity = number; SP!
|
1877
1881
|
src_uids = uid_set; SP!
|
1878
1882
|
dst_uids = uid_set
|
1879
|
-
|
1883
|
+
CopyUID(validity, src_uids, dst_uids)
|
1884
|
+
end
|
1885
|
+
|
1886
|
+
def AppendUID(...) DeprecatedUIDPlus(...) || AppendUIDData.new(...) end
|
1887
|
+
def CopyUID(...) DeprecatedUIDPlus(...) || CopyUIDData.new(...) end
|
1888
|
+
|
1889
|
+
# TODO: remove this code in the v0.6.0 release
|
1890
|
+
def DeprecatedUIDPlus(validity, src_uids = nil, dst_uids)
|
1891
|
+
return unless config.parser_use_deprecated_uidplus_data
|
1892
|
+
compact_uid_sets = [src_uids, dst_uids].compact
|
1893
|
+
count = compact_uid_sets.map { _1.count_with_duplicates }.max
|
1894
|
+
max = config.parser_max_deprecated_uidplus_data_size
|
1895
|
+
if count <= max
|
1896
|
+
src_uids &&= src_uids.each_ordered_number.to_a
|
1897
|
+
dst_uids = dst_uids.each_ordered_number.to_a
|
1898
|
+
UIDPlusData.new(validity, src_uids, dst_uids)
|
1899
|
+
elsif config.parser_use_deprecated_uidplus_data != :up_to_max_size
|
1900
|
+
parse_error("uid-set is too large: %d > %d", count, max)
|
1901
|
+
end
|
1880
1902
|
end
|
1881
1903
|
|
1882
1904
|
ADDRESS_REGEXP = /\G
|
@@ -2002,15 +2024,9 @@ module Net
|
|
2002
2024
|
# uniqueid = nz-number
|
2003
2025
|
# ; Strictly ascending
|
2004
2026
|
def uid_set
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
when T_ATOM
|
2009
|
-
token.value.split(",").flat_map {|range|
|
2010
|
-
range = range.split(":").map {|uniqueid| Integer(uniqueid) }
|
2011
|
-
range.size == 1 ? range : Range.new(range.min, range.max).to_a
|
2012
|
-
}
|
2013
|
-
end
|
2027
|
+
set = sequence_set
|
2028
|
+
parse_error("uid-set cannot contain '*'") if set.include_star?
|
2029
|
+
set
|
2014
2030
|
end
|
2015
2031
|
|
2016
2032
|
def nil_atom
|