net-imap 0.4.12 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +7 -1
- data/README.md +10 -4
- data/docs/styles.css +75 -14
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +61 -48
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +61 -0
- data/lib/net/imap/config.rb +402 -0
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +8 -5
- data/lib/net/imap/errors.rb +6 -0
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +126 -47
- data/lib/net/imap/response_data.rb +126 -193
- data/lib/net/imap/response_parser/parser_utils.rb +11 -6
- data/lib/net/imap/response_parser.rb +159 -21
- data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
- 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 +4 -4
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
- data/lib/net/imap/sasl/external_authenticator.rb +2 -2
- data/lib/net/imap/sasl/gs2_header.rb +7 -7
- data/lib/net/imap/sasl/login_authenticator.rb +4 -3
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
- data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
- data/lib/net/imap/sasl.rb +7 -4
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/search_result.rb +2 -2
- data/lib/net/imap/sequence_set.rb +28 -24
- data/lib/net/imap/stringprep/nameprep.rb +1 -1
- data/lib/net/imap/stringprep/trace.rb +4 -4
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +1001 -319
- data/net-imap.gemspec +3 -3
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/string_prep_tables_generator.rb +2 -0
- metadata +11 -10
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/pages.yml +0 -46
- data/.github/workflows/push_gem.yml +0 -48
- data/.github/workflows/test.yml +0 -31
- data/.gitignore +0 -12
- data/.mailmap +0 -13
@@ -0,0 +1,402 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config/attr_accessors"
|
4
|
+
require_relative "config/attr_inheritance"
|
5
|
+
require_relative "config/attr_type_coercion"
|
6
|
+
|
7
|
+
module Net
|
8
|
+
class IMAP
|
9
|
+
|
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
|
+
#
|
15
|
+
# When creating a new client, all unhandled keyword arguments to
|
16
|
+
# Net::IMAP.new are delegated to Config.new. Every client has its own
|
17
|
+
# config.
|
18
|
+
#
|
19
|
+
# debug_client = Net::IMAP.new(hostname, debug: true)
|
20
|
+
# quiet_client = Net::IMAP.new(hostname, debug: false)
|
21
|
+
# debug_client.config.debug? # => true
|
22
|
+
# quiet_client.config.debug? # => false
|
23
|
+
#
|
24
|
+
# == Inheritance
|
25
|
+
#
|
26
|
+
# Configs have a parent[rdoc-ref:Config::AttrInheritance#parent] config, and
|
27
|
+
# any attributes which have not been set locally will inherit the parent's
|
28
|
+
# value. Every client creates its own specific config. By default, client
|
29
|
+
# configs inherit from Config.global.
|
30
|
+
#
|
31
|
+
# plain_client = Net::IMAP.new(hostname)
|
32
|
+
# debug_client = Net::IMAP.new(hostname, debug: true)
|
33
|
+
# quiet_client = Net::IMAP.new(hostname, debug: false)
|
34
|
+
#
|
35
|
+
# plain_client.config.inherited?(:debug) # => true
|
36
|
+
# debug_client.config.inherited?(:debug) # => false
|
37
|
+
# quiet_client.config.inherited?(:debug) # => false
|
38
|
+
#
|
39
|
+
# plain_client.config.debug? # => false
|
40
|
+
# debug_client.config.debug? # => true
|
41
|
+
# quiet_client.config.debug? # => false
|
42
|
+
#
|
43
|
+
# # Net::IMAP.debug is delegated to Net::IMAP::Config.global.debug
|
44
|
+
# Net::IMAP.debug = true
|
45
|
+
# plain_client.config.debug? # => true
|
46
|
+
# debug_client.config.debug? # => true
|
47
|
+
# quiet_client.config.debug? # => false
|
48
|
+
#
|
49
|
+
# Net::IMAP.debug = false
|
50
|
+
# plain_client.config.debug = true
|
51
|
+
# plain_client.config.inherited?(:debug) # => false
|
52
|
+
# plain_client.config.debug? # => true
|
53
|
+
# plain_client.config.reset(:debug)
|
54
|
+
# plain_client.config.inherited?(:debug) # => true
|
55
|
+
# plain_client.config.debug? # => false
|
56
|
+
#
|
57
|
+
# == Versioned defaults
|
58
|
+
#
|
59
|
+
# The effective default configuration for a specific +x.y+ version of
|
60
|
+
# +net-imap+ can be loaded with the +config+ keyword argument to
|
61
|
+
# Net::IMAP.new. Requesting default configurations for previous versions
|
62
|
+
# enables extra backward compatibility with those versions:
|
63
|
+
#
|
64
|
+
# client = Net::IMAP.new(hostname, config: 0.3)
|
65
|
+
# client.config.sasl_ir # => false
|
66
|
+
# client.config.responses_without_block # => :silence_deprecation_warning
|
67
|
+
#
|
68
|
+
# client = Net::IMAP.new(hostname, config: 0.4)
|
69
|
+
# client.config.sasl_ir # => true
|
70
|
+
# client.config.responses_without_block # => :silence_deprecation_warning
|
71
|
+
#
|
72
|
+
# client = Net::IMAP.new(hostname, config: 0.5)
|
73
|
+
# client.config.sasl_ir # => true
|
74
|
+
# client.config.responses_without_block # => :warn
|
75
|
+
#
|
76
|
+
# client = Net::IMAP.new(hostname, config: :future)
|
77
|
+
# client.config.sasl_ir # => true
|
78
|
+
# client.config.responses_without_block # => :frozen_dup
|
79
|
+
#
|
80
|
+
# The versioned default configs inherit certain specific config options from
|
81
|
+
# Config.global, for example #debug:
|
82
|
+
#
|
83
|
+
# client = Net::IMAP.new(hostname, config: 0.4)
|
84
|
+
# Net::IMAP.debug = false
|
85
|
+
# client.config.debug? # => false
|
86
|
+
#
|
87
|
+
# Net::IMAP.debug = true
|
88
|
+
# client.config.debug? # => true
|
89
|
+
#
|
90
|
+
# Use #load_defaults to globally behave like a specific version:
|
91
|
+
# client = Net::IMAP.new(hostname)
|
92
|
+
# client.config.sasl_ir # => true
|
93
|
+
# Net::IMAP.config.load_defaults 0.3
|
94
|
+
# client.config.sasl_ir # => false
|
95
|
+
#
|
96
|
+
# === Named defaults
|
97
|
+
# In addition to +x.y+ version numbers, the following aliases are supported:
|
98
|
+
#
|
99
|
+
# [+:default+]
|
100
|
+
# An alias for +:current+.
|
101
|
+
#
|
102
|
+
# >>>
|
103
|
+
# *NOTE*: This is _not_ the same as Config.default. It inherits some
|
104
|
+
# attributes from Config.global, for example: #debug.
|
105
|
+
# [+:current+]
|
106
|
+
# An alias for the current +x.y+ version's defaults.
|
107
|
+
# [+:next+]
|
108
|
+
# The _planned_ config for the next +x.y+ version.
|
109
|
+
# [+:future+]
|
110
|
+
# The _planned_ eventual config for some future +x.y+ version.
|
111
|
+
#
|
112
|
+
# For example, to disable all currently deprecated behavior:
|
113
|
+
# client = Net::IMAP.new(hostname, config: :future)
|
114
|
+
# client.config.response_without_args # => :frozen_dup
|
115
|
+
# client.responses.frozen? # => true
|
116
|
+
# client.responses.values.all?(&:frozen?) # => true
|
117
|
+
#
|
118
|
+
# == Thread Safety
|
119
|
+
#
|
120
|
+
# *NOTE:* Updates to config objects are not synchronized for thread-safety.
|
121
|
+
#
|
122
|
+
class Config
|
123
|
+
# Array of attribute names that are _not_ loaded by #load_defaults.
|
124
|
+
DEFAULT_TO_INHERIT = %i[debug].freeze
|
125
|
+
private_constant :DEFAULT_TO_INHERIT
|
126
|
+
|
127
|
+
# The default config, which is hardcoded and frozen.
|
128
|
+
def self.default; @default end
|
129
|
+
|
130
|
+
# The global config object. Also available from Net::IMAP.config.
|
131
|
+
def self.global; @global if defined?(@global) end
|
132
|
+
|
133
|
+
# A hash of hard-coded configurations, indexed by version number or name.
|
134
|
+
def self.version_defaults; @version_defaults end
|
135
|
+
@version_defaults = {}
|
136
|
+
|
137
|
+
# :call-seq:
|
138
|
+
# Net::IMAP::Config[number] -> versioned config
|
139
|
+
# Net::IMAP::Config[symbol] -> named config
|
140
|
+
# Net::IMAP::Config[hash] -> new frozen config
|
141
|
+
# Net::IMAP::Config[config] -> same config
|
142
|
+
#
|
143
|
+
# Given a version number, returns the default configuration for the target
|
144
|
+
# version. See Config@Versioned+defaults.
|
145
|
+
#
|
146
|
+
# Given a version name, returns the default configuration for the target
|
147
|
+
# version. See Config@Named+defaults.
|
148
|
+
#
|
149
|
+
# Given a Hash, creates a new _frozen_ config which inherits from
|
150
|
+
# Config.global. Use Config.new for an unfrozen config.
|
151
|
+
#
|
152
|
+
# Given a config, returns that same config.
|
153
|
+
def self.[](config)
|
154
|
+
if config.is_a?(Config) then config
|
155
|
+
elsif config.nil? && global.nil? then nil
|
156
|
+
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
|
157
|
+
else
|
158
|
+
version_defaults.fetch(config) do
|
159
|
+
case config
|
160
|
+
when Numeric
|
161
|
+
raise RangeError, "unknown config version: %p" % [config]
|
162
|
+
when Symbol
|
163
|
+
raise KeyError, "unknown config name: %p" % [config]
|
164
|
+
else
|
165
|
+
raise TypeError, "no implicit conversion of %s to %s" % [
|
166
|
+
config.class, Config
|
167
|
+
]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
include AttrAccessors
|
174
|
+
include AttrInheritance
|
175
|
+
include AttrTypeCoercion
|
176
|
+
|
177
|
+
# The debug mode (boolean). The default value is +false+.
|
178
|
+
#
|
179
|
+
# When #debug is +true+:
|
180
|
+
# * Data sent to and received from the server will be logged.
|
181
|
+
# * ResponseParser will print warnings with extra detail for parse
|
182
|
+
# errors. _This may include recoverable errors._
|
183
|
+
# * ResponseParser makes extra assertions.
|
184
|
+
#
|
185
|
+
# *NOTE:* Versioned default configs inherit #debug from Config.global, and
|
186
|
+
# #load_defaults will not override #debug.
|
187
|
+
attr_accessor :debug, type: :boolean
|
188
|
+
|
189
|
+
# method: debug?
|
190
|
+
# :call-seq: debug? -> boolean
|
191
|
+
#
|
192
|
+
# Alias for #debug
|
193
|
+
|
194
|
+
# Seconds to wait until a connection is opened.
|
195
|
+
#
|
196
|
+
# If the IMAP object cannot open a connection within this time,
|
197
|
+
# it raises a Net::OpenTimeout exception.
|
198
|
+
#
|
199
|
+
# See Net::IMAP.new.
|
200
|
+
#
|
201
|
+
# The default value is +30+ seconds.
|
202
|
+
attr_accessor :open_timeout, type: Integer
|
203
|
+
|
204
|
+
# Seconds to wait until an IDLE response is received, after
|
205
|
+
# the client asks to leave the IDLE state.
|
206
|
+
#
|
207
|
+
# See Net::IMAP#idle and Net::IMAP#idle_done.
|
208
|
+
#
|
209
|
+
# The default value is +5+ seconds.
|
210
|
+
attr_accessor :idle_response_timeout, type: Integer
|
211
|
+
|
212
|
+
# Whether to use the +SASL-IR+ extension when the server and \SASL
|
213
|
+
# mechanism both support it. Can be overridden by the +sasl_ir+ keyword
|
214
|
+
# parameter to Net::IMAP#authenticate.
|
215
|
+
#
|
216
|
+
# <em>(Support for +SASL-IR+ was added in +v0.4.0+.)</em>
|
217
|
+
#
|
218
|
+
# ==== Valid options
|
219
|
+
#
|
220
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
221
|
+
# Do not use +SASL-IR+, even when it is supported by the server and the
|
222
|
+
# mechanism.
|
223
|
+
#
|
224
|
+
# [+true+ <em>(default since +v0.4+)</em>]
|
225
|
+
# Use +SASL-IR+ when it is supported by the server and the mechanism.
|
226
|
+
attr_accessor :sasl_ir, type: :boolean
|
227
|
+
|
228
|
+
# Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
|
229
|
+
# capability is present. When enforced, Net::IMAP will raise a
|
230
|
+
# LoginDisabledError when that capability is present.
|
231
|
+
#
|
232
|
+
# <em>(Support for +LOGINDISABLED+ was added in +v0.5.0+.)</em>
|
233
|
+
#
|
234
|
+
# ==== Valid options
|
235
|
+
#
|
236
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
237
|
+
# Send the +LOGIN+ command without checking for +LOGINDISABLED+.
|
238
|
+
#
|
239
|
+
# [+:when_capabilities_cached+]
|
240
|
+
# Enforce the requirement when Net::IMAP#capabilities_cached? is true,
|
241
|
+
# but do not send a +CAPABILITY+ command to discover the capabilities.
|
242
|
+
#
|
243
|
+
# [+true+ <em>(default since +v0.5+)</em>]
|
244
|
+
# Only send the +LOGIN+ command if the +LOGINDISABLED+ capability is not
|
245
|
+
# present. When capabilities are unknown, Net::IMAP will automatically
|
246
|
+
# send a +CAPABILITY+ command first before sending +LOGIN+.
|
247
|
+
#
|
248
|
+
attr_accessor :enforce_logindisabled, type: [
|
249
|
+
false, :when_capabilities_cached, true
|
250
|
+
]
|
251
|
+
|
252
|
+
# Controls the behavior of Net::IMAP#responses when called without any
|
253
|
+
# arguments (+type+ or +block+).
|
254
|
+
#
|
255
|
+
# ==== Valid options
|
256
|
+
#
|
257
|
+
# [+:silence_deprecation_warning+ <em>(original behavior)</em>]
|
258
|
+
# Returns the mutable responses hash (without any warnings).
|
259
|
+
# <em>This is not thread-safe.</em>
|
260
|
+
#
|
261
|
+
# [+:warn+ <em>(default since +v0.5+)</em>]
|
262
|
+
# Prints a warning and returns the mutable responses hash.
|
263
|
+
# <em>This is not thread-safe.</em>
|
264
|
+
#
|
265
|
+
# [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
|
266
|
+
# Returns a frozen copy of the unhandled responses hash, with frozen
|
267
|
+
# array values.
|
268
|
+
#
|
269
|
+
# Note that calling IMAP#responses with a +type+ and without a block is
|
270
|
+
# not configurable and always behaves like +:frozen_dup+.
|
271
|
+
#
|
272
|
+
# <em>(+:frozen_dup+ config option was added in +v0.4.17+)</em>
|
273
|
+
#
|
274
|
+
# [+:raise+]
|
275
|
+
# Raise an ArgumentError with the deprecation warning.
|
276
|
+
#
|
277
|
+
# Note: #responses_without_args is an alias for #responses_without_block.
|
278
|
+
attr_accessor :responses_without_block, type: [
|
279
|
+
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
|
280
|
+
]
|
281
|
+
|
282
|
+
alias responses_without_args responses_without_block # :nodoc:
|
283
|
+
alias responses_without_args= responses_without_block= # :nodoc:
|
284
|
+
|
285
|
+
##
|
286
|
+
# :attr_accessor: responses_without_args
|
287
|
+
#
|
288
|
+
# Alias for responses_without_block
|
289
|
+
|
290
|
+
# Creates a new config object and initialize its attribute with +attrs+.
|
291
|
+
#
|
292
|
+
# If +parent+ is not given, the global config is used by default.
|
293
|
+
#
|
294
|
+
# If a block is given, the new config object is yielded to it.
|
295
|
+
def initialize(parent = Config.global, **attrs)
|
296
|
+
super(parent)
|
297
|
+
update(**attrs)
|
298
|
+
yield self if block_given?
|
299
|
+
end
|
300
|
+
|
301
|
+
# :call-seq: update(**attrs) -> self
|
302
|
+
#
|
303
|
+
# Assigns all of the provided +attrs+ to this config, and returns +self+.
|
304
|
+
#
|
305
|
+
# An ArgumentError is raised unless every key in +attrs+ matches an
|
306
|
+
# assignment method on Config.
|
307
|
+
#
|
308
|
+
# >>>
|
309
|
+
# *NOTE:* #update is not atomic. If an exception is raised due to an
|
310
|
+
# invalid attribute value, +attrs+ may be partially applied.
|
311
|
+
def update(**attrs)
|
312
|
+
unless (bad = attrs.keys.reject { respond_to?(:"#{_1}=") }).empty?
|
313
|
+
raise ArgumentError, "invalid config options: #{bad.join(", ")}"
|
314
|
+
end
|
315
|
+
attrs.each do send(:"#{_1}=", _2) end
|
316
|
+
self
|
317
|
+
end
|
318
|
+
|
319
|
+
# :call-seq:
|
320
|
+
# with(**attrs) -> config
|
321
|
+
# with(**attrs) {|config| } -> result
|
322
|
+
#
|
323
|
+
# Without a block, returns a new config which inherits from self. With a
|
324
|
+
# block, yields the new config and returns the block's result.
|
325
|
+
#
|
326
|
+
# If no keyword arguments are given, an ArgumentError will be raised.
|
327
|
+
#
|
328
|
+
# If +self+ is frozen, the copy will also be frozen.
|
329
|
+
def with(**attrs)
|
330
|
+
attrs.empty? and
|
331
|
+
raise ArgumentError, "expected keyword arguments, none given"
|
332
|
+
copy = new(**attrs)
|
333
|
+
copy.freeze if frozen?
|
334
|
+
block_given? ? yield(copy) : copy
|
335
|
+
end
|
336
|
+
|
337
|
+
# :call-seq: load_defaults(version) -> self
|
338
|
+
#
|
339
|
+
# Resets the current config to behave like the versioned default
|
340
|
+
# configuration for +version+. #parent will not be changed.
|
341
|
+
#
|
342
|
+
# Some config attributes default to inheriting from their #parent (which
|
343
|
+
# is usually Config.global) and are left unchanged, for example: #debug.
|
344
|
+
#
|
345
|
+
# See Config@Versioned+defaults and Config@Named+defaults.
|
346
|
+
def load_defaults(version)
|
347
|
+
[Numeric, Symbol, String].any? { _1 === version } or
|
348
|
+
raise ArgumentError, "expected number or symbol, got %p" % [version]
|
349
|
+
update(**Config[version].defaults_hash)
|
350
|
+
end
|
351
|
+
|
352
|
+
# :call-seq: to_h -> hash
|
353
|
+
#
|
354
|
+
# Returns all config attributes in a hash.
|
355
|
+
def to_h; data.members.to_h { [_1, send(_1)] } end
|
356
|
+
|
357
|
+
protected
|
358
|
+
|
359
|
+
def defaults_hash
|
360
|
+
to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
|
361
|
+
end
|
362
|
+
|
363
|
+
@default = new(
|
364
|
+
debug: false,
|
365
|
+
open_timeout: 30,
|
366
|
+
idle_response_timeout: 5,
|
367
|
+
sasl_ir: true,
|
368
|
+
enforce_logindisabled: true,
|
369
|
+
responses_without_block: :warn,
|
370
|
+
).freeze
|
371
|
+
|
372
|
+
@global = default.new
|
373
|
+
|
374
|
+
version_defaults[:default] = Config[default.send(:defaults_hash)]
|
375
|
+
version_defaults[:current] = Config[:default]
|
376
|
+
|
377
|
+
version_defaults[0] = Config[:current].dup.update(
|
378
|
+
sasl_ir: false,
|
379
|
+
responses_without_block: :silence_deprecation_warning,
|
380
|
+
enforce_logindisabled: false,
|
381
|
+
).freeze
|
382
|
+
version_defaults[0.0] = Config[0]
|
383
|
+
version_defaults[0.1] = Config[0]
|
384
|
+
version_defaults[0.2] = Config[0]
|
385
|
+
version_defaults[0.3] = Config[0]
|
386
|
+
|
387
|
+
version_defaults[0.4] = Config[0.3].dup.update(
|
388
|
+
sasl_ir: true,
|
389
|
+
).freeze
|
390
|
+
|
391
|
+
version_defaults[0.5] = Config[:current]
|
392
|
+
|
393
|
+
version_defaults[0.6] = Config[0.5].dup.update(
|
394
|
+
responses_without_block: :frozen_dup,
|
395
|
+
).freeze
|
396
|
+
version_defaults[:next] = Config[0.6]
|
397
|
+
version_defaults[:future] = Config[:next]
|
398
|
+
|
399
|
+
version_defaults.freeze
|
400
|
+
end
|
401
|
+
end
|
402
|
+
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
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Some of the code in this file was copied from the polyfill-data gem.
|
4
|
+
#
|
5
|
+
# MIT License
|
6
|
+
#
|
7
|
+
# Copyright (c) 2023 Jim Gay, Joel Drapper, Nicholas Evans
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
# of this software and associated documentation files (the "Software"), to deal
|
11
|
+
# in the Software without restriction, including without limitation the rights
|
12
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13
|
+
# copies of the Software, and to permit persons to whom the Software is
|
14
|
+
# furnished to do so, subject to the following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be included in all
|
17
|
+
# copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
|
+
# SOFTWARE.
|
26
|
+
|
27
|
+
|
28
|
+
module Net
|
29
|
+
class IMAP
|
30
|
+
data_or_object = RUBY_VERSION >= "3.2.0" ? ::Data : Object
|
31
|
+
class DataLite < data_or_object
|
32
|
+
def encode_with(coder) coder.map = to_h.transform_keys(&:to_s) end
|
33
|
+
def init_with(coder) initialize(**coder.map.transform_keys(&:to_sym)) end
|
34
|
+
end
|
35
|
+
|
36
|
+
Data = DataLite
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# :nocov:
|
41
|
+
# Need to skip test coverage for the rest, because it isn't loaded by ruby 3.2+.
|
42
|
+
return if RUBY_VERSION >= "3.2.0"
|
43
|
+
|
44
|
+
module Net
|
45
|
+
class IMAP
|
46
|
+
# DataLite is a temporary substitute for ruby 3.2's +Data+ class. DataLite
|
47
|
+
# is aliased as Net::IMAP::Data, so that code using it won't need to be
|
48
|
+
# updated when it is removed.
|
49
|
+
#
|
50
|
+
# See {ruby 3.2's documentation for Data}[https://docs.ruby-lang.org/en/3.2/Data.html].
|
51
|
+
#
|
52
|
+
# [When running ruby 3.1]
|
53
|
+
# This class reimplements the API for ruby 3.2's +Data+, and should be
|
54
|
+
# compatible for nearly all use-cases. This reimplementation <em>will be
|
55
|
+
# removed</em> in +net-imap+ 0.6, when support for ruby 3.1 is dropped.
|
56
|
+
#
|
57
|
+
# _NOTE:_ +net-imap+ no longer supports ruby versions prior to 3.1.
|
58
|
+
# [When running ruby >= 3.2]
|
59
|
+
# This class inherits from +Data+ and _only_ defines the methods needed
|
60
|
+
# for YAML serialization. This will be dropped when +psych+ adds support
|
61
|
+
# for +Data+.
|
62
|
+
#
|
63
|
+
# Some of the code in this class was copied or adapted from the
|
64
|
+
# {polyfill-data gem}[https://rubygems.org/gems/polyfill-data], by Jim Gay
|
65
|
+
# and Joel Drapper, under the MIT license terms.
|
66
|
+
class DataLite
|
67
|
+
singleton_class.undef_method :new
|
68
|
+
|
69
|
+
TYPE_ERROR = "%p is not a symbol nor a string"
|
70
|
+
ATTRSET_ERROR = "invalid data member: %p"
|
71
|
+
DUP_ERROR = "duplicate member: %p"
|
72
|
+
ARITY_ERROR = "wrong number of arguments (given %d, expected %s)"
|
73
|
+
private_constant :TYPE_ERROR, :ATTRSET_ERROR, :DUP_ERROR, :ARITY_ERROR
|
74
|
+
|
75
|
+
# Defines a new Data class.
|
76
|
+
#
|
77
|
+
# _NOTE:_ Unlike ruby 3.2's +Data.define+, DataLite.define only supports
|
78
|
+
# member names which are valid local variable names. Member names can't
|
79
|
+
# be keywords (e.g: +next+ or +class+) or start with capital letters, "@",
|
80
|
+
# etc.
|
81
|
+
def self.define(*args, &block)
|
82
|
+
members = args.each_with_object({}) do |arg, members|
|
83
|
+
arg = arg.to_str unless arg in Symbol | String if arg.respond_to?(:to_str)
|
84
|
+
arg = arg.to_sym if arg in String
|
85
|
+
arg in Symbol or raise TypeError, TYPE_ERROR % [arg]
|
86
|
+
arg in %r{=} and raise ArgumentError, ATTRSET_ERROR % [arg]
|
87
|
+
members.key?(arg) and raise ArgumentError, DUP_ERROR % [arg]
|
88
|
+
members[arg] = true
|
89
|
+
end
|
90
|
+
members = members.keys.freeze
|
91
|
+
|
92
|
+
klass = ::Class.new(self)
|
93
|
+
|
94
|
+
klass.singleton_class.undef_method :define
|
95
|
+
klass.define_singleton_method(:members) { members }
|
96
|
+
|
97
|
+
def klass.new(*args, **kwargs, &block)
|
98
|
+
if kwargs.size.positive?
|
99
|
+
if args.size.positive?
|
100
|
+
raise ArgumentError, ARITY_ERROR % [args.size, 0]
|
101
|
+
end
|
102
|
+
elsif members.size < args.size
|
103
|
+
expected = members.size.zero? ? 0 : 0..members.size
|
104
|
+
raise ArgumentError, ARITY_ERROR % [args.size, expected]
|
105
|
+
else
|
106
|
+
kwargs = Hash[members.take(args.size).zip(args)]
|
107
|
+
end
|
108
|
+
allocate.tap do |instance|
|
109
|
+
instance.__send__(:initialize, **kwargs, &block)
|
110
|
+
end.freeze
|
111
|
+
end
|
112
|
+
|
113
|
+
klass.singleton_class.alias_method :[], :new
|
114
|
+
klass.attr_reader(*members)
|
115
|
+
|
116
|
+
# Dynamically defined initializer methods are in an included module,
|
117
|
+
# rather than directly on DataLite (like in ruby 3.2+):
|
118
|
+
# * simpler to handle required kwarg ArgumentErrors
|
119
|
+
# * easier to ensure consistent ivar assignment order (object shape)
|
120
|
+
# * faster than instance_variable_set
|
121
|
+
klass.include(Module.new do
|
122
|
+
if members.any?
|
123
|
+
kwargs = members.map{"#{_1.name}:"}.join(", ")
|
124
|
+
params = members.map(&:name).join(", ")
|
125
|
+
ivars = members.map{"@#{_1.name}"}.join(", ")
|
126
|
+
attrs = members.map{"attrs[:#{_1.name}]"}.join(", ")
|
127
|
+
module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
128
|
+
protected
|
129
|
+
def initialize(#{kwargs}) #{ivars} = #{params}; freeze end
|
130
|
+
def marshal_load(attrs) #{ivars} = #{attrs}; freeze end
|
131
|
+
RUBY
|
132
|
+
end
|
133
|
+
end)
|
134
|
+
|
135
|
+
klass.module_eval do _1.module_eval(&block) end if block_given?
|
136
|
+
|
137
|
+
klass
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# singleton-method: new
|
142
|
+
# call-seq:
|
143
|
+
# new(*args) -> instance
|
144
|
+
# new(**kwargs) -> instance
|
145
|
+
#
|
146
|
+
# Constuctor for classes defined with ::define.
|
147
|
+
#
|
148
|
+
# Aliased as ::[].
|
149
|
+
|
150
|
+
##
|
151
|
+
# singleton-method: []
|
152
|
+
# call-seq:
|
153
|
+
# ::[](*args) -> instance
|
154
|
+
# ::[](**kwargs) -> instance
|
155
|
+
#
|
156
|
+
# Constuctor for classes defined with ::define.
|
157
|
+
#
|
158
|
+
# Alias for ::new
|
159
|
+
|
160
|
+
##
|
161
|
+
def members; self.class.members end
|
162
|
+
def to_h(&block) block ? __to_h__.to_h(&block) : __to_h__ end
|
163
|
+
def hash; [self.class, __to_h__].hash end
|
164
|
+
def ==(other) self.class == other.class && to_h == other.to_h end
|
165
|
+
def eql?(other) self.class == other.class && hash == other.hash end
|
166
|
+
def deconstruct; __to_h__.values end
|
167
|
+
|
168
|
+
def deconstruct_keys(keys)
|
169
|
+
raise TypeError unless keys.is_a?(Array) || keys.nil?
|
170
|
+
return __to_h__ if keys&.first.nil?
|
171
|
+
__to_h__.slice(*keys)
|
172
|
+
end
|
173
|
+
|
174
|
+
def with(**kwargs)
|
175
|
+
return self if kwargs.empty?
|
176
|
+
self.class.new(**__to_h__.merge(kwargs))
|
177
|
+
end
|
178
|
+
|
179
|
+
def inspect
|
180
|
+
__inspect_guard__(self) do |seen|
|
181
|
+
return "#<data #{self.class}:...>" if seen
|
182
|
+
attrs = __to_h__.map {|kv| "%s=%p" % kv }.join(", ")
|
183
|
+
display = ["data", self.class.name, attrs].compact.join(" ")
|
184
|
+
"#<#{display}>"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
alias_method :to_s, :inspect
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def initialize_copy(source) super.freeze end
|
192
|
+
def marshal_dump; __to_h__ end
|
193
|
+
|
194
|
+
def __to_h__; Hash[members.map {|m| [m, send(m)] }] end
|
195
|
+
|
196
|
+
# Yields +true+ if +obj+ has been seen already, +false+ if it hasn't.
|
197
|
+
# Marks +obj+ as seen inside the block, so circuler references don't
|
198
|
+
# recursively trigger a SystemStackError (stack level too deep).
|
199
|
+
#
|
200
|
+
# Making circular references inside a Data object _should_ be very
|
201
|
+
# uncommon, but we'll support them for the sake of completeness.
|
202
|
+
def __inspect_guard__(obj)
|
203
|
+
preexisting = Thread.current[:__net_imap_data__inspect__]
|
204
|
+
Thread.current[:__net_imap_data__inspect__] ||= {}.compare_by_identity
|
205
|
+
inspect_guard = Thread.current[:__net_imap_data__inspect__]
|
206
|
+
if inspect_guard.include?(obj)
|
207
|
+
yield true
|
208
|
+
else
|
209
|
+
begin
|
210
|
+
inspect_guard[obj] = true
|
211
|
+
yield false
|
212
|
+
ensure
|
213
|
+
inspect_guard.delete(obj)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
ensure
|
217
|
+
unless preexisting.equal?(inspect_guard)
|
218
|
+
Thread.current[:__net_imap_data__inspect__] = preexisting
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|
226
|
+
# :nocov:
|
@@ -16,8 +16,8 @@ module Net
|
|
16
16
|
#
|
17
17
|
# ==== Obsolete arguments
|
18
18
|
#
|
19
|
-
#
|
20
|
-
# deprecated by a future release.
|
19
|
+
# Use of obsolete arguments does not print a warning. Obsolete arguments
|
20
|
+
# will be deprecated by a future release.
|
21
21
|
#
|
22
22
|
# If a second positional argument is given and it is a hash (or is
|
23
23
|
# convertible via +#to_hash+), it is converted to keyword arguments.
|
@@ -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
|