net-imap 0.4.24 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +8 -2
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +32 -182
- data/lib/net/imap/config/attr_type_coercion.rb +22 -23
- data/lib/net/imap/config.rb +38 -162
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +6 -33
- data/lib/net/imap/response_data.rb +62 -118
- data/lib/net/imap/response_parser.rb +18 -45
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +1 -1
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +213 -51
- data/lib/net/imap/sasl/login_authenticator.rb +2 -1
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +0 -74
- data/lib/net/imap/sasl.rb +6 -3
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/sequence_set.rb +132 -282
- data/lib/net/imap.rb +97 -269
- data/net-imap.gemspec +1 -1
- metadata +7 -6
- data/lib/net/imap/response_reader.rb +0 -82
- data/lib/net/imap/uidplus_data.rb +0 -326
data/lib/net/imap/config.rb
CHANGED
|
@@ -129,25 +129,8 @@ module Net
|
|
|
129
129
|
def self.global; @global if defined?(@global) end
|
|
130
130
|
|
|
131
131
|
# A hash of hard-coded configurations, indexed by version number or name.
|
|
132
|
-
# Values can be accessed with any object that responds to +to_sym+ or
|
|
133
|
-
# +to_r+/+to_f+ with a non-zero number.
|
|
134
|
-
#
|
|
135
|
-
# Config::[] gets named or numbered versions from this hash.
|
|
136
|
-
#
|
|
137
|
-
# For example:
|
|
138
|
-
# Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
|
|
139
|
-
# Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
|
|
140
|
-
# Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
|
|
141
|
-
# Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
|
|
142
132
|
def self.version_defaults; @version_defaults end
|
|
143
|
-
@version_defaults =
|
|
144
|
-
# NOTE: String responds to both so the order is significant.
|
|
145
|
-
# And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
|
|
146
|
-
(h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
|
|
147
|
-
(h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
|
|
148
|
-
(h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
|
|
149
|
-
(h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
|
|
150
|
-
}
|
|
133
|
+
@version_defaults = {}
|
|
151
134
|
|
|
152
135
|
# :call-seq:
|
|
153
136
|
# Net::IMAP::Config[number] -> versioned config
|
|
@@ -170,17 +153,18 @@ module Net
|
|
|
170
153
|
elsif config.nil? && global.nil? then nil
|
|
171
154
|
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
|
|
172
155
|
else
|
|
173
|
-
version_defaults
|
|
156
|
+
version_defaults.fetch(config) do
|
|
174
157
|
case config
|
|
175
158
|
when Numeric
|
|
176
159
|
raise RangeError, "unknown config version: %p" % [config]
|
|
177
|
-
when
|
|
160
|
+
when Symbol
|
|
178
161
|
raise KeyError, "unknown config name: %p" % [config]
|
|
179
162
|
else
|
|
180
163
|
raise TypeError, "no implicit conversion of %s to %s" % [
|
|
181
164
|
config.class, Config
|
|
182
165
|
]
|
|
183
166
|
end
|
|
167
|
+
end
|
|
184
168
|
end
|
|
185
169
|
end
|
|
186
170
|
|
|
@@ -207,13 +191,10 @@ module Net
|
|
|
207
191
|
|
|
208
192
|
# Seconds to wait until a connection is opened.
|
|
209
193
|
#
|
|
210
|
-
# Applied separately for establishing TCP connection and starting a TLS
|
|
211
|
-
# connection.
|
|
212
|
-
#
|
|
213
194
|
# If the IMAP object cannot open a connection within this time,
|
|
214
195
|
# it raises a Net::OpenTimeout exception.
|
|
215
196
|
#
|
|
216
|
-
# See Net::IMAP.new
|
|
197
|
+
# See Net::IMAP.new.
|
|
217
198
|
#
|
|
218
199
|
# The default value is +30+ seconds.
|
|
219
200
|
attr_accessor :open_timeout, type: Integer
|
|
@@ -242,40 +223,29 @@ module Net
|
|
|
242
223
|
# Use +SASL-IR+ when it is supported by the server and the mechanism.
|
|
243
224
|
attr_accessor :sasl_ir, type: :boolean
|
|
244
225
|
|
|
245
|
-
#
|
|
246
|
-
#
|
|
226
|
+
# Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
|
|
227
|
+
# capability is present. When enforced, Net::IMAP will raise a
|
|
228
|
+
# LoginDisabledError when that capability is present.
|
|
247
229
|
#
|
|
248
|
-
#
|
|
249
|
-
# unlikely to be reached. To use a lower limit, fetch message bodies in
|
|
250
|
-
# chunks rather than all at once. A _much_ lower value should be used
|
|
251
|
-
# with untrusted servers (for example, when connecting to a user-provided
|
|
252
|
-
# hostname).
|
|
230
|
+
# <em>(Support for +LOGINDISABLED+ was added in +v0.5.0+.)</em>
|
|
253
231
|
#
|
|
254
|
-
#
|
|
255
|
-
# not prevent a flood of individual responses and it does not limit how
|
|
256
|
-
# many unhandled responses may be stored on the responses hash. See
|
|
257
|
-
# Net::IMAP@Unbounded+memory+use.
|
|
258
|
-
#
|
|
259
|
-
# Socket reads are limited to the maximum remaining bytes for the current
|
|
260
|
-
# response: max_response_size minus the bytes that have already been read.
|
|
261
|
-
# When the limit is reached, or reading a +literal+ _would_ go over the
|
|
262
|
-
# limit, ResponseTooLargeError is raised and the connection is closed.
|
|
263
|
-
# See also #socket_read_limit.
|
|
232
|
+
# ==== Valid options
|
|
264
233
|
#
|
|
265
|
-
#
|
|
266
|
-
#
|
|
267
|
-
# value. Net::IMAP#noop can force a response and enforce the new setting
|
|
268
|
-
# immediately.
|
|
234
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
|
235
|
+
# Send the +LOGIN+ command without checking for +LOGINDISABLED+.
|
|
269
236
|
#
|
|
270
|
-
#
|
|
237
|
+
# [+:when_capabilities_cached+]
|
|
238
|
+
# Enforce the requirement when Net::IMAP#capabilities_cached? is true,
|
|
239
|
+
# but do not send a +CAPABILITY+ command to discover the capabilities.
|
|
271
240
|
#
|
|
272
|
-
#
|
|
273
|
-
#
|
|
274
|
-
#
|
|
241
|
+
# [+true+ <em>(default since +v0.5+)</em>]
|
|
242
|
+
# Only send the +LOGIN+ command if the +LOGINDISABLED+ capability is not
|
|
243
|
+
# present. When capabilities are unknown, Net::IMAP will automatically
|
|
244
|
+
# send a +CAPABILITY+ command first before sending +LOGIN+.
|
|
275
245
|
#
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
246
|
+
attr_accessor :enforce_logindisabled, type: [
|
|
247
|
+
false, :when_capabilities_cached, true
|
|
248
|
+
]
|
|
279
249
|
|
|
280
250
|
# Controls the behavior of Net::IMAP#responses when called without any
|
|
281
251
|
# arguments (+type+ or +block+).
|
|
@@ -303,7 +273,7 @@ module Net
|
|
|
303
273
|
# Raise an ArgumentError with the deprecation warning.
|
|
304
274
|
#
|
|
305
275
|
# Note: #responses_without_args is an alias for #responses_without_block.
|
|
306
|
-
attr_accessor :responses_without_block, type:
|
|
276
|
+
attr_accessor :responses_without_block, type: [
|
|
307
277
|
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
|
|
308
278
|
]
|
|
309
279
|
|
|
@@ -315,67 +285,6 @@ module Net
|
|
|
315
285
|
#
|
|
316
286
|
# Alias for responses_without_block
|
|
317
287
|
|
|
318
|
-
# Whether ResponseParser should use the deprecated UIDPlusData or
|
|
319
|
-
# CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
|
|
320
|
-
# AppendUIDData for +APPENDUID+ response codes.
|
|
321
|
-
#
|
|
322
|
-
# UIDPlusData stores its data in arrays of numbers, which is vulnerable to
|
|
323
|
-
# a memory exhaustion denial of service attack from an untrusted or
|
|
324
|
-
# compromised server. Set this option to +false+ to completely block this
|
|
325
|
-
# vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
|
|
326
|
-
# mitigates this vulnerability.
|
|
327
|
-
#
|
|
328
|
-
# AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
|
|
329
|
-
# UIDPlusData. Most applications should be able to upgrade with little
|
|
330
|
-
# or no changes.
|
|
331
|
-
#
|
|
332
|
-
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
|
333
|
-
#
|
|
334
|
-
# <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
|
|
335
|
-
#
|
|
336
|
-
# <em>UIDPlusData will be removed in +v0.6+ and this config setting will
|
|
337
|
-
# be ignored.</em>
|
|
338
|
-
#
|
|
339
|
-
# ==== Valid options
|
|
340
|
-
#
|
|
341
|
-
# [+true+ <em>(original default)</em>]
|
|
342
|
-
# ResponseParser only uses UIDPlusData.
|
|
343
|
-
#
|
|
344
|
-
# [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
|
|
345
|
-
# ResponseParser uses UIDPlusData when the +uid-set+ size is below
|
|
346
|
-
# parser_max_deprecated_uidplus_data_size. Above that size,
|
|
347
|
-
# ResponseParser uses AppendUIDData or CopyUIDData.
|
|
348
|
-
#
|
|
349
|
-
# [+false+ <em>(planned default for +v0.6+)</em>]
|
|
350
|
-
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
|
|
351
|
-
attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
|
|
352
|
-
true, :up_to_max_size, false
|
|
353
|
-
]
|
|
354
|
-
|
|
355
|
-
# The maximum +uid-set+ size that ResponseParser will parse into
|
|
356
|
-
# deprecated UIDPlusData. This limit only applies when
|
|
357
|
-
# parser_use_deprecated_uidplus_data is not +false+.
|
|
358
|
-
#
|
|
359
|
-
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
|
360
|
-
#
|
|
361
|
-
# <em>Support for limiting UIDPlusData to a maximum size was added in
|
|
362
|
-
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
|
|
363
|
-
#
|
|
364
|
-
# <em>UIDPlusData will be removed in +v0.6+.</em>
|
|
365
|
-
#
|
|
366
|
-
# ==== Versioned Defaults
|
|
367
|
-
#
|
|
368
|
-
# Because this limit guards against a remote server causing catastrophic
|
|
369
|
-
# memory exhaustion, the versioned default (used by #load_defaults) also
|
|
370
|
-
# applies to versions without the feature.
|
|
371
|
-
#
|
|
372
|
-
# * +0.3+ and prior: <tt>10,000</tt>
|
|
373
|
-
# * +0.4+: <tt>1,000</tt>
|
|
374
|
-
# * +0.5+: <tt>100</tt>
|
|
375
|
-
# * +0.6+: <tt>0</tt>
|
|
376
|
-
#
|
|
377
|
-
attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
|
|
378
|
-
|
|
379
288
|
# Creates a new config object and initialize its attribute with +attrs+.
|
|
380
289
|
#
|
|
381
290
|
# If +parent+ is not given, the global config is used by default.
|
|
@@ -454,71 +363,38 @@ module Net
|
|
|
454
363
|
open_timeout: 30,
|
|
455
364
|
idle_response_timeout: 5,
|
|
456
365
|
sasl_ir: true,
|
|
457
|
-
|
|
458
|
-
responses_without_block: :
|
|
459
|
-
parser_use_deprecated_uidplus_data: true,
|
|
460
|
-
parser_max_deprecated_uidplus_data_size: 1000,
|
|
366
|
+
enforce_logindisabled: true,
|
|
367
|
+
responses_without_block: :warn,
|
|
461
368
|
).freeze
|
|
462
369
|
|
|
463
370
|
@global = default.new
|
|
464
371
|
|
|
465
372
|
version_defaults[:default] = Config[default.send(:defaults_hash)]
|
|
373
|
+
version_defaults[:current] = Config[:default]
|
|
466
374
|
|
|
467
|
-
version_defaults[
|
|
375
|
+
version_defaults[0] = Config[:current].dup.update(
|
|
468
376
|
sasl_ir: false,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
parser_max_deprecated_uidplus_data_size: 10_000,
|
|
377
|
+
responses_without_block: :silence_deprecation_warning,
|
|
378
|
+
enforce_logindisabled: false,
|
|
472
379
|
).freeze
|
|
473
|
-
version_defaults[0.
|
|
474
|
-
version_defaults[0.
|
|
475
|
-
version_defaults[0.
|
|
476
|
-
version_defaults[0.
|
|
380
|
+
version_defaults[0.0] = Config[0]
|
|
381
|
+
version_defaults[0.1] = Config[0]
|
|
382
|
+
version_defaults[0.2] = Config[0]
|
|
383
|
+
version_defaults[0.3] = Config[0]
|
|
477
384
|
|
|
478
|
-
version_defaults[0.
|
|
385
|
+
version_defaults[0.4] = Config[0.3].dup.update(
|
|
479
386
|
sasl_ir: true,
|
|
480
|
-
parser_max_deprecated_uidplus_data_size: 1000,
|
|
481
387
|
).freeze
|
|
482
388
|
|
|
483
|
-
version_defaults[0.
|
|
484
|
-
max_response_size: 512 << 20, # 512 MiB
|
|
485
|
-
responses_without_block: :warn,
|
|
486
|
-
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
|
487
|
-
parser_max_deprecated_uidplus_data_size: 100,
|
|
488
|
-
).freeze
|
|
389
|
+
version_defaults[0.5] = Config[:current]
|
|
489
390
|
|
|
490
|
-
version_defaults[0.
|
|
391
|
+
version_defaults[0.6] = Config[0.5].dup.update(
|
|
491
392
|
responses_without_block: :frozen_dup,
|
|
492
|
-
parser_use_deprecated_uidplus_data: false,
|
|
493
|
-
parser_max_deprecated_uidplus_data_size: 0,
|
|
494
|
-
).freeze
|
|
495
|
-
|
|
496
|
-
version_defaults[0.7r] = Config[0.6r].dup.update(
|
|
497
393
|
).freeze
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
# 0.6r.to_f == 0.6 # => true
|
|
501
|
-
# 0.6 .to_r == 0.6r # => false
|
|
502
|
-
version_defaults.to_a.each do |k, v|
|
|
503
|
-
next unless k.is_a? Rational
|
|
504
|
-
version_defaults[k.to_f] = v
|
|
505
|
-
end
|
|
506
|
-
|
|
507
|
-
current = VERSION.to_r
|
|
508
|
-
version_defaults[:original] = Config[0]
|
|
509
|
-
version_defaults[:current] = Config[current]
|
|
510
|
-
version_defaults[:next] = Config[current + 0.1r]
|
|
511
|
-
|
|
512
|
-
version_defaults[:future] = Config[0.7r]
|
|
394
|
+
version_defaults[:next] = Config[0.6]
|
|
395
|
+
version_defaults[:future] = Config[:next]
|
|
513
396
|
|
|
514
397
|
version_defaults.freeze
|
|
515
|
-
|
|
516
|
-
if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h
|
|
517
|
-
warn "Misconfigured Net::IMAP::Config[:current] => %p,\n" \
|
|
518
|
-
" not equal to Net::IMAP::Config[:default] => %p" % [
|
|
519
|
-
self[:current].to_h, self[:default].to_h
|
|
520
|
-
]
|
|
521
|
-
end
|
|
522
398
|
end
|
|
523
399
|
end
|
|
524
400
|
end
|
|
@@ -186,7 +186,7 @@ module Net
|
|
|
186
186
|
|
|
187
187
|
# Ensure argument is 'number' or raise DataFormatError
|
|
188
188
|
def ensure_number(num)
|
|
189
|
-
return if valid_number?(num)
|
|
189
|
+
return num if valid_number?(num)
|
|
190
190
|
|
|
191
191
|
msg = "number must be unsigned 32-bit integer: #{num}"
|
|
192
192
|
raise DataFormatError, msg
|
|
@@ -194,7 +194,7 @@ module Net
|
|
|
194
194
|
|
|
195
195
|
# Ensure argument is 'nz_number' or raise DataFormatError
|
|
196
196
|
def ensure_nz_number(num)
|
|
197
|
-
return if valid_nz_number?(num)
|
|
197
|
+
return num if valid_nz_number?(num)
|
|
198
198
|
|
|
199
199
|
msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
|
|
200
200
|
raise DataFormatError, msg
|
|
@@ -202,7 +202,7 @@ module Net
|
|
|
202
202
|
|
|
203
203
|
# Ensure argument is 'mod_sequence_value' or raise DataFormatError
|
|
204
204
|
def ensure_mod_sequence_value(num)
|
|
205
|
-
return if valid_mod_sequence_value?(num)
|
|
205
|
+
return num if valid_mod_sequence_value?(num)
|
|
206
206
|
|
|
207
207
|
msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
|
|
208
208
|
raise DataFormatError, msg
|
|
@@ -83,10 +83,12 @@ module Net
|
|
|
83
83
|
elsif deprecated.empty?
|
|
84
84
|
super host, port: port_or_options
|
|
85
85
|
elsif deprecated.shift
|
|
86
|
-
warn
|
|
86
|
+
warn("DEPRECATED: Call Net::IMAP.new with keyword options",
|
|
87
|
+
uplevel: 1, category: :deprecated)
|
|
87
88
|
super host, port: port_or_options, ssl: create_ssl_params(*deprecated)
|
|
88
89
|
else
|
|
89
|
-
warn
|
|
90
|
+
warn("DEPRECATED: Call Net::IMAP.new with keyword options",
|
|
91
|
+
uplevel: 1, category: :deprecated)
|
|
90
92
|
super host, port: port_or_options, ssl: false
|
|
91
93
|
end
|
|
92
94
|
end
|
|
@@ -113,7 +115,8 @@ module Net
|
|
|
113
115
|
elsif deprecated.first.respond_to?(:to_hash)
|
|
114
116
|
super(**Hash.try_convert(deprecated.first))
|
|
115
117
|
else
|
|
116
|
-
warn
|
|
118
|
+
warn("DEPRECATED: Call Net::IMAP#starttls with keyword options",
|
|
119
|
+
uplevel: 1, category: :deprecated)
|
|
117
120
|
super(**create_ssl_params(*deprecated))
|
|
118
121
|
end
|
|
119
122
|
end
|
data/lib/net/imap/errors.rb
CHANGED
|
@@ -7,41 +7,14 @@ module Net
|
|
|
7
7
|
class Error < StandardError
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Error raised when the socket cannot be read, due to a Config limit.
|
|
15
|
-
class ResponseReadError < Error
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Error raised when a response is larger than IMAP#max_response_size.
|
|
19
|
-
class ResponseTooLargeError < ResponseReadError
|
|
20
|
-
attr_reader :bytes_read, :literal_size
|
|
21
|
-
attr_reader :max_response_size
|
|
22
|
-
|
|
23
|
-
def initialize(msg = nil, *args,
|
|
24
|
-
bytes_read: nil,
|
|
25
|
-
literal_size: nil,
|
|
26
|
-
max_response_size: nil,
|
|
27
|
-
**kwargs)
|
|
28
|
-
@bytes_read = bytes_read
|
|
29
|
-
@literal_size = literal_size
|
|
30
|
-
@max_response_size = max_response_size
|
|
31
|
-
msg ||= [
|
|
32
|
-
"Response size", response_size_msg, "exceeds max_response_size",
|
|
33
|
-
max_response_size && "(#{max_response_size}B)",
|
|
34
|
-
].compact.join(" ")
|
|
35
|
-
super(msg, *args, **kwargs)
|
|
10
|
+
class LoginDisabledError < Error
|
|
11
|
+
def initialize(msg = "Remote server has disabled the LOGIN command", ...)
|
|
12
|
+
super
|
|
36
13
|
end
|
|
14
|
+
end
|
|
37
15
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def response_size_msg
|
|
41
|
-
if bytes_read && literal_size
|
|
42
|
-
"(#{bytes_read}B read + #{literal_size}B literal)"
|
|
43
|
-
end
|
|
44
|
-
end
|
|
16
|
+
# Error raised when data is in the incorrect format.
|
|
17
|
+
class DataFormatError < Error
|
|
45
18
|
end
|
|
46
19
|
|
|
47
20
|
# Error raised when a response from the server is non-parsable.
|
|
@@ -5,9 +5,6 @@ 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"
|
|
11
8
|
|
|
12
9
|
# Net::IMAP::ContinuationRequest represents command continuation requests.
|
|
13
10
|
#
|
|
@@ -295,14 +292,6 @@ module Net
|
|
|
295
292
|
# because the server doesn't allow deletion of mailboxes with children.
|
|
296
293
|
# #data is +nil+.
|
|
297
294
|
#
|
|
298
|
-
# ==== <tt>QUOTA=RES-*</tt> response codes
|
|
299
|
-
# See {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208.html#section-4.3].
|
|
300
|
-
# * +OVERQUOTA+ (also in RFC5530[https://www.rfc-editor.org/rfc/rfc5530]),
|
|
301
|
-
# with a tagged +NO+ response to an +APPEND+/+COPY+/+MOVE+ command when
|
|
302
|
-
# the command would put the target mailbox over any quota, and with an
|
|
303
|
-
# untagged +NO+ when a mailbox exceeds a soft quota (which may be caused
|
|
304
|
-
# be external events). #data is +nil+.
|
|
305
|
-
#
|
|
306
295
|
# ==== +CONDSTORE+ extension
|
|
307
296
|
# See {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
|
308
297
|
# * +NOMODSEQ+, when selecting a mailbox that does not support
|
|
@@ -335,6 +324,60 @@ module Net
|
|
|
335
324
|
# code data can take.
|
|
336
325
|
end
|
|
337
326
|
|
|
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
|
+
|
|
338
381
|
# Net::IMAP::MailboxList represents contents of the LIST response,
|
|
339
382
|
# representing a single mailbox path.
|
|
340
383
|
#
|
|
@@ -377,24 +420,12 @@ module Net
|
|
|
377
420
|
# Net::IMAP#getquotaroot returns an array containing both MailboxQuotaRoot
|
|
378
421
|
# and MailboxQuota objects.
|
|
379
422
|
#
|
|
380
|
-
# ==== Required capability
|
|
381
|
-
#
|
|
382
|
-
# Requires +QUOTA+ [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]]
|
|
383
|
-
# or <tt>QUOTA=RES-STORAGE</tt>
|
|
384
|
-
# [RFC9208[https://www.rfc-editor.org/rfc/rfc9208]] capability.
|
|
385
423
|
class MailboxQuota < Struct.new(:mailbox, :usage, :quota)
|
|
386
424
|
##
|
|
387
425
|
# method: mailbox
|
|
388
426
|
# :call-seq: mailbox -> string
|
|
389
427
|
#
|
|
390
|
-
# The
|
|
391
|
-
#
|
|
392
|
-
# NOTE: this was mistakenly named "mailbox". But the quota root's name may
|
|
393
|
-
# differ from the mailbox. A single quota root may cover multiple
|
|
394
|
-
# mailboxes, and a single mailbox may be governed by multiple quota roots.
|
|
395
|
-
|
|
396
|
-
# The quota root with the associated quota.
|
|
397
|
-
alias quota_root mailbox
|
|
428
|
+
# The mailbox with the associated quota.
|
|
398
429
|
|
|
399
430
|
##
|
|
400
431
|
# method: usage
|
|
@@ -406,7 +437,7 @@ module Net
|
|
|
406
437
|
# method: quota
|
|
407
438
|
# :call-seq: quota -> Integer
|
|
408
439
|
#
|
|
409
|
-
#
|
|
440
|
+
# Quota limit imposed on the mailbox.
|
|
410
441
|
#
|
|
411
442
|
end
|
|
412
443
|
|
|
@@ -908,7 +939,8 @@ module Net
|
|
|
908
939
|
# for something else?
|
|
909
940
|
#++
|
|
910
941
|
def media_subtype
|
|
911
|
-
warn("media_subtype is obsolete, use subtype instead.\n",
|
|
942
|
+
warn("media_subtype is obsolete, use subtype instead.\n",
|
|
943
|
+
uplevel: 1, category: :deprecated)
|
|
912
944
|
return subtype
|
|
913
945
|
end
|
|
914
946
|
end
|
|
@@ -953,7 +985,8 @@ module Net
|
|
|
953
985
|
# generate a warning message to +stderr+, then return
|
|
954
986
|
# the value of +subtype+.
|
|
955
987
|
def media_subtype
|
|
956
|
-
warn("media_subtype is obsolete, use subtype instead.\n",
|
|
988
|
+
warn("media_subtype is obsolete, use subtype instead.\n",
|
|
989
|
+
uplevel: 1, category: :deprecated)
|
|
957
990
|
return subtype
|
|
958
991
|
end
|
|
959
992
|
end
|
|
@@ -1009,77 +1042,6 @@ module Net
|
|
|
1009
1042
|
end
|
|
1010
1043
|
end
|
|
1011
1044
|
|
|
1012
|
-
# BodyTypeAttachment is not used and will be removed in an upcoming release.
|
|
1013
|
-
#
|
|
1014
|
-
# === Bug Analysis
|
|
1015
|
-
#
|
|
1016
|
-
# \IMAP body structures are parenthesized lists and assign their fields
|
|
1017
|
-
# positionally, so missing fields change the interpretation of all
|
|
1018
|
-
# following fields. Additionally, different body types have a different
|
|
1019
|
-
# number of required fields, followed by optional "extension" fields.
|
|
1020
|
-
#
|
|
1021
|
-
# BodyTypeAttachment was previously returned when a "message/rfc822" part,
|
|
1022
|
-
# which should be sent as <tt>body-type-msg</tt> with ten required fields,
|
|
1023
|
-
# was actually sent as a <tt>body-type-basic</tt> with _seven_ required
|
|
1024
|
-
# fields.
|
|
1025
|
-
#
|
|
1026
|
-
# basic => type, subtype, param, id, desc, enc, octets, md5=nil, dsp=nil, lang=nil, loc=nil, *ext
|
|
1027
|
-
# msg => type, subtype, param, id, desc, enc, octets, envelope, body, lines, md5=nil, ...
|
|
1028
|
-
#
|
|
1029
|
-
# Normally, +envelope+ and +md5+ are incompatible, but Net::IMAP leniently
|
|
1030
|
-
# allowed buggy servers to send +NIL+ for +envelope+. As a result, when a
|
|
1031
|
-
# server sent a <tt>message/rfc822</tt> part with +NIL+ for +md5+ and a
|
|
1032
|
-
# non-<tt>NIL</tt> +dsp+, Net::IMAP misinterpreted the
|
|
1033
|
-
# <tt>Content-Disposition</tt> as if it were a strange body type. In all
|
|
1034
|
-
# reported cases, the <tt>Content-Disposition</tt> was "attachment", so
|
|
1035
|
-
# BodyTypeAttachment was created as the workaround.
|
|
1036
|
-
#
|
|
1037
|
-
# === Current behavior
|
|
1038
|
-
#
|
|
1039
|
-
# When interpreted strictly, +envelope+ and +md5+ are incompatible. So the
|
|
1040
|
-
# current parsing algorithm peeks ahead after it has received the seventh
|
|
1041
|
-
# body field. If the next token is not the start of an +envelope+, we assume
|
|
1042
|
-
# the server has incorrectly sent us a <tt>body-type-basic</tt> and return
|
|
1043
|
-
# BodyTypeBasic. As a result, what was previously BodyTypeMessage#body =>
|
|
1044
|
-
# BodyTypeAttachment is now BodyTypeBasic#disposition => ContentDisposition.
|
|
1045
|
-
#
|
|
1046
|
-
class BodyTypeAttachment < Struct.new(:dsp_type, :_unused_, :param)
|
|
1047
|
-
# *invalid for BodyTypeAttachment*
|
|
1048
|
-
def media_type
|
|
1049
|
-
warn(<<~WARN, uplevel: 1)
|
|
1050
|
-
BodyTypeAttachment#media_type is obsolete. Use dsp_type instead.
|
|
1051
|
-
WARN
|
|
1052
|
-
dsp_type
|
|
1053
|
-
end
|
|
1054
|
-
|
|
1055
|
-
# *invalid for BodyTypeAttachment*
|
|
1056
|
-
def subtype
|
|
1057
|
-
warn("BodyTypeAttachment#subtype is obsolete.\n", uplevel: 1)
|
|
1058
|
-
nil
|
|
1059
|
-
end
|
|
1060
|
-
|
|
1061
|
-
##
|
|
1062
|
-
# method: dsp_type
|
|
1063
|
-
# :call-seq: dsp_type -> string
|
|
1064
|
-
#
|
|
1065
|
-
# Returns the content disposition type, as defined by
|
|
1066
|
-
# [DISPOSITION[https://tools.ietf.org/html/rfc2183]].
|
|
1067
|
-
|
|
1068
|
-
##
|
|
1069
|
-
# method: param
|
|
1070
|
-
# :call-seq: param -> hash
|
|
1071
|
-
#
|
|
1072
|
-
# Returns a hash representing parameters of the Content-Disposition
|
|
1073
|
-
# field, as defined by [DISPOSITION[https://tools.ietf.org/html/rfc2183]].
|
|
1074
|
-
|
|
1075
|
-
##
|
|
1076
|
-
def multipart?
|
|
1077
|
-
return false
|
|
1078
|
-
end
|
|
1079
|
-
end
|
|
1080
|
-
|
|
1081
|
-
deprecate_constant :BodyTypeAttachment
|
|
1082
|
-
|
|
1083
1045
|
# Net::IMAP::BodyTypeMultipart represents body structures of messages and
|
|
1084
1046
|
# message parts, when <tt>Content-Type</tt> is <tt>multipart/*</tt>.
|
|
1085
1047
|
class BodyTypeMultipart < Struct.new(:media_type, :subtype,
|
|
@@ -1151,29 +1113,11 @@ module Net
|
|
|
1151
1113
|
# generate a warning message to +stderr+, then return
|
|
1152
1114
|
# the value of +subtype+.
|
|
1153
1115
|
def media_subtype
|
|
1154
|
-
warn("media_subtype is obsolete, use subtype instead.\n",
|
|
1116
|
+
warn("media_subtype is obsolete, use subtype instead.\n",
|
|
1117
|
+
uplevel: 1, category: :deprecated)
|
|
1155
1118
|
return subtype
|
|
1156
1119
|
end
|
|
1157
1120
|
end
|
|
1158
1121
|
|
|
1159
|
-
# === Obsolete
|
|
1160
|
-
# BodyTypeExtension is not used and will be removed in an upcoming release.
|
|
1161
|
-
#
|
|
1162
|
-
# >>>
|
|
1163
|
-
# BodyTypeExtension was (incorrectly) used for <tt>message/*</tt> parts
|
|
1164
|
-
# (besides <tt>message/rfc822</tt>, which correctly uses BodyTypeMessage).
|
|
1165
|
-
#
|
|
1166
|
-
# Net::IMAP now (correctly) parses all message types (other than
|
|
1167
|
-
# <tt>message/rfc822</tt> or <tt>message/global</tt>) as BodyTypeBasic.
|
|
1168
|
-
class BodyTypeExtension < Struct.new(:media_type, :subtype,
|
|
1169
|
-
:params, :content_id,
|
|
1170
|
-
:description, :encoding, :size)
|
|
1171
|
-
def multipart?
|
|
1172
|
-
return false
|
|
1173
|
-
end
|
|
1174
|
-
end
|
|
1175
|
-
|
|
1176
|
-
deprecate_constant :BodyTypeExtension
|
|
1177
|
-
|
|
1178
1122
|
end
|
|
1179
1123
|
end
|