net-imap 0.4.12 → 0.4.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5147af8d1017e772136634ea2144c33a1666a00a25d03de20a6d89f9b026ecc
4
- data.tar.gz: 66a41c1fcfc932da451f65074ae6659d872b85aeeef831208cf47178f944cd08
3
+ metadata.gz: 06be993e038a4432ec954b11701ce86f946c456f9125d2b539e6c21d05e728ab
4
+ data.tar.gz: 7807fb77537a843e9d9a2ad0ad892dc3e75af3a734f2c4c68a1e2cbae4d34ef1
5
5
  SHA512:
6
- metadata.gz: 67a5545d46acd17051097ad3e10304817980fad7621ca0225c614f8bd49d62365d9b9d3305fa1ac3fbebd63bd1ba2155cb5a94d448ca6a6763655dd31cdb22a2
7
- data.tar.gz: dbc107f94925486cbc3eb28a26c45560fce568f3836d65127628599fd7a117046d5cc0225f1999b387802316b781bce367da5b660da79200c7e576515669ca70
6
+ metadata.gz: 182edade4a45d1c6f4772332f2f9cd4704827895770764202aa843bfbb754defff11a8037c1955e352642201c57cea850ea51aa8e4fe6c1761b36c78562b1c5b
7
+ data.tar.gz: 2fff884a15a87ae7cb4a5ac422f743b5accd1ea98b34bf994cbb18238577cc259a1a672a04e5dac14864ad61bdafd90c43c160ede8ba369246fd3bb265e5cff2
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Net
6
+ class IMAP
7
+ class Config
8
+ # >>>
9
+ # *NOTE:* This module is an internal implementation detail, with no
10
+ # guarantee of backward compatibility.
11
+ #
12
+ # +attr_accessor+ values are stored in a struct rather than ivars, making
13
+ # it simpler to ensure that all config objects share a single object
14
+ # shape. This also simplifies iteration over all defined attributes.
15
+ module AttrAccessors
16
+ module Macros # :nodoc: internal API
17
+ def attr_accessor(name) AttrAccessors.attr_accessor(name) end
18
+ end
19
+ private_constant :Macros
20
+
21
+ def self.included(mod)
22
+ mod.extend Macros
23
+ end
24
+ private_class_method :included
25
+
26
+ extend Forwardable
27
+
28
+ def self.attr_accessor(name) # :nodoc: internal API
29
+ name = name.to_sym
30
+ def_delegators :data, name, :"#{name}="
31
+ end
32
+
33
+ def self.attributes
34
+ instance_methods.grep(/=\z/).map { _1.to_s.delete_suffix("=").to_sym }
35
+ end
36
+ private_class_method :attributes
37
+
38
+ def self.struct # :nodoc: internal API
39
+ unless defined?(self::Struct)
40
+ const_set :Struct, Struct.new(*attributes)
41
+ end
42
+ self::Struct
43
+ end
44
+
45
+ def initialize # :notnew:
46
+ super()
47
+ @data = AttrAccessors.struct.new
48
+ end
49
+
50
+ # Freezes the internal attributes struct, in addition to +self+.
51
+ def freeze
52
+ data.freeze
53
+ super
54
+ end
55
+
56
+ protected
57
+
58
+ attr_reader :data # :nodoc: internal API
59
+
60
+ private
61
+
62
+ def initialize_clone(other)
63
+ super
64
+ @data = other.data.clone
65
+ end
66
+
67
+ def initialize_dup(other)
68
+ super
69
+ @data = other.data.dup
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ class Config
6
+ # >>>
7
+ # *NOTE:* The public methods on this module are part of the stable
8
+ # public API of Net::IMAP::Config. But the module itself is an internal
9
+ # implementation detail, with no guarantee of backward compatibility.
10
+ #
11
+ # +attr_accessor+ methods will delegate to their #parent when the local
12
+ # value does not contain an override. Inheritance forms a singly linked
13
+ # list, so lookup will be <tt>O(n)</tt> on the number of ancestors. In
14
+ # practice, the ancestor chain is not expected to be long. Without
15
+ # customization, it is only three deep:
16
+ # >>>
17
+ # IMAP#config → Config.global → Config.default
18
+ #
19
+ # When creating a client with the +config+ keyword, for example to use
20
+ # the appropriate defaults for an application or a library while still
21
+ # relying on global for configuration of +debug+ or +logger+, most likely
22
+ # the ancestor chain is still only four deep:
23
+ # >>>
24
+ # IMAP#config → alternate defaults → Config.global → Config.default
25
+ module AttrInheritance
26
+ INHERITED = Module.new.freeze
27
+ private_constant :INHERITED
28
+
29
+ module Macros # :nodoc: internal API
30
+ def attr_accessor(name) super; AttrInheritance.attr_accessor(name) end
31
+ end
32
+ private_constant :Macros
33
+
34
+ def self.included(mod)
35
+ mod.extend Macros
36
+ end
37
+ private_class_method :included
38
+
39
+ def self.attr_accessor(name) # :nodoc: internal API
40
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
41
+ def #{name}; (val = super) == INHERITED ? parent&.#{name} : val end
42
+ RUBY
43
+ end
44
+
45
+ # The parent Config object
46
+ attr_reader :parent
47
+
48
+ def initialize(parent = nil) # :notnew:
49
+ super()
50
+ @parent = Config[parent]
51
+ reset
52
+ end
53
+
54
+ # Creates a new config, which inherits from +self+.
55
+ def new(**attrs) self.class.new(self, **attrs) end
56
+
57
+ # Returns +true+ if +attr+ is inherited from #parent and not overridden
58
+ # by this config.
59
+ def inherited?(attr) data[attr] == INHERITED end
60
+
61
+ # :call-seq:
62
+ # reset -> self
63
+ # reset(attr) -> attribute value
64
+ #
65
+ # Resets an +attr+ to inherit from the #parent config.
66
+ #
67
+ # When +attr+ is nil or not given, all attributes are reset.
68
+ def reset(attr = nil)
69
+ if attr.nil?
70
+ data.members.each do |attr| data[attr] = INHERITED end
71
+ self
72
+ elsif inherited?(attr)
73
+ nil
74
+ else
75
+ old, data[attr] = data[attr], INHERITED
76
+ old
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def initialize_copy(other)
83
+ super
84
+ @parent ||= other # only default has nil parent
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ class Config
6
+ # >>>
7
+ # *NOTE:* This module is an internal implementation detail, with no
8
+ # guarantee of backward compatibility.
9
+ #
10
+ # Adds a +type+ keyword parameter to +attr_accessor+, to enforce that
11
+ # config attributes have valid types, for example: boolean, numeric,
12
+ # enumeration, non-nullable, etc.
13
+ module AttrTypeCoercion
14
+ # :stopdoc: internal APIs only
15
+
16
+ module Macros # :nodoc: internal API
17
+ def attr_accessor(attr, type: nil)
18
+ super(attr)
19
+ AttrTypeCoercion.attr_accessor(attr, type: type)
20
+ end
21
+ end
22
+ private_constant :Macros
23
+
24
+ def self.included(mod)
25
+ mod.extend Macros
26
+ end
27
+ private_class_method :included
28
+
29
+ def self.attr_accessor(attr, type: nil)
30
+ return unless type
31
+ if :boolean == type then boolean attr
32
+ elsif Integer == type then integer attr
33
+ elsif Array === type then enum attr, type
34
+ else raise ArgumentError, "unknown type coercion %p" % [type]
35
+ end
36
+ end
37
+
38
+ def self.boolean(attr)
39
+ define_method :"#{attr}=" do |val| super !!val end
40
+ define_method :"#{attr}?" do send attr end
41
+ end
42
+
43
+ def self.integer(attr)
44
+ define_method :"#{attr}=" do |val| super Integer val end
45
+ end
46
+
47
+ def self.enum(attr, enum)
48
+ enum = enum.dup.freeze
49
+ expected = -"one of #{enum.map(&:inspect).join(", ")}"
50
+ define_method :"#{attr}=" do |val|
51
+ unless enum.include?(val)
52
+ raise ArgumentError, "expected %s, got %p" % [expected, val]
53
+ end
54
+ super val
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,339 @@
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 stores configuration options for Net::IMAP clients.
11
+ # The global configuration can be seen at either Net::IMAP.config or
12
+ # Net::IMAP::Config.global, and the client-specific configuration can be
13
+ # 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.
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)
176
+ #
177
+ # The default value is +false+.
178
+ attr_accessor :debug, type: :boolean
179
+
180
+ # method: debug?
181
+ # :call-seq: debug? -> boolean
182
+ #
183
+ # Alias for #debug
184
+
185
+ # Seconds to wait until a connection is opened.
186
+ #
187
+ # If the IMAP object cannot open a connection within this time,
188
+ # it raises a Net::OpenTimeout exception.
189
+ #
190
+ # See Net::IMAP.new.
191
+ #
192
+ # The default value is +30+ seconds.
193
+ attr_accessor :open_timeout, type: Integer
194
+
195
+ # Seconds to wait until an IDLE response is received, after
196
+ # the client asks to leave the IDLE state.
197
+ #
198
+ # See Net::IMAP#idle and Net::IMAP#idle_done.
199
+ #
200
+ # The default value is +5+ seconds.
201
+ attr_accessor :idle_response_timeout, type: Integer
202
+
203
+ # :markup: markdown
204
+ #
205
+ # Whether to use the +SASL-IR+ extension when the server and \SASL
206
+ # mechanism both support it.
207
+ #
208
+ # See Net::IMAP#authenticate.
209
+ #
210
+ # | Starting with version | The default value is |
211
+ # |-----------------------|------------------------------------------|
212
+ # | _original_ | +false+ <em>(extension unsupported)</em> |
213
+ # | v0.4 | +true+ <em>(support added)</em> |
214
+ attr_accessor :sasl_ir, type: :boolean
215
+
216
+ # :markup: markdown
217
+ #
218
+ # Controls the behavior of Net::IMAP#responses when called without a
219
+ # block. Valid options are `:warn`, `:raise`, or
220
+ # `:silence_deprecation_warning`.
221
+ #
222
+ # | Starting with version | The default value is |
223
+ # |-------------------------|--------------------------------|
224
+ # | v0.4.13 | +:silence_deprecation_warning+ |
225
+ # | v0.5 <em>(planned)</em> | +:warn+ |
226
+ # | _eventually_ | +:raise+ |
227
+ attr_accessor :responses_without_block, type: [
228
+ :silence_deprecation_warning, :warn, :raise,
229
+ ]
230
+
231
+ # Creates a new config object and initialize its attribute with +attrs+.
232
+ #
233
+ # If +parent+ is not given, the global config is used by default.
234
+ #
235
+ # If a block is given, the new config object is yielded to it.
236
+ def initialize(parent = Config.global, **attrs)
237
+ super(parent)
238
+ update(**attrs)
239
+ yield self if block_given?
240
+ end
241
+
242
+ # :call-seq: update(**attrs) -> self
243
+ #
244
+ # Assigns all of the provided +attrs+ to this config, and returns +self+.
245
+ #
246
+ # An ArgumentError is raised unless every key in +attrs+ matches an
247
+ # assignment method on Config.
248
+ #
249
+ # >>>
250
+ # *NOTE:* #update is not atomic. If an exception is raised due to an
251
+ # invalid attribute value, +attrs+ may be partially applied.
252
+ def update(**attrs)
253
+ unless (bad = attrs.keys.reject { respond_to?(:"#{_1}=") }).empty?
254
+ raise ArgumentError, "invalid config options: #{bad.join(", ")}"
255
+ end
256
+ attrs.each do send(:"#{_1}=", _2) end
257
+ self
258
+ end
259
+
260
+ # :call-seq:
261
+ # with(**attrs) -> config
262
+ # with(**attrs) {|config| } -> result
263
+ #
264
+ # Without a block, returns a new config which inherits from self. With a
265
+ # block, yields the new config and returns the block's result.
266
+ #
267
+ # If no keyword arguments are given, an ArgumentError will be raised.
268
+ #
269
+ # If +self+ is frozen, the copy will also be frozen.
270
+ def with(**attrs)
271
+ attrs.empty? and
272
+ raise ArgumentError, "expected keyword arguments, none given"
273
+ copy = new(**attrs)
274
+ copy.freeze if frozen?
275
+ block_given? ? yield(copy) : copy
276
+ end
277
+
278
+ # :call-seq: load_defaults(version) -> self
279
+ #
280
+ # Resets the current config to behave like the versioned default
281
+ # configuration for +version+. #parent will not be changed.
282
+ #
283
+ # Some config attributes default to inheriting from their #parent (which
284
+ # is usually Config.global) and are left unchanged, for example: #debug.
285
+ #
286
+ # See Config@Versioned+defaults and Config@Named+defaults.
287
+ def load_defaults(version)
288
+ [Numeric, Symbol, String].any? { _1 === version } or
289
+ raise ArgumentError, "expected number or symbol, got %p" % [version]
290
+ update(**Config[version].defaults_hash)
291
+ end
292
+
293
+ # :call-seq: to_h -> hash
294
+ #
295
+ # Returns all config attributes in a hash.
296
+ def to_h; data.members.to_h { [_1, send(_1)] } end
297
+
298
+ protected
299
+
300
+ def defaults_hash
301
+ to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
302
+ end
303
+
304
+ @default = new(
305
+ debug: false,
306
+ open_timeout: 30,
307
+ idle_response_timeout: 5,
308
+ sasl_ir: true,
309
+ responses_without_block: :silence_deprecation_warning,
310
+ ).freeze
311
+
312
+ @global = default.new
313
+
314
+ version_defaults[0.4] = Config[default.send(:defaults_hash)]
315
+
316
+ version_defaults[0] = Config[0.4].dup.update(
317
+ sasl_ir: false,
318
+ ).freeze
319
+ version_defaults[0.0] = Config[0]
320
+ version_defaults[0.1] = Config[0]
321
+ version_defaults[0.2] = Config[0]
322
+ version_defaults[0.3] = Config[0]
323
+
324
+ version_defaults[0.5] = Config[0.4].dup.update(
325
+ responses_without_block: :warn,
326
+ ).freeze
327
+
328
+ version_defaults[:default] = Config[0.4]
329
+ version_defaults[:current] = Config[0.4]
330
+ version_defaults[:next] = Config[0.5]
331
+
332
+ version_defaults[:future] = Config[0.5].dup.update(
333
+ responses_without_block: :raise,
334
+ ).freeze
335
+
336
+ version_defaults.freeze
337
+ end
338
+ end
339
+ end
@@ -16,8 +16,8 @@ module Net
16
16
  #
17
17
  # ==== Obsolete arguments
18
18
  #
19
- # Using obsolete arguments does not a warning. Obsolete arguments will be
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.
@@ -154,7 +154,7 @@ module Net
154
154
  end
155
155
 
156
156
  # To be used conditionally:
157
- # assert_no_lookahead if Net::IMAP.debug
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 Net::IMAP.debug
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 Net::IMAP.debug
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 Net::IMAP.debug
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 Net::IMAP.debug
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 IMAP.debug
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]
@@ -11,12 +11,15 @@ module Net
11
11
  include ParserUtils
12
12
  extend ParserUtils::Generator
13
13
 
14
+ attr_reader :config
15
+
14
16
  # :call-seq: Net::IMAP::ResponseParser.new -> Net::IMAP::ResponseParser
15
- def initialize
17
+ def initialize(config: Config.global)
16
18
  @str = nil
17
19
  @pos = nil
18
20
  @lex_state = nil
19
21
  @token = nil
22
+ @config = Config[config]
20
23
  end
21
24
 
22
25
  # :call-seq:
data/lib/net/imap.rb CHANGED
@@ -717,7 +717,7 @@ module Net
717
717
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
718
718
  #
719
719
  class IMAP < Protocol
720
- VERSION = "0.4.12"
720
+ VERSION = "0.4.14"
721
721
 
722
722
  # Aliases for supported capabilities, to be used with the #enable command.
723
723
  ENABLE_ALIASES = {
@@ -735,14 +735,15 @@ module Net
735
735
  include SSL
736
736
  end
737
737
 
738
- # Returns the debug mode.
739
- def self.debug
740
- return @@debug
741
- end
738
+ # Returns the global Config object
739
+ def self.config; Config.global end
740
+
741
+ # Returns the global debug mode.
742
+ def self.debug; config.debug end
742
743
 
743
- # Sets the debug mode.
744
+ # Sets the global debug mode.
744
745
  def self.debug=(val)
745
- return @@debug = val
746
+ config.debug = val
746
747
  end
747
748
 
748
749
  # The default port for IMAP connections, port 143
@@ -764,13 +765,19 @@ module Net
764
765
  # Returns the initial greeting the server, an UntaggedResponse.
765
766
  attr_reader :greeting
766
767
 
768
+ # The client configuration. See Net::IMAP::Config.
769
+ #
770
+ # By default, the client's local configuration inherits from the global
771
+ # Net::IMAP.config.
772
+ attr_reader :config
773
+
767
774
  # Seconds to wait until a connection is opened.
768
775
  # If the IMAP object cannot open a connection within this time,
769
776
  # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
770
- attr_reader :open_timeout
777
+ def open_timeout; config.open_timeout end
771
778
 
772
779
  # Seconds to wait until an IDLE response is received.
773
- attr_reader :idle_response_timeout
780
+ def idle_response_timeout; config.idle_response_timeout end
774
781
 
775
782
  # The hostname this client connected to
776
783
  attr_reader :host
@@ -809,14 +816,40 @@ module Net
809
816
  # If +ssl+ is a hash, it's passed to
810
817
  # {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
811
818
  # the keys are names of attribute assignment methods on
812
- # SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
819
+ # SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]. For example:
820
+ #
821
+ # [{ca_file}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_file]]
822
+ # The path to a file containing a PEM-format CA certificate.
823
+ # [{ca_path}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_path]]
824
+ # The path to a directory containing CA certificates in PEM format.
825
+ # [{min_version}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D]]
826
+ # Sets the lower bound on the supported SSL/TLS protocol version. Set to
827
+ # an +OpenSSL+ constant such as +OpenSSL::SSL::TLS1_2_VERSION+,
828
+ # [{verify_mode}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-verify_mode]]
829
+ # SSL session verification mode. Valid modes include
830
+ # +OpenSSL::SSL::VERIFY_PEER+ and +OpenSSL::SSL::VERIFY_NONE+.
831
+ #
832
+ # See {OpenSSL::SSL::SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html] for other valid SSL context params.
833
+ #
834
+ # See DeprecatedClientOptions.new for deprecated SSL arguments.
813
835
  #
814
- # [open_timeout]
815
- # Seconds to wait until a connection is opened
816
- # [idle_response_timeout]
817
- # Seconds to wait until an IDLE response is received
836
+ # [config]
837
+ # A Net::IMAP::Config object to use as the basis for #config. By default,
838
+ # the global Net::IMAP.config is used.
818
839
  #
819
- # See DeprecatedClientOptions.new for deprecated arguments.
840
+ # >>>
841
+ # *NOTE:* +config+ does not set #config directly---it sets the _parent_
842
+ # config for inheritance. Every client creates its own unique #config.
843
+ #
844
+ # All other keyword arguments are forwarded to Net::IMAP::Config.new, to
845
+ # initialize the client's #config. For example:
846
+ #
847
+ # [{open_timeout}[rdoc-ref:Config#open_timeout]]
848
+ # Seconds to wait until a connection is opened
849
+ # [{idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]]
850
+ # Seconds to wait until an IDLE response is received
851
+ #
852
+ # See Net::IMAP::Config for other valid options.
820
853
  #
821
854
  # ==== Examples
822
855
  #
@@ -872,13 +905,12 @@ module Net
872
905
  # Connected to the host successfully, but it immediately said goodbye.
873
906
  #
874
907
  def initialize(host, port: nil, ssl: nil,
875
- open_timeout: 30, idle_response_timeout: 5)
908
+ config: Config.global, **config_options)
876
909
  super()
877
910
  # Config options
878
911
  @host = host
912
+ @config = Config.new(config, **config_options)
879
913
  @port = port || (ssl ? SSL_PORT : PORT)
880
- @open_timeout = Integer(open_timeout)
881
- @idle_response_timeout = Integer(idle_response_timeout)
882
914
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
883
915
 
884
916
  # Basic Client State
@@ -889,7 +921,7 @@ module Net
889
921
  @capabilities = nil
890
922
 
891
923
  # Client Protocol Receiver
892
- @parser = ResponseParser.new
924
+ @parser = ResponseParser.new(config: @config)
893
925
  @responses = Hash.new {|h, k| h[k] = [] }
894
926
  @response_handlers = []
895
927
  @receiver_thread = nil
@@ -1198,7 +1230,7 @@ module Net
1198
1230
  end
1199
1231
 
1200
1232
  # :call-seq:
1201
- # authenticate(mechanism, *, sasl_ir: true, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
1233
+ # authenticate(mechanism, *, sasl_ir: config.sasl_ir, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
1202
1234
  #
1203
1235
  # Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
1204
1236
  # to authenticate the client. If successful, the connection enters the
@@ -1207,7 +1239,8 @@ module Net
1207
1239
  # +mechanism+ is the name of the \SASL authentication mechanism to be used.
1208
1240
  #
1209
1241
  # +sasl_ir+ allows or disallows sending an "initial response" (see the
1210
- # +SASL-IR+ capability, below).
1242
+ # +SASL-IR+ capability, below). Defaults to the #config value for
1243
+ # {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
1211
1244
  #
1212
1245
  # All other arguments are forwarded to the registered SASL authenticator for
1213
1246
  # the requested mechanism. <em>The documentation for each individual
@@ -1303,7 +1336,9 @@ module Net
1303
1336
  # Previously cached #capabilities will be cleared when this method
1304
1337
  # completes. If the TaggedResponse to #authenticate includes updated
1305
1338
  # capabilities, they will be cached.
1306
- def authenticate(mechanism, *creds, sasl_ir: true, **props, &callback)
1339
+ def authenticate(mechanism, *creds,
1340
+ sasl_ir: config.sasl_ir,
1341
+ **props, &callback)
1307
1342
  mechanism = mechanism.to_s.tr("_", "-").upcase
1308
1343
  authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
1309
1344
  cmdargs = ["AUTHENTICATE", mechanism]
@@ -2397,11 +2432,17 @@ module Net
2397
2432
  # checks the connection for each 60 seconds.
2398
2433
  #
2399
2434
  # loop do
2400
- # imap.idle(60) do |res|
2401
- # ...
2435
+ # imap.idle(60) do |response|
2436
+ # do_something_with(response)
2437
+ # imap.idle_done if some_condition?(response)
2402
2438
  # end
2403
2439
  # end
2404
2440
  #
2441
+ # Returns the server's response to indicate the IDLE state has ended.
2442
+ # Returns +nil+ if the server does not respond to #idle_done within
2443
+ # {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
2444
+ # seconds.
2445
+ #
2405
2446
  # Related: #idle_done, #noop, #check
2406
2447
  #
2407
2448
  # ===== Capabilities
@@ -2429,7 +2470,7 @@ module Net
2429
2470
  unless @receiver_thread_terminating
2430
2471
  remove_response_handler(response_handler)
2431
2472
  put_string("DONE#{CRLF}")
2432
- response = get_tagged_response(tag, "IDLE", @idle_response_timeout)
2473
+ response = get_tagged_response(tag, "IDLE", idle_response_timeout)
2433
2474
  end
2434
2475
  end
2435
2476
  end
@@ -2437,7 +2478,11 @@ module Net
2437
2478
  return response
2438
2479
  end
2439
2480
 
2440
- # Leaves IDLE.
2481
+ # Leaves IDLE, allowing #idle to return.
2482
+ #
2483
+ # If the server does not respond within
2484
+ # {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
2485
+ # seconds, #idle will return +nil+.
2441
2486
  #
2442
2487
  # Related: #idle
2443
2488
  def idle_done
@@ -2477,6 +2522,7 @@ module Net
2477
2522
  #
2478
2523
  # Calling without a block is unsafe and deprecated. Future releases will
2479
2524
  # raise ArgumentError unless a block is given.
2525
+ # See Config#responses_without_block.
2480
2526
  #
2481
2527
  # Previously unhandled responses are automatically cleared before entering a
2482
2528
  # mailbox with #select or #examine. Long-lived connections can receive many
@@ -2501,7 +2547,12 @@ module Net
2501
2547
  elsif type
2502
2548
  raise ArgumentError, "Pass a block or use #clear_responses"
2503
2549
  else
2504
- # warn("DEPRECATED: pass a block or use #clear_responses", uplevel: 1)
2550
+ case config.responses_without_block
2551
+ when :raise
2552
+ raise ArgumentError, "Pass a block or use #clear_responses"
2553
+ when :warn
2554
+ warn("DEPRECATED: pass a block or use #clear_responses", uplevel: 1)
2555
+ end
2505
2556
  @responses
2506
2557
  end
2507
2558
  end
@@ -2582,8 +2633,6 @@ module Net
2582
2633
  PORT = 143 # :nodoc:
2583
2634
  SSL_PORT = 993 # :nodoc:
2584
2635
 
2585
- @@debug = false
2586
-
2587
2636
  def start_imap_connection
2588
2637
  @greeting = get_server_greeting
2589
2638
  @capabilities = capabilities_from_resp_code @greeting
@@ -2611,12 +2660,12 @@ module Net
2611
2660
  end
2612
2661
 
2613
2662
  def tcp_socket(host, port)
2614
- s = Socket.tcp(host, port, :connect_timeout => @open_timeout)
2663
+ s = Socket.tcp(host, port, :connect_timeout => open_timeout)
2615
2664
  s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
2616
2665
  s
2617
2666
  rescue Errno::ETIMEDOUT
2618
2667
  raise Net::OpenTimeout, "Timeout to open TCP connection to " +
2619
- "#{host}:#{port} (exceeds #{@open_timeout} seconds)"
2668
+ "#{host}:#{port} (exceeds #{open_timeout} seconds)"
2620
2669
  end
2621
2670
 
2622
2671
  def receive_responses
@@ -2728,7 +2777,7 @@ module Net
2728
2777
  end
2729
2778
  end
2730
2779
  return nil if buff.length == 0
2731
- if @@debug
2780
+ if config.debug?
2732
2781
  $stderr.print(buff.gsub(/^/n, "S: "))
2733
2782
  end
2734
2783
  return @parser.parse(buff)
@@ -2807,7 +2856,7 @@ module Net
2807
2856
 
2808
2857
  def put_string(str)
2809
2858
  @sock.print(str)
2810
- if @@debug
2859
+ if config.debug?
2811
2860
  if @debug_output_bol
2812
2861
  $stderr.print("C: ")
2813
2862
  end
@@ -2934,7 +2983,7 @@ module Net
2934
2983
  @sock = SSLSocket.new(@sock, ssl_ctx)
2935
2984
  @sock.sync_close = true
2936
2985
  @sock.hostname = @host if @sock.respond_to? :hostname=
2937
- ssl_socket_connect(@sock, @open_timeout)
2986
+ ssl_socket_connect(@sock, open_timeout)
2938
2987
  if ssl_ctx.verify_mode != VERIFY_NONE
2939
2988
  @sock.post_connection_check(@host)
2940
2989
  @tls_verified = true
@@ -2959,6 +3008,7 @@ module Net
2959
3008
  end
2960
3009
 
2961
3010
  require_relative "imap/errors"
3011
+ require_relative "imap/config"
2962
3012
  require_relative "imap/command_data"
2963
3013
  require_relative "imap/data_encoding"
2964
3014
  require_relative "imap/flags"
data/net-imap.gemspec CHANGED
@@ -25,9 +25,9 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  # Specify which files should be added to the gem when it is released.
27
27
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
28
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
+ spec.files = Dir.chdir(__dir__) do
29
29
  `git ls-files -z 2>/dev/null`.split("\x0")
30
- .grep_v(%r{^(\.git|\.mailmap|bin|test|spec|benchmarks|features|rfcs)/})
30
+ .grep_v(%r{^(\.git(ignore)?|\.mailmap|(\.github|bin|test|spec|benchmarks|features|rfcs)/)})
31
31
  end
32
32
  spec.bindir = "exe"
33
33
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.12
4
+ version: 0.4.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  - nicholas a. evans
9
+ autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2024-06-01 00:00:00.000000000 Z
12
+ date: 2024-06-22 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: net-protocol
@@ -46,12 +47,6 @@ executables: []
46
47
  extensions: []
47
48
  extra_rdoc_files: []
48
49
  files:
49
- - ".github/dependabot.yml"
50
- - ".github/workflows/pages.yml"
51
- - ".github/workflows/push_gem.yml"
52
- - ".github/workflows/test.yml"
53
- - ".gitignore"
54
- - ".mailmap"
55
50
  - BSDL
56
51
  - COPYING
57
52
  - Gemfile
@@ -62,6 +57,10 @@ files:
62
57
  - lib/net/imap.rb
63
58
  - lib/net/imap/authenticators.rb
64
59
  - lib/net/imap/command_data.rb
60
+ - lib/net/imap/config.rb
61
+ - lib/net/imap/config/attr_accessors.rb
62
+ - lib/net/imap/config/attr_inheritance.rb
63
+ - lib/net/imap/config/attr_type_coercion.rb
65
64
  - lib/net/imap/data_encoding.rb
66
65
  - lib/net/imap/deprecated_client_options.rb
67
66
  - lib/net/imap/errors.rb
@@ -111,6 +110,7 @@ metadata:
111
110
  homepage_uri: https://github.com/ruby/net-imap
112
111
  source_code_uri: https://github.com/ruby/net-imap
113
112
  changelog_uri: https://github.com/ruby/net-imap/releases
113
+ post_install_message:
114
114
  rdoc_options: []
115
115
  require_paths:
116
116
  - lib
@@ -125,7 +125,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubygems_version: 3.6.0.dev
128
+ rubygems_version: 3.5.9
129
+ signing_key:
129
130
  specification_version: 4
130
131
  summary: Ruby client api for Internet Message Access Protocol
131
132
  test_files: []
@@ -1,6 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: 'github-actions'
4
- directory: '/'
5
- schedule:
6
- interval: 'weekly'
@@ -1,46 +0,0 @@
1
- name: Deploy RDoc site to Pages
2
-
3
- on:
4
- push:
5
- branches: [ 'master' ]
6
- workflow_dispatch:
7
-
8
- permissions:
9
- contents: read
10
- pages: write
11
- id-token: write
12
-
13
- concurrency:
14
- group: "pages"
15
- cancel-in-progress: true
16
-
17
- jobs:
18
- build:
19
- runs-on: ubuntu-latest
20
- steps:
21
- - name: Checkout
22
- uses: actions/checkout@v4
23
- - name: Setup Ruby
24
- uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0
25
- with:
26
- ruby-version: '3.2'
27
- bundler-cache: true
28
- - name: Setup Pages
29
- id: pages
30
- uses: actions/configure-pages@v5
31
- - name: Build with RDoc
32
- run: bundle exec rake rdoc
33
- - name: Upload artifact
34
- uses: actions/upload-pages-artifact@v3
35
- with: { path: 'doc' }
36
-
37
- deploy:
38
- environment:
39
- name: github-pages
40
- url: ${{ steps.deployment.outputs.page_url }}
41
- runs-on: ubuntu-latest
42
- needs: build
43
- steps:
44
- - name: Deploy to GitHub Pages
45
- id: deployment
46
- uses: actions/deploy-pages@v4
@@ -1,48 +0,0 @@
1
- name: Publish gem to rubygems.org
2
-
3
- on:
4
- push:
5
- tags:
6
- - 'v*'
7
-
8
- permissions:
9
- contents: read
10
-
11
- jobs:
12
- push:
13
- if: github.repository == 'ruby/net-imap'
14
- runs-on: ubuntu-latest
15
-
16
- environment:
17
- name: rubygems.org
18
- url: https://rubygems.org/gems/net-imap
19
-
20
- permissions:
21
- contents: write
22
- id-token: write
23
-
24
- steps:
25
- # Set up
26
- - name: Harden Runner
27
- uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
28
- with:
29
- egress-policy: audit
30
-
31
- - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
32
-
33
- - name: Set up Ruby
34
- uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0
35
- with:
36
- bundler-cache: true
37
- ruby-version: ruby
38
-
39
- # Release
40
- - name: Publish to RubyGems
41
- uses: rubygems/release-gem@612653d273a73bdae1df8453e090060bb4db5f31 # v1
42
-
43
- - name: Create GitHub release
44
- run: |
45
- tag_name="$(git describe --tags --abbrev=0)"
46
- gh release create "${tag_name}" --verify-tag --draft --generate-notes
47
- env:
48
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1,31 +0,0 @@
1
- name: Run tests
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- ruby-versions:
7
- uses: ruby/actions/.github/workflows/ruby_versions.yml@master
8
- with:
9
- engine: cruby
10
- min_version: 2.7
11
-
12
- build:
13
- needs: ruby-versions
14
- name: build (${{ matrix.ruby }} / ${{ matrix.os }})
15
- strategy:
16
- matrix:
17
- ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
18
- os: [ ubuntu-latest, macos-latest ]
19
- experimental: [false]
20
- runs-on: ${{ matrix.os }}
21
- continue-on-error: ${{ matrix.experimental }}
22
- steps:
23
- - uses: actions/checkout@v4
24
- - name: Set up Ruby
25
- uses: ruby/setup-ruby@v1
26
- with:
27
- ruby-version: ${{ matrix.ruby }}
28
- bundler-cache: true
29
- rubygems: 3.4.22
30
- - name: Run test
31
- run: bundle exec rake test
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /rfcs
8
- /spec/reports/
9
- /tmp/
10
- /Gemfile.lock
11
- /benchmarks/Gemfile*
12
- /benchmarks/parser.yml
data/.mailmap DELETED
@@ -1,13 +0,0 @@
1
- nicholas a. evans <nick@rubinick.dev>
2
- nicholas a. evans <nick@rubinick.dev> <nick@410labs.com>
3
- nicholas a. evans <nick@rubinick.dev> <nick@ekenosen.net>
4
- nicholas a. evans <nick@rubinick.dev> <nicholas.evans@gmail.com>
5
-
6
- Shugo Maeda <shugo@ruby-lang.org>
7
- Shugo Maeda <shugo@ruby-lang.org> <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
8
-
9
- Nobuyoshi Nakada <nobu@ruby-lang.org>
10
- Nobuyoshi Nakada <nobu@ruby-lang.org> <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
11
-
12
- Hiroshi SHIBATA <hsbt@ruby-lang.org>
13
- Hiroshi SHIBATA <hsbt@ruby-lang.org> <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>