net-imap 0.4.24 → 0.5.0
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 +8 -2
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +32 -182
- data/lib/net/imap/config/attr_type_coercion.rb +22 -23
- data/lib/net/imap/config.rb +38 -162
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +6 -33
- data/lib/net/imap/response_data.rb +62 -118
- data/lib/net/imap/response_parser.rb +18 -45
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +1 -1
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +213 -51
- data/lib/net/imap/sasl/login_authenticator.rb +2 -1
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +0 -74
- data/lib/net/imap/sasl.rb +6 -3
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/sequence_set.rb +132 -282
- data/lib/net/imap.rb +97 -269
- data/net-imap.gemspec +1 -1
- metadata +7 -6
- data/lib/net/imap/response_reader.rb +0 -82
- data/lib/net/imap/uidplus_data.rb +0 -326
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c89d97e842bce303df112c3fbc0f048f20a2ff1dbf3b85bb2314ea0d2e207c5
|
|
4
|
+
data.tar.gz: 11d6ec196e1e768f61a17b0ce7f385a11eb03e3e92af8ab327ba9a90699ee940
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca70e20c3c70f4ca57822f70936546adb3129dd7c0771ef2d054b0356ed4f6a994b7453803cc06bd25ebb37dc0ed10f60b5541bc56749dee38a6126264344599
|
|
7
|
+
data.tar.gz: 2921a79dbdab7bd0476d883c18db50150388d587c711270faac99ad4505574a23e6532fe61a4fb48f51c8bddea6641d96f81d9dcbe245b4d4f4542fcc7418566
|
data/Gemfile
CHANGED
|
@@ -4,7 +4,7 @@ source "https://rubygems.org"
|
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
gem "digest"
|
|
8
8
|
gem "strscan"
|
|
9
9
|
gem "base64"
|
|
10
10
|
|
|
@@ -13,4 +13,10 @@ gem "rdoc"
|
|
|
13
13
|
gem "test-unit"
|
|
14
14
|
gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
|
|
15
15
|
|
|
16
|
-
gem "benchmark-driver"
|
|
16
|
+
gem "benchmark-driver", require: false
|
|
17
|
+
|
|
18
|
+
group :test do
|
|
19
|
+
gem "simplecov", require: false
|
|
20
|
+
gem "simplecov-html", require: false
|
|
21
|
+
gem "simplecov-json", require: false
|
|
22
|
+
end
|
|
@@ -9,7 +9,7 @@ module Net::IMAP::Authenticators
|
|
|
9
9
|
"%s.%s is deprecated. Use %s.%s instead." % [
|
|
10
10
|
Net::IMAP, __method__, Net::IMAP::SASL, __method__
|
|
11
11
|
],
|
|
12
|
-
uplevel: 1
|
|
12
|
+
uplevel: 1, category: :deprecated
|
|
13
13
|
)
|
|
14
14
|
Net::IMAP::SASL.add_authenticator(...)
|
|
15
15
|
end
|
|
@@ -20,7 +20,7 @@ module Net::IMAP::Authenticators
|
|
|
20
20
|
"%s.%s is deprecated. Use %s.%s instead." % [
|
|
21
21
|
Net::IMAP, __method__, Net::IMAP::SASL, __method__
|
|
22
22
|
],
|
|
23
|
-
uplevel: 1
|
|
23
|
+
uplevel: 1, category: :deprecated
|
|
24
24
|
)
|
|
25
25
|
Net::IMAP::SASL.authenticator(...)
|
|
26
26
|
end
|
|
@@ -25,7 +25,6 @@ module Net
|
|
|
25
25
|
end
|
|
26
26
|
when Time, Date, DateTime
|
|
27
27
|
when Symbol
|
|
28
|
-
Flag.validate(data)
|
|
29
28
|
else
|
|
30
29
|
data.validate
|
|
31
30
|
end
|
|
@@ -46,7 +45,7 @@ module Net
|
|
|
46
45
|
when Date
|
|
47
46
|
send_date_data(data)
|
|
48
47
|
when Symbol
|
|
49
|
-
|
|
48
|
+
send_symbol_data(data)
|
|
50
49
|
else
|
|
51
50
|
data.send_data(self, tag)
|
|
52
51
|
end
|
|
@@ -78,23 +77,9 @@ module Net
|
|
|
78
77
|
put_string('"' + str.gsub(/["\\]/, "\\\\\\&") + '"')
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
# `non_sync` is an optional tri-state flag:
|
|
84
|
-
# * `true` -> Force non-synchronizing `LITERAL+`/`LITERAL-` behavior.
|
|
85
|
-
# TODO: raise or warn when capabilities don't allow non_sync.
|
|
86
|
-
# * `false` -> Force normal synchronizing literal behavior.
|
|
87
|
-
# * `nil` -> (default) Currently behaves like `false` (will be dynamic).
|
|
88
|
-
# TODO: Dynamic, based on capabilities and bytesize.
|
|
89
|
-
def send_literal(str, tag = nil, binary: false, non_sync: nil)
|
|
80
|
+
def send_literal(str, tag = nil)
|
|
90
81
|
synchronize do
|
|
91
|
-
|
|
92
|
-
plus = "+" if non_sync
|
|
93
|
-
put_string("#{prefix}{#{str.bytesize}#{plus}}\r\n")
|
|
94
|
-
if non_sync
|
|
95
|
-
put_string(str)
|
|
96
|
-
return
|
|
97
|
-
end
|
|
82
|
+
put_string("{" + str.bytesize.to_s + "}" + CRLF)
|
|
98
83
|
@continued_command_tag = tag
|
|
99
84
|
@continuation_request_exception = nil
|
|
100
85
|
begin
|
|
@@ -130,148 +115,37 @@ module Net
|
|
|
130
115
|
def send_date_data(date) put_string Net::IMAP.encode_date(date) end
|
|
131
116
|
def send_time_data(time) put_string Net::IMAP.encode_time(time) end
|
|
132
117
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class << self
|
|
136
|
-
def new(arg = nil, data: arg) super(data: data) end
|
|
137
|
-
alias :[] :new
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def initialize(data:)
|
|
141
|
-
@data = data
|
|
142
|
-
freeze
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
attr_reader :data
|
|
146
|
-
|
|
147
|
-
def to_h(&block) block ? to_h.to_h(&block) : { data: data } end
|
|
148
|
-
def ==(other) self.class === other && to_h == other.to_h end
|
|
149
|
-
def eql?(other) self.class === other && to_h.eql?(other.to_h) end
|
|
150
|
-
|
|
151
|
-
# following class definition goes beyond the basic Data.define(:data)
|
|
152
|
-
##
|
|
153
|
-
|
|
154
|
-
def self.validate(...)
|
|
155
|
-
data = new(...)
|
|
156
|
-
data.validate
|
|
157
|
-
data
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def send_data(imap, tag)
|
|
161
|
-
raise NoMethodError, "#{self.class} must implement #{__method__}"
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def validate
|
|
165
|
-
end
|
|
118
|
+
def send_symbol_data(symbol)
|
|
119
|
+
put_string("\\" + symbol.to_s)
|
|
166
120
|
end
|
|
167
121
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# been enabled, or when the server supports only +IMAP4rev2+ and not earlier
|
|
172
|
-
# IMAP revisions, or when the server advertises +UTF8=ONLY+.
|
|
173
|
-
#
|
|
174
|
-
# NOTE: The current implementation does not validate whether the connection
|
|
175
|
-
# currently supports UTF-8. Future versions may change.
|
|
176
|
-
#
|
|
177
|
-
# The string's bytes must be valid ASCII or valid UTF-8. The string's
|
|
178
|
-
# reported encoding is ignored, but the string is _not_ transcoded.
|
|
179
|
-
class RawText < CommandData # :nodoc:
|
|
180
|
-
def initialize(data:)
|
|
181
|
-
data = String(data.to_str)
|
|
182
|
-
data = if [Encoding::ASCII, Encoding::UTF_8].include?(data.encoding)
|
|
183
|
-
-data
|
|
184
|
-
elsif data.ascii_only?
|
|
185
|
-
-(data.dup.force_encoding("ASCII"))
|
|
186
|
-
else
|
|
187
|
-
-(data.dup.force_encoding("UTF-8"))
|
|
188
|
-
end
|
|
189
|
-
super
|
|
190
|
-
validate
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def validate
|
|
194
|
-
if data.include?("\0")
|
|
195
|
-
raise DataFormatError, "NULL byte must be binary literal encoded"
|
|
196
|
-
elsif !data.valid_encoding?
|
|
197
|
-
raise DataFormatError, "invalid UTF-8 must be literal encoded"
|
|
198
|
-
elsif /[\r\n]/.match?(data)
|
|
199
|
-
raise DataFormatError, "CR and LF bytes must be literal encoded"
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
def ascii_only?; data.ascii_only? end
|
|
204
|
-
|
|
205
|
-
def send_data(imap, tag) imap.__send__(:put_string, data) end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
class RawData < CommandData # :nodoc:
|
|
209
|
-
def initialize(data:)
|
|
210
|
-
data = split_parts(data)
|
|
211
|
-
super
|
|
212
|
-
validate
|
|
122
|
+
class RawData # :nodoc:
|
|
123
|
+
def send_data(imap, tag)
|
|
124
|
+
imap.__send__(:put_string, @data)
|
|
213
125
|
end
|
|
214
126
|
|
|
215
|
-
def send_data(imap, tag) data.each do _1.send_data(imap, tag) end end
|
|
216
|
-
|
|
217
127
|
def validate
|
|
218
|
-
return unless RawText === data.last
|
|
219
|
-
text = data.last.data
|
|
220
|
-
if text.rindex(/~?\{[1-9]\d*\+?\}\z/n)
|
|
221
|
-
raise DataFormatError, "RawData cannot end with literal continuation"
|
|
222
|
-
end
|
|
223
128
|
end
|
|
224
129
|
|
|
225
130
|
private
|
|
226
131
|
|
|
227
|
-
def
|
|
228
|
-
data = data
|
|
229
|
-
parts = []
|
|
230
|
-
while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n)
|
|
231
|
-
text, binary, bytesize, non_sync, data = $`, !!$1, $2, !!$3, $'
|
|
232
|
-
bytesize = Integer bytesize, 10
|
|
233
|
-
parts << RawText[text] unless text.empty?
|
|
234
|
-
parts << extract_literal(data,
|
|
235
|
-
binary: binary,
|
|
236
|
-
bytesize: bytesize,
|
|
237
|
-
non_sync: non_sync)
|
|
238
|
-
data[0, bytesize] = ""
|
|
239
|
-
end
|
|
240
|
-
parts << RawText[data] unless data.empty?
|
|
241
|
-
parts
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def extract_literal(data, binary:, bytesize:, non_sync:)
|
|
245
|
-
if data.bytesize < bytesize
|
|
246
|
-
raise DataFormatError, "Too few bytes in string for literal, " \
|
|
247
|
-
"expected: %s, remaining: %s" % [bytesize, data.bytesize]
|
|
248
|
-
end
|
|
249
|
-
literal = data.byteslice(0, bytesize)
|
|
250
|
-
(binary ? Literal8 : Literal).new(data: literal, non_sync: non_sync)
|
|
132
|
+
def initialize(data)
|
|
133
|
+
@data = data
|
|
251
134
|
end
|
|
252
135
|
end
|
|
253
136
|
|
|
254
|
-
class Atom
|
|
255
|
-
def
|
|
256
|
-
|
|
257
|
-
validate
|
|
137
|
+
class Atom # :nodoc:
|
|
138
|
+
def send_data(imap, tag)
|
|
139
|
+
imap.__send__(:put_string, @data)
|
|
258
140
|
end
|
|
259
141
|
|
|
260
142
|
def validate
|
|
261
|
-
data.to_s.ascii_only? \
|
|
262
|
-
or raise DataFormatError, "#{self.class} must be ASCII only"
|
|
263
|
-
data.match?(ResponseParser::Patterns::ATOM_SPECIALS) \
|
|
264
|
-
and raise DataFormatError, "#{self.class} must not contain atom-specials"
|
|
265
143
|
end
|
|
266
144
|
|
|
267
|
-
|
|
268
|
-
imap.__send__(:put_string, data.to_s)
|
|
269
|
-
end
|
|
270
|
-
end
|
|
145
|
+
private
|
|
271
146
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
imap.__send__(:put_string, "\\#{data}")
|
|
147
|
+
def initialize(data)
|
|
148
|
+
@data = data
|
|
275
149
|
end
|
|
276
150
|
end
|
|
277
151
|
|
|
@@ -291,55 +165,21 @@ module Net
|
|
|
291
165
|
end
|
|
292
166
|
|
|
293
167
|
class Literal # :nodoc:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
super(data: data, non_sync: non_sync)
|
|
297
|
-
end
|
|
298
|
-
alias :[] :new
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
attr_reader :data, :non_sync
|
|
302
|
-
|
|
303
|
-
def to_h(&block) block ? to_h.to_h(&block) : { data: data, non_sync: non_sync } end
|
|
304
|
-
def ==(other) self.class === other && to_h == other.to_h end
|
|
305
|
-
def eql?(other) self.class === other && to_h.eql?(other.to_h) end
|
|
306
|
-
|
|
307
|
-
def initialize(data:, non_sync: nil)
|
|
308
|
-
data = -String(data.to_str).b or
|
|
309
|
-
raise DataFormatError, "#{self.class} expects string input"
|
|
310
|
-
@data, @non_sync = data, non_sync
|
|
311
|
-
validate
|
|
312
|
-
freeze
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
def self.validate(...)
|
|
316
|
-
data = new(...)
|
|
317
|
-
data.validate
|
|
318
|
-
data
|
|
168
|
+
def send_data(imap, tag)
|
|
169
|
+
imap.__send__(:send_literal, @data, tag)
|
|
319
170
|
end
|
|
320
171
|
|
|
321
|
-
def bytesize; data.bytesize end
|
|
322
|
-
|
|
323
172
|
def validate
|
|
324
|
-
if data.include?("\0")
|
|
325
|
-
raise DataFormatError, "NULL byte not allowed in #{self.class}. " \
|
|
326
|
-
"Use #{Literal8} or a null-safe encoding."
|
|
327
|
-
end
|
|
328
173
|
end
|
|
329
174
|
|
|
330
|
-
|
|
331
|
-
imap.__send__(:send_literal, data, tag, non_sync: non_sync)
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
class Literal8 < Literal # :nodoc:
|
|
336
|
-
def validate; nil end # all bytes are okay
|
|
175
|
+
private
|
|
337
176
|
|
|
338
|
-
def
|
|
339
|
-
|
|
177
|
+
def initialize(data)
|
|
178
|
+
@data = data
|
|
340
179
|
end
|
|
341
180
|
end
|
|
342
181
|
|
|
182
|
+
# *DEPRECATED*. Replaced by SequenceSet.
|
|
343
183
|
class MessageSet # :nodoc:
|
|
344
184
|
def send_data(imap, tag)
|
|
345
185
|
imap.__send__(:put_string, format_internal(@data))
|
|
@@ -353,6 +193,16 @@ module Net
|
|
|
353
193
|
|
|
354
194
|
def initialize(data)
|
|
355
195
|
@data = data
|
|
196
|
+
warn("DEPRECATED: #{MessageSet} should be replaced with #{SequenceSet}.",
|
|
197
|
+
uplevel: 1, category: :deprecated)
|
|
198
|
+
begin
|
|
199
|
+
# to ensure the input works with SequenceSet, too
|
|
200
|
+
SequenceSet.new(data)
|
|
201
|
+
rescue
|
|
202
|
+
warn "MessageSet input is incompatible with SequenceSet: [%s] %s" % [
|
|
203
|
+
$!.class, $!.message
|
|
204
|
+
]
|
|
205
|
+
end
|
|
356
206
|
end
|
|
357
207
|
|
|
358
208
|
def format_internal(data)
|
|
@@ -18,8 +18,6 @@ module Net
|
|
|
18
18
|
super(attr)
|
|
19
19
|
AttrTypeCoercion.attr_accessor(attr, type: type)
|
|
20
20
|
end
|
|
21
|
-
|
|
22
|
-
module_function def Integer?; NilOrInteger end
|
|
23
21
|
end
|
|
24
22
|
private_constant :Macros
|
|
25
23
|
|
|
@@ -28,33 +26,34 @@ module Net
|
|
|
28
26
|
end
|
|
29
27
|
private_class_method :included
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
38
36
|
end
|
|
39
|
-
Types[:boolean] = Boolean = safe{-> {!!_1}}
|
|
40
|
-
Types[Integer] = safe{->{Integer(_1)}}
|
|
41
37
|
|
|
42
|
-
def self.
|
|
43
|
-
|
|
44
|
-
define_method :"#{attr}
|
|
45
|
-
define_method :"#{attr}?" do send attr end if type == Boolean
|
|
38
|
+
def self.boolean(attr)
|
|
39
|
+
define_method :"#{attr}=" do |val| super !!val end
|
|
40
|
+
define_method :"#{attr}?" do send attr end
|
|
46
41
|
end
|
|
47
42
|
|
|
48
|
-
|
|
43
|
+
def self.integer(attr)
|
|
44
|
+
define_method :"#{attr}=" do |val| super Integer val end
|
|
45
|
+
end
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
enum
|
|
47
|
+
def self.enum(attr, enum)
|
|
48
|
+
enum = enum.dup.freeze
|
|
52
49
|
expected = -"one of #{enum.map(&:inspect).join(", ")}"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
58
57
|
|
|
59
58
|
end
|
|
60
59
|
end
|