net-imap 0.3.9 → 0.5.8
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/BSDL +22 -0
- data/COPYING +56 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +3 -22
- data/README.md +25 -8
- data/Rakefile +0 -7
- data/docs/styles.css +72 -23
- data/lib/net/imap/authenticators.rb +26 -57
- data/lib/net/imap/command_data.rb +74 -54
- 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 +62 -0
- data/lib/net/imap/config.rb +552 -0
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +18 -6
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +142 -0
- data/lib/net/imap/errors.rb +28 -3
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +597 -0
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +250 -440
- data/lib/net/imap/response_parser/parser_utils.rb +245 -0
- data/lib/net/imap/response_parser.rb +1873 -1210
- data/lib/net/imap/response_reader.rb +10 -12
- data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
- data/lib/net/imap/sasl/authentication_exchange.rb +139 -0
- data/lib/net/imap/sasl/authenticators.rb +122 -0
- data/lib/net/imap/sasl/client_adapter.rb +123 -0
- data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +24 -14
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +342 -0
- data/lib/net/imap/sasl/external_authenticator.rb +83 -0
- data/lib/net/imap/sasl/gs2_header.rb +80 -0
- data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +28 -18
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
- data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
- data/lib/net/imap/sasl/protocol_adapters.rb +101 -0
- data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
- data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
- data/lib/net/imap/sasl/stringprep.rb +6 -66
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
- data/lib/net/imap/sasl.rb +148 -44
- data/lib/net/imap/sasl_adapter.rb +20 -0
- data/lib/net/imap/search_result.rb +146 -0
- data/lib/net/imap/sequence_set.rb +1721 -0
- data/lib/net/imap/stringprep/nameprep.rb +70 -0
- data/lib/net/imap/stringprep/saslprep.rb +69 -0
- data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
- data/lib/net/imap/stringprep/tables.rb +146 -0
- data/lib/net/imap/stringprep/trace.rb +85 -0
- data/lib/net/imap/stringprep.rb +159 -0
- data/lib/net/imap/uidplus_data.rb +244 -0
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +2217 -861
- data/net-imap.gemspec +7 -8
- data/rakelib/benchmarks.rake +91 -0
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/saslprep.rake +4 -4
- data/rakelib/string_prep_tables_generator.rb +84 -60
- data/sample/net-imap.rb +167 -0
- metadata +45 -45
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/test.yml +0 -38
- data/.gitignore +0 -10
- data/benchmarks/stringprep.yml +0 -65
- data/benchmarks/table-regexps.yml +0 -39
- data/lib/net/imap/authenticators/digest_md5.rb +0 -115
- data/lib/net/imap/authenticators/plain.rb +0 -41
- data/lib/net/imap/authenticators/xoauth2.rb +0 -20
- data/lib/net/imap/sasl/saslprep.rb +0 -55
- data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
- data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
@@ -0,0 +1,552 @@
|
|
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
|
+
# Values can be accessed with any object that responds to +to_sym+ or
|
135
|
+
# +to_r+/+to_f+ with a non-zero number.
|
136
|
+
#
|
137
|
+
# Config::[] gets named or numbered versions from this hash.
|
138
|
+
#
|
139
|
+
# For example:
|
140
|
+
# Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
|
141
|
+
# Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
|
142
|
+
# Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
|
143
|
+
# Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
|
144
|
+
def self.version_defaults; @version_defaults end
|
145
|
+
@version_defaults = Hash.new {|h, k|
|
146
|
+
# NOTE: String responds to both so the order is significant.
|
147
|
+
# And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
|
148
|
+
(h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
|
149
|
+
(h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
|
150
|
+
(h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
|
151
|
+
(h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
|
152
|
+
}
|
153
|
+
|
154
|
+
# :call-seq:
|
155
|
+
# Net::IMAP::Config[number] -> versioned config
|
156
|
+
# Net::IMAP::Config[symbol] -> named config
|
157
|
+
# Net::IMAP::Config[hash] -> new frozen config
|
158
|
+
# Net::IMAP::Config[config] -> same config
|
159
|
+
#
|
160
|
+
# Given a version number, returns the default configuration for the target
|
161
|
+
# version. See Config@Versioned+defaults.
|
162
|
+
#
|
163
|
+
# Given a version name, returns the default configuration for the target
|
164
|
+
# version. See Config@Named+defaults.
|
165
|
+
#
|
166
|
+
# Given a Hash, creates a new _frozen_ config which inherits from
|
167
|
+
# Config.global. Use Config.new for an unfrozen config.
|
168
|
+
#
|
169
|
+
# Given a config, returns that same config.
|
170
|
+
def self.[](config)
|
171
|
+
if config.is_a?(Config) then config
|
172
|
+
elsif config.nil? && global.nil? then nil
|
173
|
+
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
|
174
|
+
else
|
175
|
+
version_defaults[config] or
|
176
|
+
case config
|
177
|
+
when Numeric
|
178
|
+
raise RangeError, "unknown config version: %p" % [config]
|
179
|
+
when String, Symbol
|
180
|
+
raise KeyError, "unknown config name: %p" % [config]
|
181
|
+
else
|
182
|
+
raise TypeError, "no implicit conversion of %s to %s" % [
|
183
|
+
config.class, Config
|
184
|
+
]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
include AttrAccessors
|
190
|
+
include AttrInheritance
|
191
|
+
include AttrTypeCoercion
|
192
|
+
|
193
|
+
# The debug mode (boolean). The default value is +false+.
|
194
|
+
#
|
195
|
+
# When #debug is +true+:
|
196
|
+
# * Data sent to and received from the server will be logged.
|
197
|
+
# * ResponseParser will print warnings with extra detail for parse
|
198
|
+
# errors. _This may include recoverable errors._
|
199
|
+
# * ResponseParser makes extra assertions.
|
200
|
+
#
|
201
|
+
# *NOTE:* Versioned default configs inherit #debug from Config.global, and
|
202
|
+
# #load_defaults will not override #debug.
|
203
|
+
attr_accessor :debug, type: :boolean
|
204
|
+
|
205
|
+
# method: debug?
|
206
|
+
# :call-seq: debug? -> boolean
|
207
|
+
#
|
208
|
+
# Alias for #debug
|
209
|
+
|
210
|
+
# Seconds to wait until a connection is opened.
|
211
|
+
#
|
212
|
+
# Applied separately for establishing TCP connection and starting a TLS
|
213
|
+
# connection.
|
214
|
+
#
|
215
|
+
# If the IMAP object cannot open a connection within this time,
|
216
|
+
# it raises a Net::OpenTimeout exception.
|
217
|
+
#
|
218
|
+
# See Net::IMAP.new and Net::IMAP#starttls.
|
219
|
+
#
|
220
|
+
# The default value is +30+ seconds.
|
221
|
+
attr_accessor :open_timeout, type: Integer
|
222
|
+
|
223
|
+
# Seconds to wait until an IDLE response is received, after
|
224
|
+
# the client asks to leave the IDLE state.
|
225
|
+
#
|
226
|
+
# See Net::IMAP#idle and Net::IMAP#idle_done.
|
227
|
+
#
|
228
|
+
# The default value is +5+ seconds.
|
229
|
+
attr_accessor :idle_response_timeout, type: Integer
|
230
|
+
|
231
|
+
# Whether to use the +SASL-IR+ extension when the server and \SASL
|
232
|
+
# mechanism both support it. Can be overridden by the +sasl_ir+ keyword
|
233
|
+
# parameter to Net::IMAP#authenticate.
|
234
|
+
#
|
235
|
+
# <em>(Support for +SASL-IR+ was added in +v0.4.0+.)</em>
|
236
|
+
#
|
237
|
+
# ==== Valid options
|
238
|
+
#
|
239
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
240
|
+
# Do not use +SASL-IR+, even when it is supported by the server and the
|
241
|
+
# mechanism.
|
242
|
+
#
|
243
|
+
# [+true+ <em>(default since +v0.4+)</em>]
|
244
|
+
# Use +SASL-IR+ when it is supported by the server and the mechanism.
|
245
|
+
attr_accessor :sasl_ir, type: :boolean
|
246
|
+
|
247
|
+
# Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
|
248
|
+
# capability is present. When enforced, Net::IMAP will raise a
|
249
|
+
# LoginDisabledError when that capability is present.
|
250
|
+
#
|
251
|
+
# <em>(Support for +LOGINDISABLED+ was added in +v0.5.0+.)</em>
|
252
|
+
#
|
253
|
+
# ==== Valid options
|
254
|
+
#
|
255
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
256
|
+
# Send the +LOGIN+ command without checking for +LOGINDISABLED+.
|
257
|
+
#
|
258
|
+
# [+:when_capabilities_cached+]
|
259
|
+
# Enforce the requirement when Net::IMAP#capabilities_cached? is true,
|
260
|
+
# but do not send a +CAPABILITY+ command to discover the capabilities.
|
261
|
+
#
|
262
|
+
# [+true+ <em>(default since +v0.5+)</em>]
|
263
|
+
# Only send the +LOGIN+ command if the +LOGINDISABLED+ capability is not
|
264
|
+
# present. When capabilities are unknown, Net::IMAP will automatically
|
265
|
+
# send a +CAPABILITY+ command first before sending +LOGIN+.
|
266
|
+
#
|
267
|
+
attr_accessor :enforce_logindisabled, type: Enum[
|
268
|
+
false, :when_capabilities_cached, true
|
269
|
+
]
|
270
|
+
|
271
|
+
# The maximum allowed server response size. When +nil+, there is no limit
|
272
|
+
# on response size.
|
273
|
+
#
|
274
|
+
# The default value (512 MiB, since +v0.5.7+) is <em>very high</em> and
|
275
|
+
# unlikely to be reached. A _much_ lower value should be used with
|
276
|
+
# untrusted servers (for example, when connecting to a user-provided
|
277
|
+
# hostname). When using a lower limit, message bodies should be fetched
|
278
|
+
# in chunks rather than all at once.
|
279
|
+
#
|
280
|
+
# <em>Please Note:</em> this only limits the size per response. It does
|
281
|
+
# not prevent a flood of individual responses and it does not limit how
|
282
|
+
# many unhandled responses may be stored on the responses hash. See
|
283
|
+
# Net::IMAP@Unbounded+memory+use.
|
284
|
+
#
|
285
|
+
# Socket reads are limited to the maximum remaining bytes for the current
|
286
|
+
# response: max_response_size minus the bytes that have already been read.
|
287
|
+
# When the limit is reached, or reading a +literal+ _would_ go over the
|
288
|
+
# limit, ResponseTooLargeError is raised and the connection is closed.
|
289
|
+
#
|
290
|
+
# Note that changes will not take effect immediately, because the receiver
|
291
|
+
# thread may already be waiting for the next response using the previous
|
292
|
+
# value. Net::IMAP#noop can force a response and enforce the new setting
|
293
|
+
# immediately.
|
294
|
+
#
|
295
|
+
# ==== Versioned Defaults
|
296
|
+
#
|
297
|
+
# Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
|
298
|
+
# attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to this
|
299
|
+
# config attribute.</em>
|
300
|
+
#
|
301
|
+
# * original: +nil+ <em>(no limit)</em>
|
302
|
+
# * +0.5+: 512 MiB
|
303
|
+
attr_accessor :max_response_size, type: Integer?
|
304
|
+
|
305
|
+
# Controls the behavior of Net::IMAP#responses when called without any
|
306
|
+
# arguments (+type+ or +block+).
|
307
|
+
#
|
308
|
+
# ==== Valid options
|
309
|
+
#
|
310
|
+
# [+:silence_deprecation_warning+ <em>(original behavior)</em>]
|
311
|
+
# Returns the mutable responses hash (without any warnings).
|
312
|
+
# <em>This is not thread-safe.</em>
|
313
|
+
#
|
314
|
+
# [+:warn+ <em>(default since +v0.5+)</em>]
|
315
|
+
# Prints a warning and returns the mutable responses hash.
|
316
|
+
# <em>This is not thread-safe.</em>
|
317
|
+
#
|
318
|
+
# [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
|
319
|
+
# Returns a frozen copy of the unhandled responses hash, with frozen
|
320
|
+
# array values.
|
321
|
+
#
|
322
|
+
# Note that calling IMAP#responses with a +type+ and without a block is
|
323
|
+
# not configurable and always behaves like +:frozen_dup+.
|
324
|
+
#
|
325
|
+
# <em>(+:frozen_dup+ config option was added in +v0.4.17+)</em>
|
326
|
+
#
|
327
|
+
# [+:raise+]
|
328
|
+
# Raise an ArgumentError with the deprecation warning.
|
329
|
+
#
|
330
|
+
# Note: #responses_without_args is an alias for #responses_without_block.
|
331
|
+
attr_accessor :responses_without_block, type: Enum[
|
332
|
+
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
|
333
|
+
]
|
334
|
+
|
335
|
+
alias responses_without_args responses_without_block # :nodoc:
|
336
|
+
alias responses_without_args= responses_without_block= # :nodoc:
|
337
|
+
|
338
|
+
##
|
339
|
+
# :attr_accessor: responses_without_args
|
340
|
+
#
|
341
|
+
# Alias for responses_without_block
|
342
|
+
|
343
|
+
# Whether ResponseParser should use the deprecated UIDPlusData or
|
344
|
+
# CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
|
345
|
+
# AppendUIDData for +APPENDUID+ response codes.
|
346
|
+
#
|
347
|
+
# UIDPlusData stores its data in arrays of numbers, which is vulnerable to
|
348
|
+
# a memory exhaustion denial of service attack from an untrusted or
|
349
|
+
# compromised server. Set this option to +false+ to completely block this
|
350
|
+
# vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
|
351
|
+
# mitigates this vulnerability.
|
352
|
+
#
|
353
|
+
# AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
|
354
|
+
# UIDPlusData. Most applications should be able to upgrade with little
|
355
|
+
# or no changes.
|
356
|
+
#
|
357
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
358
|
+
#
|
359
|
+
# <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
|
360
|
+
#
|
361
|
+
# <em>UIDPlusData will be removed in +v0.6+ and this config setting will
|
362
|
+
# be ignored.</em>
|
363
|
+
#
|
364
|
+
# ==== Valid options
|
365
|
+
#
|
366
|
+
# [+true+ <em>(original default)</em>]
|
367
|
+
# ResponseParser only uses UIDPlusData.
|
368
|
+
#
|
369
|
+
# [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
|
370
|
+
# ResponseParser uses UIDPlusData when the +uid-set+ size is below
|
371
|
+
# parser_max_deprecated_uidplus_data_size. Above that size,
|
372
|
+
# ResponseParser uses AppendUIDData or CopyUIDData.
|
373
|
+
#
|
374
|
+
# [+false+ <em>(planned default for +v0.6+)</em>]
|
375
|
+
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
|
376
|
+
attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
|
377
|
+
true, :up_to_max_size, false
|
378
|
+
]
|
379
|
+
|
380
|
+
# The maximum +uid-set+ size that ResponseParser will parse into
|
381
|
+
# deprecated UIDPlusData. This limit only applies when
|
382
|
+
# parser_use_deprecated_uidplus_data is not +false+.
|
383
|
+
#
|
384
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
385
|
+
#
|
386
|
+
# <em>Support for limiting UIDPlusData to a maximum size was added in
|
387
|
+
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
|
388
|
+
#
|
389
|
+
# <em>UIDPlusData will be removed in +v0.6+.</em>
|
390
|
+
#
|
391
|
+
# ==== Versioned Defaults
|
392
|
+
#
|
393
|
+
# Because this limit guards against a remote server causing catastrophic
|
394
|
+
# memory exhaustion, the versioned default (used by #load_defaults) also
|
395
|
+
# applies to versions without the feature.
|
396
|
+
#
|
397
|
+
# * +0.3+ and prior: <tt>10,000</tt>
|
398
|
+
# * +0.4+: <tt>1,000</tt>
|
399
|
+
# * +0.5+: <tt>100</tt>
|
400
|
+
# * +0.6+: <tt>0</tt>
|
401
|
+
#
|
402
|
+
attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
|
403
|
+
|
404
|
+
# Creates a new config object and initialize its attribute with +attrs+.
|
405
|
+
#
|
406
|
+
# If +parent+ is not given, the global config is used by default.
|
407
|
+
#
|
408
|
+
# If a block is given, the new config object is yielded to it.
|
409
|
+
def initialize(parent = Config.global, **attrs)
|
410
|
+
super(parent)
|
411
|
+
update(**attrs)
|
412
|
+
yield self if block_given?
|
413
|
+
end
|
414
|
+
|
415
|
+
# :call-seq: update(**attrs) -> self
|
416
|
+
#
|
417
|
+
# Assigns all of the provided +attrs+ to this config, and returns +self+.
|
418
|
+
#
|
419
|
+
# An ArgumentError is raised unless every key in +attrs+ matches an
|
420
|
+
# assignment method on Config.
|
421
|
+
#
|
422
|
+
# >>>
|
423
|
+
# *NOTE:* #update is not atomic. If an exception is raised due to an
|
424
|
+
# invalid attribute value, +attrs+ may be partially applied.
|
425
|
+
def update(**attrs)
|
426
|
+
unless (bad = attrs.keys.reject { respond_to?(:"#{_1}=") }).empty?
|
427
|
+
raise ArgumentError, "invalid config options: #{bad.join(", ")}"
|
428
|
+
end
|
429
|
+
attrs.each do send(:"#{_1}=", _2) end
|
430
|
+
self
|
431
|
+
end
|
432
|
+
|
433
|
+
# :call-seq:
|
434
|
+
# with(**attrs) -> config
|
435
|
+
# with(**attrs) {|config| } -> result
|
436
|
+
#
|
437
|
+
# Without a block, returns a new config which inherits from self. With a
|
438
|
+
# block, yields the new config and returns the block's result.
|
439
|
+
#
|
440
|
+
# If no keyword arguments are given, an ArgumentError will be raised.
|
441
|
+
#
|
442
|
+
# If +self+ is frozen, the copy will also be frozen.
|
443
|
+
def with(**attrs)
|
444
|
+
attrs.empty? and
|
445
|
+
raise ArgumentError, "expected keyword arguments, none given"
|
446
|
+
copy = new(**attrs)
|
447
|
+
copy.freeze if frozen?
|
448
|
+
block_given? ? yield(copy) : copy
|
449
|
+
end
|
450
|
+
|
451
|
+
# :call-seq: load_defaults(version) -> self
|
452
|
+
#
|
453
|
+
# Resets the current config to behave like the versioned default
|
454
|
+
# configuration for +version+. #parent will not be changed.
|
455
|
+
#
|
456
|
+
# Some config attributes default to inheriting from their #parent (which
|
457
|
+
# is usually Config.global) and are left unchanged, for example: #debug.
|
458
|
+
#
|
459
|
+
# See Config@Versioned+defaults and Config@Named+defaults.
|
460
|
+
def load_defaults(version)
|
461
|
+
[Numeric, Symbol, String].any? { _1 === version } or
|
462
|
+
raise ArgumentError, "expected number or symbol, got %p" % [version]
|
463
|
+
update(**Config[version].defaults_hash)
|
464
|
+
end
|
465
|
+
|
466
|
+
# :call-seq: to_h -> hash
|
467
|
+
#
|
468
|
+
# Returns all config attributes in a hash.
|
469
|
+
def to_h; data.members.to_h { [_1, send(_1)] } end
|
470
|
+
|
471
|
+
protected
|
472
|
+
|
473
|
+
def defaults_hash
|
474
|
+
to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
|
475
|
+
end
|
476
|
+
|
477
|
+
@default = new(
|
478
|
+
debug: false,
|
479
|
+
open_timeout: 30,
|
480
|
+
idle_response_timeout: 5,
|
481
|
+
sasl_ir: true,
|
482
|
+
enforce_logindisabled: true,
|
483
|
+
max_response_size: 512 << 20, # 512 MiB
|
484
|
+
responses_without_block: :warn,
|
485
|
+
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
486
|
+
parser_max_deprecated_uidplus_data_size: 100,
|
487
|
+
).freeze
|
488
|
+
|
489
|
+
@global = default.new
|
490
|
+
|
491
|
+
version_defaults[:default] = Config[default.send(:defaults_hash)]
|
492
|
+
|
493
|
+
version_defaults[0r] = Config[:default].dup.update(
|
494
|
+
sasl_ir: false,
|
495
|
+
responses_without_block: :silence_deprecation_warning,
|
496
|
+
enforce_logindisabled: false,
|
497
|
+
max_response_size: nil,
|
498
|
+
parser_use_deprecated_uidplus_data: true,
|
499
|
+
parser_max_deprecated_uidplus_data_size: 10_000,
|
500
|
+
).freeze
|
501
|
+
version_defaults[0.0r] = Config[0r]
|
502
|
+
version_defaults[0.1r] = Config[0r]
|
503
|
+
version_defaults[0.2r] = Config[0r]
|
504
|
+
version_defaults[0.3r] = Config[0r]
|
505
|
+
|
506
|
+
version_defaults[0.4r] = Config[0.3r].dup.update(
|
507
|
+
sasl_ir: true,
|
508
|
+
parser_max_deprecated_uidplus_data_size: 1000,
|
509
|
+
).freeze
|
510
|
+
|
511
|
+
version_defaults[0.5r] = Config[0.4r].dup.update(
|
512
|
+
enforce_logindisabled: true,
|
513
|
+
max_response_size: 512 << 20, # 512 MiB
|
514
|
+
responses_without_block: :warn,
|
515
|
+
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
516
|
+
parser_max_deprecated_uidplus_data_size: 100,
|
517
|
+
).freeze
|
518
|
+
|
519
|
+
version_defaults[0.6r] = Config[0.5r].dup.update(
|
520
|
+
responses_without_block: :frozen_dup,
|
521
|
+
parser_use_deprecated_uidplus_data: false,
|
522
|
+
parser_max_deprecated_uidplus_data_size: 0,
|
523
|
+
).freeze
|
524
|
+
|
525
|
+
version_defaults[0.7r] = Config[0.6r].dup.update(
|
526
|
+
).freeze
|
527
|
+
|
528
|
+
# Safe conversions one way only:
|
529
|
+
# 0.6r.to_f == 0.6 # => true
|
530
|
+
# 0.6 .to_r == 0.6r # => false
|
531
|
+
version_defaults.to_a.each do |k, v|
|
532
|
+
next unless k in Rational
|
533
|
+
version_defaults[k.to_f] = v
|
534
|
+
end
|
535
|
+
|
536
|
+
current = VERSION.to_r
|
537
|
+
version_defaults[:original] = Config[0]
|
538
|
+
version_defaults[:current] = Config[current]
|
539
|
+
version_defaults[:next] = Config[current + 0.1r]
|
540
|
+
version_defaults[:future] = Config[current + 0.2r]
|
541
|
+
|
542
|
+
version_defaults.freeze
|
543
|
+
|
544
|
+
if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h
|
545
|
+
warn "Misconfigured Net::IMAP::Config[:current] => %p,\n" \
|
546
|
+
" not equal to Net::IMAP::Config[:default] => %p" % [
|
547
|
+
self[:current].to_h, self[:default].to_h
|
548
|
+
]
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP
|
5
|
+
class ConnectionState < Net::IMAP::Data # :nodoc:
|
6
|
+
def self.define(symbol, *attrs)
|
7
|
+
symbol => Symbol
|
8
|
+
state = super(*attrs)
|
9
|
+
state.const_set :NAME, symbol
|
10
|
+
state
|
11
|
+
end
|
12
|
+
|
13
|
+
def symbol; self.class::NAME end
|
14
|
+
def name; self.class::NAME.name end
|
15
|
+
alias to_sym symbol
|
16
|
+
|
17
|
+
def deconstruct; [symbol, *super] end
|
18
|
+
|
19
|
+
def deconstruct_keys(names)
|
20
|
+
hash = super
|
21
|
+
hash[:symbol] = symbol if names.nil? || names.include?(:symbol)
|
22
|
+
hash[:name] = name if names.nil? || names.include?(:name)
|
23
|
+
hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h(&block)
|
27
|
+
hash = deconstruct_keys(nil)
|
28
|
+
block ? hash.to_h(&block) : hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def not_authenticated?; to_sym == :not_authenticated end
|
32
|
+
def authenticated?; to_sym == :authenticated end
|
33
|
+
def selected?; to_sym == :selected end
|
34
|
+
def logout?; to_sym == :logout end
|
35
|
+
|
36
|
+
NotAuthenticated = define(:not_authenticated)
|
37
|
+
Authenticated = define(:authenticated)
|
38
|
+
Selected = define(:selected)
|
39
|
+
Logout = define(:logout)
|
40
|
+
|
41
|
+
class << self
|
42
|
+
undef :define
|
43
|
+
end
|
44
|
+
freeze
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "date"
|
4
|
+
require "time"
|
4
5
|
|
5
6
|
require_relative "errors"
|
6
7
|
|
@@ -102,8 +103,16 @@ module Net
|
|
102
103
|
#
|
103
104
|
# Decodes +string+ as an IMAP4 formatted "date-time".
|
104
105
|
#
|
105
|
-
#
|
106
|
+
# NOTE: Although double-quotes are not optional in the IMAP grammar,
|
107
|
+
# Net::IMAP currently parses "date-time" values as "quoted" strings and this
|
108
|
+
# removes the quotation marks. To be useful for strings which have already
|
109
|
+
# been parsed as a quoted string, this method makes double-quotes optional.
|
110
|
+
#
|
111
|
+
# See STRFTIME.
|
106
112
|
def self.decode_datetime(string)
|
113
|
+
unless string.start_with?(?") && string.end_with?(?")
|
114
|
+
string = '"%s"' % [string]
|
115
|
+
end
|
107
116
|
DateTime.strptime(string, STRFTIME)
|
108
117
|
end
|
109
118
|
|
@@ -113,7 +122,10 @@ module Net
|
|
113
122
|
#
|
114
123
|
# Same as +decode_datetime+, but returning a Time instead.
|
115
124
|
def self.decode_time(string)
|
116
|
-
|
125
|
+
unless string.start_with?(?") && string.end_with?(?")
|
126
|
+
string = '"%s"' % [string]
|
127
|
+
end
|
128
|
+
Time.strptime(string, STRFTIME)
|
117
129
|
end
|
118
130
|
|
119
131
|
class << self
|
@@ -124,7 +136,7 @@ module Net
|
|
124
136
|
alias parse_datetime decode_datetime
|
125
137
|
alias parse_time decode_time
|
126
138
|
|
127
|
-
# alias format_datetime encode_datetime # n.b
|
139
|
+
# alias format_datetime encode_datetime # n.b: this is overridden below...
|
128
140
|
end
|
129
141
|
|
130
142
|
# DEPRECATED:: The original version returned incorrectly formatted strings.
|
@@ -174,7 +186,7 @@ module Net
|
|
174
186
|
|
175
187
|
# Ensure argument is 'number' or raise DataFormatError
|
176
188
|
def ensure_number(num)
|
177
|
-
return if valid_number?(num)
|
189
|
+
return num if valid_number?(num)
|
178
190
|
|
179
191
|
msg = "number must be unsigned 32-bit integer: #{num}"
|
180
192
|
raise DataFormatError, msg
|
@@ -182,7 +194,7 @@ module Net
|
|
182
194
|
|
183
195
|
# Ensure argument is 'nz_number' or raise DataFormatError
|
184
196
|
def ensure_nz_number(num)
|
185
|
-
return if valid_nz_number?(num)
|
197
|
+
return num if valid_nz_number?(num)
|
186
198
|
|
187
199
|
msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
|
188
200
|
raise DataFormatError, msg
|
@@ -190,7 +202,7 @@ module Net
|
|
190
202
|
|
191
203
|
# Ensure argument is 'mod_sequence_value' or raise DataFormatError
|
192
204
|
def ensure_mod_sequence_value(num)
|
193
|
-
return if valid_mod_sequence_value?(num)
|
205
|
+
return num if valid_mod_sequence_value?(num)
|
194
206
|
|
195
207
|
msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
|
196
208
|
raise DataFormatError, msg
|