net-imap 0.4.12 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -1
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +13 -2
- 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 +400 -0
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/deprecated_client_options.rb +8 -5
- data/lib/net/imap/errors.rb +6 -0
- data/lib/net/imap/response_data.rb +6 -93
- data/lib/net/imap/response_parser/parser_utils.rb +6 -6
- data/lib/net/imap/response_parser.rb +9 -17
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +1 -1
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +213 -51
- data/lib/net/imap/sasl/login_authenticator.rb +2 -1
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl.rb +6 -3
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/sequence_set.rb +28 -24
- data/lib/net/imap.rb +467 -152
- data/net-imap.gemspec +3 -3
- 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,400 @@
|
|
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 # => :raise
|
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 raise exceptions for all current deprecations:
|
113
|
+
# client = Net::IMAP.new(hostname, config: :future)
|
114
|
+
# client.responses # raises an ArgumentError
|
115
|
+
#
|
116
|
+
# == Thread Safety
|
117
|
+
#
|
118
|
+
# *NOTE:* Updates to config objects are not synchronized for thread-safety.
|
119
|
+
#
|
120
|
+
class Config
|
121
|
+
# Array of attribute names that are _not_ loaded by #load_defaults.
|
122
|
+
DEFAULT_TO_INHERIT = %i[debug].freeze
|
123
|
+
private_constant :DEFAULT_TO_INHERIT
|
124
|
+
|
125
|
+
# The default config, which is hardcoded and frozen.
|
126
|
+
def self.default; @default end
|
127
|
+
|
128
|
+
# The global config object. Also available from Net::IMAP.config.
|
129
|
+
def self.global; @global if defined?(@global) end
|
130
|
+
|
131
|
+
# A hash of hard-coded configurations, indexed by version number or name.
|
132
|
+
def self.version_defaults; @version_defaults end
|
133
|
+
@version_defaults = {}
|
134
|
+
|
135
|
+
# :call-seq:
|
136
|
+
# Net::IMAP::Config[number] -> versioned config
|
137
|
+
# Net::IMAP::Config[symbol] -> named config
|
138
|
+
# Net::IMAP::Config[hash] -> new frozen config
|
139
|
+
# Net::IMAP::Config[config] -> same config
|
140
|
+
#
|
141
|
+
# Given a version number, returns the default configuration for the target
|
142
|
+
# version. See Config@Versioned+defaults.
|
143
|
+
#
|
144
|
+
# Given a version name, returns the default configuration for the target
|
145
|
+
# version. See Config@Named+defaults.
|
146
|
+
#
|
147
|
+
# Given a Hash, creates a new _frozen_ config which inherits from
|
148
|
+
# Config.global. Use Config.new for an unfrozen config.
|
149
|
+
#
|
150
|
+
# Given a config, returns that same config.
|
151
|
+
def self.[](config)
|
152
|
+
if config.is_a?(Config) then config
|
153
|
+
elsif config.nil? && global.nil? then nil
|
154
|
+
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
|
155
|
+
else
|
156
|
+
version_defaults.fetch(config) do
|
157
|
+
case config
|
158
|
+
when Numeric
|
159
|
+
raise RangeError, "unknown config version: %p" % [config]
|
160
|
+
when Symbol
|
161
|
+
raise KeyError, "unknown config name: %p" % [config]
|
162
|
+
else
|
163
|
+
raise TypeError, "no implicit conversion of %s to %s" % [
|
164
|
+
config.class, Config
|
165
|
+
]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
include AttrAccessors
|
172
|
+
include AttrInheritance
|
173
|
+
include AttrTypeCoercion
|
174
|
+
|
175
|
+
# The debug mode (boolean). The default value is +false+.
|
176
|
+
#
|
177
|
+
# When #debug is +true+:
|
178
|
+
# * Data sent to and received from the server will be logged.
|
179
|
+
# * ResponseParser will print warnings with extra detail for parse
|
180
|
+
# errors. _This may include recoverable errors._
|
181
|
+
# * ResponseParser makes extra assertions.
|
182
|
+
#
|
183
|
+
# *NOTE:* Versioned default configs inherit #debug from Config.global, and
|
184
|
+
# #load_defaults will not override #debug.
|
185
|
+
attr_accessor :debug, type: :boolean
|
186
|
+
|
187
|
+
# method: debug?
|
188
|
+
# :call-seq: debug? -> boolean
|
189
|
+
#
|
190
|
+
# Alias for #debug
|
191
|
+
|
192
|
+
# Seconds to wait until a connection is opened.
|
193
|
+
#
|
194
|
+
# If the IMAP object cannot open a connection within this time,
|
195
|
+
# it raises a Net::OpenTimeout exception.
|
196
|
+
#
|
197
|
+
# See Net::IMAP.new.
|
198
|
+
#
|
199
|
+
# The default value is +30+ seconds.
|
200
|
+
attr_accessor :open_timeout, type: Integer
|
201
|
+
|
202
|
+
# Seconds to wait until an IDLE response is received, after
|
203
|
+
# the client asks to leave the IDLE state.
|
204
|
+
#
|
205
|
+
# See Net::IMAP#idle and Net::IMAP#idle_done.
|
206
|
+
#
|
207
|
+
# The default value is +5+ seconds.
|
208
|
+
attr_accessor :idle_response_timeout, type: Integer
|
209
|
+
|
210
|
+
# Whether to use the +SASL-IR+ extension when the server and \SASL
|
211
|
+
# mechanism both support it. Can be overridden by the +sasl_ir+ keyword
|
212
|
+
# parameter to Net::IMAP#authenticate.
|
213
|
+
#
|
214
|
+
# <em>(Support for +SASL-IR+ was added in +v0.4.0+.)</em>
|
215
|
+
#
|
216
|
+
# ==== Valid options
|
217
|
+
#
|
218
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
219
|
+
# Do not use +SASL-IR+, even when it is supported by the server and the
|
220
|
+
# mechanism.
|
221
|
+
#
|
222
|
+
# [+true+ <em>(default since +v0.4+)</em>]
|
223
|
+
# Use +SASL-IR+ when it is supported by the server and the mechanism.
|
224
|
+
attr_accessor :sasl_ir, type: :boolean
|
225
|
+
|
226
|
+
# Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
|
227
|
+
# capability is present. When enforced, Net::IMAP will raise a
|
228
|
+
# LoginDisabledError when that capability is present.
|
229
|
+
#
|
230
|
+
# <em>(Support for +LOGINDISABLED+ was added in +v0.5.0+.)</em>
|
231
|
+
#
|
232
|
+
# ==== Valid options
|
233
|
+
#
|
234
|
+
# [+false+ <em>(original behavior, before support was added)</em>]
|
235
|
+
# Send the +LOGIN+ command without checking for +LOGINDISABLED+.
|
236
|
+
#
|
237
|
+
# [+:when_capabilities_cached+]
|
238
|
+
# Enforce the requirement when Net::IMAP#capabilities_cached? is true,
|
239
|
+
# but do not send a +CAPABILITY+ command to discover the capabilities.
|
240
|
+
#
|
241
|
+
# [+true+ <em>(default since +v0.5+)</em>]
|
242
|
+
# Only send the +LOGIN+ command if the +LOGINDISABLED+ capability is not
|
243
|
+
# present. When capabilities are unknown, Net::IMAP will automatically
|
244
|
+
# send a +CAPABILITY+ command first before sending +LOGIN+.
|
245
|
+
#
|
246
|
+
attr_accessor :enforce_logindisabled, type: [
|
247
|
+
false, :when_capabilities_cached, true
|
248
|
+
]
|
249
|
+
|
250
|
+
# Controls the behavior of Net::IMAP#responses when called without any
|
251
|
+
# arguments (+type+ or +block+).
|
252
|
+
#
|
253
|
+
# ==== Valid options
|
254
|
+
#
|
255
|
+
# [+:silence_deprecation_warning+ <em>(original behavior)</em>]
|
256
|
+
# Returns the mutable responses hash (without any warnings).
|
257
|
+
# <em>This is not thread-safe.</em>
|
258
|
+
#
|
259
|
+
# [+:warn+ <em>(default since +v0.5+)</em>]
|
260
|
+
# Prints a warning and returns the mutable responses hash.
|
261
|
+
# <em>This is not thread-safe.</em>
|
262
|
+
#
|
263
|
+
# [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
|
264
|
+
# Returns a frozen copy of the unhandled responses hash, with frozen
|
265
|
+
# array values.
|
266
|
+
#
|
267
|
+
# Note that calling IMAP#responses with a +type+ and without a block is
|
268
|
+
# not configurable and always behaves like +:frozen_dup+.
|
269
|
+
#
|
270
|
+
# <em>(+:frozen_dup+ config option was added in +v0.4.17+)</em>
|
271
|
+
#
|
272
|
+
# [+:raise+]
|
273
|
+
# Raise an ArgumentError with the deprecation warning.
|
274
|
+
#
|
275
|
+
# Note: #responses_without_args is an alias for #responses_without_block.
|
276
|
+
attr_accessor :responses_without_block, type: [
|
277
|
+
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
|
278
|
+
]
|
279
|
+
|
280
|
+
alias responses_without_args responses_without_block # :nodoc:
|
281
|
+
alias responses_without_args= responses_without_block= # :nodoc:
|
282
|
+
|
283
|
+
##
|
284
|
+
# :attr_accessor: responses_without_args
|
285
|
+
#
|
286
|
+
# Alias for responses_without_block
|
287
|
+
|
288
|
+
# Creates a new config object and initialize its attribute with +attrs+.
|
289
|
+
#
|
290
|
+
# If +parent+ is not given, the global config is used by default.
|
291
|
+
#
|
292
|
+
# If a block is given, the new config object is yielded to it.
|
293
|
+
def initialize(parent = Config.global, **attrs)
|
294
|
+
super(parent)
|
295
|
+
update(**attrs)
|
296
|
+
yield self if block_given?
|
297
|
+
end
|
298
|
+
|
299
|
+
# :call-seq: update(**attrs) -> self
|
300
|
+
#
|
301
|
+
# Assigns all of the provided +attrs+ to this config, and returns +self+.
|
302
|
+
#
|
303
|
+
# An ArgumentError is raised unless every key in +attrs+ matches an
|
304
|
+
# assignment method on Config.
|
305
|
+
#
|
306
|
+
# >>>
|
307
|
+
# *NOTE:* #update is not atomic. If an exception is raised due to an
|
308
|
+
# invalid attribute value, +attrs+ may be partially applied.
|
309
|
+
def update(**attrs)
|
310
|
+
unless (bad = attrs.keys.reject { respond_to?(:"#{_1}=") }).empty?
|
311
|
+
raise ArgumentError, "invalid config options: #{bad.join(", ")}"
|
312
|
+
end
|
313
|
+
attrs.each do send(:"#{_1}=", _2) end
|
314
|
+
self
|
315
|
+
end
|
316
|
+
|
317
|
+
# :call-seq:
|
318
|
+
# with(**attrs) -> config
|
319
|
+
# with(**attrs) {|config| } -> result
|
320
|
+
#
|
321
|
+
# Without a block, returns a new config which inherits from self. With a
|
322
|
+
# block, yields the new config and returns the block's result.
|
323
|
+
#
|
324
|
+
# If no keyword arguments are given, an ArgumentError will be raised.
|
325
|
+
#
|
326
|
+
# If +self+ is frozen, the copy will also be frozen.
|
327
|
+
def with(**attrs)
|
328
|
+
attrs.empty? and
|
329
|
+
raise ArgumentError, "expected keyword arguments, none given"
|
330
|
+
copy = new(**attrs)
|
331
|
+
copy.freeze if frozen?
|
332
|
+
block_given? ? yield(copy) : copy
|
333
|
+
end
|
334
|
+
|
335
|
+
# :call-seq: load_defaults(version) -> self
|
336
|
+
#
|
337
|
+
# Resets the current config to behave like the versioned default
|
338
|
+
# configuration for +version+. #parent will not be changed.
|
339
|
+
#
|
340
|
+
# Some config attributes default to inheriting from their #parent (which
|
341
|
+
# is usually Config.global) and are left unchanged, for example: #debug.
|
342
|
+
#
|
343
|
+
# See Config@Versioned+defaults and Config@Named+defaults.
|
344
|
+
def load_defaults(version)
|
345
|
+
[Numeric, Symbol, String].any? { _1 === version } or
|
346
|
+
raise ArgumentError, "expected number or symbol, got %p" % [version]
|
347
|
+
update(**Config[version].defaults_hash)
|
348
|
+
end
|
349
|
+
|
350
|
+
# :call-seq: to_h -> hash
|
351
|
+
#
|
352
|
+
# Returns all config attributes in a hash.
|
353
|
+
def to_h; data.members.to_h { [_1, send(_1)] } end
|
354
|
+
|
355
|
+
protected
|
356
|
+
|
357
|
+
def defaults_hash
|
358
|
+
to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
|
359
|
+
end
|
360
|
+
|
361
|
+
@default = new(
|
362
|
+
debug: false,
|
363
|
+
open_timeout: 30,
|
364
|
+
idle_response_timeout: 5,
|
365
|
+
sasl_ir: true,
|
366
|
+
enforce_logindisabled: true,
|
367
|
+
responses_without_block: :warn,
|
368
|
+
).freeze
|
369
|
+
|
370
|
+
@global = default.new
|
371
|
+
|
372
|
+
version_defaults[:default] = Config[default.send(:defaults_hash)]
|
373
|
+
version_defaults[:current] = Config[:default]
|
374
|
+
|
375
|
+
version_defaults[0] = Config[:current].dup.update(
|
376
|
+
sasl_ir: false,
|
377
|
+
responses_without_block: :silence_deprecation_warning,
|
378
|
+
enforce_logindisabled: false,
|
379
|
+
).freeze
|
380
|
+
version_defaults[0.0] = Config[0]
|
381
|
+
version_defaults[0.1] = Config[0]
|
382
|
+
version_defaults[0.2] = Config[0]
|
383
|
+
version_defaults[0.3] = Config[0]
|
384
|
+
|
385
|
+
version_defaults[0.4] = Config[0.3].dup.update(
|
386
|
+
sasl_ir: true,
|
387
|
+
).freeze
|
388
|
+
|
389
|
+
version_defaults[0.5] = Config[:current]
|
390
|
+
|
391
|
+
version_defaults[0.6] = Config[0.5].dup.update(
|
392
|
+
responses_without_block: :frozen_dup,
|
393
|
+
).freeze
|
394
|
+
version_defaults[:next] = Config[0.6]
|
395
|
+
version_defaults[:future] = Config[:next]
|
396
|
+
|
397
|
+
version_defaults.freeze
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
@@ -186,7 +186,7 @@ module Net
|
|
186
186
|
|
187
187
|
# Ensure argument is 'number' or raise DataFormatError
|
188
188
|
def ensure_number(num)
|
189
|
-
return if valid_number?(num)
|
189
|
+
return num if valid_number?(num)
|
190
190
|
|
191
191
|
msg = "number must be unsigned 32-bit integer: #{num}"
|
192
192
|
raise DataFormatError, msg
|
@@ -194,7 +194,7 @@ module Net
|
|
194
194
|
|
195
195
|
# Ensure argument is 'nz_number' or raise DataFormatError
|
196
196
|
def ensure_nz_number(num)
|
197
|
-
return if valid_nz_number?(num)
|
197
|
+
return num if valid_nz_number?(num)
|
198
198
|
|
199
199
|
msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
|
200
200
|
raise DataFormatError, msg
|
@@ -202,7 +202,7 @@ module Net
|
|
202
202
|
|
203
203
|
# Ensure argument is 'mod_sequence_value' or raise DataFormatError
|
204
204
|
def ensure_mod_sequence_value(num)
|
205
|
-
return if valid_mod_sequence_value?(num)
|
205
|
+
return num if valid_mod_sequence_value?(num)
|
206
206
|
|
207
207
|
msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
|
208
208
|
raise DataFormatError, msg
|
@@ -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
|
data/lib/net/imap/errors.rb
CHANGED
@@ -7,6 +7,12 @@ module Net
|
|
7
7
|
class Error < StandardError
|
8
8
|
end
|
9
9
|
|
10
|
+
class LoginDisabledError < Error
|
11
|
+
def initialize(msg = "Remote server has disabled the LOGIN command", ...)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
10
16
|
# Error raised when data is in the incorrect format.
|
11
17
|
class DataFormatError < Error
|
12
18
|
end
|
@@ -939,7 +939,8 @@ module Net
|
|
939
939
|
# for something else?
|
940
940
|
#++
|
941
941
|
def media_subtype
|
942
|
-
warn("media_subtype is obsolete, use subtype instead.\n",
|
942
|
+
warn("media_subtype is obsolete, use subtype instead.\n",
|
943
|
+
uplevel: 1, category: :deprecated)
|
943
944
|
return subtype
|
944
945
|
end
|
945
946
|
end
|
@@ -984,7 +985,8 @@ module Net
|
|
984
985
|
# generate a warning message to +stderr+, then return
|
985
986
|
# the value of +subtype+.
|
986
987
|
def media_subtype
|
987
|
-
warn("media_subtype is obsolete, use subtype instead.\n",
|
988
|
+
warn("media_subtype is obsolete, use subtype instead.\n",
|
989
|
+
uplevel: 1, category: :deprecated)
|
988
990
|
return subtype
|
989
991
|
end
|
990
992
|
end
|
@@ -1040,77 +1042,6 @@ module Net
|
|
1040
1042
|
end
|
1041
1043
|
end
|
1042
1044
|
|
1043
|
-
# BodyTypeAttachment is not used and will be removed in an upcoming release.
|
1044
|
-
#
|
1045
|
-
# === Bug Analysis
|
1046
|
-
#
|
1047
|
-
# \IMAP body structures are parenthesized lists and assign their fields
|
1048
|
-
# positionally, so missing fields change the interpretation of all
|
1049
|
-
# following fields. Additionally, different body types have a different
|
1050
|
-
# number of required fields, followed by optional "extension" fields.
|
1051
|
-
#
|
1052
|
-
# BodyTypeAttachment was previously returned when a "message/rfc822" part,
|
1053
|
-
# which should be sent as <tt>body-type-msg</tt> with ten required fields,
|
1054
|
-
# was actually sent as a <tt>body-type-basic</tt> with _seven_ required
|
1055
|
-
# fields.
|
1056
|
-
#
|
1057
|
-
# basic => type, subtype, param, id, desc, enc, octets, md5=nil, dsp=nil, lang=nil, loc=nil, *ext
|
1058
|
-
# msg => type, subtype, param, id, desc, enc, octets, envelope, body, lines, md5=nil, ...
|
1059
|
-
#
|
1060
|
-
# Normally, +envelope+ and +md5+ are incompatible, but Net::IMAP leniently
|
1061
|
-
# allowed buggy servers to send +NIL+ for +envelope+. As a result, when a
|
1062
|
-
# server sent a <tt>message/rfc822</tt> part with +NIL+ for +md5+ and a
|
1063
|
-
# non-<tt>NIL</tt> +dsp+, Net::IMAP misinterpreted the
|
1064
|
-
# <tt>Content-Disposition</tt> as if it were a strange body type. In all
|
1065
|
-
# reported cases, the <tt>Content-Disposition</tt> was "attachment", so
|
1066
|
-
# BodyTypeAttachment was created as the workaround.
|
1067
|
-
#
|
1068
|
-
# === Current behavior
|
1069
|
-
#
|
1070
|
-
# When interpreted strictly, +envelope+ and +md5+ are incompatible. So the
|
1071
|
-
# current parsing algorithm peeks ahead after it has received the seventh
|
1072
|
-
# body field. If the next token is not the start of an +envelope+, we assume
|
1073
|
-
# the server has incorrectly sent us a <tt>body-type-basic</tt> and return
|
1074
|
-
# BodyTypeBasic. As a result, what was previously BodyTypeMessage#body =>
|
1075
|
-
# BodyTypeAttachment is now BodyTypeBasic#disposition => ContentDisposition.
|
1076
|
-
#
|
1077
|
-
class BodyTypeAttachment < Struct.new(:dsp_type, :_unused_, :param)
|
1078
|
-
# *invalid for BodyTypeAttachment*
|
1079
|
-
def media_type
|
1080
|
-
warn(<<~WARN, uplevel: 1)
|
1081
|
-
BodyTypeAttachment#media_type is obsolete. Use dsp_type instead.
|
1082
|
-
WARN
|
1083
|
-
dsp_type
|
1084
|
-
end
|
1085
|
-
|
1086
|
-
# *invalid for BodyTypeAttachment*
|
1087
|
-
def subtype
|
1088
|
-
warn("BodyTypeAttachment#subtype is obsolete.\n", uplevel: 1)
|
1089
|
-
nil
|
1090
|
-
end
|
1091
|
-
|
1092
|
-
##
|
1093
|
-
# method: dsp_type
|
1094
|
-
# :call-seq: dsp_type -> string
|
1095
|
-
#
|
1096
|
-
# Returns the content disposition type, as defined by
|
1097
|
-
# [DISPOSITION[https://tools.ietf.org/html/rfc2183]].
|
1098
|
-
|
1099
|
-
##
|
1100
|
-
# method: param
|
1101
|
-
# :call-seq: param -> hash
|
1102
|
-
#
|
1103
|
-
# Returns a hash representing parameters of the Content-Disposition
|
1104
|
-
# field, as defined by [DISPOSITION[https://tools.ietf.org/html/rfc2183]].
|
1105
|
-
|
1106
|
-
##
|
1107
|
-
def multipart?
|
1108
|
-
return false
|
1109
|
-
end
|
1110
|
-
end
|
1111
|
-
|
1112
|
-
deprecate_constant :BodyTypeAttachment
|
1113
|
-
|
1114
1045
|
# Net::IMAP::BodyTypeMultipart represents body structures of messages and
|
1115
1046
|
# message parts, when <tt>Content-Type</tt> is <tt>multipart/*</tt>.
|
1116
1047
|
class BodyTypeMultipart < Struct.new(:media_type, :subtype,
|
@@ -1182,29 +1113,11 @@ module Net
|
|
1182
1113
|
# generate a warning message to +stderr+, then return
|
1183
1114
|
# the value of +subtype+.
|
1184
1115
|
def media_subtype
|
1185
|
-
warn("media_subtype is obsolete, use subtype instead.\n",
|
1116
|
+
warn("media_subtype is obsolete, use subtype instead.\n",
|
1117
|
+
uplevel: 1, category: :deprecated)
|
1186
1118
|
return subtype
|
1187
1119
|
end
|
1188
1120
|
end
|
1189
1121
|
|
1190
|
-
# === Obsolete
|
1191
|
-
# BodyTypeExtension is not used and will be removed in an upcoming release.
|
1192
|
-
#
|
1193
|
-
# >>>
|
1194
|
-
# BodyTypeExtension was (incorrectly) used for <tt>message/*</tt> parts
|
1195
|
-
# (besides <tt>message/rfc822</tt>, which correctly uses BodyTypeMessage).
|
1196
|
-
#
|
1197
|
-
# Net::IMAP now (correctly) parses all message types (other than
|
1198
|
-
# <tt>message/rfc822</tt> or <tt>message/global</tt>) as BodyTypeBasic.
|
1199
|
-
class BodyTypeExtension < Struct.new(:media_type, :subtype,
|
1200
|
-
:params, :content_id,
|
1201
|
-
:description, :encoding, :size)
|
1202
|
-
def multipart?
|
1203
|
-
return false
|
1204
|
-
end
|
1205
|
-
end
|
1206
|
-
|
1207
|
-
deprecate_constant :BodyTypeExtension
|
1208
|
-
|
1209
1122
|
end
|
1210
1123
|
end
|
@@ -154,7 +154,7 @@ module Net
|
|
154
154
|
end
|
155
155
|
|
156
156
|
# To be used conditionally:
|
157
|
-
# assert_no_lookahead if
|
157
|
+
# assert_no_lookahead if config.debug?
|
158
158
|
def assert_no_lookahead
|
159
159
|
@token.nil? or
|
160
160
|
parse_error("assertion failed: expected @token.nil?, actual %s: %p",
|
@@ -181,23 +181,23 @@ module Net
|
|
181
181
|
end
|
182
182
|
|
183
183
|
def peek_str?(str)
|
184
|
-
assert_no_lookahead if
|
184
|
+
assert_no_lookahead if config.debug?
|
185
185
|
@str[@pos, str.length] == str
|
186
186
|
end
|
187
187
|
|
188
188
|
def peek_re(re)
|
189
|
-
assert_no_lookahead if
|
189
|
+
assert_no_lookahead if config.debug?
|
190
190
|
re.match(@str, @pos)
|
191
191
|
end
|
192
192
|
|
193
193
|
def accept_re(re)
|
194
|
-
assert_no_lookahead if
|
194
|
+
assert_no_lookahead if config.debug?
|
195
195
|
re.match(@str, @pos) and @pos = $~.end(0)
|
196
196
|
$~
|
197
197
|
end
|
198
198
|
|
199
199
|
def match_re(re, name)
|
200
|
-
assert_no_lookahead if
|
200
|
+
assert_no_lookahead if config.debug?
|
201
201
|
if re.match(@str, @pos)
|
202
202
|
@pos = $~.end(0)
|
203
203
|
$~
|
@@ -212,7 +212,7 @@ module Net
|
|
212
212
|
|
213
213
|
def parse_error(fmt, *args)
|
214
214
|
msg = format(fmt, *args)
|
215
|
-
if
|
215
|
+
if config.debug?
|
216
216
|
local_path = File.dirname(__dir__)
|
217
217
|
tok = @token ? "%s: %p" % [@token.symbol, @token.value] : "nil"
|
218
218
|
warn "%s %s: %s" % [self.class, __method__, msg]
|