net-imap 0.5.2 → 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +3 -1
- data/docs/styles.css +12 -7
- data/lib/net/imap/command_data.rb +32 -0
- data/lib/net/imap/config.rb +73 -3
- data/lib/net/imap/data_lite.rb +11 -10
- data/lib/net/imap/esearch_result.rb +44 -4
- data/lib/net/imap/fetch_data.rb +126 -47
- data/lib/net/imap/response_data.rb +117 -144
- data/lib/net/imap/response_parser.rb +106 -16
- data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +3 -3
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +8 -8
- data/lib/net/imap/sasl/external_authenticator.rb +2 -2
- data/lib/net/imap/sasl/gs2_header.rb +7 -7
- data/lib/net/imap/sasl/login_authenticator.rb +2 -2
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
- data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
- data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
- data/lib/net/imap/sasl.rb +1 -1
- data/lib/net/imap/search_result.rb +2 -2
- data/lib/net/imap/sequence_set.rb +193 -58
- data/lib/net/imap/stringprep/nameprep.rb +1 -1
- data/lib/net/imap/stringprep/trace.rb +4 -4
- data/lib/net/imap/uidplus_data.rb +244 -0
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +325 -161
- data/rakelib/rfcs.rake +2 -0
- metadata +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdbdda0ed73da899ec338f66022a16104562d3701c568b0a6d4897270a608ac5
|
4
|
+
data.tar.gz: b6a7ec70776b32f8eb57d01a0869503eb5d76f719ac091eb92e0206608e936e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 381bf2428719ed8decb5d241fda0e19f28031dd4a77980b3717bb29c37bed1c927f00e5b57862e209ecf24b2e9b38c01088d6e1a90fc4b4cc026cdd9e6611100
|
7
|
+
data.tar.gz: 513c6a77d46b6d2cf67aea4511023acc76c69940e3b1a0d0eae7223b53ff63bc8e6e009f51fef826b09f76f6ad1d92e84243e8457f59d3912db7e74bf69d3d1b
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# Net::IMAP
|
2
2
|
|
3
3
|
Net::IMAP implements Internet Message Access Protocol (IMAP) client
|
4
|
-
functionality. The protocol is described in
|
4
|
+
functionality. The protocol is described in
|
5
|
+
[RFC3501](https://www.rfc-editor.org/rfc/rfc3501),
|
6
|
+
[RFC9051](https://www.rfc-editor.org/rfc/rfc9051) and various extensions.
|
5
7
|
|
6
8
|
## Installation
|
7
9
|
|
data/docs/styles.css
CHANGED
@@ -6,33 +6,38 @@
|
|
6
6
|
|
7
7
|
main .method-detail {
|
8
8
|
display: grid;
|
9
|
-
grid-template-
|
10
|
-
"description description";
|
11
|
-
grid-template-columns: 1fr min-content;
|
9
|
+
grid-template-columns: 1fr auto;
|
12
10
|
justify-content: space-between;
|
13
11
|
}
|
14
12
|
|
15
|
-
main .method-header,
|
13
|
+
main .method-header,
|
14
|
+
main .method-controls,
|
15
|
+
.attribute-method-heading {
|
16
16
|
padding: 0.5em;
|
17
17
|
/* border: 1px solid var(--highlight-color); */
|
18
18
|
background: var(--table-header-background-color);
|
19
19
|
line-height: 1.6;
|
20
20
|
}
|
21
21
|
|
22
|
+
.attribute-method-heading .attribute-access-type {
|
23
|
+
float: right;
|
24
|
+
}
|
25
|
+
|
22
26
|
main .method-header {
|
23
|
-
grid-area: "header";
|
24
27
|
border-right: none;
|
25
28
|
border-radius: 4px 0 0 4px;
|
26
29
|
}
|
27
30
|
|
31
|
+
main .method-heading :any-link {
|
32
|
+
text-decoration: none;
|
33
|
+
}
|
34
|
+
|
28
35
|
main .method-controls {
|
29
|
-
grid-area: "controls";
|
30
36
|
border-left: none;
|
31
37
|
border-radius: 0 4px 4px 0;
|
32
38
|
}
|
33
39
|
|
34
40
|
main .method-description, main .aliases {
|
35
|
-
grid-area: "description";
|
36
41
|
grid-column: 1 / span 2;
|
37
42
|
padding-left: 1em;
|
38
43
|
}
|
@@ -153,6 +153,38 @@ module Net
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
+
class PartialRange < CommandData # :nodoc:
|
157
|
+
uint32_max = 2**32 - 1
|
158
|
+
POS_RANGE = 1..uint32_max
|
159
|
+
NEG_RANGE = -uint32_max..-1
|
160
|
+
Positive = ->{ (_1 in Range) and POS_RANGE.cover?(_1) }
|
161
|
+
Negative = ->{ (_1 in Range) and NEG_RANGE.cover?(_1) }
|
162
|
+
|
163
|
+
def initialize(data:)
|
164
|
+
min, max = case data
|
165
|
+
in Range
|
166
|
+
data.minmax.map { Integer _1 }
|
167
|
+
in ResponseParser::Patterns::PARTIAL_RANGE
|
168
|
+
data.split(":").map { Integer _1 }.minmax
|
169
|
+
else
|
170
|
+
raise ArgumentError, "invalid partial range input: %p" % [data]
|
171
|
+
end
|
172
|
+
data = min..max
|
173
|
+
unless data in Positive | Negative
|
174
|
+
raise ArgumentError, "invalid partial-range: %p" % [data]
|
175
|
+
end
|
176
|
+
super
|
177
|
+
rescue TypeError, RangeError
|
178
|
+
raise ArgumentError, "expected range min/max to be Integers"
|
179
|
+
end
|
180
|
+
|
181
|
+
def formatted = "%d:%d" % data.minmax
|
182
|
+
|
183
|
+
def send_data(imap, tag)
|
184
|
+
imap.__send__(:put_string, formatted)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
156
188
|
# *DEPRECATED*. Replaced by SequenceSet.
|
157
189
|
class MessageSet < CommandData # :nodoc:
|
158
190
|
def send_data(imap, tag)
|
data/lib/net/imap/config.rb
CHANGED
@@ -75,7 +75,7 @@ module Net
|
|
75
75
|
#
|
76
76
|
# client = Net::IMAP.new(hostname, config: :future)
|
77
77
|
# client.config.sasl_ir # => true
|
78
|
-
# client.config.responses_without_block # => :
|
78
|
+
# client.config.responses_without_block # => :frozen_dup
|
79
79
|
#
|
80
80
|
# The versioned default configs inherit certain specific config options from
|
81
81
|
# Config.global, for example #debug:
|
@@ -109,9 +109,11 @@ module Net
|
|
109
109
|
# [+:future+]
|
110
110
|
# The _planned_ eventual config for some future +x.y+ version.
|
111
111
|
#
|
112
|
-
# For example, to
|
112
|
+
# For example, to disable all currently deprecated behavior:
|
113
113
|
# client = Net::IMAP.new(hostname, config: :future)
|
114
|
-
# client.
|
114
|
+
# client.config.response_without_args # => :frozen_dup
|
115
|
+
# client.responses.frozen? # => true
|
116
|
+
# client.responses.values.all?(&:frozen?) # => true
|
115
117
|
#
|
116
118
|
# == Thread Safety
|
117
119
|
#
|
@@ -285,6 +287,67 @@ module Net
|
|
285
287
|
#
|
286
288
|
# Alias for responses_without_block
|
287
289
|
|
290
|
+
# Whether ResponseParser should use the deprecated UIDPlusData or
|
291
|
+
# CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
|
292
|
+
# AppendUIDData for +APPENDUID+ response codes.
|
293
|
+
#
|
294
|
+
# UIDPlusData stores its data in arrays of numbers, which is vulnerable to
|
295
|
+
# a memory exhaustion denial of service attack from an untrusted or
|
296
|
+
# compromised server. Set this option to +false+ to completely block this
|
297
|
+
# vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
|
298
|
+
# mitigates this vulnerability.
|
299
|
+
#
|
300
|
+
# AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
|
301
|
+
# UIDPlusData. Most applications should be able to upgrade with little
|
302
|
+
# or no changes.
|
303
|
+
#
|
304
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
305
|
+
#
|
306
|
+
# <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
|
307
|
+
#
|
308
|
+
# <em>UIDPlusData will be removed in +v0.6+ and this config setting will
|
309
|
+
# be ignored.</em>
|
310
|
+
#
|
311
|
+
# ==== Valid options
|
312
|
+
#
|
313
|
+
# [+true+ <em>(original default)</em>]
|
314
|
+
# ResponseParser only uses UIDPlusData.
|
315
|
+
#
|
316
|
+
# [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
|
317
|
+
# ResponseParser uses UIDPlusData when the +uid-set+ size is below
|
318
|
+
# parser_max_deprecated_uidplus_data_size. Above that size,
|
319
|
+
# ResponseParser uses AppendUIDData or CopyUIDData.
|
320
|
+
#
|
321
|
+
# [+false+ <em>(planned default for +v0.6+)</em>]
|
322
|
+
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
|
323
|
+
attr_accessor :parser_use_deprecated_uidplus_data, type: [
|
324
|
+
true, :up_to_max_size, false
|
325
|
+
]
|
326
|
+
|
327
|
+
# The maximum +uid-set+ size that ResponseParser will parse into
|
328
|
+
# deprecated UIDPlusData. This limit only applies when
|
329
|
+
# parser_use_deprecated_uidplus_data is not +false+.
|
330
|
+
#
|
331
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
332
|
+
#
|
333
|
+
# <em>Support for limiting UIDPlusData to a maximum size was added in
|
334
|
+
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
|
335
|
+
#
|
336
|
+
# <em>UIDPlusData will be removed in +v0.6+.</em>
|
337
|
+
#
|
338
|
+
# ==== Versioned Defaults
|
339
|
+
#
|
340
|
+
# Because this limit guards against a remote server causing catastrophic
|
341
|
+
# memory exhaustion, the versioned default (used by #load_defaults) also
|
342
|
+
# applies to versions without the feature.
|
343
|
+
#
|
344
|
+
# * +0.3+ and prior: <tt>10,000</tt>
|
345
|
+
# * +0.4+: <tt>1,000</tt>
|
346
|
+
# * +0.5+: <tt>100</tt>
|
347
|
+
# * +0.6+: <tt>0</tt>
|
348
|
+
#
|
349
|
+
attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
|
350
|
+
|
288
351
|
# Creates a new config object and initialize its attribute with +attrs+.
|
289
352
|
#
|
290
353
|
# If +parent+ is not given, the global config is used by default.
|
@@ -365,6 +428,8 @@ module Net
|
|
365
428
|
sasl_ir: true,
|
366
429
|
enforce_logindisabled: true,
|
367
430
|
responses_without_block: :warn,
|
431
|
+
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
432
|
+
parser_max_deprecated_uidplus_data_size: 100,
|
368
433
|
).freeze
|
369
434
|
|
370
435
|
@global = default.new
|
@@ -376,6 +441,8 @@ module Net
|
|
376
441
|
sasl_ir: false,
|
377
442
|
responses_without_block: :silence_deprecation_warning,
|
378
443
|
enforce_logindisabled: false,
|
444
|
+
parser_use_deprecated_uidplus_data: true,
|
445
|
+
parser_max_deprecated_uidplus_data_size: 10_000,
|
379
446
|
).freeze
|
380
447
|
version_defaults[0.0] = Config[0]
|
381
448
|
version_defaults[0.1] = Config[0]
|
@@ -384,12 +451,15 @@ module Net
|
|
384
451
|
|
385
452
|
version_defaults[0.4] = Config[0.3].dup.update(
|
386
453
|
sasl_ir: true,
|
454
|
+
parser_max_deprecated_uidplus_data_size: 1000,
|
387
455
|
).freeze
|
388
456
|
|
389
457
|
version_defaults[0.5] = Config[:current]
|
390
458
|
|
391
459
|
version_defaults[0.6] = Config[0.5].dup.update(
|
392
460
|
responses_without_block: :frozen_dup,
|
461
|
+
parser_use_deprecated_uidplus_data: false,
|
462
|
+
parser_max_deprecated_uidplus_data_size: 0,
|
393
463
|
).freeze
|
394
464
|
version_defaults[:next] = Config[0.6]
|
395
465
|
version_defaults[:future] = Config[:next]
|
data/lib/net/imap/data_lite.rb
CHANGED
@@ -29,7 +29,7 @@ module Net
|
|
29
29
|
class IMAP
|
30
30
|
data_or_object = RUBY_VERSION >= "3.2.0" ? ::Data : Object
|
31
31
|
class DataLite < data_or_object
|
32
|
-
def encode_with(coder) coder.map =
|
32
|
+
def encode_with(coder) coder.map = to_h.transform_keys(&:to_s) end
|
33
33
|
def init_with(coder) initialize(**coder.map.transform_keys(&:to_sym)) end
|
34
34
|
end
|
35
35
|
|
@@ -159,28 +159,27 @@ module Net
|
|
159
159
|
|
160
160
|
##
|
161
161
|
def members; self.class.members end
|
162
|
-
def
|
163
|
-
def
|
164
|
-
def hash; [self.class, attributes].hash end
|
162
|
+
def to_h(&block) block ? __to_h__.to_h(&block) : __to_h__ end
|
163
|
+
def hash; [self.class, __to_h__].hash end
|
165
164
|
def ==(other) self.class == other.class && to_h == other.to_h end
|
166
165
|
def eql?(other) self.class == other.class && hash == other.hash end
|
167
|
-
def deconstruct;
|
166
|
+
def deconstruct; __to_h__.values end
|
168
167
|
|
169
168
|
def deconstruct_keys(keys)
|
170
169
|
raise TypeError unless keys.is_a?(Array) || keys.nil?
|
171
|
-
return
|
172
|
-
|
170
|
+
return __to_h__ if keys&.first.nil?
|
171
|
+
__to_h__.slice(*keys)
|
173
172
|
end
|
174
173
|
|
175
174
|
def with(**kwargs)
|
176
175
|
return self if kwargs.empty?
|
177
|
-
self.class.new(**
|
176
|
+
self.class.new(**__to_h__.merge(kwargs))
|
178
177
|
end
|
179
178
|
|
180
179
|
def inspect
|
181
180
|
__inspect_guard__(self) do |seen|
|
182
181
|
return "#<data #{self.class}:...>" if seen
|
183
|
-
attrs =
|
182
|
+
attrs = __to_h__.map {|kv| "%s=%p" % kv }.join(", ")
|
184
183
|
display = ["data", self.class.name, attrs].compact.join(" ")
|
185
184
|
"#<#{display}>"
|
186
185
|
end
|
@@ -190,7 +189,9 @@ module Net
|
|
190
189
|
private
|
191
190
|
|
192
191
|
def initialize_copy(source) super.freeze end
|
193
|
-
def marshal_dump;
|
192
|
+
def marshal_dump; __to_h__ end
|
193
|
+
|
194
|
+
def __to_h__; Hash[members.map {|m| [m, send(m)] }] end
|
194
195
|
|
195
196
|
# Yields +true+ if +obj+ has been seen already, +false+ if it hasn't.
|
196
197
|
# Marks +obj+ as seen inside the block, so circuler references don't
|
@@ -35,16 +35,16 @@ module Net
|
|
35
35
|
|
36
36
|
# :call-seq: to_a -> Array of integers
|
37
37
|
#
|
38
|
-
# When #all contains a SequenceSet of message sequence
|
38
|
+
# When either #all or #partial contains a SequenceSet of message sequence
|
39
39
|
# numbers or UIDs, +to_a+ returns that set as an array of integers.
|
40
40
|
#
|
41
|
-
# When #all
|
42
|
-
# returned no results or because +ALL+
|
41
|
+
# When both #all and #partial are +nil+, either because the server
|
42
|
+
# returned no results or because +ALL+ and +PARTIAL+ were not included in
|
43
43
|
# the IMAP#search +RETURN+ options, #to_a returns an empty array.
|
44
44
|
#
|
45
45
|
# Note that SearchResult also implements +to_a+, so it can be used without
|
46
46
|
# checking if the server returned +SEARCH+ or +ESEARCH+ data.
|
47
|
-
def to_a; all&.numbers || [] end
|
47
|
+
def to_a; all&.numbers || partial&.to_a || [] end
|
48
48
|
|
49
49
|
##
|
50
50
|
# attr_reader: tag
|
@@ -135,6 +135,46 @@ module Net
|
|
135
135
|
# and +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.2].
|
136
136
|
def modseq; data.assoc("MODSEQ")&.last end
|
137
137
|
|
138
|
+
# Returned by ESearchResult#partial.
|
139
|
+
#
|
140
|
+
# Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
|
141
|
+
# or <tt>CONTEXT=SEARCH</tt>/<tt>CONTEXT=SORT</tt>
|
142
|
+
# {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
|
143
|
+
#
|
144
|
+
# See also: #to_a
|
145
|
+
class PartialResult < Data.define(:range, :results)
|
146
|
+
def initialize(range:, results:)
|
147
|
+
range => Range
|
148
|
+
results = SequenceSet[results] unless results.nil?
|
149
|
+
super
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# method: range
|
154
|
+
# :call-seq: range -> range
|
155
|
+
|
156
|
+
##
|
157
|
+
# method: results
|
158
|
+
# :call-seq: results -> sequence set or nil
|
159
|
+
|
160
|
+
# Converts #results to an array of integers.
|
161
|
+
#
|
162
|
+
# See also: ESearchResult#to_a.
|
163
|
+
def to_a; results&.numbers || [] end
|
164
|
+
end
|
165
|
+
|
166
|
+
# :call-seq: partial -> PartialResult or nil
|
167
|
+
#
|
168
|
+
# A PartialResult containing a subset of the message sequence numbers or
|
169
|
+
# UIDs that satisfy the SEARCH criteria.
|
170
|
+
#
|
171
|
+
# Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
|
172
|
+
# or <tt>CONTEXT=SEARCH</tt>/<tt>CONTEXT=SORT</tt>
|
173
|
+
# {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
|
174
|
+
#
|
175
|
+
# See also: #to_a
|
176
|
+
def partial; data.assoc("PARTIAL")&.last end
|
177
|
+
|
138
178
|
end
|
139
179
|
end
|
140
180
|
end
|