net-imap 0.6.4 → 0.6.4.1
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/lib/net/imap/command_data.rb +85 -45
- data/lib/net/imap/config.rb +1 -1
- data/lib/net/imap/data_encoding.rb +10 -9
- data/lib/net/imap/response_parser.rb +12 -12
- data/lib/net/imap/response_reader.rb +6 -1
- data/lib/net/imap.rb +47 -19
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 032600f694434b77edab2496c4bff9a7adc5c912301c0a7b386706dfccdc2345
|
|
4
|
+
data.tar.gz: 69dd23250cda6c1eb7a9bbeabe10969389c10047f12610d6bec3407d23e16854
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98ede1ee28c608ceea04bd5a00a114029da96eae454dc222cb046308c625241df423ceac7811322e6f641f3899ba1ca7a7d3f1bbfbf839632d2cf1a01c963c59
|
|
7
|
+
data.tar.gz: 810b3514de99b738216d7d49c917a05ee0c73211530bd81fe55540d158736dd7ba84f841d13f6d959b3dca01c3e32d792e500e7ee8f8eb5a2ab6899a53bf095e
|
|
@@ -16,14 +16,15 @@ module Net
|
|
|
16
16
|
when nil
|
|
17
17
|
when String
|
|
18
18
|
when Integer
|
|
19
|
-
|
|
19
|
+
# Covers modseq-valzer, which is the largest valid IMAP integer
|
|
20
|
+
if data.negative?
|
|
21
|
+
raise DataFormatError, "Integer argument must be unsigned: #{data}"
|
|
22
|
+
elsif 0xffff_ffff_ffff_ffff < data
|
|
23
|
+
raise DataFormatError, "Integer argument must fit in 64 bits: #{data}"
|
|
24
|
+
end
|
|
20
25
|
when Array
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
else
|
|
24
|
-
data.each do |i|
|
|
25
|
-
validate_data(i)
|
|
26
|
-
end
|
|
26
|
+
data.each do |i|
|
|
27
|
+
validate_data(i)
|
|
27
28
|
end
|
|
28
29
|
when Time, Date, DateTime
|
|
29
30
|
when Symbol
|
|
@@ -76,23 +77,29 @@ module Net
|
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
79
|
-
def send_quoted_string(str)
|
|
80
|
-
put_string('"' + str.gsub(/["\\]/, "\\\\\\&") + '"')
|
|
81
|
-
end
|
|
80
|
+
def send_quoted_string(str) = QuotedString.new(data: str).send_data(self)
|
|
82
81
|
|
|
83
82
|
def send_binary_literal(*, **) = send_literal(*, **, binary: true)
|
|
84
83
|
|
|
85
84
|
# `non_sync` is an optional tri-state flag:
|
|
86
85
|
# * `true` -> Force non-synchronizing `LITERAL+`/`LITERAL-` behavior.
|
|
87
|
-
#
|
|
86
|
+
# NOTE: raises DataFormatError when server doesn't support
|
|
87
|
+
# non-synchronizing literal, or literal is too large for LITERAL-.
|
|
88
88
|
# * `false` -> Force normal synchronizing literal behavior.
|
|
89
89
|
# * `nil` -> (default) Currently behaves like `false` (will be dynamic).
|
|
90
90
|
def send_literal(str, tag = nil, binary: false, non_sync: nil)
|
|
91
|
+
bytesize = str.bytesize
|
|
91
92
|
synchronize do
|
|
92
|
-
non_sync
|
|
93
|
+
if non_sync && !non_sync_literal_allowed?(bytesize)
|
|
94
|
+
# TODO: check in Printer, so we don't need to close the connection.
|
|
95
|
+
@sock.close
|
|
96
|
+
raise DataFormatError, "Connection closed: " \
|
|
97
|
+
"Cannot send non-synchronizing literal without known server support"
|
|
98
|
+
end
|
|
99
|
+
non_sync = non_sync_literal?(bytesize) if non_sync.nil?
|
|
93
100
|
prefix = "~" if binary
|
|
94
101
|
plus = "+" if non_sync
|
|
95
|
-
put_string("#{prefix}{#{
|
|
102
|
+
put_string("#{prefix}{#{bytesize}#{plus}}\r\n")
|
|
96
103
|
if non_sync
|
|
97
104
|
put_string(str)
|
|
98
105
|
return
|
|
@@ -112,14 +119,22 @@ module Net
|
|
|
112
119
|
end
|
|
113
120
|
|
|
114
121
|
def non_sync_literal?(bytesize)
|
|
115
|
-
|
|
116
|
-
bytesize
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
bytesize <= config.max_non_synchronizing_literal \
|
|
123
|
+
&& non_sync_literal_allowed?(bytesize)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def non_sync_literal_allowed?(bytesize)
|
|
127
|
+
return unless capabilities_cached?
|
|
128
|
+
return "+" if capable?("LITERAL+")
|
|
129
|
+
return "-" if capable_literal_minus? && bytesize <= 4096
|
|
130
|
+
false
|
|
119
131
|
end
|
|
120
132
|
|
|
133
|
+
def capable_literal_minus? = capable?("LITERAL-") || capable?("IMAP4rev2")
|
|
134
|
+
|
|
135
|
+
# NOTE: +num+ should already be an Integer
|
|
121
136
|
def send_number_data(num)
|
|
122
|
-
put_string(num.to_s)
|
|
137
|
+
put_string(Integer(num).to_s)
|
|
123
138
|
end
|
|
124
139
|
|
|
125
140
|
def send_list_data(list, tag = nil)
|
|
@@ -154,36 +169,38 @@ module Net
|
|
|
154
169
|
end
|
|
155
170
|
end
|
|
156
171
|
|
|
157
|
-
# Represents IMAP +text+ data, which
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
172
|
+
# Represents IMAP +text+ or +quoted+ data, which share the same
|
|
173
|
+
# validations of decoded #data, and differ only in how they are formatted.
|
|
174
|
+
#
|
|
175
|
+
# +data+ may contain any 7-bit ASCII character except +NULL+, +CR+, or +LF+.
|
|
176
|
+
# Any multibyte +UTF-8+ character is also allowed when the connection
|
|
177
|
+
# supports UTF8: either +UTF8=ACCEPT+ or +IMAP4rev2+ have been enabled, or
|
|
178
|
+
# the server supports only +IMAP4rev2+ and not earlier IMAP revisions, or
|
|
179
|
+
# the server advertises +UTF8=ONLY+.
|
|
162
180
|
#
|
|
163
|
-
# NOTE:
|
|
164
|
-
#
|
|
181
|
+
# NOTE: This does not verify whether the connection supports UTF-8, but that
|
|
182
|
+
# may change in future versions.
|
|
165
183
|
#
|
|
166
184
|
# The string's bytes must be valid ASCII or valid UTF-8. The string's
|
|
167
185
|
# reported encoding is ignored, but the string is _not_ transcoded.
|
|
168
|
-
class
|
|
186
|
+
class ValidNonLiteralData < CommandData
|
|
169
187
|
def initialize(data:)
|
|
170
188
|
data = String(data.to_str)
|
|
171
|
-
|
|
172
|
-
-
|
|
173
|
-
elsif data.ascii_only?
|
|
174
|
-
-(data.dup.force_encoding("ASCII"))
|
|
175
|
-
else
|
|
176
|
-
-(data.dup.force_encoding("UTF-8"))
|
|
189
|
+
unless data.encoding in Encoding::ASCII | Encoding::UTF_8
|
|
190
|
+
data = data.dup.force_encoding(data.ascii_only? ? "ASCII" : "UTF-8")
|
|
177
191
|
end
|
|
192
|
+
data = -data
|
|
178
193
|
super
|
|
179
194
|
validate
|
|
180
195
|
end
|
|
181
196
|
|
|
182
197
|
def validate
|
|
183
|
-
if data.
|
|
184
|
-
raise DataFormatError, "
|
|
198
|
+
if !(data.encoding in Encoding::ASCII | Encoding::UTF_8)
|
|
199
|
+
raise DataFormatError, "must use ASCII or UTF-8 encoding"
|
|
185
200
|
elsif !data.valid_encoding?
|
|
186
201
|
raise DataFormatError, "invalid UTF-8 must be literal encoded"
|
|
202
|
+
elsif data.include?("\0")
|
|
203
|
+
raise DataFormatError, "NULL byte must be binary literal encoded"
|
|
187
204
|
elsif /[\r\n]/.match?(data)
|
|
188
205
|
raise DataFormatError, "CR and LF bytes must be literal encoded"
|
|
189
206
|
end
|
|
@@ -191,12 +208,27 @@ module Net
|
|
|
191
208
|
|
|
192
209
|
def ascii_only? = data.ascii_only?
|
|
193
210
|
|
|
194
|
-
def send_data(imap, tag) = imap.__send__(:put_string,
|
|
211
|
+
def send_data(imap, tag = nil) = imap.__send__(:put_string, formatted)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Represents IMAP +text+ data, which covers everything in the IMAP grammar,
|
|
215
|
+
# except for +literal+, +literal8+, and the concluding +CRLF+.
|
|
216
|
+
#
|
|
217
|
+
# NOTE: The current implementation does not verify that the connection
|
|
218
|
+
# supports UTF-8. Future versions may validate this.
|
|
219
|
+
class RawText < ValidNonLiteralData # :nodoc:
|
|
220
|
+
# raw: no formatting necessary
|
|
221
|
+
alias formatted data
|
|
195
222
|
end
|
|
196
223
|
|
|
197
224
|
class RawData < CommandData # :nodoc:
|
|
198
225
|
def initialize(data:)
|
|
199
|
-
|
|
226
|
+
case data
|
|
227
|
+
in String then data = self.class.split(data)
|
|
228
|
+
in Array if data.all? { _1 in RawText | Literal }
|
|
229
|
+
else
|
|
230
|
+
raise TypeError, "expected String or Array[#{RawText} | #{Literal}]"
|
|
231
|
+
end
|
|
200
232
|
super
|
|
201
233
|
validate
|
|
202
234
|
end
|
|
@@ -205,14 +237,16 @@ module Net
|
|
|
205
237
|
|
|
206
238
|
def validate
|
|
207
239
|
return unless data.last in RawText(data: text)
|
|
208
|
-
if text.rindex(
|
|
240
|
+
if text.rindex(/\{\d+\+?\}\z/n)
|
|
209
241
|
raise DataFormatError, "RawData cannot end with literal continuation"
|
|
210
242
|
end
|
|
211
243
|
end
|
|
212
244
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
245
|
+
# Splits an input +string+ into an array of RawText and Literal/Literal8.
|
|
246
|
+
#
|
|
247
|
+
# NOTE: unlike RawData#validate, this does not prevent the final RawText
|
|
248
|
+
# from ending with a literal prefix.
|
|
249
|
+
def self.split(data)
|
|
216
250
|
data = data.b # dups and ensures BINARY encoding
|
|
217
251
|
parts = []
|
|
218
252
|
while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n)
|
|
@@ -226,7 +260,7 @@ module Net
|
|
|
226
260
|
parts
|
|
227
261
|
end
|
|
228
262
|
|
|
229
|
-
def extract_literal(data, binary:, bytesize:, non_sync:)
|
|
263
|
+
def self.extract_literal(data, binary:, bytesize:, non_sync:)
|
|
230
264
|
if data.bytesize < bytesize
|
|
231
265
|
raise DataFormatError, "Too few bytes in string for literal, " \
|
|
232
266
|
"expected: %s, remaining: %s" % [bytesize, data.bytesize]
|
|
@@ -234,6 +268,7 @@ module Net
|
|
|
234
268
|
literal = data.byteslice(0, bytesize)
|
|
235
269
|
(binary ? Literal8 : Literal).new(data: literal, non_sync:)
|
|
236
270
|
end
|
|
271
|
+
private_class_method :extract_literal
|
|
237
272
|
end
|
|
238
273
|
|
|
239
274
|
class Atom < CommandData # :nodoc:
|
|
@@ -247,6 +282,8 @@ module Net
|
|
|
247
282
|
or raise DataFormatError, "#{self.class} must be ASCII only"
|
|
248
283
|
data.match?(ResponseParser::Patterns::ATOM_SPECIALS) \
|
|
249
284
|
and raise DataFormatError, "#{self.class} must not contain atom-specials"
|
|
285
|
+
data.empty? \
|
|
286
|
+
and raise DataFormatError, "#{self.class} must not be empty"
|
|
250
287
|
end
|
|
251
288
|
|
|
252
289
|
def send_data(imap, tag)
|
|
@@ -260,10 +297,13 @@ module Net
|
|
|
260
297
|
end
|
|
261
298
|
end
|
|
262
299
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
300
|
+
# Represents a IMAP +quoted+ string, which can encode any valid ASCII or
|
|
301
|
+
# UTF-8 string, unless it contains any +CR+, +LF+, or +NULL+ bytes.
|
|
302
|
+
#
|
|
303
|
+
# NOTE: The current implementation does not verify that the connection
|
|
304
|
+
# supports UTF-8. Future versions may validate this.
|
|
305
|
+
class QuotedString < ValidNonLiteralData # :nodoc:
|
|
306
|
+
def formatted = %("#{data.gsub(/["\\]/, "\\\\\\&")}")
|
|
267
307
|
end
|
|
268
308
|
|
|
269
309
|
class Literal < Data.define(:data, :non_sync) # :nodoc:
|
data/lib/net/imap/config.rb
CHANGED
|
@@ -310,7 +310,7 @@ module Net
|
|
|
310
310
|
#
|
|
311
311
|
# * original: +-1+ (_never_ send non-synchronizing literals)
|
|
312
312
|
# * +0.6+: 16 KiB
|
|
313
|
-
attr_accessor :max_non_synchronizing_literal, type: Integer
|
|
313
|
+
attr_accessor :max_non_synchronizing_literal, type: Integer, defaults: {
|
|
314
314
|
0.0r => -1,
|
|
315
315
|
0.6r => 16 << 16, # 16 KiB
|
|
316
316
|
}
|
|
@@ -155,7 +155,8 @@ module Net
|
|
|
155
155
|
|
|
156
156
|
# Common validators of number and nz_number types
|
|
157
157
|
module NumValidator # :nodoc
|
|
158
|
-
NUMBER_RE = /\A
|
|
158
|
+
NUMBER_RE = /\A\d+\z/
|
|
159
|
+
NZ_NUMBER_RE = /\A[1-9]\d*\z/
|
|
159
160
|
module_function
|
|
160
161
|
|
|
161
162
|
# Check if argument is a valid 'number' according to RFC 3501
|
|
@@ -251,7 +252,7 @@ module Net
|
|
|
251
252
|
def coerce_number(num)
|
|
252
253
|
case num
|
|
253
254
|
when Integer then ensure_number num
|
|
254
|
-
when NUMBER_RE then ensure_number
|
|
255
|
+
when NUMBER_RE then ensure_number num.to_i
|
|
255
256
|
else
|
|
256
257
|
raise DataFormatError, "%p is not a valid number" % [num]
|
|
257
258
|
end
|
|
@@ -260,8 +261,8 @@ module Net
|
|
|
260
261
|
# Like #ensure_nz_number, but usable with numeric String input.
|
|
261
262
|
def coerce_nz_number(num)
|
|
262
263
|
case num
|
|
263
|
-
when Integer
|
|
264
|
-
when
|
|
264
|
+
when Integer then ensure_nz_number num
|
|
265
|
+
when NZ_NUMBER_RE then ensure_nz_number num.to_i
|
|
265
266
|
else
|
|
266
267
|
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
|
267
268
|
end
|
|
@@ -271,7 +272,7 @@ module Net
|
|
|
271
272
|
def coerce_number64(num)
|
|
272
273
|
case num
|
|
273
274
|
when Integer then ensure_number64 num
|
|
274
|
-
when NUMBER_RE then ensure_number64
|
|
275
|
+
when NUMBER_RE then ensure_number64 num.to_i
|
|
275
276
|
else
|
|
276
277
|
raise DataFormatError, "%p is not a valid number64" % [num]
|
|
277
278
|
end
|
|
@@ -280,8 +281,8 @@ module Net
|
|
|
280
281
|
# Like #ensure_nz_number64, but usable with numeric String input.
|
|
281
282
|
def coerce_nz_number64(num)
|
|
282
283
|
case num
|
|
283
|
-
when Integer
|
|
284
|
-
when
|
|
284
|
+
when Integer then ensure_nz_number64 num
|
|
285
|
+
when NZ_NUMBER_RE then ensure_nz_number64 num.to_i
|
|
285
286
|
else
|
|
286
287
|
raise DataFormatError, "%p is not a valid nz-number64" % [num]
|
|
287
288
|
end
|
|
@@ -291,7 +292,7 @@ module Net
|
|
|
291
292
|
def coerce_mod_sequence_value(num)
|
|
292
293
|
case num
|
|
293
294
|
when Integer then ensure_mod_sequence_value num
|
|
294
|
-
when NUMBER_RE then ensure_mod_sequence_value
|
|
295
|
+
when NUMBER_RE then ensure_mod_sequence_value num.to_i
|
|
295
296
|
else
|
|
296
297
|
raise DataFormatError, "%p is not a valid mod-sequence-value" % [num]
|
|
297
298
|
end
|
|
@@ -301,7 +302,7 @@ module Net
|
|
|
301
302
|
def coerce_mod_sequence_valzer(num)
|
|
302
303
|
case num
|
|
303
304
|
when Integer then ensure_mod_sequence_valzer num
|
|
304
|
-
when NUMBER_RE then ensure_mod_sequence_valzer
|
|
305
|
+
when NUMBER_RE then ensure_mod_sequence_valzer num.to_i
|
|
305
306
|
else
|
|
306
307
|
raise DataFormatError, "%p is not a valid mod-sequence-valzer" % [num]
|
|
307
308
|
end
|
|
@@ -2212,10 +2212,7 @@ module Net
|
|
|
2212
2212
|
if $1
|
|
2213
2213
|
return Token.new(T_SPACE, $+)
|
|
2214
2214
|
elsif $2
|
|
2215
|
-
|
|
2216
|
-
val = @str[@pos, len]
|
|
2217
|
-
@pos += len
|
|
2218
|
-
return Token.new(T_LITERAL8, val)
|
|
2215
|
+
literal_token($+, T_LITERAL8)
|
|
2219
2216
|
elsif $3 && $7
|
|
2220
2217
|
# greedily match ATOM, prefixed with NUMBER, NIL, or PLUS.
|
|
2221
2218
|
return Token.new(T_ATOM, $3)
|
|
@@ -2243,10 +2240,7 @@ module Net
|
|
|
2243
2240
|
elsif $15
|
|
2244
2241
|
return Token.new(T_RBRA, $+)
|
|
2245
2242
|
elsif $16
|
|
2246
|
-
|
|
2247
|
-
val = @str[@pos, len]
|
|
2248
|
-
@pos += len
|
|
2249
|
-
return Token.new(T_LITERAL, val)
|
|
2243
|
+
literal_token($+)
|
|
2250
2244
|
elsif $17
|
|
2251
2245
|
return Token.new(T_PERCENT, $+)
|
|
2252
2246
|
elsif $18
|
|
@@ -2272,10 +2266,7 @@ module Net
|
|
|
2272
2266
|
elsif $4
|
|
2273
2267
|
return Token.new(T_QUOTED, Patterns.unescape_quoted($+))
|
|
2274
2268
|
elsif $5
|
|
2275
|
-
|
|
2276
|
-
val = @str[@pos, len]
|
|
2277
|
-
@pos += len
|
|
2278
|
-
return Token.new(T_LITERAL, val)
|
|
2269
|
+
literal_token($+)
|
|
2279
2270
|
elsif $6
|
|
2280
2271
|
return Token.new(T_LPAR, $+)
|
|
2281
2272
|
elsif $7
|
|
@@ -2290,6 +2281,15 @@ module Net
|
|
|
2290
2281
|
else
|
|
2291
2282
|
parse_error("invalid @lex_state - %s", @lex_state.inspect)
|
|
2292
2283
|
end
|
|
2284
|
+
rescue DataFormatError => error
|
|
2285
|
+
parse_error error.message
|
|
2286
|
+
end
|
|
2287
|
+
|
|
2288
|
+
def literal_token(len, type = T_LITERAL)
|
|
2289
|
+
len = NumValidator.coerce_number64 len
|
|
2290
|
+
val = @str[@pos, len]
|
|
2291
|
+
@pos += len
|
|
2292
|
+
Token.new(type, val)
|
|
2293
2293
|
end
|
|
2294
2294
|
|
|
2295
2295
|
end
|
|
@@ -4,6 +4,8 @@ module Net
|
|
|
4
4
|
class IMAP
|
|
5
5
|
# See https://www.rfc-editor.org/rfc/rfc9051#section-2.2.2
|
|
6
6
|
class ResponseReader # :nodoc:
|
|
7
|
+
include NumValidator
|
|
8
|
+
|
|
7
9
|
attr_reader :client
|
|
8
10
|
|
|
9
11
|
def initialize(client, sock)
|
|
@@ -46,7 +48,10 @@ module Net
|
|
|
46
48
|
def line_done? = buff.end_with?(CRLF)
|
|
47
49
|
|
|
48
50
|
def get_literal_size(buff)
|
|
49
|
-
buff.end_with?("}\r\n") && buff.rindex(/\{(\d+)\}\r\n\z/n) &&
|
|
51
|
+
buff.end_with?("}\r\n") && buff.rindex(/\{(\d+)\}\r\n\z/n) &&
|
|
52
|
+
coerce_number64($1)
|
|
53
|
+
rescue DataFormatError
|
|
54
|
+
raise DataFormatError, format("invalid response literal size (%s)", $1)
|
|
50
55
|
end
|
|
51
56
|
|
|
52
57
|
def read_line
|
data/lib/net/imap.rb
CHANGED
|
@@ -813,7 +813,7 @@ module Net
|
|
|
813
813
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
|
814
814
|
#
|
|
815
815
|
class IMAP < Protocol
|
|
816
|
-
VERSION = "0.6.4"
|
|
816
|
+
VERSION = "0.6.4.1"
|
|
817
817
|
|
|
818
818
|
# Aliases for supported capabilities, to be used with the #enable command.
|
|
819
819
|
ENABLE_ALIASES = {
|
|
@@ -1160,16 +1160,29 @@ module Net
|
|
|
1160
1160
|
# imap.logout
|
|
1161
1161
|
# imap.inspect #=> "#<Net::IMAP imap.example.net:993 TLS logout>"
|
|
1162
1162
|
#
|
|
1163
|
+
# imap = Net::IMAP.new(hostname, ssl: false)
|
|
1164
|
+
# imap.inspect #=> "#<Net::IMAP imap.example.net:143 PLAINTEXT not_authenticated>"
|
|
1165
|
+
#
|
|
1166
|
+
# imap.starttls verify_mode: OpenSSL::SSL::VERIFY_NONE
|
|
1167
|
+
# imap.inspect #=> "#<Net::IMAP imap.example.net:993 TLS (NOT VERIFIED) not_authenticated>"
|
|
1168
|
+
#
|
|
1163
1169
|
def inspect
|
|
1164
|
-
tls_state = tls_verified? ? "TLS" :
|
|
1165
|
-
ssl_ctx ? "TLS (NOT VERIFIED)" :
|
|
1166
|
-
"PLAINTEXT"
|
|
1167
1170
|
conn_state = disconnected? ? "disconnected" : connection_state.to_sym
|
|
1168
1171
|
"#<%s:0x%08x %s:%s %s %s>" % [
|
|
1169
|
-
self.class.name, __id__, host, port,
|
|
1172
|
+
self.class.name, __id__, host, port, inspect_tls_state, conn_state
|
|
1170
1173
|
]
|
|
1171
1174
|
end
|
|
1172
1175
|
|
|
1176
|
+
private def inspect_tls_state
|
|
1177
|
+
if tls_verified?
|
|
1178
|
+
"TLS"
|
|
1179
|
+
elsif ssl_ctx && @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
|
1180
|
+
"TLS (#{@sock.session ? "NOT VERIFIED" : "NOT ESTABLISHED"})"
|
|
1181
|
+
else
|
|
1182
|
+
"PLAINTEXT#{" (TLS NOT STARTED)" if ssl_ctx}"
|
|
1183
|
+
end
|
|
1184
|
+
end
|
|
1185
|
+
|
|
1173
1186
|
# Returns true after the TLS negotiation has completed and the remote
|
|
1174
1187
|
# hostname has been verified. Returns false when TLS has been established
|
|
1175
1188
|
# but peer verification was disabled.
|
|
@@ -1177,22 +1190,24 @@ module Net
|
|
|
1177
1190
|
|
|
1178
1191
|
# Disconnects from the server.
|
|
1179
1192
|
#
|
|
1180
|
-
# Waits for receiver thread to close before returning
|
|
1181
|
-
#
|
|
1193
|
+
# Waits for receiver thread to close before returning, except when called
|
|
1194
|
+
# from inside the connection mutex such as from a response handler. Slow or
|
|
1195
|
+
# stuck response handlers can cause #disconnect to hang until they complete.
|
|
1182
1196
|
#
|
|
1183
1197
|
# Related: #logout, #logout!
|
|
1184
1198
|
def disconnect
|
|
1185
1199
|
in_logout_state = try_state_logout?
|
|
1186
1200
|
return if disconnected?
|
|
1201
|
+
in_receiver_thread = Thread.current == @receiver_thread
|
|
1187
1202
|
begin
|
|
1188
1203
|
@sock.to_io.shutdown
|
|
1189
1204
|
rescue Errno::ENOTCONN
|
|
1190
1205
|
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
|
|
1191
1206
|
rescue Exception => e
|
|
1192
|
-
@receiver_thread.raise(e)
|
|
1207
|
+
@receiver_thread.raise(e) unless in_receiver_thread
|
|
1193
1208
|
end
|
|
1194
1209
|
@sock.close
|
|
1195
|
-
@receiver_thread.join
|
|
1210
|
+
@receiver_thread.join unless mon_owned? || in_receiver_thread
|
|
1196
1211
|
raise e if e
|
|
1197
1212
|
ensure
|
|
1198
1213
|
# Try again after shutting down the receiver thread. With no reciever
|
|
@@ -2242,6 +2257,7 @@ module Net
|
|
|
2242
2257
|
# provided as an array or a string.
|
|
2243
2258
|
# See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
|
|
2244
2259
|
# and {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
|
|
2260
|
+
# <em>Please note</em> the warning for when +criteria+ is a String.
|
|
2245
2261
|
#
|
|
2246
2262
|
# +return+ options control what kind of information is returned about
|
|
2247
2263
|
# messages matching the search +criteria+. Specifying +return+ should force
|
|
@@ -2652,7 +2668,8 @@ module Net
|
|
|
2652
2668
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
|
2653
2669
|
# capability has been enabled.
|
|
2654
2670
|
#
|
|
2655
|
-
# See #search for documentation of parameters.
|
|
2671
|
+
# See #search for documentation of parameters. <em>Please note</em> the
|
|
2672
|
+
# warning for when +criteria+ is a String.
|
|
2656
2673
|
#
|
|
2657
2674
|
# ==== Capabilities
|
|
2658
2675
|
#
|
|
@@ -2738,7 +2755,8 @@ module Net
|
|
|
2738
2755
|
# {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
|
|
2739
2756
|
# (For message sequence numbers, use #fetch instead.)
|
|
2740
2757
|
#
|
|
2741
|
-
# +attr+ behaves the same as with #fetch.
|
|
2758
|
+
# +attr+ behaves the same as with #fetch. <em>Please note</em> the #fetch
|
|
2759
|
+
# warning on the +attr+ argument.
|
|
2742
2760
|
# >>>
|
|
2743
2761
|
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
|
|
2744
2762
|
# part of any +FETCH+ response caused by a +UID+ command, regardless of
|
|
@@ -2950,8 +2968,10 @@ module Net
|
|
|
2950
2968
|
|
|
2951
2969
|
# Sends a {SORT command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2952
2970
|
# to search a mailbox for messages that match +search_keys+ and return an
|
|
2953
|
-
# array of message sequence numbers, sorted by +sort_keys+.
|
|
2954
|
-
#
|
|
2971
|
+
# array of message sequence numbers, sorted by +sort_keys+.
|
|
2972
|
+
#
|
|
2973
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2974
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2955
2975
|
#
|
|
2956
2976
|
#--
|
|
2957
2977
|
# TODO: describe +sort_keys+
|
|
@@ -2976,8 +2996,10 @@ module Net
|
|
|
2976
2996
|
|
|
2977
2997
|
# Sends a {UID SORT command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2978
2998
|
# to search a mailbox for messages that match +search_keys+ and return an
|
|
2979
|
-
# array of unique identifiers, sorted by +sort_keys+.
|
|
2980
|
-
#
|
|
2999
|
+
# array of unique identifiers, sorted by +sort_keys+.
|
|
3000
|
+
#
|
|
3001
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
3002
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2981
3003
|
#
|
|
2982
3004
|
# Related: #sort, #search, #uid_search, #thread, #uid_thread
|
|
2983
3005
|
#
|
|
@@ -2991,8 +3013,10 @@ module Net
|
|
|
2991
3013
|
|
|
2992
3014
|
# Sends a {THREAD command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2993
3015
|
# to search a mailbox and return message sequence numbers in threaded
|
|
2994
|
-
# format, as a ThreadMember tree.
|
|
2995
|
-
#
|
|
3016
|
+
# format, as a ThreadMember tree.
|
|
3017
|
+
#
|
|
3018
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
3019
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2996
3020
|
#
|
|
2997
3021
|
# The supported algorithms are:
|
|
2998
3022
|
#
|
|
@@ -3018,6 +3042,9 @@ module Net
|
|
|
3018
3042
|
# Similar to #thread, but returns unique identifiers instead of
|
|
3019
3043
|
# message sequence numbers.
|
|
3020
3044
|
#
|
|
3045
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
3046
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
3047
|
+
#
|
|
3021
3048
|
# Related: #thread, #search, #uid_search, #sort, #uid_sort
|
|
3022
3049
|
#
|
|
3023
3050
|
# ==== Capabilities
|
|
@@ -3127,10 +3154,11 @@ module Net
|
|
|
3127
3154
|
capabilities = capabilities
|
|
3128
3155
|
.flatten
|
|
3129
3156
|
.map {|e| ENABLE_ALIASES[e] || e }
|
|
3157
|
+
.flat_map { _1.is_a?(String) && !_1.empty? ? _1.split(/ /, -1) : [_1] }
|
|
3130
3158
|
.uniq
|
|
3131
|
-
.
|
|
3159
|
+
.map { Atom[_1] }
|
|
3132
3160
|
synchronize do
|
|
3133
|
-
send_command("ENABLE
|
|
3161
|
+
send_command("ENABLE", *capabilities)
|
|
3134
3162
|
result = clear_responses("ENABLED").last || []
|
|
3135
3163
|
@utf8_strings ||= result.include? "UTF8=ACCEPT"
|
|
3136
3164
|
@utf8_strings ||= result.include? "IMAP4REV2"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: net-imap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.4
|
|
4
|
+
version: 0.6.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shugo Maeda
|
|
@@ -131,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
131
131
|
- !ruby/object:Gem::Version
|
|
132
132
|
version: '0'
|
|
133
133
|
requirements: []
|
|
134
|
-
rubygems_version: 4.0.
|
|
134
|
+
rubygems_version: 4.0.13
|
|
135
135
|
specification_version: 4
|
|
136
136
|
summary: Ruby client api for Internet Message Access Protocol
|
|
137
137
|
test_files: []
|