net-imap 0.5.2 → 0.5.6
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.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- 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
|