net-imap 0.4.12 → 0.5.5
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.
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
|