net-imap 0.5.1 → 0.5.7
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/Gemfile +1 -0
- data/README.md +10 -4
- data/docs/styles.css +75 -14
- data/lib/net/imap/command_data.rb +48 -46
- data/lib/net/imap/config/attr_type_coercion.rb +20 -23
- data/lib/net/imap/config.rb +173 -21
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/errors.rb +33 -0
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +126 -47
- data/lib/net/imap/response_data.rb +118 -144
- data/lib/net/imap/response_parser/parser_utils.rb +5 -0
- data/lib/net/imap/response_parser.rb +178 -17
- data/lib/net/imap/response_reader.rb +73 -0
- 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 +267 -118
- 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 +911 -365
- data/rakelib/rfcs.rake +2 -0
- metadata +9 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee48ac78f129043fd8ccb4f6e0b42535cc0ef3a6acd8ea1067cbd9b512815e49
|
|
4
|
+
data.tar.gz: 5bfae3bf0ee2e63c5b61c19d30187fe722adfe8018746e64d583124531de87a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f90a4500f3c218dd3a51cca84a76c620fa8f8488f2ca73486b538a43e67e21e0b897d23d957f490afe1590fd755c692f2e0dede562460ed2cfcd7f4d9bec3262
|
|
7
|
+
data.tar.gz: 22abb3cf5699bb715c33c69b596ead46ff400109925f58d633ecdc82797be9b80fbbd040c02aa7cd9286f3aa9c697e9fc426f1115b9019455b2ac4e6667cc236
|
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
|
|
|
@@ -50,12 +52,16 @@ end
|
|
|
50
52
|
|
|
51
53
|
```ruby
|
|
52
54
|
imap.select('Mail/sent-mail')
|
|
53
|
-
if
|
|
55
|
+
if imap.list('Mail/', 'sent-apr03').empty?
|
|
54
56
|
imap.create('Mail/sent-apr03')
|
|
55
57
|
end
|
|
56
58
|
imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
|
|
57
|
-
imap.
|
|
58
|
-
|
|
59
|
+
if imap.capable?(:move) || imap.capable?(:IMAP4rev2)
|
|
60
|
+
imap.move(message_id, "Mail/sent-apr03")
|
|
61
|
+
else
|
|
62
|
+
imap.copy(message_id, "Mail/sent-apr03")
|
|
63
|
+
imap.store(message_id, "+FLAGS", [:Deleted])
|
|
64
|
+
end
|
|
59
65
|
end
|
|
60
66
|
imap.expunge
|
|
61
67
|
```
|
data/docs/styles.css
CHANGED
|
@@ -1,24 +1,85 @@
|
|
|
1
1
|
/* this is a work in progress. :) */
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/***********************************************
|
|
4
|
+
* Method descriptions
|
|
5
|
+
***********************************************/
|
|
6
|
+
|
|
7
|
+
main .method-detail {
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: 1fr auto;
|
|
10
|
+
justify-content: space-between;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
main .method-header,
|
|
14
|
+
main .method-controls,
|
|
15
|
+
.attribute-method-heading {
|
|
6
16
|
padding: 0.5em;
|
|
7
|
-
border
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
/* border: 1px solid var(--highlight-color); */
|
|
18
|
+
background: var(--table-header-background-color);
|
|
19
|
+
line-height: 1.6;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.attribute-method-heading .attribute-access-type {
|
|
23
|
+
float: right;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
main .method-header {
|
|
27
|
+
border-right: none;
|
|
28
|
+
border-radius: 4px 0 0 4px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main .method-heading :any-link {
|
|
32
|
+
text-decoration: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
main .method-controls {
|
|
36
|
+
border-left: none;
|
|
37
|
+
border-radius: 0 4px 4px 0;
|
|
12
38
|
}
|
|
13
39
|
|
|
14
40
|
main .method-description, main .aliases {
|
|
41
|
+
grid-column: 1 / span 2;
|
|
15
42
|
padding-left: 1em;
|
|
16
43
|
}
|
|
17
44
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
45
|
+
@media (max-width: 700px) {
|
|
46
|
+
main .method-header, main .method-controls, main .method-description {
|
|
47
|
+
grid-column: 1 / span 2;
|
|
48
|
+
margin: 0;
|
|
49
|
+
}
|
|
50
|
+
main .method-controls {
|
|
51
|
+
background: none;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/***********************************************
|
|
56
|
+
* Description lists
|
|
57
|
+
***********************************************/
|
|
58
|
+
|
|
59
|
+
main dt {
|
|
60
|
+
margin-bottom: 0; /* override rdoc 6.8 */
|
|
61
|
+
float: unset; /* override rdoc 6.8 */
|
|
62
|
+
line-height: 1.5; /* matches `main p` */
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main dl.note-list dt {
|
|
66
|
+
margin-right: 1em;
|
|
67
|
+
float: left;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
main dl.note-list dt:has(+ dt) {
|
|
71
|
+
margin-right: 0.25em;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main dl.note-list dt:has(+ dt)::after {
|
|
75
|
+
content: ', ';
|
|
76
|
+
font-weight: normal;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
main dd {
|
|
80
|
+
margin: 0 0 1em 1em;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
main dd p:first-child {
|
|
84
|
+
margin-top: 0;
|
|
24
85
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "date"
|
|
4
4
|
|
|
5
5
|
require_relative "errors"
|
|
6
|
+
require_relative "data_lite"
|
|
6
7
|
|
|
7
8
|
module Net
|
|
8
9
|
class IMAP < Protocol
|
|
@@ -119,80 +120,85 @@ module Net
|
|
|
119
120
|
put_string("\\" + symbol.to_s)
|
|
120
121
|
end
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
CommandData = Data.define(:data) do # :nodoc:
|
|
123
124
|
def send_data(imap, tag)
|
|
124
|
-
|
|
125
|
+
raise NoMethodError, "#{self.class} must implement #{__method__}"
|
|
125
126
|
end
|
|
126
127
|
|
|
127
128
|
def validate
|
|
128
129
|
end
|
|
129
|
-
|
|
130
|
-
private
|
|
131
|
-
|
|
132
|
-
def initialize(data)
|
|
133
|
-
@data = data
|
|
134
|
-
end
|
|
135
130
|
end
|
|
136
131
|
|
|
137
|
-
class
|
|
132
|
+
class RawData < CommandData # :nodoc:
|
|
138
133
|
def send_data(imap, tag)
|
|
139
|
-
imap.__send__(:put_string,
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def validate
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
private
|
|
146
|
-
|
|
147
|
-
def initialize(data)
|
|
148
|
-
@data = data
|
|
134
|
+
imap.__send__(:put_string, data)
|
|
149
135
|
end
|
|
150
136
|
end
|
|
151
137
|
|
|
152
|
-
class
|
|
138
|
+
class Atom < CommandData # :nodoc:
|
|
153
139
|
def send_data(imap, tag)
|
|
154
|
-
imap.__send__(:
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def validate
|
|
140
|
+
imap.__send__(:put_string, data)
|
|
158
141
|
end
|
|
142
|
+
end
|
|
159
143
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@data = data
|
|
144
|
+
class QuotedString < CommandData # :nodoc:
|
|
145
|
+
def send_data(imap, tag)
|
|
146
|
+
imap.__send__(:send_quoted_string, data)
|
|
164
147
|
end
|
|
165
148
|
end
|
|
166
149
|
|
|
167
|
-
class Literal # :nodoc:
|
|
150
|
+
class Literal < CommandData # :nodoc:
|
|
168
151
|
def send_data(imap, tag)
|
|
169
|
-
imap.__send__(:send_literal,
|
|
152
|
+
imap.__send__(:send_literal, data, tag)
|
|
170
153
|
end
|
|
154
|
+
end
|
|
171
155
|
|
|
172
|
-
|
|
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"
|
|
173
179
|
end
|
|
174
180
|
|
|
175
|
-
|
|
181
|
+
def formatted = "%d:%d" % data.minmax
|
|
176
182
|
|
|
177
|
-
def
|
|
178
|
-
|
|
183
|
+
def send_data(imap, tag)
|
|
184
|
+
imap.__send__(:put_string, formatted)
|
|
179
185
|
end
|
|
180
186
|
end
|
|
181
187
|
|
|
182
188
|
# *DEPRECATED*. Replaced by SequenceSet.
|
|
183
|
-
class MessageSet # :nodoc:
|
|
189
|
+
class MessageSet < CommandData # :nodoc:
|
|
184
190
|
def send_data(imap, tag)
|
|
185
|
-
imap.__send__(:put_string, format_internal(
|
|
191
|
+
imap.__send__(:put_string, format_internal(data))
|
|
186
192
|
end
|
|
187
193
|
|
|
188
194
|
def validate
|
|
189
|
-
validate_internal(
|
|
195
|
+
validate_internal(data)
|
|
190
196
|
end
|
|
191
197
|
|
|
192
198
|
private
|
|
193
199
|
|
|
194
|
-
def initialize(data)
|
|
195
|
-
|
|
200
|
+
def initialize(data:)
|
|
201
|
+
super
|
|
196
202
|
warn("DEPRECATED: #{MessageSet} should be replaced with #{SequenceSet}.",
|
|
197
203
|
uplevel: 1, category: :deprecated)
|
|
198
204
|
begin
|
|
@@ -246,22 +252,18 @@ module Net
|
|
|
246
252
|
end
|
|
247
253
|
end
|
|
248
254
|
|
|
249
|
-
class ClientID # :nodoc:
|
|
255
|
+
class ClientID < CommandData # :nodoc:
|
|
250
256
|
|
|
251
257
|
def send_data(imap, tag)
|
|
252
|
-
imap.__send__(:send_data, format_internal(
|
|
258
|
+
imap.__send__(:send_data, format_internal(data), tag)
|
|
253
259
|
end
|
|
254
260
|
|
|
255
261
|
def validate
|
|
256
|
-
validate_internal(
|
|
262
|
+
validate_internal(data)
|
|
257
263
|
end
|
|
258
264
|
|
|
259
265
|
private
|
|
260
266
|
|
|
261
|
-
def initialize(data)
|
|
262
|
-
@data = data
|
|
263
|
-
end
|
|
264
|
-
|
|
265
267
|
def validate_internal(client_id)
|
|
266
268
|
client_id.to_h.each do |k,v|
|
|
267
269
|
unless StringFormatter.valid_string?(k)
|
|
@@ -18,6 +18,8 @@ module Net
|
|
|
18
18
|
super(attr)
|
|
19
19
|
AttrTypeCoercion.attr_accessor(attr, type: type)
|
|
20
20
|
end
|
|
21
|
+
|
|
22
|
+
module_function def Integer? = NilOrInteger
|
|
21
23
|
end
|
|
22
24
|
private_constant :Macros
|
|
23
25
|
|
|
@@ -26,34 +28,29 @@ module Net
|
|
|
26
28
|
end
|
|
27
29
|
private_class_method :included
|
|
28
30
|
|
|
29
|
-
def self.
|
|
30
|
-
|
|
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
|
|
31
|
+
def self.safe(...) = Ractor.make_shareable nil.instance_eval(...).freeze
|
|
32
|
+
private_class_method :safe
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
end
|
|
34
|
+
Types = Hash.new do |h, type| type => Proc | nil; safe{type} end
|
|
35
|
+
Types[:boolean] = Boolean = safe{-> {!!_1}}
|
|
36
|
+
Types[Integer] = safe{->{Integer(_1)}}
|
|
42
37
|
|
|
43
|
-
def self.
|
|
44
|
-
|
|
38
|
+
def self.attr_accessor(attr, type: nil)
|
|
39
|
+
type = Types[type] or return
|
|
40
|
+
define_method :"#{attr}=" do |val| super type[val] end
|
|
41
|
+
define_method :"#{attr}?" do send attr end if type == Boolean
|
|
45
42
|
end
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
NilOrInteger = safe{->val { Integer val unless val.nil? }}
|
|
45
|
+
|
|
46
|
+
Enum = ->(*enum) {
|
|
47
|
+
enum = safe{enum}
|
|
49
48
|
expected = -"one of #{enum.map(&:inspect).join(", ")}"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
56
|
-
end
|
|
49
|
+
safe{->val {
|
|
50
|
+
return val if enum.include?(val)
|
|
51
|
+
raise ArgumentError, "expected %s, got %p" % [expected, val]
|
|
52
|
+
}}
|
|
53
|
+
}
|
|
57
54
|
|
|
58
55
|
end
|
|
59
56
|
end
|
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
|
#
|
|
@@ -129,8 +131,25 @@ module Net
|
|
|
129
131
|
def self.global; @global if defined?(@global) end
|
|
130
132
|
|
|
131
133
|
# A hash of hard-coded configurations, indexed by version number or name.
|
|
134
|
+
# Values can be accessed with any object that responds to +to_sym+ or
|
|
135
|
+
# +to_r+/+to_f+ with a non-zero number.
|
|
136
|
+
#
|
|
137
|
+
# Config::[] gets named or numbered versions from this hash.
|
|
138
|
+
#
|
|
139
|
+
# For example:
|
|
140
|
+
# Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
|
|
141
|
+
# Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
|
|
142
|
+
# Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
|
|
143
|
+
# Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
|
|
132
144
|
def self.version_defaults; @version_defaults end
|
|
133
|
-
@version_defaults = {
|
|
145
|
+
@version_defaults = Hash.new {|h, k|
|
|
146
|
+
# NOTE: String responds to both so the order is significant.
|
|
147
|
+
# And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
|
|
148
|
+
(h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
|
|
149
|
+
(h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
|
|
150
|
+
(h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
|
|
151
|
+
(h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
|
|
152
|
+
}
|
|
134
153
|
|
|
135
154
|
# :call-seq:
|
|
136
155
|
# Net::IMAP::Config[number] -> versioned config
|
|
@@ -153,18 +172,17 @@ module Net
|
|
|
153
172
|
elsif config.nil? && global.nil? then nil
|
|
154
173
|
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
|
|
155
174
|
else
|
|
156
|
-
version_defaults
|
|
175
|
+
version_defaults[config] or
|
|
157
176
|
case config
|
|
158
177
|
when Numeric
|
|
159
178
|
raise RangeError, "unknown config version: %p" % [config]
|
|
160
|
-
when Symbol
|
|
179
|
+
when String, Symbol
|
|
161
180
|
raise KeyError, "unknown config name: %p" % [config]
|
|
162
181
|
else
|
|
163
182
|
raise TypeError, "no implicit conversion of %s to %s" % [
|
|
164
183
|
config.class, Config
|
|
165
184
|
]
|
|
166
185
|
end
|
|
167
|
-
end
|
|
168
186
|
end
|
|
169
187
|
end
|
|
170
188
|
|
|
@@ -191,10 +209,13 @@ module Net
|
|
|
191
209
|
|
|
192
210
|
# Seconds to wait until a connection is opened.
|
|
193
211
|
#
|
|
212
|
+
# Applied separately for establishing TCP connection and starting a TLS
|
|
213
|
+
# connection.
|
|
214
|
+
#
|
|
194
215
|
# If the IMAP object cannot open a connection within this time,
|
|
195
216
|
# it raises a Net::OpenTimeout exception.
|
|
196
217
|
#
|
|
197
|
-
# See Net::IMAP.new.
|
|
218
|
+
# See Net::IMAP.new and Net::IMAP#starttls.
|
|
198
219
|
#
|
|
199
220
|
# The default value is +30+ seconds.
|
|
200
221
|
attr_accessor :open_timeout, type: Integer
|
|
@@ -243,10 +264,44 @@ module Net
|
|
|
243
264
|
# present. When capabilities are unknown, Net::IMAP will automatically
|
|
244
265
|
# send a +CAPABILITY+ command first before sending +LOGIN+.
|
|
245
266
|
#
|
|
246
|
-
attr_accessor :enforce_logindisabled, type: [
|
|
267
|
+
attr_accessor :enforce_logindisabled, type: Enum[
|
|
247
268
|
false, :when_capabilities_cached, true
|
|
248
269
|
]
|
|
249
270
|
|
|
271
|
+
# The maximum allowed server response size. When +nil+, there is no limit
|
|
272
|
+
# on response size.
|
|
273
|
+
#
|
|
274
|
+
# The default value (512 MiB, since +v0.5.7+) is <em>very high</em> and
|
|
275
|
+
# unlikely to be reached. A _much_ lower value should be used with
|
|
276
|
+
# untrusted servers (for example, when connecting to a user-provided
|
|
277
|
+
# hostname). When using a lower limit, message bodies should be fetched
|
|
278
|
+
# in chunks rather than all at once.
|
|
279
|
+
#
|
|
280
|
+
# <em>Please Note:</em> this only limits the size per response. It does
|
|
281
|
+
# not prevent a flood of individual responses and it does not limit how
|
|
282
|
+
# many unhandled responses may be stored on the responses hash. See
|
|
283
|
+
# Net::IMAP@Unbounded+memory+use.
|
|
284
|
+
#
|
|
285
|
+
# Socket reads are limited to the maximum remaining bytes for the current
|
|
286
|
+
# response: max_response_size minus the bytes that have already been read.
|
|
287
|
+
# When the limit is reached, or reading a +literal+ _would_ go over the
|
|
288
|
+
# limit, ResponseTooLargeError is raised and the connection is closed.
|
|
289
|
+
#
|
|
290
|
+
# Note that changes will not take effect immediately, because the receiver
|
|
291
|
+
# thread may already be waiting for the next response using the previous
|
|
292
|
+
# value. Net::IMAP#noop can force a response and enforce the new setting
|
|
293
|
+
# immediately.
|
|
294
|
+
#
|
|
295
|
+
# ==== Versioned Defaults
|
|
296
|
+
#
|
|
297
|
+
# Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
|
|
298
|
+
# attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to this
|
|
299
|
+
# config attribute.</em>
|
|
300
|
+
#
|
|
301
|
+
# * original: +nil+ <em>(no limit)</em>
|
|
302
|
+
# * +0.5+: 512 MiB
|
|
303
|
+
attr_accessor :max_response_size, type: Integer?
|
|
304
|
+
|
|
250
305
|
# Controls the behavior of Net::IMAP#responses when called without any
|
|
251
306
|
# arguments (+type+ or +block+).
|
|
252
307
|
#
|
|
@@ -273,7 +328,7 @@ module Net
|
|
|
273
328
|
# Raise an ArgumentError with the deprecation warning.
|
|
274
329
|
#
|
|
275
330
|
# Note: #responses_without_args is an alias for #responses_without_block.
|
|
276
|
-
attr_accessor :responses_without_block, type: [
|
|
331
|
+
attr_accessor :responses_without_block, type: Enum[
|
|
277
332
|
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
|
|
278
333
|
]
|
|
279
334
|
|
|
@@ -285,6 +340,67 @@ module Net
|
|
|
285
340
|
#
|
|
286
341
|
# Alias for responses_without_block
|
|
287
342
|
|
|
343
|
+
# Whether ResponseParser should use the deprecated UIDPlusData or
|
|
344
|
+
# CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
|
|
345
|
+
# AppendUIDData for +APPENDUID+ response codes.
|
|
346
|
+
#
|
|
347
|
+
# UIDPlusData stores its data in arrays of numbers, which is vulnerable to
|
|
348
|
+
# a memory exhaustion denial of service attack from an untrusted or
|
|
349
|
+
# compromised server. Set this option to +false+ to completely block this
|
|
350
|
+
# vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
|
|
351
|
+
# mitigates this vulnerability.
|
|
352
|
+
#
|
|
353
|
+
# AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
|
|
354
|
+
# UIDPlusData. Most applications should be able to upgrade with little
|
|
355
|
+
# or no changes.
|
|
356
|
+
#
|
|
357
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
|
358
|
+
#
|
|
359
|
+
# <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
|
|
360
|
+
#
|
|
361
|
+
# <em>UIDPlusData will be removed in +v0.6+ and this config setting will
|
|
362
|
+
# be ignored.</em>
|
|
363
|
+
#
|
|
364
|
+
# ==== Valid options
|
|
365
|
+
#
|
|
366
|
+
# [+true+ <em>(original default)</em>]
|
|
367
|
+
# ResponseParser only uses UIDPlusData.
|
|
368
|
+
#
|
|
369
|
+
# [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
|
|
370
|
+
# ResponseParser uses UIDPlusData when the +uid-set+ size is below
|
|
371
|
+
# parser_max_deprecated_uidplus_data_size. Above that size,
|
|
372
|
+
# ResponseParser uses AppendUIDData or CopyUIDData.
|
|
373
|
+
#
|
|
374
|
+
# [+false+ <em>(planned default for +v0.6+)</em>]
|
|
375
|
+
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
|
|
376
|
+
attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
|
|
377
|
+
true, :up_to_max_size, false
|
|
378
|
+
]
|
|
379
|
+
|
|
380
|
+
# The maximum +uid-set+ size that ResponseParser will parse into
|
|
381
|
+
# deprecated UIDPlusData. This limit only applies when
|
|
382
|
+
# parser_use_deprecated_uidplus_data is not +false+.
|
|
383
|
+
#
|
|
384
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
|
385
|
+
#
|
|
386
|
+
# <em>Support for limiting UIDPlusData to a maximum size was added in
|
|
387
|
+
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
|
|
388
|
+
#
|
|
389
|
+
# <em>UIDPlusData will be removed in +v0.6+.</em>
|
|
390
|
+
#
|
|
391
|
+
# ==== Versioned Defaults
|
|
392
|
+
#
|
|
393
|
+
# Because this limit guards against a remote server causing catastrophic
|
|
394
|
+
# memory exhaustion, the versioned default (used by #load_defaults) also
|
|
395
|
+
# applies to versions without the feature.
|
|
396
|
+
#
|
|
397
|
+
# * +0.3+ and prior: <tt>10,000</tt>
|
|
398
|
+
# * +0.4+: <tt>1,000</tt>
|
|
399
|
+
# * +0.5+: <tt>100</tt>
|
|
400
|
+
# * +0.6+: <tt>0</tt>
|
|
401
|
+
#
|
|
402
|
+
attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
|
|
403
|
+
|
|
288
404
|
# Creates a new config object and initialize its attribute with +attrs+.
|
|
289
405
|
#
|
|
290
406
|
# If +parent+ is not given, the global config is used by default.
|
|
@@ -364,37 +480,73 @@ module Net
|
|
|
364
480
|
idle_response_timeout: 5,
|
|
365
481
|
sasl_ir: true,
|
|
366
482
|
enforce_logindisabled: true,
|
|
483
|
+
max_response_size: 512 << 20, # 512 MiB
|
|
367
484
|
responses_without_block: :warn,
|
|
485
|
+
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
|
486
|
+
parser_max_deprecated_uidplus_data_size: 100,
|
|
368
487
|
).freeze
|
|
369
488
|
|
|
370
489
|
@global = default.new
|
|
371
490
|
|
|
372
491
|
version_defaults[:default] = Config[default.send(:defaults_hash)]
|
|
373
|
-
version_defaults[:current] = Config[:default]
|
|
374
492
|
|
|
375
|
-
version_defaults[
|
|
493
|
+
version_defaults[0r] = Config[:default].dup.update(
|
|
376
494
|
sasl_ir: false,
|
|
377
495
|
responses_without_block: :silence_deprecation_warning,
|
|
378
496
|
enforce_logindisabled: false,
|
|
497
|
+
max_response_size: nil,
|
|
498
|
+
parser_use_deprecated_uidplus_data: true,
|
|
499
|
+
parser_max_deprecated_uidplus_data_size: 10_000,
|
|
379
500
|
).freeze
|
|
380
|
-
version_defaults[0.
|
|
381
|
-
version_defaults[0.
|
|
382
|
-
version_defaults[0.
|
|
383
|
-
version_defaults[0.
|
|
501
|
+
version_defaults[0.0r] = Config[0r]
|
|
502
|
+
version_defaults[0.1r] = Config[0r]
|
|
503
|
+
version_defaults[0.2r] = Config[0r]
|
|
504
|
+
version_defaults[0.3r] = Config[0r]
|
|
384
505
|
|
|
385
|
-
version_defaults[0.
|
|
506
|
+
version_defaults[0.4r] = Config[0.3r].dup.update(
|
|
386
507
|
sasl_ir: true,
|
|
508
|
+
parser_max_deprecated_uidplus_data_size: 1000,
|
|
387
509
|
).freeze
|
|
388
510
|
|
|
389
|
-
version_defaults[0.
|
|
511
|
+
version_defaults[0.5r] = Config[0.4r].dup.update(
|
|
512
|
+
enforce_logindisabled: true,
|
|
513
|
+
max_response_size: 512 << 20, # 512 MiB
|
|
514
|
+
responses_without_block: :warn,
|
|
515
|
+
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
|
516
|
+
parser_max_deprecated_uidplus_data_size: 100,
|
|
517
|
+
).freeze
|
|
390
518
|
|
|
391
|
-
version_defaults[0.
|
|
519
|
+
version_defaults[0.6r] = Config[0.5r].dup.update(
|
|
392
520
|
responses_without_block: :frozen_dup,
|
|
521
|
+
parser_use_deprecated_uidplus_data: false,
|
|
522
|
+
parser_max_deprecated_uidplus_data_size: 0,
|
|
393
523
|
).freeze
|
|
394
|
-
|
|
395
|
-
version_defaults[
|
|
524
|
+
|
|
525
|
+
version_defaults[0.7r] = Config[0.6r].dup.update(
|
|
526
|
+
).freeze
|
|
527
|
+
|
|
528
|
+
# Safe conversions one way only:
|
|
529
|
+
# 0.6r.to_f == 0.6 # => true
|
|
530
|
+
# 0.6 .to_r == 0.6r # => false
|
|
531
|
+
version_defaults.to_a.each do |k, v|
|
|
532
|
+
next unless k in Rational
|
|
533
|
+
version_defaults[k.to_f] = v
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
current = VERSION.to_r
|
|
537
|
+
version_defaults[:original] = Config[0]
|
|
538
|
+
version_defaults[:current] = Config[current]
|
|
539
|
+
version_defaults[:next] = Config[current + 0.1r]
|
|
540
|
+
version_defaults[:future] = Config[current + 0.2r]
|
|
396
541
|
|
|
397
542
|
version_defaults.freeze
|
|
543
|
+
|
|
544
|
+
if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h
|
|
545
|
+
warn "Misconfigured Net::IMAP::Config[:current] => %p,\n" \
|
|
546
|
+
" not equal to Net::IMAP::Config[:default] => %p" % [
|
|
547
|
+
self[:current].to_h, self[:default].to_h
|
|
548
|
+
]
|
|
549
|
+
end
|
|
398
550
|
end
|
|
399
551
|
end
|
|
400
552
|
end
|