net-imap 0.6.2 → 0.6.4
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/.document +3 -0
- data/.rdoc_options +7 -0
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/lib/net/imap/command_data.rb +170 -12
- data/lib/net/imap/config/attr_accessors.rb +8 -9
- data/lib/net/imap/config/attr_inheritance.rb +50 -0
- data/lib/net/imap/config/attr_version_defaults.rb +2 -5
- data/lib/net/imap/config.rb +35 -1
- data/lib/net/imap/data_encoding.rb +50 -0
- data/lib/net/imap/errors.rb +189 -0
- data/lib/net/imap/response_data.rb +108 -11
- data/lib/net/imap/response_parser/parser_utils.rb +14 -23
- data/lib/net/imap/response_parser.rb +34 -6
- data/lib/net/imap/response_reader.rb +25 -16
- data/lib/net/imap/sasl/scram_authenticator.rb +74 -0
- data/lib/net/imap/search_result.rb +5 -1
- data/lib/net/imap.rb +167 -61
- data/rakelib/rdoc.rake +1 -18
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ffcec5e3cc42d7f72c63520110ec442f760c35472401de495001f420cfa059c5
|
|
4
|
+
data.tar.gz: aedd703997fc651cb6c67635a3d20a4159720c6e1bda41c07322f2bd67a627d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1fa95302f29607d152b991535a9d14cda5dae1a1fce06b1d831dd2b09f69b2fd845367e0ebed73dd4e79d04c064a801e59865e76947060a4811304728458f3c7
|
|
7
|
+
data.tar.gz: 2dc9a56758d6f370affc9540a8ca26b8ad866ca5ef0e5b9bc2c30ff6257e3461e5ed23173e1c5e98a2e95c1e0f0a844c96f9e34d41151e9196ab513ac6987d33
|
data/.document
ADDED
data/.rdoc_options
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -61,9 +61,9 @@ imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_i
|
|
|
61
61
|
else
|
|
62
62
|
imap.copy(message_id, "Mail/sent-apr03")
|
|
63
63
|
imap.store(message_id, "+FLAGS", [:Deleted])
|
|
64
|
+
imap.expunge
|
|
64
65
|
end
|
|
65
66
|
end
|
|
66
|
-
imap.expunge
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
## Development
|
|
@@ -4,6 +4,8 @@ require "date"
|
|
|
4
4
|
|
|
5
5
|
require_relative "errors"
|
|
6
6
|
|
|
7
|
+
# :enddoc:
|
|
8
|
+
|
|
7
9
|
module Net
|
|
8
10
|
class IMAP < Protocol
|
|
9
11
|
|
|
@@ -25,6 +27,7 @@ module Net
|
|
|
25
27
|
end
|
|
26
28
|
when Time, Date, DateTime
|
|
27
29
|
when Symbol
|
|
30
|
+
Flag.validate(data)
|
|
28
31
|
else
|
|
29
32
|
data.validate
|
|
30
33
|
end
|
|
@@ -45,7 +48,7 @@ module Net
|
|
|
45
48
|
when Date
|
|
46
49
|
send_date_data(data)
|
|
47
50
|
when Symbol
|
|
48
|
-
|
|
51
|
+
Flag[data].send_data(self, tag)
|
|
49
52
|
else
|
|
50
53
|
data.send_data(self, tag)
|
|
51
54
|
end
|
|
@@ -77,9 +80,23 @@ module Net
|
|
|
77
80
|
put_string('"' + str.gsub(/["\\]/, "\\\\\\&") + '"')
|
|
78
81
|
end
|
|
79
82
|
|
|
80
|
-
def
|
|
83
|
+
def send_binary_literal(*, **) = send_literal(*, **, binary: true)
|
|
84
|
+
|
|
85
|
+
# `non_sync` is an optional tri-state flag:
|
|
86
|
+
# * `true` -> Force non-synchronizing `LITERAL+`/`LITERAL-` behavior.
|
|
87
|
+
# TODO: raise or warn when capabilities don't allow non_sync.
|
|
88
|
+
# * `false` -> Force normal synchronizing literal behavior.
|
|
89
|
+
# * `nil` -> (default) Currently behaves like `false` (will be dynamic).
|
|
90
|
+
def send_literal(str, tag = nil, binary: false, non_sync: nil)
|
|
81
91
|
synchronize do
|
|
82
|
-
|
|
92
|
+
non_sync = non_sync_literal?(str.bytesize) if non_sync.nil?
|
|
93
|
+
prefix = "~" if binary
|
|
94
|
+
plus = "+" if non_sync
|
|
95
|
+
put_string("#{prefix}{#{str.bytesize}#{plus}}\r\n")
|
|
96
|
+
if non_sync
|
|
97
|
+
put_string(str)
|
|
98
|
+
return
|
|
99
|
+
end
|
|
83
100
|
@continued_command_tag = tag
|
|
84
101
|
@continuation_request_exception = nil
|
|
85
102
|
begin
|
|
@@ -94,6 +111,13 @@ module Net
|
|
|
94
111
|
end
|
|
95
112
|
end
|
|
96
113
|
|
|
114
|
+
def non_sync_literal?(bytesize)
|
|
115
|
+
capabilities_cached? &&
|
|
116
|
+
bytesize <= config.max_non_synchronizing_literal &&
|
|
117
|
+
(capable?("LITERAL+") ||
|
|
118
|
+
bytesize <= 4096 && (capable?("IMAP4rev2") || capable?("LITERAL-")))
|
|
119
|
+
end
|
|
120
|
+
|
|
97
121
|
def send_number_data(num)
|
|
98
122
|
put_string(num.to_s)
|
|
99
123
|
end
|
|
@@ -115,11 +139,13 @@ module Net
|
|
|
115
139
|
def send_date_data(date) put_string Net::IMAP.encode_date(date) end
|
|
116
140
|
def send_time_data(time) put_string Net::IMAP.encode_time(time) end
|
|
117
141
|
|
|
118
|
-
def send_symbol_data(symbol)
|
|
119
|
-
put_string("\\" + symbol.to_s)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
142
|
CommandData = Data.define(:data) do # :nodoc:
|
|
143
|
+
def self.validate(...)
|
|
144
|
+
data = new(...)
|
|
145
|
+
data.validate
|
|
146
|
+
data
|
|
147
|
+
end
|
|
148
|
+
|
|
123
149
|
def send_data(imap, tag)
|
|
124
150
|
raise NoMethodError, "#{self.class} must implement #{__method__}"
|
|
125
151
|
end
|
|
@@ -128,15 +154,109 @@ module Net
|
|
|
128
154
|
end
|
|
129
155
|
end
|
|
130
156
|
|
|
157
|
+
# Represents IMAP +text+ data, which may contain any 7-bit ASCII character,
|
|
158
|
+
# except for +NULL+, +CR+, or +LF+. +text+ is extended to allow any
|
|
159
|
+
# multibyte +UTF-8+ character when either +UTF8=ACCEPT+ or +IMAP4rev2+ have
|
|
160
|
+
# been enabled, or when the server supports only +IMAP4rev2+ and not earlier
|
|
161
|
+
# IMAP revisions, or when the server advertises +UTF8=ONLY+.
|
|
162
|
+
#
|
|
163
|
+
# NOTE: The current implementation does not validate whether the connection
|
|
164
|
+
# currently supports UTF-8. Future versions may change.
|
|
165
|
+
#
|
|
166
|
+
# The string's bytes must be valid ASCII or valid UTF-8. The string's
|
|
167
|
+
# reported encoding is ignored, but the string is _not_ transcoded.
|
|
168
|
+
class RawText < CommandData # :nodoc:
|
|
169
|
+
def initialize(data:)
|
|
170
|
+
data = String(data.to_str)
|
|
171
|
+
data = if data.encoding in Encoding::ASCII | Encoding::UTF_8
|
|
172
|
+
-data
|
|
173
|
+
elsif data.ascii_only?
|
|
174
|
+
-(data.dup.force_encoding("ASCII"))
|
|
175
|
+
else
|
|
176
|
+
-(data.dup.force_encoding("UTF-8"))
|
|
177
|
+
end
|
|
178
|
+
super
|
|
179
|
+
validate
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def validate
|
|
183
|
+
if data.include?("\0")
|
|
184
|
+
raise DataFormatError, "NULL byte must be binary literal encoded"
|
|
185
|
+
elsif !data.valid_encoding?
|
|
186
|
+
raise DataFormatError, "invalid UTF-8 must be literal encoded"
|
|
187
|
+
elsif /[\r\n]/.match?(data)
|
|
188
|
+
raise DataFormatError, "CR and LF bytes must be literal encoded"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def ascii_only? = data.ascii_only?
|
|
193
|
+
|
|
194
|
+
def send_data(imap, tag) = imap.__send__(:put_string, data)
|
|
195
|
+
end
|
|
196
|
+
|
|
131
197
|
class RawData < CommandData # :nodoc:
|
|
132
|
-
def
|
|
133
|
-
|
|
198
|
+
def initialize(data:)
|
|
199
|
+
data = split_parts(data)
|
|
200
|
+
super
|
|
201
|
+
validate
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def send_data(imap, tag) = data.each do _1.send_data(imap, tag) end
|
|
205
|
+
|
|
206
|
+
def validate
|
|
207
|
+
return unless data.last in RawText(data: text)
|
|
208
|
+
if text.rindex(/~?\{[1-9]\d*\+?\}\z/n)
|
|
209
|
+
raise DataFormatError, "RawData cannot end with literal continuation"
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
private
|
|
214
|
+
|
|
215
|
+
def split_parts(data)
|
|
216
|
+
data = data.b # dups and ensures BINARY encoding
|
|
217
|
+
parts = []
|
|
218
|
+
while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n)
|
|
219
|
+
text, binary, bytesize, non_sync, data = $`, !!$1, $2, !!$3, $'
|
|
220
|
+
bytesize = NumValidator.coerce_number64 bytesize
|
|
221
|
+
parts << RawText[text] unless text.empty?
|
|
222
|
+
parts << extract_literal(data, binary:, bytesize:, non_sync:)
|
|
223
|
+
data.bytesplice(0, bytesize, "")
|
|
224
|
+
end
|
|
225
|
+
parts << RawText[data] unless data.empty?
|
|
226
|
+
parts
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def extract_literal(data, binary:, bytesize:, non_sync:)
|
|
230
|
+
if data.bytesize < bytesize
|
|
231
|
+
raise DataFormatError, "Too few bytes in string for literal, " \
|
|
232
|
+
"expected: %s, remaining: %s" % [bytesize, data.bytesize]
|
|
233
|
+
end
|
|
234
|
+
literal = data.byteslice(0, bytesize)
|
|
235
|
+
(binary ? Literal8 : Literal).new(data: literal, non_sync:)
|
|
134
236
|
end
|
|
135
237
|
end
|
|
136
238
|
|
|
137
239
|
class Atom < CommandData # :nodoc:
|
|
240
|
+
def initialize(**)
|
|
241
|
+
super
|
|
242
|
+
validate
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def validate
|
|
246
|
+
data.to_s.ascii_only? \
|
|
247
|
+
or raise DataFormatError, "#{self.class} must be ASCII only"
|
|
248
|
+
data.match?(ResponseParser::Patterns::ATOM_SPECIALS) \
|
|
249
|
+
and raise DataFormatError, "#{self.class} must not contain atom-specials"
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def send_data(imap, tag)
|
|
253
|
+
imap.__send__(:put_string, data.to_s)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
class Flag < Atom # :nodoc:
|
|
138
258
|
def send_data(imap, tag)
|
|
139
|
-
imap.__send__(:put_string, data)
|
|
259
|
+
imap.__send__(:put_string, "\\#{data}")
|
|
140
260
|
end
|
|
141
261
|
end
|
|
142
262
|
|
|
@@ -146,9 +266,39 @@ module Net
|
|
|
146
266
|
end
|
|
147
267
|
end
|
|
148
268
|
|
|
149
|
-
class Literal <
|
|
269
|
+
class Literal < Data.define(:data, :non_sync) # :nodoc:
|
|
270
|
+
def self.validate(...)
|
|
271
|
+
data = new(...)
|
|
272
|
+
data.validate
|
|
273
|
+
data
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def initialize(data:, non_sync: nil)
|
|
277
|
+
data = -String(data.to_str).b or
|
|
278
|
+
raise DataFormatError, "#{self.class} expects string input"
|
|
279
|
+
super
|
|
280
|
+
validate
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def bytesize = data.bytesize
|
|
284
|
+
|
|
285
|
+
def validate
|
|
286
|
+
if data.include?("\0")
|
|
287
|
+
raise DataFormatError, "NULL byte not allowed in #{self.class}. " \
|
|
288
|
+
"Use #{Literal8} or a null-safe encoding."
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def send_data(imap, tag)
|
|
293
|
+
imap.__send__(:send_literal, data, tag, non_sync:)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
class Literal8 < Literal # :nodoc:
|
|
298
|
+
def validate = nil # all bytes are okay
|
|
299
|
+
|
|
150
300
|
def send_data(imap, tag)
|
|
151
|
-
imap.__send__(:
|
|
301
|
+
imap.__send__(:send_binary_literal, data, tag, non_sync:)
|
|
152
302
|
end
|
|
153
303
|
end
|
|
154
304
|
|
|
@@ -221,6 +371,14 @@ module Net
|
|
|
221
371
|
|
|
222
372
|
module_function
|
|
223
373
|
|
|
374
|
+
def literal_or_literal8(input, name: "argument")
|
|
375
|
+
return input if input in Literal | Literal8
|
|
376
|
+
data = String.try_convert(input) \
|
|
377
|
+
or raise TypeError, "expected #{name} to be String, got #{input.class}"
|
|
378
|
+
type = data.include?("\0") ? Literal8 : Literal
|
|
379
|
+
type.new(data:)
|
|
380
|
+
end
|
|
381
|
+
|
|
224
382
|
# Allows symbols in addition to strings
|
|
225
383
|
def valid_string?(str)
|
|
226
384
|
str.is_a?(Symbol) || str.respond_to?(:to_str)
|
|
@@ -27,24 +27,23 @@ module Net
|
|
|
27
27
|
|
|
28
28
|
def self.attr_accessor(name) # :nodoc: internal API
|
|
29
29
|
name = name.to_sym
|
|
30
|
+
raise ArgumentError, "already defined #{name}" if attributes.include?(name)
|
|
31
|
+
attributes << name
|
|
30
32
|
def_delegators :data, name, :"#{name}="
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
private_class_method :attributes
|
|
35
|
+
# An array of Config attribute names
|
|
36
|
+
singleton_class.attr_reader :attributes
|
|
37
|
+
@attributes = []
|
|
37
38
|
|
|
38
39
|
def self.struct # :nodoc: internal API
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
end
|
|
42
|
-
self::Struct
|
|
40
|
+
attributes.freeze
|
|
41
|
+
Struct.new(*attributes)
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
def initialize # :notnew:
|
|
46
45
|
super()
|
|
47
|
-
@data =
|
|
46
|
+
@data = Config::Struct.new
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
# Freezes the internal attributes struct, in addition to +self+.
|
|
@@ -66,11 +66,61 @@ module Net
|
|
|
66
66
|
# inherited, or +false+ if any of them are overriden. When no +attrs+
|
|
67
67
|
# are given, returns +true+ if *all* attributes are inherited, or
|
|
68
68
|
# +false+ if any attribute is overriden.
|
|
69
|
+
#
|
|
70
|
+
# Related: #overrides?
|
|
69
71
|
def inherited?(*attrs)
|
|
70
72
|
attrs = data.members if attrs.empty?
|
|
71
73
|
attrs.all? { data[_1] == INHERITED }
|
|
72
74
|
end
|
|
73
75
|
|
|
76
|
+
# :call-seq:
|
|
77
|
+
# inherits_defaults?(*attrs) -> true | Rational | nil | false
|
|
78
|
+
#
|
|
79
|
+
# Returns whether all +attrs+ are inherited from a default config.
|
|
80
|
+
# When no +attrs+ are given, returns whether *all* attributes are
|
|
81
|
+
# inherited from a default config.
|
|
82
|
+
#
|
|
83
|
+
# Returns +true+ when all attributes inherit from Config.default, the
|
|
84
|
+
# version number (as a Rational) when all attributes inherit from a
|
|
85
|
+
# versioned default (see Config@Versioned+defaults), +nil+ if any
|
|
86
|
+
# attributes inherit from Config.global overrides (but not from
|
|
87
|
+
# non-global ancestors), or +false+ when any attributes have been
|
|
88
|
+
# overridden by +self+ or an ancestor (besides global or default
|
|
89
|
+
# configs),
|
|
90
|
+
#
|
|
91
|
+
# Related: #overrides?
|
|
92
|
+
def inherits_defaults?(*attrs)
|
|
93
|
+
if equal?(Config.default)
|
|
94
|
+
true
|
|
95
|
+
elsif equal?(Config.global)
|
|
96
|
+
true if inherited?(*attrs)
|
|
97
|
+
elsif (v = AttrVersionDefaults::VERSIONS.find { equal? Config[_1] })
|
|
98
|
+
attrs = DEFAULT_TO_INHERIT if attrs.empty?
|
|
99
|
+
attrs &= DEFAULT_TO_INHERIT
|
|
100
|
+
(attrs.empty? || parent.inherits_defaults?(*attrs)) && v
|
|
101
|
+
else
|
|
102
|
+
inherited?(*attrs) && parent.inherits_defaults?(*attrs)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# :call-seq:
|
|
107
|
+
# overrides?(attr) -> true or false
|
|
108
|
+
# overrides?(*attrs) -> true or false
|
|
109
|
+
# overrides? -> true or false
|
|
110
|
+
#
|
|
111
|
+
# Returns +true+ if +attr+ is defined on this config and not inherited
|
|
112
|
+
# from #parent.
|
|
113
|
+
#
|
|
114
|
+
# When multiple +attrs+ are given, returns +true+ if
|
|
115
|
+
# *any* of them are defined on +self+. When no +attrs+ are given,
|
|
116
|
+
# returns +true+ if *any* attribute is overriden.
|
|
117
|
+
#
|
|
118
|
+
# Related: #inherited?
|
|
119
|
+
def overrides?(*attrs)
|
|
120
|
+
attrs = data.members if attrs.empty?
|
|
121
|
+
attrs.any? { data[_1] != INHERITED }
|
|
122
|
+
end
|
|
123
|
+
|
|
74
124
|
# :call-seq:
|
|
75
125
|
# reset -> self
|
|
76
126
|
# reset(attr) -> attribute value
|
|
@@ -24,7 +24,7 @@ module Net
|
|
|
24
24
|
VERSIONS = ((0.0r..FUTURE_VERSION) % 0.1r).to_a.freeze
|
|
25
25
|
|
|
26
26
|
# See Config.version_defaults.
|
|
27
|
-
singleton_class.
|
|
27
|
+
singleton_class.attr_reader :version_defaults
|
|
28
28
|
|
|
29
29
|
@version_defaults = Hash.new {|h, k|
|
|
30
30
|
# NOTE: String responds to both so the order is significant.
|
|
@@ -59,10 +59,6 @@ module Net
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def self.compile_version_defaults!
|
|
62
|
-
# Temporarily assign Config.default, enabling #load_defaults(:default)
|
|
63
|
-
version_defaults[:default] = Config.default
|
|
64
|
-
# Use #load_defaults so some attributes are inherited from global.
|
|
65
|
-
version_defaults[:default] = Config.new.load_defaults(:default).freeze
|
|
66
62
|
version_defaults[0.0r] = Config[version_defaults.fetch(0.0r)]
|
|
67
63
|
|
|
68
64
|
VERSIONS.each_cons(2) do |prior, version|
|
|
@@ -81,6 +77,7 @@ module Net
|
|
|
81
77
|
|
|
82
78
|
version_defaults[:original] = Config[0.0r]
|
|
83
79
|
version_defaults[:current] = Config[CURRENT_VERSION]
|
|
80
|
+
version_defaults[:default] = Config[CURRENT_VERSION]
|
|
84
81
|
version_defaults[:next] = Config[NEXT_VERSION]
|
|
85
82
|
version_defaults[:future] = Config[FUTURE_VERSION]
|
|
86
83
|
|
data/lib/net/imap/config.rb
CHANGED
|
@@ -281,6 +281,40 @@ module Net
|
|
|
281
281
|
0.5r => true,
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
# The maximum bytesize for sending non-synchronizing literals, when the
|
|
285
|
+
# server supports them. To disable non-synchronizing literals, set the
|
|
286
|
+
# value to +-1+.
|
|
287
|
+
#
|
|
288
|
+
# Non-synchronizing literals are only sent when the server's
|
|
289
|
+
# capabilities[rdoc-ref:IMAP#capabilities] have been
|
|
290
|
+
# cached[rdoc-ref:IMAP#capabilities_cached?] and include either
|
|
291
|
+
# <tt>LITERAL+</tt> [RFC7888[https://www.rfc-editor.org/rfc/rfc7888]],
|
|
292
|
+
# <tt>LITERAL-</tt> [RFC7888[https://www.rfc-editor.org/rfc/rfc7888]], or
|
|
293
|
+
# +IMAP4rev2+ [RFC9051[https://www.rfc-editor.org/rfc/rfc9051]].
|
|
294
|
+
#
|
|
295
|
+
# For <tt>LITERAL+</tt>, this value is the only limit on whether a literal
|
|
296
|
+
# value is sent as non-synchronizing literals. For <tt>LITERAL-</tt> and
|
|
297
|
+
# <tt>IMAP4rev2</tt>, non-synchronizing literals must also be smaller than
|
|
298
|
+
# +4096+ bytes.
|
|
299
|
+
#
|
|
300
|
+
# Non-synchronizing literals avoid the latency of waiting for the server
|
|
301
|
+
# to allow continuation. However, if a client sends a non-synchronizing
|
|
302
|
+
# literal that is too large for the server, the server may need to close
|
|
303
|
+
# the connection. Because <tt>LITERAL+</tt> does not directly indicate
|
|
304
|
+
# the server's limits, it's best to avoid sending very large
|
|
305
|
+
# non-synchronized literals.
|
|
306
|
+
#
|
|
307
|
+
# ==== Versioned Defaults
|
|
308
|
+
#
|
|
309
|
+
# max_non_synchronizing_literal <em>was added in +v0.6.4+.</em>
|
|
310
|
+
#
|
|
311
|
+
# * original: +-1+ (_never_ send non-synchronizing literals)
|
|
312
|
+
# * +0.6+: 16 KiB
|
|
313
|
+
attr_accessor :max_non_synchronizing_literal, type: Integer?, defaults: {
|
|
314
|
+
0.0r => -1,
|
|
315
|
+
0.6r => 16 << 16, # 16 KiB
|
|
316
|
+
}
|
|
317
|
+
|
|
284
318
|
# The maximum allowed server response size. When +nil+, there is no limit
|
|
285
319
|
# on response size.
|
|
286
320
|
#
|
|
@@ -578,7 +612,6 @@ module Net
|
|
|
578
612
|
if equal? Config.default then "#{Config}.default"
|
|
579
613
|
elsif equal? Config.global then "#{Config}.global"
|
|
580
614
|
elsif equal? Config[0.0r] then "#{Config}[:original]"
|
|
581
|
-
elsif equal? Config[:default] then "#{Config}[:default]"
|
|
582
615
|
elsif (v = AttrVersionDefaults::VERSIONS.find { equal? Config[_1] })
|
|
583
616
|
"%s[%0.1f]" % [Config, v]
|
|
584
617
|
else
|
|
@@ -631,6 +664,7 @@ module Net
|
|
|
631
664
|
to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
|
|
632
665
|
end
|
|
633
666
|
|
|
667
|
+
Struct = AttrAccessors.struct
|
|
634
668
|
@default = AttrVersionDefaults.compile_default!
|
|
635
669
|
@global = default.new
|
|
636
670
|
AttrVersionDefaults.compile_version_defaults!
|
|
@@ -174,6 +174,22 @@ module Net
|
|
|
174
174
|
0 < num && num <= 0xffff_ffff
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
+
# Check if argument is a valid 'number64' according to RFC 9051
|
|
178
|
+
# number64 = 1*DIGIT
|
|
179
|
+
# ; Unsigned 63-bit integer
|
|
180
|
+
# ; (0 <= n <= 9,223,372,036,854,775,807)
|
|
181
|
+
def valid_number64?(num)
|
|
182
|
+
0 <= num && num <= 0x7fff_ffff_ffff_ffff
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Check if argument is a valid 'number64' according to RFC 9051
|
|
186
|
+
# nz-number64 = digit-nz *DIGIT
|
|
187
|
+
# ; Unsigned 63-bit integer
|
|
188
|
+
# ; (0 < n <= 9,223,372,036,854,775,807)
|
|
189
|
+
def valid_nz_number64?(num)
|
|
190
|
+
0 < num && num <= 0x7fff_ffff_ffff_ffff
|
|
191
|
+
end
|
|
192
|
+
|
|
177
193
|
# Check if argument is a valid 'mod-sequence-value' according to RFC 4551
|
|
178
194
|
# mod-sequence-value = 1*DIGIT
|
|
179
195
|
# ; Positive unsigned 64-bit integer
|
|
@@ -203,6 +219,20 @@ module Net
|
|
|
203
219
|
"nz-number must be non-zero unsigned 32-bit integer: #{num}"
|
|
204
220
|
end
|
|
205
221
|
|
|
222
|
+
# Ensure argument is 'number64' or raise DataFormatError
|
|
223
|
+
def ensure_number64(num)
|
|
224
|
+
return num if valid_number64?(num)
|
|
225
|
+
raise DataFormatError,
|
|
226
|
+
"number64 must be unsigned 63-bit integer: #{num}"
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Ensure argument is 'nz-number64' or raise DataFormatError
|
|
230
|
+
def ensure_nz_number64(num)
|
|
231
|
+
return num if valid_nz_number64?(num)
|
|
232
|
+
raise DataFormatError,
|
|
233
|
+
"nz-number64 must be non-zero unsigned 63-bit integer: #{num}"
|
|
234
|
+
end
|
|
235
|
+
|
|
206
236
|
# Ensure argument is 'mod-sequence-value' or raise DataFormatError
|
|
207
237
|
def ensure_mod_sequence_value(num)
|
|
208
238
|
return num if valid_mod_sequence_value?(num)
|
|
@@ -237,6 +267,26 @@ module Net
|
|
|
237
267
|
end
|
|
238
268
|
end
|
|
239
269
|
|
|
270
|
+
# Like #ensure_number64, but usable with numeric String input.
|
|
271
|
+
def coerce_number64(num)
|
|
272
|
+
case num
|
|
273
|
+
when Integer then ensure_number64 num
|
|
274
|
+
when NUMBER_RE then ensure_number64 Integer num
|
|
275
|
+
else
|
|
276
|
+
raise DataFormatError, "%p is not a valid number64" % [num]
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Like #ensure_nz_number64, but usable with numeric String input.
|
|
281
|
+
def coerce_nz_number64(num)
|
|
282
|
+
case num
|
|
283
|
+
when Integer then ensure_nz_number64 num
|
|
284
|
+
when NUMBER_RE then ensure_nz_number64 Integer num
|
|
285
|
+
else
|
|
286
|
+
raise DataFormatError, "%p is not a valid nz-number64" % [num]
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
240
290
|
# Like #ensure_mod_sequence_value, but usable with numeric String input.
|
|
241
291
|
def coerce_mod_sequence_value(num)
|
|
242
292
|
case num
|