net-imap 0.4.24 → 0.4.25
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 -46
- data/lib/net/imap/response_parser.rb +20 -12
- data/lib/net/imap/response_reader.rb +14 -2
- data/lib/net/imap.rb +35 -24
- 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: bf05b4f7aec9127531e83134b740620da216f847851f8ca464ef54e71e3431ad
|
|
4
|
+
data.tar.gz: 18361d6ee25983da0e4814d42a0ec851e10ce88789559e30778a945a7024a297
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 697214c436f129746ad8986dde31e4b819854cf131472158c4aacf5a35ef665b8529528bd0a843ba17d9be105711b4163ecccfa525989d9fe0b3f807f4ae9d58
|
|
7
|
+
data.tar.gz: ef657a1dbfd5bc73fa317248ae19589a842f87eabfe966b43b026e9dceff68d510621d3e17136b266d22f68b85d20fc22b4ea67a482fdca193d8a5290c643abf
|
|
@@ -14,14 +14,15 @@ module Net
|
|
|
14
14
|
when nil
|
|
15
15
|
when String
|
|
16
16
|
when Integer
|
|
17
|
-
|
|
17
|
+
# Covers modseq-valzer, which is the largest valid IMAP integer
|
|
18
|
+
if data.negative?
|
|
19
|
+
raise DataFormatError, "Integer argument must be unsigned: #{data}"
|
|
20
|
+
elsif 0xffff_ffff_ffff_ffff < data
|
|
21
|
+
raise DataFormatError, "Integer argument must fit in 64 bits: #{data}"
|
|
22
|
+
end
|
|
18
23
|
when Array
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
else
|
|
22
|
-
data.each do |i|
|
|
23
|
-
validate_data(i)
|
|
24
|
-
end
|
|
24
|
+
data.each do |i|
|
|
25
|
+
validate_data(i)
|
|
25
26
|
end
|
|
26
27
|
when Time, Date, DateTime
|
|
27
28
|
when Symbol
|
|
@@ -82,15 +83,23 @@ module Net
|
|
|
82
83
|
|
|
83
84
|
# `non_sync` is an optional tri-state flag:
|
|
84
85
|
# * `true` -> Force non-synchronizing `LITERAL+`/`LITERAL-` behavior.
|
|
85
|
-
#
|
|
86
|
+
# NOTE: raises DataFormatError when server doesn't support
|
|
87
|
+
# non-synchronizing literal, or literal is too large for LITERAL-.
|
|
86
88
|
# * `false` -> Force normal synchronizing literal behavior.
|
|
87
89
|
# * `nil` -> (default) Currently behaves like `false` (will be dynamic).
|
|
88
90
|
# TODO: Dynamic, based on capabilities and bytesize.
|
|
89
91
|
def send_literal(str, tag = nil, binary: false, non_sync: nil)
|
|
92
|
+
bytesize = str.bytesize
|
|
90
93
|
synchronize do
|
|
94
|
+
if non_sync && !non_sync_literal_allowed?(bytesize)
|
|
95
|
+
# TODO: check in Printer, so we don't need to close the connection.
|
|
96
|
+
@sock.close
|
|
97
|
+
raise DataFormatError, "Connection closed: " \
|
|
98
|
+
"Cannot send non-synchronizing literal without known server support"
|
|
99
|
+
end
|
|
91
100
|
prefix = "~" if binary
|
|
92
101
|
plus = "+" if non_sync
|
|
93
|
-
put_string("#{prefix}{#{
|
|
102
|
+
put_string("#{prefix}{#{bytesize}#{plus}}\r\n")
|
|
94
103
|
if non_sync
|
|
95
104
|
put_string(str)
|
|
96
105
|
return
|
|
@@ -109,8 +118,18 @@ module Net
|
|
|
109
118
|
end
|
|
110
119
|
end
|
|
111
120
|
|
|
121
|
+
def non_sync_literal_allowed?(bytesize)
|
|
122
|
+
return unless capabilities_cached?
|
|
123
|
+
return "+" if capable?("LITERAL+")
|
|
124
|
+
return "-" if capable_literal_minus? && bytesize <= 4096
|
|
125
|
+
false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def capable_literal_minus?; capable?("LITERAL-") || capable?("IMAP4rev2") end
|
|
129
|
+
|
|
130
|
+
# NOTE: +num+ should already be an Integer
|
|
112
131
|
def send_number_data(num)
|
|
113
|
-
put_string(num.to_s)
|
|
132
|
+
put_string(Integer(num).to_s)
|
|
114
133
|
end
|
|
115
134
|
|
|
116
135
|
def send_list_data(list, tag = nil)
|
|
@@ -165,36 +184,38 @@ module Net
|
|
|
165
184
|
end
|
|
166
185
|
end
|
|
167
186
|
|
|
168
|
-
# Represents IMAP +text+ data, which
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
#
|
|
187
|
+
# Represents IMAP +text+ or +quoted+ data, which share the same
|
|
188
|
+
# validations of decoded #data, and differ only in how they are formatted.
|
|
189
|
+
#
|
|
190
|
+
# +data+ may contain any 7-bit ASCII character except +NULL+, +CR+, or +LF+.
|
|
191
|
+
# Any multibyte +UTF-8+ character is also allowed when the connection
|
|
192
|
+
# supports UTF8: either +UTF8=ACCEPT+ or +IMAP4rev2+ have been enabled, or
|
|
193
|
+
# the server supports only +IMAP4rev2+ and not earlier IMAP revisions, or
|
|
194
|
+
# the server advertises +UTF8=ONLY+.
|
|
173
195
|
#
|
|
174
|
-
# NOTE:
|
|
175
|
-
#
|
|
196
|
+
# NOTE: This does not verify whether the connection supports UTF-8, but that
|
|
197
|
+
# may change in future versions.
|
|
176
198
|
#
|
|
177
199
|
# The string's bytes must be valid ASCII or valid UTF-8. The string's
|
|
178
200
|
# reported encoding is ignored, but the string is _not_ transcoded.
|
|
179
|
-
class
|
|
201
|
+
class ValidNonLiteralData < CommandData
|
|
180
202
|
def initialize(data:)
|
|
181
203
|
data = String(data.to_str)
|
|
182
|
-
|
|
183
|
-
-
|
|
184
|
-
elsif data.ascii_only?
|
|
185
|
-
-(data.dup.force_encoding("ASCII"))
|
|
186
|
-
else
|
|
187
|
-
-(data.dup.force_encoding("UTF-8"))
|
|
204
|
+
unless [Encoding::ASCII, Encoding::UTF_8].include?(data.encoding)
|
|
205
|
+
data = data.dup.force_encoding(data.ascii_only? ? "ASCII" : "UTF-8")
|
|
188
206
|
end
|
|
207
|
+
data = -data
|
|
189
208
|
super
|
|
190
209
|
validate
|
|
191
210
|
end
|
|
192
211
|
|
|
193
212
|
def validate
|
|
194
|
-
if
|
|
195
|
-
raise DataFormatError, "
|
|
213
|
+
if ![Encoding::ASCII, Encoding::UTF_8].include?(data.encoding)
|
|
214
|
+
raise DataFormatError, "must use ASCII or UTF-8 encoding"
|
|
196
215
|
elsif !data.valid_encoding?
|
|
197
216
|
raise DataFormatError, "invalid UTF-8 must be literal encoded"
|
|
217
|
+
elsif data.include?("\0")
|
|
218
|
+
raise DataFormatError, "NULL byte must be binary literal encoded"
|
|
198
219
|
elsif /[\r\n]/.match?(data)
|
|
199
220
|
raise DataFormatError, "CR and LF bytes must be literal encoded"
|
|
200
221
|
end
|
|
@@ -202,12 +223,31 @@ module Net
|
|
|
202
223
|
|
|
203
224
|
def ascii_only?; data.ascii_only? end
|
|
204
225
|
|
|
205
|
-
def send_data(imap, tag) imap.__send__(:put_string,
|
|
226
|
+
def send_data(imap, tag = nil) imap.__send__(:put_string, formatted) end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Represents IMAP +text+ data, which covers everything in the IMAP grammar,
|
|
230
|
+
# except for +literal+, +literal8+, and the concluding +CRLF+.
|
|
231
|
+
#
|
|
232
|
+
# NOTE: The current implementation does not verify that the connection
|
|
233
|
+
# supports UTF-8. Future versions may validate this.
|
|
234
|
+
class RawText < ValidNonLiteralData # :nodoc:
|
|
235
|
+
# raw: no formatting necessary
|
|
236
|
+
alias formatted data
|
|
206
237
|
end
|
|
207
238
|
|
|
208
239
|
class RawData < CommandData # :nodoc:
|
|
209
240
|
def initialize(data:)
|
|
210
|
-
|
|
241
|
+
case data
|
|
242
|
+
when String
|
|
243
|
+
data = self.class.split(data)
|
|
244
|
+
when Array
|
|
245
|
+
unless data.all? { |part| RawText === part || Literal === part }
|
|
246
|
+
raise TypeError, "expected String or Array[#{RawText} | #{Literal}]"
|
|
247
|
+
end
|
|
248
|
+
else
|
|
249
|
+
raise TypeError, "expected String or Array[#{RawText} | #{Literal}]"
|
|
250
|
+
end
|
|
211
251
|
super
|
|
212
252
|
validate
|
|
213
253
|
end
|
|
@@ -217,14 +257,16 @@ module Net
|
|
|
217
257
|
def validate
|
|
218
258
|
return unless RawText === data.last
|
|
219
259
|
text = data.last.data
|
|
220
|
-
if text.rindex(
|
|
260
|
+
if text.rindex(/\{\d+\+?\}\z/n)
|
|
221
261
|
raise DataFormatError, "RawData cannot end with literal continuation"
|
|
222
262
|
end
|
|
223
263
|
end
|
|
224
264
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
265
|
+
# Splits an input +string+ into an array of RawText and Literal/Literal8.
|
|
266
|
+
#
|
|
267
|
+
# NOTE: unlike RawData#validate, this does not prevent the final RawText
|
|
268
|
+
# from ending with a literal prefix.
|
|
269
|
+
def self.split(data)
|
|
228
270
|
data = data.b # dups and ensures BINARY encoding
|
|
229
271
|
parts = []
|
|
230
272
|
while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n)
|
|
@@ -241,7 +283,7 @@ module Net
|
|
|
241
283
|
parts
|
|
242
284
|
end
|
|
243
285
|
|
|
244
|
-
def extract_literal(data, binary:, bytesize:, non_sync:)
|
|
286
|
+
def self.extract_literal(data, binary:, bytesize:, non_sync:)
|
|
245
287
|
if data.bytesize < bytesize
|
|
246
288
|
raise DataFormatError, "Too few bytes in string for literal, " \
|
|
247
289
|
"expected: %s, remaining: %s" % [bytesize, data.bytesize]
|
|
@@ -249,6 +291,7 @@ module Net
|
|
|
249
291
|
literal = data.byteslice(0, bytesize)
|
|
250
292
|
(binary ? Literal8 : Literal).new(data: literal, non_sync: non_sync)
|
|
251
293
|
end
|
|
294
|
+
private_class_method :extract_literal
|
|
252
295
|
end
|
|
253
296
|
|
|
254
297
|
class Atom < CommandData # :nodoc:
|
|
@@ -262,6 +305,8 @@ module Net
|
|
|
262
305
|
or raise DataFormatError, "#{self.class} must be ASCII only"
|
|
263
306
|
data.match?(ResponseParser::Patterns::ATOM_SPECIALS) \
|
|
264
307
|
and raise DataFormatError, "#{self.class} must not contain atom-specials"
|
|
308
|
+
data.empty? \
|
|
309
|
+
and raise DataFormatError, "#{self.class} must not be empty"
|
|
265
310
|
end
|
|
266
311
|
|
|
267
312
|
def send_data(imap, tag)
|
|
@@ -275,19 +320,13 @@ module Net
|
|
|
275
320
|
end
|
|
276
321
|
end
|
|
277
322
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
private
|
|
287
|
-
|
|
288
|
-
def initialize(data)
|
|
289
|
-
@data = data
|
|
290
|
-
end
|
|
323
|
+
# Represents a IMAP +quoted+ string, which can encode any valid ASCII or
|
|
324
|
+
# UTF-8 string, unless it contains any +CR+, +LF+, or +NULL+ bytes.
|
|
325
|
+
#
|
|
326
|
+
# NOTE: The current implementation does not verify that the connection
|
|
327
|
+
# supports UTF-8. Future versions may validate this.
|
|
328
|
+
class QuotedString < ValidNonLiteralData # :nodoc:
|
|
329
|
+
def formatted; %("#{data.gsub(/["\\]/, "\\\\\\&")}") end
|
|
291
330
|
end
|
|
292
331
|
|
|
293
332
|
class Literal # :nodoc:
|
|
@@ -2055,10 +2055,7 @@ module Net
|
|
|
2055
2055
|
if $1
|
|
2056
2056
|
return Token.new(T_SPACE, $+)
|
|
2057
2057
|
elsif $2
|
|
2058
|
-
|
|
2059
|
-
val = @str[@pos, len]
|
|
2060
|
-
@pos += len
|
|
2061
|
-
return Token.new(T_LITERAL8, val)
|
|
2058
|
+
literal_token($+, T_LITERAL8)
|
|
2062
2059
|
elsif $3 && $7
|
|
2063
2060
|
# greedily match ATOM, prefixed with NUMBER, NIL, or PLUS.
|
|
2064
2061
|
return Token.new(T_ATOM, $3)
|
|
@@ -2086,10 +2083,7 @@ module Net
|
|
|
2086
2083
|
elsif $15
|
|
2087
2084
|
return Token.new(T_RBRA, $+)
|
|
2088
2085
|
elsif $16
|
|
2089
|
-
|
|
2090
|
-
val = @str[@pos, len]
|
|
2091
|
-
@pos += len
|
|
2092
|
-
return Token.new(T_LITERAL, val)
|
|
2086
|
+
literal_token($+)
|
|
2093
2087
|
elsif $17
|
|
2094
2088
|
return Token.new(T_PERCENT, $+)
|
|
2095
2089
|
elsif $18
|
|
@@ -2115,10 +2109,7 @@ module Net
|
|
|
2115
2109
|
elsif $4
|
|
2116
2110
|
return Token.new(T_QUOTED, Patterns.unescape_quoted($+))
|
|
2117
2111
|
elsif $5
|
|
2118
|
-
|
|
2119
|
-
val = @str[@pos, len]
|
|
2120
|
-
@pos += len
|
|
2121
|
-
return Token.new(T_LITERAL, val)
|
|
2112
|
+
literal_token($+)
|
|
2122
2113
|
elsif $6
|
|
2123
2114
|
return Token.new(T_LPAR, $+)
|
|
2124
2115
|
elsif $7
|
|
@@ -2133,6 +2124,23 @@ module Net
|
|
|
2133
2124
|
else
|
|
2134
2125
|
parse_error("invalid @lex_state - %s", @lex_state.inspect)
|
|
2135
2126
|
end
|
|
2127
|
+
rescue DataFormatError => error
|
|
2128
|
+
parse_error error.message
|
|
2129
|
+
end
|
|
2130
|
+
|
|
2131
|
+
def literal_token(len, type = T_LITERAL)
|
|
2132
|
+
len = coerce_number64 len.to_i
|
|
2133
|
+
val = @str[@pos, len]
|
|
2134
|
+
@pos += len
|
|
2135
|
+
Token.new(type, val)
|
|
2136
|
+
end
|
|
2137
|
+
|
|
2138
|
+
# copied/adapted from NumValidator in v0.6
|
|
2139
|
+
def coerce_number64(num)
|
|
2140
|
+
int = num.to_i
|
|
2141
|
+
return int if 0 <= int && int <= 0x7fff_ffff_ffff_ffff
|
|
2142
|
+
raise DataFormatError,
|
|
2143
|
+
"number64 must be unsigned 63-bit integer: #{num}"
|
|
2136
2144
|
end
|
|
2137
2145
|
|
|
2138
2146
|
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)
|
|
@@ -31,12 +33,14 @@ module Net
|
|
|
31
33
|
|
|
32
34
|
def bytes_read; buff.bytesize end
|
|
33
35
|
def empty?; buff.empty? end
|
|
34
|
-
def done?; line_done? && !get_literal_size end
|
|
35
36
|
def done?; line_done? && !literal_size end
|
|
36
37
|
def line_done?; buff.end_with?(CRLF) end
|
|
37
38
|
|
|
38
39
|
def get_literal_size(buff)
|
|
39
|
-
buff.end_with?("}\r\n") && buff.rindex(/\{(\d+)\}\r\n\z/n) &&
|
|
40
|
+
buff.end_with?("}\r\n") && buff.rindex(/\{(\d+)\}\r\n\z/n) &&
|
|
41
|
+
coerce_number64($1)
|
|
42
|
+
rescue DataFormatError
|
|
43
|
+
raise DataFormatError, format("invalid response literal size (%s)", $1)
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
def read_line
|
|
@@ -77,6 +81,14 @@ module Net
|
|
|
77
81
|
)
|
|
78
82
|
end
|
|
79
83
|
|
|
84
|
+
# copied/adapted from NumValidator in v0.6
|
|
85
|
+
def coerce_number64(num)
|
|
86
|
+
int = num.to_i
|
|
87
|
+
return int if 0 <= int && int <= 0x7fff_ffff_ffff_ffff
|
|
88
|
+
raise DataFormatError,
|
|
89
|
+
"number64 must be unsigned 63-bit integer: #{num}"
|
|
90
|
+
end
|
|
91
|
+
|
|
80
92
|
end
|
|
81
93
|
end
|
|
82
94
|
end
|
data/lib/net/imap.rb
CHANGED
|
@@ -779,7 +779,7 @@ module Net
|
|
|
779
779
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
|
780
780
|
#
|
|
781
781
|
class IMAP < Protocol
|
|
782
|
-
VERSION = "0.4.
|
|
782
|
+
VERSION = "0.4.25"
|
|
783
783
|
|
|
784
784
|
# Aliases for supported capabilities, to be used with the #enable command.
|
|
785
785
|
ENABLE_ALIASES = {
|
|
@@ -1049,26 +1049,22 @@ module Net
|
|
|
1049
1049
|
|
|
1050
1050
|
# Disconnects from the server.
|
|
1051
1051
|
#
|
|
1052
|
+
# Waits for receiver thread to close before returning. Slow or stuck
|
|
1053
|
+
# response handlers can cause #disconnect to hang until they complete.
|
|
1054
|
+
#
|
|
1052
1055
|
# Related: #logout, #logout!
|
|
1053
1056
|
def disconnect
|
|
1054
1057
|
return if disconnected?
|
|
1058
|
+
in_receiver_thread = Thread.current == @receiver_thread
|
|
1055
1059
|
begin
|
|
1056
|
-
|
|
1057
|
-
# try to call SSL::SSLSocket#io.
|
|
1058
|
-
@sock.io.shutdown
|
|
1059
|
-
rescue NoMethodError
|
|
1060
|
-
# @sock is not an SSL::SSLSocket.
|
|
1061
|
-
@sock.shutdown
|
|
1062
|
-
end
|
|
1060
|
+
@sock.to_io.shutdown
|
|
1063
1061
|
rescue Errno::ENOTCONN
|
|
1064
1062
|
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
|
|
1065
1063
|
rescue Exception => e
|
|
1066
|
-
@receiver_thread.raise(e)
|
|
1067
|
-
end
|
|
1068
|
-
@receiver_thread.join
|
|
1069
|
-
synchronize do
|
|
1070
|
-
@sock.close
|
|
1064
|
+
@receiver_thread.raise(e) unless in_receiver_thread
|
|
1071
1065
|
end
|
|
1066
|
+
@sock.close
|
|
1067
|
+
@receiver_thread.join unless mon_owned? || in_receiver_thread
|
|
1072
1068
|
raise e if e
|
|
1073
1069
|
end
|
|
1074
1070
|
|
|
@@ -2095,6 +2091,8 @@ module Net
|
|
|
2095
2091
|
# string holding the entire search string, or a single-dimension array of
|
|
2096
2092
|
# search keywords and arguments.
|
|
2097
2093
|
#
|
|
2094
|
+
# <em>Please note</em> the warning (below) when +keys+ is a String.
|
|
2095
|
+
#
|
|
2098
2096
|
# Returns a SearchResult object. SearchResult inherits from Array (for
|
|
2099
2097
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
|
2100
2098
|
# capability has been enabled.
|
|
@@ -2104,8 +2102,8 @@ module Net
|
|
|
2104
2102
|
# ===== Search criteria
|
|
2105
2103
|
#
|
|
2106
2104
|
# >>>
|
|
2107
|
-
# When +
|
|
2108
|
-
# formatted. When +
|
|
2105
|
+
# When +keys+ is an Array, elements in the array will be validated and
|
|
2106
|
+
# formatted. When +keys+ is a String, it will be sent <em>with
|
|
2109
2107
|
# minimal validation and no encoding or formatting</em>.
|
|
2110
2108
|
#
|
|
2111
2109
|
# <em>*WARNING:* Although CRLF is prohibited, this is vulnerable to other
|
|
@@ -2176,7 +2174,8 @@ module Net
|
|
|
2176
2174
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
|
2177
2175
|
# capability has been enabled.
|
|
2178
2176
|
#
|
|
2179
|
-
# See #search for documentation of
|
|
2177
|
+
# See #search for documentation of parameters. <em>Please note</em> the
|
|
2178
|
+
# warning for when +keys+ is a String.
|
|
2180
2179
|
def uid_search(keys, charset = nil)
|
|
2181
2180
|
return search_internal("UID SEARCH", keys, charset)
|
|
2182
2181
|
end
|
|
@@ -2253,6 +2252,8 @@ module Net
|
|
|
2253
2252
|
# Similar to #fetch, but the +set+ parameter contains unique identifiers
|
|
2254
2253
|
# instead of message sequence numbers.
|
|
2255
2254
|
#
|
|
2255
|
+
# +attr+ behaves the same as with #fetch. <em>Please note</em> the #fetch
|
|
2256
|
+
# warning on the +attr+ argument.
|
|
2256
2257
|
# >>>
|
|
2257
2258
|
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
|
|
2258
2259
|
# part of any +FETCH+ response caused by a +UID+ command, regardless of
|
|
@@ -2405,8 +2406,10 @@ module Net
|
|
|
2405
2406
|
|
|
2406
2407
|
# Sends a {SORT command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2407
2408
|
# to search a mailbox for messages that match +search_keys+ and return an
|
|
2408
|
-
# array of message sequence numbers, sorted by +sort_keys+.
|
|
2409
|
-
#
|
|
2409
|
+
# array of message sequence numbers, sorted by +sort_keys+.
|
|
2410
|
+
#
|
|
2411
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2412
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2410
2413
|
#
|
|
2411
2414
|
#--
|
|
2412
2415
|
# TODO: describe +sort_keys+
|
|
@@ -2431,8 +2434,10 @@ module Net
|
|
|
2431
2434
|
|
|
2432
2435
|
# Sends a {UID SORT command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2433
2436
|
# to search a mailbox for messages that match +search_keys+ and return an
|
|
2434
|
-
# array of unique identifiers, sorted by +sort_keys+.
|
|
2435
|
-
#
|
|
2437
|
+
# array of unique identifiers, sorted by +sort_keys+.
|
|
2438
|
+
#
|
|
2439
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2440
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2436
2441
|
#
|
|
2437
2442
|
# Related: #sort, #search, #uid_search, #thread, #uid_thread
|
|
2438
2443
|
#
|
|
@@ -2446,8 +2451,10 @@ module Net
|
|
|
2446
2451
|
|
|
2447
2452
|
# Sends a {THREAD command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2448
2453
|
# to search a mailbox and return message sequence numbers in threaded
|
|
2449
|
-
# format, as a ThreadMember tree.
|
|
2450
|
-
#
|
|
2454
|
+
# format, as a ThreadMember tree.
|
|
2455
|
+
#
|
|
2456
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2457
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2451
2458
|
#
|
|
2452
2459
|
# The supported algorithms are:
|
|
2453
2460
|
#
|
|
@@ -2473,6 +2480,9 @@ module Net
|
|
|
2473
2480
|
# Similar to #thread, but returns unique identifiers instead of
|
|
2474
2481
|
# message sequence numbers.
|
|
2475
2482
|
#
|
|
2483
|
+
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2484
|
+
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2485
|
+
#
|
|
2476
2486
|
# Related: #thread, #search, #uid_search, #sort, #uid_sort
|
|
2477
2487
|
#
|
|
2478
2488
|
# ===== Capabilities
|
|
@@ -2560,10 +2570,11 @@ module Net
|
|
|
2560
2570
|
capabilities = capabilities
|
|
2561
2571
|
.flatten
|
|
2562
2572
|
.map {|e| ENABLE_ALIASES[e] || e }
|
|
2573
|
+
.flat_map { _1.is_a?(String) && !_1.empty? ? _1.split(/ /, -1) : [_1] }
|
|
2563
2574
|
.uniq
|
|
2564
|
-
.
|
|
2575
|
+
.map { Atom[_1] }
|
|
2565
2576
|
synchronize do
|
|
2566
|
-
send_command("ENABLE
|
|
2577
|
+
send_command("ENABLE", *capabilities)
|
|
2567
2578
|
result = clear_responses("ENABLED").last || []
|
|
2568
2579
|
@utf8_strings ||= result.include? "UTF8=ACCEPT"
|
|
2569
2580
|
@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.4.
|
|
4
|
+
version: 0.4.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shugo Maeda
|
|
@@ -125,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
125
|
- !ruby/object:Gem::Version
|
|
126
126
|
version: '0'
|
|
127
127
|
requirements: []
|
|
128
|
-
rubygems_version: 4.0.
|
|
128
|
+
rubygems_version: 4.0.10
|
|
129
129
|
specification_version: 4
|
|
130
130
|
summary: Ruby client api for Internet Message Access Protocol
|
|
131
131
|
test_files: []
|