net-imap 0.3.7 → 0.5.9
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/BSDL +22 -0
- data/COPYING +56 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +3 -22
- data/README.md +25 -8
- data/Rakefile +0 -7
- data/docs/styles.css +72 -23
- data/lib/net/imap/authenticators.rb +26 -57
- data/lib/net/imap/command_data.rb +74 -54
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +62 -0
- data/lib/net/imap/config.rb +552 -0
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +18 -6
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +142 -0
- data/lib/net/imap/errors.rb +60 -1
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +597 -0
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +249 -440
- data/lib/net/imap/response_parser/parser_utils.rb +245 -0
- data/lib/net/imap/response_parser.rb +1867 -1184
- data/lib/net/imap/response_reader.rb +73 -0
- data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
- data/lib/net/imap/sasl/authentication_exchange.rb +139 -0
- data/lib/net/imap/sasl/authenticators.rb +122 -0
- data/lib/net/imap/sasl/client_adapter.rb +123 -0
- data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +24 -14
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +342 -0
- data/lib/net/imap/sasl/external_authenticator.rb +83 -0
- data/lib/net/imap/sasl/gs2_header.rb +80 -0
- data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +28 -18
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
- data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
- data/lib/net/imap/sasl/protocol_adapters.rb +101 -0
- data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
- data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
- data/lib/net/imap/sasl/stringprep.rb +6 -66
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
- data/lib/net/imap/sasl.rb +148 -44
- data/lib/net/imap/sasl_adapter.rb +20 -0
- data/lib/net/imap/search_result.rb +146 -0
- data/lib/net/imap/sequence_set.rb +1871 -0
- data/lib/net/imap/stringprep/nameprep.rb +70 -0
- data/lib/net/imap/stringprep/saslprep.rb +69 -0
- data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
- data/lib/net/imap/stringprep/tables.rb +146 -0
- data/lib/net/imap/stringprep/trace.rb +85 -0
- data/lib/net/imap/stringprep.rb +159 -0
- data/lib/net/imap/uidplus_data.rb +244 -0
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +2335 -867
- data/net-imap.gemspec +7 -8
- data/rakelib/benchmarks.rake +91 -0
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/saslprep.rake +4 -4
- data/rakelib/string_prep_tables_generator.rb +86 -60
- data/sample/net-imap.rb +167 -0
- metadata +47 -49
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/test.yml +0 -38
- data/.gitignore +0 -10
- data/benchmarks/stringprep.yml +0 -65
- data/benchmarks/table-regexps.yml +0 -39
- data/lib/net/imap/authenticators/digest_md5.rb +0 -115
- data/lib/net/imap/authenticators/plain.rb +0 -41
- data/lib/net/imap/authenticators/xoauth2.rb +0 -20
- data/lib/net/imap/sasl/saslprep.rb +0 -55
- data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
- data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
data/lib/net/imap.rb
CHANGED
@@ -24,11 +24,9 @@ end
|
|
24
24
|
module Net
|
25
25
|
|
26
26
|
# Net::IMAP implements Internet Message Access Protocol (\IMAP) client
|
27
|
-
# functionality. The protocol is described
|
28
|
-
#
|
29
|
-
|
30
|
-
# TODO: and [IMAP4rev2[https://tools.ietf.org/html/rfc9051]].
|
31
|
-
#++
|
27
|
+
# functionality. The protocol is described
|
28
|
+
# in {IMAP4rev1 [RFC3501]}[https://www.rfc-editor.org/rfc/rfc3501]
|
29
|
+
# and {IMAP4rev2 [RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051].
|
32
30
|
#
|
33
31
|
# == \IMAP Overview
|
34
32
|
#
|
@@ -45,10 +43,18 @@ module Net
|
|
45
43
|
# To work on the messages within a mailbox, the client must
|
46
44
|
# first select that mailbox, using either #select or #examine
|
47
45
|
# (for read-only access). Once the client has successfully
|
48
|
-
# selected a mailbox, they enter the
|
46
|
+
# selected a mailbox, they enter the +selected+ state, and that
|
49
47
|
# mailbox becomes the _current_ mailbox, on which mail-item
|
50
48
|
# related commands implicitly operate.
|
51
49
|
#
|
50
|
+
# === Connection state
|
51
|
+
#
|
52
|
+
# Once an IMAP connection is established, the connection is in one of four
|
53
|
+
# states: <tt>not authenticated</tt>, +authenticated+, +selected+, and
|
54
|
+
# +logout+. Most commands are valid only in certain states.
|
55
|
+
#
|
56
|
+
# See #connection_state.
|
57
|
+
#
|
52
58
|
# === Sequence numbers and UIDs
|
53
59
|
#
|
54
60
|
# Messages have two sorts of identifiers: message sequence
|
@@ -77,31 +83,22 @@ module Net
|
|
77
83
|
# UIDs have to be reassigned. An \IMAP client thus cannot
|
78
84
|
# rearrange message orders.
|
79
85
|
#
|
80
|
-
# ===
|
81
|
-
#
|
82
|
-
# Net::IMAP <em>does not modify its behavior</em> according to server
|
83
|
-
# #capability. Users of the class must check for required capabilities before
|
84
|
-
# issuing commands. Special care should be taken to follow all #capability
|
85
|
-
# requirements for #starttls, #login, and #authenticate.
|
86
|
-
#
|
87
|
-
# See the #capability method for more information.
|
86
|
+
# === Examples of Usage
|
88
87
|
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# === List sender and subject of all recent messages in the default mailbox
|
88
|
+
# ==== List sender and subject of all recent messages in the default mailbox
|
92
89
|
#
|
93
90
|
# imap = Net::IMAP.new('mail.example.com')
|
94
|
-
# imap.authenticate('
|
91
|
+
# imap.authenticate('PLAIN', 'joe_user', 'joes_password')
|
95
92
|
# imap.examine('INBOX')
|
96
93
|
# imap.search(["RECENT"]).each do |message_id|
|
97
94
|
# envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
|
98
95
|
# puts "#{envelope.from[0].name}: \t#{envelope.subject}"
|
99
96
|
# end
|
100
97
|
#
|
101
|
-
#
|
98
|
+
# ==== Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
|
102
99
|
#
|
103
100
|
# imap = Net::IMAP.new('mail.example.com')
|
104
|
-
# imap.authenticate('
|
101
|
+
# imap.authenticate('PLAIN', 'joe_user', 'joes_password')
|
105
102
|
# imap.select('Mail/sent-mail')
|
106
103
|
# if not imap.list('Mail/', 'sent-apr03')
|
107
104
|
# imap.create('Mail/sent-apr03')
|
@@ -112,12 +109,96 @@ module Net
|
|
112
109
|
# end
|
113
110
|
# imap.expunge
|
114
111
|
#
|
112
|
+
# == Capabilities
|
113
|
+
#
|
114
|
+
# Most Net::IMAP methods do not _currently_ modify their behaviour according
|
115
|
+
# to the server's advertised #capabilities. Users of this class must check
|
116
|
+
# that the server is capable of extension commands or command arguments before
|
117
|
+
# sending them. Special care should be taken to follow the #capabilities
|
118
|
+
# requirements for #starttls, #login, and #authenticate.
|
119
|
+
#
|
120
|
+
# See #capable?, #auth_capable?, #capabilities, #auth_mechanisms to discover
|
121
|
+
# server capabilities. For relevant capability requirements, see the
|
122
|
+
# documentation on each \IMAP command.
|
123
|
+
#
|
124
|
+
# imap = Net::IMAP.new("mail.example.com")
|
125
|
+
# imap.capable?(:IMAP4rev1) or raise "Not an IMAP4rev1 server"
|
126
|
+
# imap.capable?(:starttls) or raise "Cannot start TLS"
|
127
|
+
# imap.starttls
|
128
|
+
#
|
129
|
+
# if imap.auth_capable?("PLAIN")
|
130
|
+
# imap.authenticate "PLAIN", username, password
|
131
|
+
# elsif !imap.capability?("LOGINDISABLED")
|
132
|
+
# imap.login username, password
|
133
|
+
# else
|
134
|
+
# raise "No acceptable authentication mechanisms"
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# # Support for "UTF8=ACCEPT" implies support for "ENABLE"
|
138
|
+
# imap.enable :utf8 if imap.capable?("UTF8=ACCEPT")
|
139
|
+
#
|
140
|
+
# namespaces = imap.namespace if imap.capable?(:namespace)
|
141
|
+
# mbox_prefix = namespaces&.personal&.first&.prefix || ""
|
142
|
+
# mbox_delim = namespaces&.personal&.first&.delim || "/"
|
143
|
+
# mbox_path = prefix + %w[path to my mailbox].join(delim)
|
144
|
+
# imap.create mbox_path
|
145
|
+
#
|
146
|
+
# === Basic IMAP4rev1 capabilities
|
147
|
+
#
|
148
|
+
# IMAP4rev1 servers must advertise +IMAP4rev1+ in their capabilities list.
|
149
|
+
# IMAP4rev1 servers must _implement_ the +STARTTLS+, <tt>AUTH=PLAIN</tt>,
|
150
|
+
# and +LOGINDISABLED+ capabilities. See #starttls, #login, and #authenticate
|
151
|
+
# for the implications of these capabilities.
|
152
|
+
#
|
153
|
+
# === Caching +CAPABILITY+ responses
|
154
|
+
#
|
155
|
+
# Net::IMAP automatically stores and discards capability data according to the
|
156
|
+
# the requirements and recommendations in
|
157
|
+
# {IMAP4rev2 §6.1.1}[https://www.rfc-editor.org/rfc/rfc9051#section-6.1.1],
|
158
|
+
# {§6.2}[https://www.rfc-editor.org/rfc/rfc9051#section-6.2], and
|
159
|
+
# {§7.1}[https://www.rfc-editor.org/rfc/rfc9051#section-7.1].
|
160
|
+
# Use #capable?, #auth_capable?, or #capabilities to use this cache and avoid
|
161
|
+
# sending the #capability command unnecessarily.
|
162
|
+
#
|
163
|
+
# The server may advertise its initial capabilities using the +CAPABILITY+
|
164
|
+
# ResponseCode in a +PREAUTH+ or +OK+ #greeting. When TLS has started
|
165
|
+
# (#starttls) and after authentication (#login or #authenticate), the server's
|
166
|
+
# capabilities may change and cached capabilities are discarded. The server
|
167
|
+
# may send updated capabilities with an +OK+ TaggedResponse to #login or
|
168
|
+
# #authenticate, and these will be cached by Net::IMAP. But the
|
169
|
+
# TaggedResponse to #starttls MUST be ignored--it is sent before TLS starts
|
170
|
+
# and is unprotected.
|
171
|
+
#
|
172
|
+
# When storing capability values to variables, be careful that they are
|
173
|
+
# discarded or reset appropriately, especially following #starttls.
|
174
|
+
#
|
175
|
+
# === Using IMAP4rev1 extensions
|
176
|
+
#
|
177
|
+
# See the {IANA IMAP4 capabilities
|
178
|
+
# registry}[http://www.iana.org/assignments/imap4-capabilities] for a list of
|
179
|
+
# all standard capabilities, and their reference RFCs.
|
180
|
+
#
|
181
|
+
# IMAP4rev1 servers must not activate behavior that is incompatible with the
|
182
|
+
# base specification until an explicit client action invokes a capability,
|
183
|
+
# e.g. sending a command or command argument specific to that capability.
|
184
|
+
# Servers may send data with backward compatible behavior, such as response
|
185
|
+
# codes or mailbox attributes, at any time without client action.
|
186
|
+
#
|
187
|
+
# Invoking capabilities which are unknown to Net::IMAP may cause unexpected
|
188
|
+
# behavior and errors. For example, ResponseParseError is raised when
|
189
|
+
# unknown response syntax is received. Invoking commands or command
|
190
|
+
# parameters that are unsupported by the server may raise NoResponseError,
|
191
|
+
# BadResponseError, or cause other unexpected behavior.
|
192
|
+
#
|
193
|
+
# Some capabilities must be explicitly activated using the #enable command.
|
194
|
+
# See #enable for details.
|
195
|
+
#
|
115
196
|
# == Thread Safety
|
116
197
|
#
|
117
198
|
# Net::IMAP supports concurrent threads. For example,
|
118
199
|
#
|
119
200
|
# imap = Net::IMAP.new("imap.foo.net", "imap2")
|
120
|
-
# imap.authenticate("
|
201
|
+
# imap.authenticate("scram-md5", "bar", "password")
|
121
202
|
# imap.select("inbox")
|
122
203
|
# fetch_thread = Thread.start { imap.fetch(1..-1, "UID") }
|
123
204
|
# search_result = imap.search(["BODY", "hello"])
|
@@ -126,6 +207,42 @@ module Net
|
|
126
207
|
#
|
127
208
|
# This script invokes the FETCH command and the SEARCH command concurrently.
|
128
209
|
#
|
210
|
+
# When running multiple commands, care must be taken to avoid ambiguity. For
|
211
|
+
# example, SEARCH responses are ambiguous about which command they are
|
212
|
+
# responding to, so search commands should not run simultaneously, unless the
|
213
|
+
# server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
|
214
|
+
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
|
215
|
+
# §5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
|
216
|
+
# other examples of command sequences which should not be pipelined.
|
217
|
+
#
|
218
|
+
# == Unbounded memory use
|
219
|
+
#
|
220
|
+
# Net::IMAP reads server responses in a separate receiver thread per client.
|
221
|
+
# Unhandled response data is saved to #responses, and response_handlers run
|
222
|
+
# inside the receiver thread. See the list of methods for {handling server
|
223
|
+
# responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
|
224
|
+
#
|
225
|
+
# Because the receiver thread continuously reads and saves new responses, some
|
226
|
+
# scenarios must be careful to avoid unbounded memory use:
|
227
|
+
#
|
228
|
+
# * Commands such as #list or #fetch can have an enormous number of responses.
|
229
|
+
# * Commands such as #fetch can result in an enormous size per response.
|
230
|
+
# * Long-lived connections will gradually accumulate unsolicited server
|
231
|
+
# responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
|
232
|
+
# * A buggy or untrusted server could send inappropriate responses, which
|
233
|
+
# could be very numerous, very large, and very rapid.
|
234
|
+
#
|
235
|
+
# Use paginated or limited versions of commands whenever possible.
|
236
|
+
#
|
237
|
+
# Use Config#max_response_size to impose a limit on incoming server responses
|
238
|
+
# as they are being read. <em>This is especially important for untrusted
|
239
|
+
# servers.</em>
|
240
|
+
#
|
241
|
+
# Use #add_response_handler to handle responses after each one is received.
|
242
|
+
# Use the +response_handlers+ argument to ::new to assign response handlers
|
243
|
+
# before the receiver thread is started. Use #extract_responses,
|
244
|
+
# #clear_responses, or #responses (with a block) to prune responses.
|
245
|
+
#
|
129
246
|
# == Errors
|
130
247
|
#
|
131
248
|
# An \IMAP server can send three different types of responses to indicate
|
@@ -173,97 +290,108 @@ module Net
|
|
173
290
|
# == What's here?
|
174
291
|
#
|
175
292
|
# * {Connection control}[rdoc-ref:Net::IMAP@Connection+control+methods]
|
176
|
-
# * {
|
177
|
-
# * {...for any state}[rdoc-ref:Net::IMAP@IMAP+commands+for+any+state]
|
178
|
-
# * {...for the "not authenticated" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Not+Authenticated-22+state]
|
179
|
-
# * {...for the "authenticated" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Authenticated-22+state]
|
180
|
-
# * {...for the "selected" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Selected-22+state]
|
181
|
-
# * {...for the "logout" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Logout-22+state]
|
182
|
-
# * {Supported IMAP extensions}[rdoc-ref:Net::IMAP@Supported+IMAP+extensions]
|
293
|
+
# * {Server capabilities}[rdoc-ref:Net::IMAP@Server+capabilities]
|
183
294
|
# * {Handling server responses}[rdoc-ref:Net::IMAP@Handling+server+responses]
|
295
|
+
# * {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands]
|
296
|
+
# * {for any state}[rdoc-ref:Net::IMAP@Any+state]
|
297
|
+
# * {for the "not authenticated" state}[rdoc-ref:Net::IMAP@Not+Authenticated+state]
|
298
|
+
# * {for the "authenticated" state}[rdoc-ref:Net::IMAP@Authenticated+state]
|
299
|
+
# * {for the "selected" state}[rdoc-ref:Net::IMAP@Selected+state]
|
300
|
+
# * {for the "logout" state}[rdoc-ref:Net::IMAP@Logout+state]
|
301
|
+
# * {IMAP extension support}[rdoc-ref:Net::IMAP@IMAP+extension+support]
|
184
302
|
#
|
185
303
|
# === Connection control methods
|
186
304
|
#
|
187
|
-
# - Net::IMAP.new:
|
188
|
-
# successful server greeting before
|
305
|
+
# - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
|
306
|
+
# waits for a successful server greeting before the method returns.
|
307
|
+
# - #connection_state: Returns the connection state.
|
189
308
|
# - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
|
190
|
-
# - #logout: Tells the server to end the session.
|
309
|
+
# - #logout: Tells the server to end the session. Enters the +logout+ state.
|
191
310
|
# - #disconnect: Disconnects the connection (without sending #logout first).
|
192
311
|
# - #disconnected?: True if the connection has been closed.
|
193
312
|
#
|
313
|
+
# === Server capabilities
|
314
|
+
#
|
315
|
+
# - #capable?: Returns whether the server supports a given capability.
|
316
|
+
# - #capabilities: Returns the server's capabilities as an array of strings.
|
317
|
+
# - #auth_capable?: Returns whether the server advertises support for a given
|
318
|
+
# SASL mechanism, for use with #authenticate.
|
319
|
+
# - #auth_mechanisms: Returns the #authenticate SASL mechanisms which
|
320
|
+
# the server claims to support as an array of strings.
|
321
|
+
# - #clear_cached_capabilities: Clears cached capabilities.
|
322
|
+
#
|
323
|
+
# <em>The capabilities cache is automatically cleared after completing
|
324
|
+
# #starttls, #login, or #authenticate.</em>
|
325
|
+
# - #capability: Sends the +CAPABILITY+ command and returns the #capabilities.
|
326
|
+
#
|
327
|
+
# <em>In general, #capable? should be used rather than explicitly sending a
|
328
|
+
# +CAPABILITY+ command to the server.</em>
|
329
|
+
#
|
330
|
+
# === Handling server responses
|
331
|
+
#
|
332
|
+
# - #greeting: The server's initial untagged response, which can indicate a
|
333
|
+
# pre-authenticated connection.
|
334
|
+
# - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
|
335
|
+
# ResponseCode#data.
|
336
|
+
# - #extract_responses: Removes and returns the responses for which the block
|
337
|
+
# returns a true value.
|
338
|
+
# - #clear_responses: Deletes unhandled data from #responses and returns it.
|
339
|
+
# - #add_response_handler: Add a block to be called inside the receiver thread
|
340
|
+
# with every server response.
|
341
|
+
# - #response_handlers: Returns the list of response handlers.
|
342
|
+
# - #remove_response_handler: Remove a previously added response handler.
|
343
|
+
#
|
194
344
|
# === Core \IMAP commands
|
195
345
|
#
|
196
346
|
# The following commands are defined either by
|
197
|
-
# the [IMAP4rev1[https://
|
347
|
+
# the [IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501]] base specification, or
|
198
348
|
# by one of the following extensions:
|
199
|
-
# [IDLE[https://
|
200
|
-
# [NAMESPACE[https://
|
201
|
-
# [UNSELECT[https://
|
202
|
-
|
203
|
-
#
|
204
|
-
# TODO: [LIST-EXTENDED[https://tools.ietf.org/html/rfc5258]],
|
205
|
-
# TODO: [LIST-STATUS[https://tools.ietf.org/html/rfc5819]],
|
206
|
-
#++
|
207
|
-
# [MOVE[https://tools.ietf.org/html/rfc6851]].
|
349
|
+
# [IDLE[https://www.rfc-editor.org/rfc/rfc2177]],
|
350
|
+
# [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]],
|
351
|
+
# [UNSELECT[https://www.rfc-editor.org/rfc/rfc3691]],
|
352
|
+
# [ENABLE[https://www.rfc-editor.org/rfc/rfc5161]],
|
353
|
+
# [MOVE[https://www.rfc-editor.org/rfc/rfc6851]].
|
208
354
|
# These extensions are widely supported by modern IMAP4rev1 servers and have
|
209
|
-
# all been integrated into [IMAP4rev2[https://
|
210
|
-
# <em
|
211
|
-
#
|
212
|
-
|
213
|
-
# TODO: When IMAP4rev2 is supported, add the following to the each of the
|
214
|
-
# appropriate commands below.
|
215
|
-
# Note:: CHECK has been removed from IMAP4rev2.
|
216
|
-
# Note:: LSUB is obsoleted by +LIST-EXTENDED and has been removed from IMAP4rev2.
|
217
|
-
# <em>Some arguments require the +LIST-EXTENDED+ or +IMAP4rev2+ capability.</em>
|
218
|
-
# <em>Requires either the +ENABLE+ or +IMAP4rev2+ capability.</em>
|
219
|
-
# <em>Requires either the +NAMESPACE+ or +IMAP4rev2+ capability.</em>
|
220
|
-
# <em>Requires either the +IDLE+ or +IMAP4rev2+ capability.</em>
|
221
|
-
# <em>Requires either the +UNSELECT+ or +IMAP4rev2+ capability.</em>
|
222
|
-
# <em>Requires either the +UIDPLUS+ or +IMAP4rev2+ capability.</em>
|
223
|
-
# <em>Requires either the +MOVE+ or +IMAP4rev2+ capability.</em>
|
224
|
-
#++
|
225
|
-
#
|
226
|
-
# ==== \IMAP commands for any state
|
355
|
+
# all been integrated into [IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]].
|
356
|
+
# <em>*NOTE:* Net::IMAP doesn't support IMAP4rev2 yet.</em>
|
357
|
+
#
|
358
|
+
# ==== Any state
|
227
359
|
#
|
228
360
|
# - #capability: Returns the server's capabilities as an array of strings.
|
229
361
|
#
|
230
|
-
# <em>
|
231
|
-
#
|
362
|
+
# <em>In general, #capable? should be used rather than explicitly sending a
|
363
|
+
# +CAPABILITY+ command to the server.</em>
|
232
364
|
# - #noop: Allows the server to send unsolicited untagged #responses.
|
233
|
-
# - #logout: Tells the server to end the session. Enters the
|
365
|
+
# - #logout: Tells the server to end the session. Enters the +logout+ state.
|
234
366
|
#
|
235
|
-
# ====
|
367
|
+
# ==== Not Authenticated state
|
236
368
|
#
|
237
|
-
# In addition to the
|
238
|
-
# the
|
369
|
+
# In addition to the commands for any state, the following commands are valid
|
370
|
+
# in the +not_authenticated+ state:
|
239
371
|
#
|
240
372
|
# - #starttls: Upgrades a clear-text connection to use TLS.
|
241
373
|
#
|
242
374
|
# <em>Requires the +STARTTLS+ capability.</em>
|
243
|
-
# - #authenticate: Identifies the client to the server using
|
244
|
-
# mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
245
|
-
# Enters the
|
375
|
+
# - #authenticate: Identifies the client to the server using the given
|
376
|
+
# {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
377
|
+
# and credentials. Enters the +authenticated+ state.
|
246
378
|
#
|
247
|
-
# <em>
|
248
|
-
#
|
379
|
+
# <em>The server should list <tt>"AUTH=#{mechanism}"</tt> capabilities for
|
380
|
+
# supported mechanisms.</em>
|
249
381
|
# - #login: Identifies the client to the server using a plain text password.
|
250
|
-
# Using #authenticate is
|
251
|
-
# state.
|
382
|
+
# Using #authenticate is preferred. Enters the +authenticated+ state.
|
252
383
|
#
|
253
384
|
# <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
|
254
385
|
#
|
255
|
-
# ====
|
386
|
+
# ==== Authenticated state
|
256
387
|
#
|
257
|
-
# In addition to the
|
258
|
-
# the
|
388
|
+
# In addition to the commands for any state, the following commands are valid
|
389
|
+
# in the +authenticated+ state:
|
259
390
|
#
|
260
|
-
|
261
|
-
#
|
262
|
-
#
|
263
|
-
#
|
264
|
-
#++
|
265
|
-
# - #select: Open a mailbox and enter the "_selected_" state.
|
266
|
-
# - #examine: Open a mailbox read-only, and enter the "_selected_" state.
|
391
|
+
# - #enable: Enables backwards incompatible server extensions.
|
392
|
+
# <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
|
393
|
+
# - #select: Open a mailbox and enter the +selected+ state.
|
394
|
+
# - #examine: Open a mailbox read-only, and enter the +selected+ state.
|
267
395
|
# - #create: Creates a new mailbox.
|
268
396
|
# - #delete: Permanently remove a mailbox.
|
269
397
|
# - #rename: Change the name of a mailbox.
|
@@ -271,37 +399,31 @@ module Net
|
|
271
399
|
# - #unsubscribe: Removes a mailbox from the "subscribed" set.
|
272
400
|
# - #list: Returns names and attributes of mailboxes matching a given pattern.
|
273
401
|
# - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
|
274
|
-
#
|
275
|
-
# <em>Requires the +NAMESPACE+ capability.</em>
|
402
|
+
# <em>Requires the +NAMESPACE+ or +IMAP4rev2+ capability.</em>
|
276
403
|
# - #status: Returns mailbox information, e.g. message count, unseen message
|
277
404
|
# count, +UIDVALIDITY+ and +UIDNEXT+.
|
278
405
|
# - #append: Appends a message to the end of a mailbox.
|
279
406
|
# - #idle: Allows the server to send updates to the client, without the client
|
280
407
|
# needing to poll using #noop.
|
408
|
+
# <em>Requires the +IDLE+ or +IMAP4rev2+ capability.</em>
|
409
|
+
# - *Obsolete* #lsub: <em>Replaced by <tt>LIST-EXTENDED</tt> and removed from
|
410
|
+
# +IMAP4rev2+.</em> Lists mailboxes in the "subscribed" set.
|
281
411
|
#
|
282
|
-
# <em
|
283
|
-
# - #lsub: Lists mailboxes the user has declared "active" or "subscribed".
|
284
|
-
#--
|
285
|
-
# <em>Replaced by</em> <tt>LIST-EXTENDED</tt> <em>and removed from</em>
|
286
|
-
# +IMAP4rev2+. <em>However, Net::IMAP hasn't implemented</em>
|
287
|
-
# <tt>LIST-EXTENDED</tt> _yet_.
|
288
|
-
#++
|
412
|
+
# <em>*Note:* Net::IMAP hasn't implemented <tt>LIST-EXTENDED</tt> yet.</em>
|
289
413
|
#
|
290
|
-
# ====
|
414
|
+
# ==== Selected state
|
291
415
|
#
|
292
|
-
# In addition to the
|
293
|
-
# following commands are valid in the
|
416
|
+
# In addition to the commands for any state and the +authenticated+
|
417
|
+
# commands, the following commands are valid in the +selected+ state:
|
294
418
|
#
|
295
|
-
# - #close: Closes the mailbox and returns to the
|
419
|
+
# - #close: Closes the mailbox and returns to the +authenticated+ state,
|
296
420
|
# expunging deleted messages, unless the mailbox was opened as read-only.
|
297
|
-
# - #unselect: Closes the mailbox and returns to the
|
421
|
+
# - #unselect: Closes the mailbox and returns to the +authenticated+ state,
|
298
422
|
# without expunging any messages.
|
299
|
-
#
|
300
|
-
# <em>Requires the +UNSELECT+ capability.</em>
|
423
|
+
# <em>Requires the +UNSELECT+ or +IMAP4rev2+ capability.</em>
|
301
424
|
# - #expunge: Permanently removes messages which have the Deleted flag set.
|
302
|
-
# - #uid_expunge: Restricts
|
303
|
-
#
|
304
|
-
# <em>Requires the +UIDPLUS+ capability.</em>
|
425
|
+
# - #uid_expunge: Restricts expunge to only remove the specified UIDs.
|
426
|
+
# <em>Requires the +UIDPLUS+ or +IMAP4rev2+ capability.</em>
|
305
427
|
# - #search, #uid_search: Returns sequence numbers or UIDs of messages that
|
306
428
|
# match the given searching criteria.
|
307
429
|
# - #fetch, #uid_fetch: Returns data associated with a set of messages,
|
@@ -311,45 +433,33 @@ module Net
|
|
311
433
|
# specified destination mailbox.
|
312
434
|
# - #move, #uid_move: Moves the specified messages to the end of the
|
313
435
|
# specified destination mailbox, expunging them from the current mailbox.
|
436
|
+
# <em>Requires the +MOVE+ or +IMAP4rev2+ capability.</em>
|
437
|
+
# - #check: <em>*Obsolete:* removed from +IMAP4rev2+.</em>
|
438
|
+
# Can be replaced with #noop or #idle.
|
314
439
|
#
|
315
|
-
#
|
316
|
-
# - #check: Mostly obsolete. Can be replaced with #noop or #idle.
|
317
|
-
#--
|
318
|
-
# <em>Removed from IMAP4rev2.</em>
|
319
|
-
#++
|
320
|
-
#
|
321
|
-
# ==== \IMAP commands for the "Logout" state
|
440
|
+
# ==== Logout state
|
322
441
|
#
|
323
442
|
# No \IMAP commands are valid in the +logout+ state. If the socket is still
|
324
443
|
# open, Net::IMAP will close it after receiving server confirmation.
|
325
444
|
# Exceptions will be raised by \IMAP commands that have already started and
|
326
445
|
# are waiting for a response, as well as any that are called after logout.
|
327
446
|
#
|
328
|
-
# ===
|
447
|
+
# === \IMAP extension support
|
329
448
|
#
|
330
449
|
# ==== RFC9051: +IMAP4rev2+
|
331
450
|
#
|
332
|
-
# Although IMAP4rev2[https://
|
333
|
-
# yet
|
334
|
-
#
|
335
|
-
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
343
|
-
#
|
344
|
-
# TODO: +BINARY+ (only the FETCH side)
|
345
|
-
# TODO: +SPECIAL-USE+
|
346
|
-
# implicitly supported, but we can do better: Response codes: RFC5530, etc
|
347
|
-
# implicitly supported, but we can do better: <tt>STATUS=SIZE</tt>
|
348
|
-
# implicitly supported, but we can do better: <tt>STATUS DELETED</tt>
|
349
|
-
#++
|
350
|
-
# Commands for these extensions are included with the {Core IMAP
|
351
|
-
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above. Other supported
|
352
|
-
# extensons are listed below.
|
451
|
+
# Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not supported
|
452
|
+
# yet, Net::IMAP supports several extensions that have been folded into it:
|
453
|
+
# +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+,
|
454
|
+
# <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
|
455
|
+
# Commands for these extensions are listed with the {Core IMAP
|
456
|
+
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
|
457
|
+
#
|
458
|
+
# >>>
|
459
|
+
# <em>The following are folded into +IMAP4rev2+ but are currently
|
460
|
+
# unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
|
461
|
+
# extensions, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
|
462
|
+
# +LITERAL-+, and +SPECIAL-USE+.</em>
|
353
463
|
#
|
354
464
|
# ==== RFC2087: +QUOTA+
|
355
465
|
# - #getquota: returns the resource usage and limits for a quota root
|
@@ -358,92 +468,60 @@ module Net
|
|
358
468
|
# - #setquota: sets the resource limits for a given quota root.
|
359
469
|
#
|
360
470
|
# ==== RFC2177: +IDLE+
|
361
|
-
# Folded into IMAP4rev2[https://
|
362
|
-
#
|
471
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
472
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
363
473
|
# - #idle: Allows the server to send updates to the client, without the client
|
364
474
|
# needing to poll using #noop.
|
365
475
|
#
|
366
476
|
# ==== RFC2342: +NAMESPACE+
|
367
|
-
# Folded into IMAP4rev2[https://
|
368
|
-
#
|
477
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
478
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
369
479
|
# - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
|
370
480
|
#
|
371
481
|
# ==== RFC2971: +ID+
|
372
482
|
# - #id: exchanges client and server implementation information.
|
373
483
|
#
|
374
|
-
#--
|
375
|
-
# ==== RFC3502: +MULTIAPPEND+
|
376
|
-
# TODO...
|
377
|
-
#++
|
378
|
-
#
|
379
|
-
#--
|
380
484
|
# ==== RFC3516: +BINARY+
|
381
|
-
#
|
382
|
-
|
485
|
+
# The fetch side of +BINARY+ has been folded into
|
486
|
+
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
|
487
|
+
# - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
|
488
|
+
# +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
|
489
|
+
#
|
490
|
+
# >>>
|
491
|
+
# *NOTE:* The binary extension the #append command is _not_ supported yet.
|
383
492
|
#
|
384
493
|
# ==== RFC3691: +UNSELECT+
|
385
|
-
# Folded into IMAP4rev2[https://
|
386
|
-
#
|
387
|
-
# - #unselect: Closes the mailbox and returns to the
|
494
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
495
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
496
|
+
# - #unselect: Closes the mailbox and returns to the +authenticated+ state,
|
388
497
|
# without expunging any messages.
|
389
498
|
#
|
390
499
|
# ==== RFC4314: +ACL+
|
391
500
|
# - #getacl: lists the authenticated user's access rights to a mailbox.
|
392
501
|
# - #setacl: sets the access rights for a user on a mailbox
|
393
|
-
|
394
|
-
#
|
395
|
-
#++
|
396
|
-
# - *_Note:_* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
|
502
|
+
# >>>
|
503
|
+
# *NOTE:* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
|
397
504
|
#
|
398
505
|
# ==== RFC4315: +UIDPLUS+
|
399
|
-
# Folded into IMAP4rev2[https://
|
400
|
-
#
|
506
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
507
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
401
508
|
# - #uid_expunge: Restricts #expunge to only remove the specified UIDs.
|
402
509
|
# - Updates #select, #examine with the +UIDNOTSTICKY+ ResponseCode
|
403
510
|
# - Updates #append with the +APPENDUID+ ResponseCode
|
404
511
|
# - Updates #copy, #move with the +COPYUID+ ResponseCode
|
405
512
|
#
|
406
|
-
|
407
|
-
#
|
408
|
-
#
|
409
|
-
#
|
410
|
-
# the protocol to enable new optional parameters to many commands: #select,
|
411
|
-
# #examine, #create, #rename, #fetch, #uid_fetch, #store, #uid_store, #search,
|
412
|
-
# #uid_search, and #append. However, specific parameters are not defined.
|
413
|
-
# Extensions to these commands use this syntax whenever possible. Net::IMAP
|
414
|
-
# may be partially compatible with extensions to these commands, even without
|
415
|
-
# any explicit support.
|
416
|
-
#++
|
417
|
-
#
|
418
|
-
#--
|
419
|
-
# ==== RFC4731 +ESEARCH+
|
420
|
-
# TODO...
|
421
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
422
|
-
# - Updates #search, #uid_search to accept result options: +MIN+, +MAX+,
|
423
|
-
# +ALL+, +COUNT+, and to return ExtendedSearchData.
|
424
|
-
#++
|
425
|
-
#
|
426
|
-
#--
|
513
|
+
# ==== RFC4731: +ESEARCH+
|
514
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
|
515
|
+
# - Updates #search, #uid_search with +return+ options and ESearchResult.
|
516
|
+
#
|
427
517
|
# ==== RFC4959: +SASL-IR+
|
428
|
-
#
|
429
|
-
#
|
430
|
-
#
|
431
|
-
|
432
|
-
#
|
433
|
-
|
434
|
-
#
|
435
|
-
# TODO...
|
436
|
-
#++
|
437
|
-
#
|
438
|
-
#--
|
439
|
-
# ==== RFC5182 +SEARCHRES+
|
440
|
-
# TODO...
|
441
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
442
|
-
# - Updates #search, #uid_search with the +SAVE+ result option.
|
443
|
-
# - Updates #copy, #uid_copy, #fetch, #uid_fetch, #move, #uid_move, #search,
|
444
|
-
# #uid_search, #store, #uid_store, and #uid_expunge with ability to
|
445
|
-
# reference the saved result of a previous #search or #uid_search command.
|
446
|
-
#++
|
518
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
|
519
|
+
# - Updates #authenticate with the option to send an initial response.
|
520
|
+
#
|
521
|
+
# ==== RFC5161: +ENABLE+
|
522
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
523
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
524
|
+
# - #enable: Enables backwards incompatible server extensions.
|
447
525
|
#
|
448
526
|
# ==== RFC5256: +SORT+
|
449
527
|
# - #sort, #uid_sort: An alternate version of #search or #uid_search which
|
@@ -453,75 +531,65 @@ module Net
|
|
453
531
|
# which arranges the results into ordered groups or threads according to a
|
454
532
|
# chosen algorithm.
|
455
533
|
#
|
456
|
-
|
457
|
-
#
|
458
|
-
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
462
|
-
# even without any explicit support.
|
463
|
-
# - Updates #list to accept selection options: +SUBSCRIBED+, +REMOTE+, and
|
464
|
-
# +RECURSIVEMATCH+, and return options: +SUBSCRIBED+ and +CHILDREN+.
|
465
|
-
#++
|
466
|
-
#
|
467
|
-
#--
|
468
|
-
# ==== RFC5819 +LIST-STATUS+
|
469
|
-
# TODO...
|
470
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
471
|
-
# - Updates #list with +STATUS+ return option.
|
472
|
-
#++
|
473
|
-
#
|
474
|
-
# ==== +XLIST+ (non-standard, deprecated)
|
534
|
+
# ==== +X-GM-EXT-1+
|
535
|
+
# +X-GM-EXT-1+ is a non-standard Gmail extension. See {Google's
|
536
|
+
# documentation}[https://developers.google.com/gmail/imap/imap-extensions].
|
537
|
+
# - Updates #fetch and #uid_fetch with support for +X-GM-MSGID+ (unique
|
538
|
+
# message ID), +X-GM-THRID+ (thread ID), and +X-GM-LABELS+ (Gmail labels).
|
539
|
+
# - Updates #search with the +X-GM-RAW+ search attribute.
|
475
540
|
# - #xlist: replaced by +SPECIAL-USE+ attributes in #list responses.
|
476
541
|
#
|
477
|
-
|
478
|
-
#
|
479
|
-
# TODO...
|
480
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
481
|
-
# - Updates #list with the +SPECIAL-USE+ selection and return options.
|
482
|
-
#++
|
542
|
+
# *NOTE:* The +OBJECTID+ extension should replace +X-GM-MSGID+ and
|
543
|
+
# +X-GM-THRID+, but Gmail does not support it (as of 2023-11-10).
|
483
544
|
#
|
484
545
|
# ==== RFC6851: +MOVE+
|
485
|
-
# Folded into IMAP4rev2[https://
|
486
|
-
#
|
546
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
547
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
487
548
|
# - #move, #uid_move: Moves the specified messages to the end of the
|
488
549
|
# specified destination mailbox, expunging them from the current mailbox.
|
489
550
|
#
|
490
|
-
|
491
|
-
# ==== RFC6855: UTF8=ACCEPT
|
492
|
-
# TODO...
|
493
|
-
# ==== RFC6855: UTF8=ONLY
|
494
|
-
# TODO...
|
495
|
-
#++
|
496
|
-
#
|
497
|
-
#--
|
498
|
-
# ==== RFC7888: <tt>LITERAL+</tt>, +LITERAL-+
|
499
|
-
# TODO...
|
500
|
-
# ==== RFC7162: +QRESYNC+
|
501
|
-
# TODO...
|
502
|
-
# ==== RFC7162: +CONDSTORE+
|
503
|
-
# TODO...
|
504
|
-
# ==== RFC8474: +OBJECTID+
|
505
|
-
# TODO...
|
506
|
-
# ==== RFC9208: +QUOTA+
|
507
|
-
# TODO...
|
508
|
-
#++
|
551
|
+
# ==== RFC6855: <tt>UTF8=ACCEPT</tt>, <tt>UTF8=ONLY</tt>
|
509
552
|
#
|
510
|
-
#
|
553
|
+
# - See #enable for information about support for UTF-8 string encoding.
|
511
554
|
#
|
512
|
-
#
|
513
|
-
# pre-authenticated connection.
|
514
|
-
# - #responses: A hash with arrays of unhandled <em>non-+nil+</em>
|
515
|
-
# UntaggedResponse and ResponseCode +#data+, keyed by +#name+.
|
516
|
-
# - #add_response_handler: Add a block to be called inside the receiver thread
|
517
|
-
# with every server response.
|
518
|
-
# - #remove_response_handler: Remove a previously added response handler.
|
555
|
+
# ==== RFC7162: +CONDSTORE+
|
519
556
|
#
|
557
|
+
# - Updates #enable with +CONDSTORE+ parameter. +CONDSTORE+ will also be
|
558
|
+
# enabled by using any of the extension's command parameters, listed below.
|
559
|
+
# - Updates #status with the +HIGHESTMODSEQ+ status attribute.
|
560
|
+
# - Updates #select and #examine with the +condstore+ modifier, and adds
|
561
|
+
# either a +HIGHESTMODSEQ+ or +NOMODSEQ+ ResponseCode to the responses.
|
562
|
+
# - Updates #search, #uid_search, #sort, and #uid_sort with the +MODSEQ+
|
563
|
+
# search criterion, and adds SearchResult#modseq to the search response.
|
564
|
+
# - Updates #thread and #uid_thread with the +MODSEQ+ search criterion
|
565
|
+
# <em>(but thread responses are unchanged)</em>.
|
566
|
+
# - Updates #fetch and #uid_fetch with the +changedsince+ modifier and
|
567
|
+
# +MODSEQ+ FetchData attribute.
|
568
|
+
# - Updates #store and #uid_store with the +unchangedsince+ modifier and adds
|
569
|
+
# the +MODIFIED+ ResponseCode to the tagged response.
|
570
|
+
#
|
571
|
+
# ==== RFC8438: <tt>STATUS=SIZE</tt>
|
572
|
+
# - Updates #status with the +SIZE+ status attribute.
|
573
|
+
#
|
574
|
+
# ==== RFC8474: +OBJECTID+
|
575
|
+
# - Adds +MAILBOXID+ ResponseCode to #create tagged response.
|
576
|
+
# - Adds +MAILBOXID+ ResponseCode to #select and #examine untagged response.
|
577
|
+
# - Updates #fetch and #uid_fetch with the +EMAILID+ and +THREADID+ items.
|
578
|
+
# See FetchData#emailid and FetchData#emailid.
|
579
|
+
# - Updates #status with support for the +MAILBOXID+ status attribute.
|
580
|
+
#
|
581
|
+
# ==== RFC9394: +PARTIAL+
|
582
|
+
# - Updates #search, #uid_search with the +PARTIAL+ return option which adds
|
583
|
+
# ESearchResult#partial return data.
|
584
|
+
# - Updates #uid_fetch with the +partial+ modifier.
|
585
|
+
#
|
586
|
+
# ==== RFC9586: +UIDONLY+
|
587
|
+
# - Updates #enable with +UIDONLY+ parameter.
|
588
|
+
# - Updates #uid_fetch and #uid_store to return +UIDFETCH+ response.
|
589
|
+
# - Updates #expunge and #uid_expunge to return +VANISHED+ response.
|
590
|
+
# - Prohibits use of message sequence numbers in responses or requests.
|
520
591
|
#
|
521
592
|
# == References
|
522
|
-
#--
|
523
|
-
# TODO: Consider moving references list to REFERENCES.md or REFERENCES.rdoc.
|
524
|
-
#++
|
525
593
|
#
|
526
594
|
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
|
527
595
|
# Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - \VERSION 4rev1",
|
@@ -551,57 +619,57 @@ module Net
|
|
551
619
|
# Gahrns, M., "IMAP4 Multi-Accessed Mailbox Practice", RFC 2180, DOI
|
552
620
|
# 10.17487/RFC2180, July 1997, <https://www.rfc-editor.org/info/rfc2180>.
|
553
621
|
#
|
554
|
-
# [UTF7[https://
|
622
|
+
# [UTF7[https://www.rfc-editor.org/rfc/rfc2152]]::
|
555
623
|
# Goldsmith, D. and M. Davis, "UTF-7 A Mail-Safe Transformation Format of
|
556
624
|
# Unicode", RFC 2152, DOI 10.17487/RFC2152, May 1997,
|
557
625
|
# <https://www.rfc-editor.org/info/rfc2152>.
|
558
626
|
#
|
559
627
|
# === Message envelope and body structure
|
560
628
|
#
|
561
|
-
# [RFC5322[https://
|
629
|
+
# [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]::
|
562
630
|
# Resnick, P., Ed., "Internet Message Format",
|
563
631
|
# RFC 5322, DOI 10.17487/RFC5322, October 2008,
|
564
632
|
# <https://www.rfc-editor.org/info/rfc5322>.
|
565
633
|
#
|
566
634
|
# <em>Note: obsoletes</em>
|
567
|
-
# RFC-2822[https://
|
568
|
-
# RFC-822[https://
|
635
|
+
# RFC-2822[https://www.rfc-editor.org/rfc/rfc2822]<em> (April 2001) and</em>
|
636
|
+
# RFC-822[https://www.rfc-editor.org/rfc/rfc822]<em> (August 1982).</em>
|
569
637
|
#
|
570
|
-
# [CHARSET[https://
|
638
|
+
# [CHARSET[https://www.rfc-editor.org/rfc/rfc2978]]::
|
571
639
|
# Freed, N. and J. Postel, "IANA Charset Registration Procedures", BCP 19,
|
572
640
|
# RFC 2978, DOI 10.17487/RFC2978, October 2000,
|
573
641
|
# <https://www.rfc-editor.org/info/rfc2978>.
|
574
642
|
#
|
575
|
-
# [DISPOSITION[https://
|
643
|
+
# [DISPOSITION[https://www.rfc-editor.org/rfc/rfc2183]]::
|
576
644
|
# Troost, R., Dorner, S., and K. Moore, Ed., "Communicating Presentation
|
577
645
|
# Information in Internet Messages: The Content-Disposition Header
|
578
646
|
# Field", RFC 2183, DOI 10.17487/RFC2183, August 1997,
|
579
647
|
# <https://www.rfc-editor.org/info/rfc2183>.
|
580
648
|
#
|
581
|
-
# [MIME-IMB[https://
|
649
|
+
# [MIME-IMB[https://www.rfc-editor.org/rfc/rfc2045]]::
|
582
650
|
# Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
|
583
651
|
# (MIME) Part One: Format of Internet Message Bodies",
|
584
652
|
# RFC 2045, DOI 10.17487/RFC2045, November 1996,
|
585
653
|
# <https://www.rfc-editor.org/info/rfc2045>.
|
586
654
|
#
|
587
|
-
# [MIME-IMT[https://
|
655
|
+
# [MIME-IMT[https://www.rfc-editor.org/rfc/rfc2046]]::
|
588
656
|
# Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
|
589
657
|
# (MIME) Part Two: Media Types", RFC 2046, DOI 10.17487/RFC2046,
|
590
658
|
# November 1996, <https://www.rfc-editor.org/info/rfc2046>.
|
591
659
|
#
|
592
|
-
# [MIME-HDRS[https://
|
660
|
+
# [MIME-HDRS[https://www.rfc-editor.org/rfc/rfc2047]]::
|
593
661
|
# Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part Three:
|
594
662
|
# Message Header Extensions for Non-ASCII Text",
|
595
663
|
# RFC 2047, DOI 10.17487/RFC2047, November 1996,
|
596
664
|
# <https://www.rfc-editor.org/info/rfc2047>.
|
597
665
|
#
|
598
|
-
# [RFC2231[https://
|
666
|
+
# [RFC2231[https://www.rfc-editor.org/rfc/rfc2231]]::
|
599
667
|
# Freed, N. and K. Moore, "MIME Parameter Value and Encoded Word
|
600
668
|
# Extensions: Character Sets, Languages, and Continuations",
|
601
669
|
# RFC 2231, DOI 10.17487/RFC2231, November 1997,
|
602
670
|
# <https://www.rfc-editor.org/info/rfc2231>.
|
603
671
|
#
|
604
|
-
# [I18n-HDRS[https://
|
672
|
+
# [I18n-HDRS[https://www.rfc-editor.org/rfc/rfc6532]]::
|
605
673
|
# Yang, A., Steele, S., and N. Freed, "Internationalized Email Headers",
|
606
674
|
# RFC 6532, DOI 10.17487/RFC6532, February 2012,
|
607
675
|
# <https://www.rfc-editor.org/info/rfc6532>.
|
@@ -617,42 +685,40 @@ module Net
|
|
617
685
|
# RFC 2557, DOI 10.17487/RFC2557, March 1999,
|
618
686
|
# <https://www.rfc-editor.org/info/rfc2557>.
|
619
687
|
#
|
620
|
-
# [MD5[https://
|
688
|
+
# [MD5[https://www.rfc-editor.org/rfc/rfc1864]]::
|
621
689
|
# Myers, J. and M. Rose, "The Content-MD5 Header Field",
|
622
690
|
# RFC 1864, DOI 10.17487/RFC1864, October 1995,
|
623
691
|
# <https://www.rfc-editor.org/info/rfc1864>.
|
624
692
|
#
|
625
|
-
|
626
|
-
#
|
693
|
+
# [RFC3503[https://www.rfc-editor.org/rfc/rfc3503]]::
|
694
|
+
# Melnikov, A., "Message Disposition Notification (MDN)
|
695
|
+
# profile for Internet Message Access Protocol (IMAP)",
|
696
|
+
# RFC 3503, DOI 10.17487/RFC3503, March 2003,
|
697
|
+
# <https://www.rfc-editor.org/info/rfc3503>.
|
627
698
|
#
|
628
|
-
#
|
629
|
-
# Melnikov, A., "Message Disposition Notification (MDN)
|
630
|
-
# profile for Internet Message Access Protocol (IMAP)",
|
631
|
-
# RFC 3503, DOI 10.17487/RFC3503, March 2003,
|
632
|
-
# <https://www.rfc-editor.org/info/rfc3503>.
|
633
|
-
#++
|
699
|
+
# === \IMAP Extensions
|
634
700
|
#
|
635
|
-
#
|
636
|
-
#
|
637
|
-
# [QUOTA[https://tools.ietf.org/html/rfc2087]]::
|
638
|
-
# Myers, J., "IMAP4 QUOTA extension", RFC 2087, DOI 10.17487/RFC2087,
|
639
|
-
# January 1997, <https://www.rfc-editor.org/info/rfc2087>.
|
640
|
-
#--
|
641
|
-
# TODO: test compatibility with updated QUOTA extension:
|
642
|
-
# [QUOTA[https://tools.ietf.org/html/rfc9208]]::
|
701
|
+
# [QUOTA[https://www.rfc-editor.org/rfc/rfc9208]]::
|
643
702
|
# Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
|
644
703
|
# March 2022, <https://www.rfc-editor.org/info/rfc9208>.
|
645
|
-
|
646
|
-
#
|
704
|
+
#
|
705
|
+
# <em>Note: obsoletes</em>
|
706
|
+
# RFC-2087[https://www.rfc-editor.org/rfc/rfc2087]<em> (January 1997)</em>.
|
707
|
+
# <em>Net::IMAP does not fully support the RFC9208 updates yet.</em>
|
708
|
+
# [IDLE[https://www.rfc-editor.org/rfc/rfc2177]]::
|
647
709
|
# Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
|
648
710
|
# June 1997, <https://www.rfc-editor.org/info/rfc2177>.
|
649
|
-
# [NAMESPACE[https://
|
711
|
+
# [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]]::
|
650
712
|
# Gahrns, M. and C. Newman, "IMAP4 Namespace", RFC 2342,
|
651
713
|
# DOI 10.17487/RFC2342, May 1998, <https://www.rfc-editor.org/info/rfc2342>.
|
652
|
-
# [ID[https://
|
714
|
+
# [ID[https://www.rfc-editor.org/rfc/rfc2971]]::
|
653
715
|
# Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
|
654
716
|
# October 2000, <https://www.rfc-editor.org/info/rfc2971>.
|
655
|
-
# [
|
717
|
+
# [BINARY[https://www.rfc-editor.org/rfc/rfc3516]]::
|
718
|
+
# Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
|
719
|
+
# DOI 10.17487/RFC3516, April 2003,
|
720
|
+
# <https://www.rfc-editor.org/info/rfc3516>.
|
721
|
+
# [ACL[https://www.rfc-editor.org/rfc/rfc4314]]::
|
656
722
|
# Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
|
657
723
|
# DOI 10.17487/RFC4314, December 2005,
|
658
724
|
# <https://www.rfc-editor.org/info/rfc4314>.
|
@@ -660,46 +726,83 @@ module Net
|
|
660
726
|
# Crispin, M., "Internet Message Access Protocol (\IMAP) - UIDPLUS
|
661
727
|
# extension", RFC 4315, DOI 10.17487/RFC4315, December 2005,
|
662
728
|
# <https://www.rfc-editor.org/info/rfc4315>.
|
663
|
-
# [SORT[https://
|
729
|
+
# [SORT[https://www.rfc-editor.org/rfc/rfc5256]]::
|
664
730
|
# Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
|
665
731
|
# THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
|
666
732
|
# <https://www.rfc-editor.org/info/rfc5256>.
|
667
|
-
# [THREAD[https://
|
733
|
+
# [THREAD[https://www.rfc-editor.org/rfc/rfc5256]]::
|
668
734
|
# Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
|
669
735
|
# THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
|
670
736
|
# <https://www.rfc-editor.org/info/rfc5256>.
|
671
737
|
# [RFC5530[https://www.rfc-editor.org/rfc/rfc5530.html]]::
|
672
738
|
# Gulbrandsen, A., "IMAP Response Codes", RFC 5530, DOI 10.17487/RFC5530,
|
673
739
|
# May 2009, <https://www.rfc-editor.org/info/rfc5530>.
|
674
|
-
# [MOVE[https://
|
740
|
+
# [MOVE[https://www.rfc-editor.org/rfc/rfc6851]]::
|
675
741
|
# Gulbrandsen, A. and N. Freed, Ed., "Internet Message Access Protocol
|
676
742
|
# (\IMAP) - MOVE Extension", RFC 6851, DOI 10.17487/RFC6851, January 2013,
|
677
743
|
# <https://www.rfc-editor.org/info/rfc6851>.
|
744
|
+
# [UTF8=ACCEPT[https://www.rfc-editor.org/rfc/rfc6855]]::
|
745
|
+
# [UTF8=ONLY[https://www.rfc-editor.org/rfc/rfc6855]]::
|
746
|
+
# Resnick, P., Ed., Newman, C., Ed., and S. Shen, Ed.,
|
747
|
+
# "IMAP Support for UTF-8", RFC 6855, DOI 10.17487/RFC6855, March 2013,
|
748
|
+
# <https://www.rfc-editor.org/info/rfc6855>.
|
749
|
+
# [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162]]::
|
750
|
+
# [QRESYNC[https://www.rfc-editor.org/rfc/rfc7162]]::
|
751
|
+
# Melnikov, A. and D. Cridland, "IMAP Extensions: Quick Flag Changes
|
752
|
+
# Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization
|
753
|
+
# (QRESYNC)", RFC 7162, DOI 10.17487/RFC7162, May 2014,
|
754
|
+
# <https://www.rfc-editor.org/info/rfc7162>.
|
755
|
+
# [OBJECTID[https://www.rfc-editor.org/rfc/rfc8474]]::
|
756
|
+
# Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
|
757
|
+
# RFC 8474, DOI 10.17487/RFC8474, September 2018,
|
758
|
+
# <https://www.rfc-editor.org/info/rfc8474>.
|
759
|
+
# [PARTIAL[https://www.rfc-editor.org/info/rfc9394]]::
|
760
|
+
# Melnikov, A., Achuthan, A., Nagulakonda, V., and L. Alves,
|
761
|
+
# "IMAP PARTIAL Extension for Paged SEARCH and FETCH", RFC 9394,
|
762
|
+
# DOI 10.17487/RFC9394, June 2023,
|
763
|
+
# <https://www.rfc-editor.org/info/rfc9394>.
|
764
|
+
# [UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.pdf]]::
|
765
|
+
# Melnikov, A., Achuthan, A., Nagulakonda, V., Singh, A., and L. Alves,
|
766
|
+
# "\IMAP Extension for Using and Returning Unique Identifiers (UIDs) Only",
|
767
|
+
# RFC 9586, DOI 10.17487/RFC9586, May 2024,
|
768
|
+
# <https://www.rfc-editor.org/info/rfc9586>.
|
678
769
|
#
|
679
770
|
# === IANA registries
|
680
|
-
#
|
681
771
|
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
|
682
772
|
# * {IMAP Response Codes}[https://www.iana.org/assignments/imap-response-codes/imap-response-codes.xhtml]
|
683
773
|
# * {IMAP Mailbox Name Attributes}[https://www.iana.org/assignments/imap-mailbox-name-attributes/imap-mailbox-name-attributes.xhtml]
|
684
774
|
# * {IMAP and JMAP Keywords}[https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml]
|
685
775
|
# * {IMAP Threading Algorithms}[https://www.iana.org/assignments/imap-threading-algorithms/imap-threading-algorithms.xhtml]
|
686
|
-
#--
|
687
|
-
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
688
|
-
# * [{LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
|
689
|
-
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
|
690
|
-
# * {IMAP ANNOTATE Extension Entries and Attributes}[https://www.iana.org/assignments/imap-annotate-extension/imap-annotate-extension.xhtml]
|
691
|
-
# * {IMAP URLAUTH Access Identifiers and Prefixes}[https://www.iana.org/assignments/urlauth-access-ids/urlauth-access-ids.xhtml]
|
692
|
-
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
693
|
-
#++
|
694
776
|
# * {SASL Mechanisms and SASL SCRAM Family Mechanisms}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
695
777
|
# * {Service Name and Transport Protocol Port Number Registry}[https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml]:
|
696
778
|
# +imap+: tcp/143, +imaps+: tcp/993
|
697
779
|
# * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
|
698
780
|
# +imap+
|
699
781
|
# * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
782
|
+
# ==== For currently unsupported features:
|
783
|
+
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
784
|
+
# * {LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
|
785
|
+
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
|
786
|
+
# * {IMAP ANNOTATE Extension Entries and Attributes}[https://www.iana.org/assignments/imap-annotate-extension/imap-annotate-extension.xhtml]
|
787
|
+
# * {IMAP URLAUTH Access Identifiers and Prefixes}[https://www.iana.org/assignments/urlauth-access-ids/urlauth-access-ids.xhtml]
|
788
|
+
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
700
789
|
#
|
701
790
|
class IMAP < Protocol
|
702
|
-
VERSION = "0.
|
791
|
+
VERSION = "0.5.9"
|
792
|
+
|
793
|
+
# Aliases for supported capabilities, to be used with the #enable command.
|
794
|
+
ENABLE_ALIASES = {
|
795
|
+
utf8: "UTF8=ACCEPT",
|
796
|
+
"UTF8=ONLY" => "UTF8=ACCEPT",
|
797
|
+
}.freeze
|
798
|
+
|
799
|
+
dir = File.expand_path("imap", __dir__)
|
800
|
+
autoload :ConnectionState, "#{dir}/connection_state"
|
801
|
+
autoload :ResponseReader, "#{dir}/response_reader"
|
802
|
+
autoload :SASL, "#{dir}/sasl"
|
803
|
+
autoload :SASLAdapter, "#{dir}/sasl_adapter"
|
804
|
+
autoload :SequenceSet, "#{dir}/sequence_set"
|
805
|
+
autoload :StringPrep, "#{dir}/stringprep"
|
703
806
|
|
704
807
|
include MonitorMixin
|
705
808
|
if defined?(OpenSSL::SSL)
|
@@ -707,43 +810,33 @@ module Net
|
|
707
810
|
include SSL
|
708
811
|
end
|
709
812
|
|
710
|
-
#
|
711
|
-
|
712
|
-
|
713
|
-
# Returns a hash with arrays of unhandled <em>non-+nil+</em>
|
714
|
-
# UntaggedResponse#data keyed by UntaggedResponse#name, and
|
715
|
-
# ResponseCode#data keyed by ResponseCode#name.
|
813
|
+
# :call-seq:
|
814
|
+
# Net::IMAP::SequenceSet(set = nil) -> SequenceSet
|
716
815
|
#
|
717
|
-
#
|
816
|
+
# Coerces +set+ into a SequenceSet, using either SequenceSet.try_convert or
|
817
|
+
# SequenceSet.new.
|
718
818
|
#
|
719
|
-
#
|
720
|
-
#
|
721
|
-
#
|
722
|
-
#
|
723
|
-
#
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
# Seconds to wait until a connection is opened.
|
730
|
-
# If the IMAP object cannot open a connection within this time,
|
731
|
-
# it raises a Net::OpenTimeout exception. The default value is 30 seconds.
|
732
|
-
attr_reader :open_timeout
|
733
|
-
|
734
|
-
# Seconds to wait until an IDLE response is received.
|
735
|
-
attr_reader :idle_response_timeout
|
819
|
+
# * When +set+ is a SequenceSet, that same set is returned.
|
820
|
+
# * When +set+ responds to +to_sequence_set+, +set.to_sequence_set+ is
|
821
|
+
# returned.
|
822
|
+
# * Otherwise, returns the result from calling SequenceSet.new with +set+.
|
823
|
+
#
|
824
|
+
# Related: SequenceSet.try_convert, SequenceSet.new, SequenceSet::[]
|
825
|
+
def self.SequenceSet(set = nil)
|
826
|
+
SequenceSet.try_convert(set) || SequenceSet.new(set)
|
827
|
+
end
|
736
828
|
|
737
|
-
|
829
|
+
# Returns the global Config object
|
830
|
+
def self.config; Config.global end
|
738
831
|
|
739
|
-
# Returns the debug mode.
|
740
|
-
|
741
|
-
|
742
|
-
end
|
832
|
+
# Returns the global debug mode.
|
833
|
+
# Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
|
834
|
+
def self.debug; config.debug end
|
743
835
|
|
744
|
-
# Sets the debug mode.
|
836
|
+
# Sets the global debug mode.
|
837
|
+
# Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
|
745
838
|
def self.debug=(val)
|
746
|
-
|
839
|
+
config.debug = val
|
747
840
|
end
|
748
841
|
|
749
842
|
# The default port for IMAP connections, port 143
|
@@ -762,29 +855,303 @@ module Net
|
|
762
855
|
alias default_ssl_port default_tls_port
|
763
856
|
end
|
764
857
|
|
858
|
+
# Returns the initial greeting sent by the server, an UntaggedResponse.
|
859
|
+
attr_reader :greeting
|
860
|
+
|
861
|
+
# The client configuration. See Net::IMAP::Config.
|
862
|
+
#
|
863
|
+
# By default, the client's local configuration inherits from the global
|
864
|
+
# Net::IMAP.config.
|
865
|
+
attr_reader :config
|
866
|
+
|
867
|
+
##
|
868
|
+
# :attr_reader: open_timeout
|
869
|
+
# Seconds to wait until a connection is opened. Also used by #starttls.
|
870
|
+
# Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout].
|
871
|
+
|
872
|
+
##
|
873
|
+
# :attr_reader: idle_response_timeout
|
874
|
+
# Seconds to wait until an IDLE response is received.
|
875
|
+
# Delegates to {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout].
|
876
|
+
|
877
|
+
##
|
878
|
+
# :attr_accessor: max_response_size
|
879
|
+
#
|
880
|
+
# The maximum allowed server response size, in bytes.
|
881
|
+
# Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
|
882
|
+
|
883
|
+
# :stopdoc:
|
884
|
+
def open_timeout; config.open_timeout end
|
885
|
+
def idle_response_timeout; config.idle_response_timeout end
|
886
|
+
def max_response_size; config.max_response_size end
|
887
|
+
def max_response_size=(val) config.max_response_size = val end
|
888
|
+
# :startdoc:
|
889
|
+
|
890
|
+
# The hostname this client connected to
|
891
|
+
attr_reader :host
|
892
|
+
|
893
|
+
# The port this client connected to
|
894
|
+
attr_reader :port
|
895
|
+
|
896
|
+
# Returns the
|
897
|
+
# {SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]
|
898
|
+
# used by the SSLSocket when TLS is attempted, even when the TLS handshake
|
899
|
+
# is unsuccessful. The context object will be frozen.
|
900
|
+
#
|
901
|
+
# Returns +nil+ for a plaintext connection.
|
902
|
+
attr_reader :ssl_ctx
|
903
|
+
|
904
|
+
# Returns the parameters that were sent to #ssl_ctx
|
905
|
+
# {set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params]
|
906
|
+
# when the connection tries to use TLS (even when unsuccessful).
|
907
|
+
#
|
908
|
+
# Returns +false+ for a plaintext connection.
|
909
|
+
attr_reader :ssl_ctx_params
|
910
|
+
|
911
|
+
# Returns the current connection state.
|
912
|
+
#
|
913
|
+
# Once an IMAP connection is established, the connection is in one of four
|
914
|
+
# states: +not_authenticated+, +authenticated+, +selected+, and +logout+.
|
915
|
+
# Most commands are valid only in certain states.
|
916
|
+
#
|
917
|
+
# The connection state object responds to +to_sym+ and +name+ with the name
|
918
|
+
# of the current connection state, as a Symbol or String. Future versions
|
919
|
+
# of +net-imap+ may store additional information on the state object.
|
920
|
+
#
|
921
|
+
# From {RFC9051}[https://www.rfc-editor.org/rfc/rfc9051#section-3]:
|
922
|
+
# +----------------------+
|
923
|
+
# |connection established|
|
924
|
+
# +----------------------+
|
925
|
+
# ||
|
926
|
+
# \/
|
927
|
+
# +--------------------------------------+
|
928
|
+
# | server greeting |
|
929
|
+
# +--------------------------------------+
|
930
|
+
# || (1) || (2) || (3)
|
931
|
+
# \/ || ||
|
932
|
+
# +-----------------+ || ||
|
933
|
+
# |Not Authenticated| || ||
|
934
|
+
# +-----------------+ || ||
|
935
|
+
# || (7) || (4) || ||
|
936
|
+
# || \/ \/ ||
|
937
|
+
# || +----------------+ ||
|
938
|
+
# || | Authenticated |<=++ ||
|
939
|
+
# || +----------------+ || ||
|
940
|
+
# || || (7) || (5) || (6) ||
|
941
|
+
# || || \/ || ||
|
942
|
+
# || || +--------+ || ||
|
943
|
+
# || || |Selected|==++ ||
|
944
|
+
# || || +--------+ ||
|
945
|
+
# || || || (7) ||
|
946
|
+
# \/ \/ \/ \/
|
947
|
+
# +--------------------------------------+
|
948
|
+
# | Logout |
|
949
|
+
# +--------------------------------------+
|
950
|
+
# ||
|
951
|
+
# \/
|
952
|
+
# +-------------------------------+
|
953
|
+
# |both sides close the connection|
|
954
|
+
# +-------------------------------+
|
955
|
+
#
|
956
|
+
# >>>
|
957
|
+
# Legend for the above diagram:
|
958
|
+
#
|
959
|
+
# 1. connection without pre-authentication (+OK+ #greeting)
|
960
|
+
# 2. pre-authenticated connection (+PREAUTH+ #greeting)
|
961
|
+
# 3. rejected connection (+BYE+ #greeting)
|
962
|
+
# 4. successful #login or #authenticate command
|
963
|
+
# 5. successful #select or #examine command
|
964
|
+
# 6. #close or #unselect command, unsolicited +CLOSED+ response code, or
|
965
|
+
# failed #select or #examine command
|
966
|
+
# 7. #logout command, server shutdown, or connection closed
|
967
|
+
#
|
968
|
+
# Before the server greeting, the state is +not_authenticated+.
|
969
|
+
# After the connection closes, the state remains +logout+.
|
970
|
+
attr_reader :connection_state
|
971
|
+
|
972
|
+
# Creates a new Net::IMAP object and connects it to the specified
|
973
|
+
# +host+.
|
974
|
+
#
|
975
|
+
# ==== Options
|
976
|
+
#
|
977
|
+
# Accepts the following options:
|
978
|
+
#
|
979
|
+
# [port]
|
980
|
+
# Port number. Defaults to 993 when +ssl+ is truthy, and 143 otherwise.
|
981
|
+
#
|
982
|
+
# [ssl]
|
983
|
+
# If +true+, the connection will use TLS with the default params set by
|
984
|
+
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params].
|
985
|
+
# If +ssl+ is a hash, it's passed to
|
986
|
+
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
|
987
|
+
# the keys are names of attribute assignment methods on
|
988
|
+
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]. For example:
|
989
|
+
#
|
990
|
+
# [{ca_file}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_file]]
|
991
|
+
# The path to a file containing a PEM-format CA certificate.
|
992
|
+
# [{ca_path}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_path]]
|
993
|
+
# The path to a directory containing CA certificates in PEM format.
|
994
|
+
# [{min_version}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D]]
|
995
|
+
# Sets the lower bound on the supported SSL/TLS protocol version. Set to
|
996
|
+
# an +OpenSSL+ constant such as +OpenSSL::SSL::TLS1_2_VERSION+,
|
997
|
+
# [{verify_mode}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-verify_mode]]
|
998
|
+
# SSL session verification mode. Valid modes include
|
999
|
+
# +OpenSSL::SSL::VERIFY_PEER+ and +OpenSSL::SSL::VERIFY_NONE+.
|
1000
|
+
#
|
1001
|
+
# See {OpenSSL::SSL::SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html] for other valid SSL context params.
|
1002
|
+
#
|
1003
|
+
# See DeprecatedClientOptions.new for deprecated SSL arguments.
|
1004
|
+
#
|
1005
|
+
# [response_handlers]
|
1006
|
+
# A list of response handlers to be added before the receiver thread is
|
1007
|
+
# started. This ensures every server response is handled, including the
|
1008
|
+
# #greeting. Note that the greeting is handled in the current thread, but
|
1009
|
+
# all other responses are handled in the receiver thread.
|
1010
|
+
#
|
1011
|
+
# [config]
|
1012
|
+
# A Net::IMAP::Config object to use as the basis for #config. By default,
|
1013
|
+
# the global Net::IMAP.config is used.
|
1014
|
+
#
|
1015
|
+
# >>>
|
1016
|
+
# *NOTE:* +config+ does not set #config directly---it sets the _parent_
|
1017
|
+
# config for inheritance. Every client creates its own unique #config.
|
1018
|
+
#
|
1019
|
+
# All other keyword arguments are forwarded to Net::IMAP::Config.new, to
|
1020
|
+
# initialize the client's #config. For example:
|
1021
|
+
#
|
1022
|
+
# [{open_timeout}[rdoc-ref:Config#open_timeout]]
|
1023
|
+
# Seconds to wait until a connection is opened
|
1024
|
+
# [{idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]]
|
1025
|
+
# Seconds to wait until an IDLE response is received
|
1026
|
+
#
|
1027
|
+
# See Net::IMAP::Config for other valid options.
|
1028
|
+
#
|
1029
|
+
# ==== Examples
|
1030
|
+
#
|
1031
|
+
# Connect to cleartext port 143 at mail.example.com and receive the server greeting:
|
1032
|
+
# imap = Net::IMAP.new('mail.example.com', ssl: false) # => #<Net::IMAP:0x00007f79b0872bd0>
|
1033
|
+
# imap.port => 143
|
1034
|
+
# imap.tls_verified? => false
|
1035
|
+
# imap.greeting => name: ("OK" | "PREAUTH") => status
|
1036
|
+
# status # => "OK"
|
1037
|
+
# # The client is connected in the "Not Authenticated" state.
|
1038
|
+
#
|
1039
|
+
# Connect with TLS to port 993
|
1040
|
+
# imap = Net::IMAP.new('mail.example.com', ssl: true) # => #<Net::IMAP:0x00007f79b0872bd0>
|
1041
|
+
# imap.port => 993
|
1042
|
+
# imap.tls_verified? => true
|
1043
|
+
# imap.greeting => name: (/OK/i | /PREAUTH/i) => status
|
1044
|
+
# case status
|
1045
|
+
# in /OK/i
|
1046
|
+
# # The client is connected in the "Not Authenticated" state.
|
1047
|
+
# imap.authenticate("PLAIN", "joe_user", "joes_password")
|
1048
|
+
# in /PREAUTH/i
|
1049
|
+
# # The client is connected in the "Authenticated" state.
|
1050
|
+
# end
|
1051
|
+
#
|
1052
|
+
# Connect with prior authentication, for example using an SSL certificate:
|
1053
|
+
# ssl_ctx_params = {
|
1054
|
+
# cert: OpenSSL::X509::Certificate.new(File.read("client.crt")),
|
1055
|
+
# key: OpenSSL::PKey::EC.new(File.read('client.key')),
|
1056
|
+
# extra_chain_cert: [
|
1057
|
+
# OpenSSL::X509::Certificate.new(File.read("intermediate.crt")),
|
1058
|
+
# ],
|
1059
|
+
# }
|
1060
|
+
# imap = Net::IMAP.new('mail.example.com', ssl: ssl_ctx_params)
|
1061
|
+
# imap.port => 993
|
1062
|
+
# imap.tls_verified? => true
|
1063
|
+
# imap.greeting => name: "PREAUTH"
|
1064
|
+
# # The client is connected in the "Authenticated" state.
|
1065
|
+
#
|
1066
|
+
# ==== Exceptions
|
1067
|
+
#
|
1068
|
+
# The most common errors are:
|
1069
|
+
#
|
1070
|
+
# [Errno::ECONNREFUSED]
|
1071
|
+
# Connection refused by +host+ or an intervening firewall.
|
1072
|
+
# [Errno::ETIMEDOUT]
|
1073
|
+
# Connection timed out (possibly due to packets being dropped by an
|
1074
|
+
# intervening firewall).
|
1075
|
+
# [Errno::ENETUNREACH]
|
1076
|
+
# There is no route to that network.
|
1077
|
+
# [SocketError]
|
1078
|
+
# Hostname not known or other socket error.
|
1079
|
+
# [Net::IMAP::ByeResponseError]
|
1080
|
+
# Connected to the host successfully, but it immediately said goodbye.
|
1081
|
+
#
|
1082
|
+
def initialize(host, port: nil, ssl: nil, response_handlers: nil,
|
1083
|
+
config: Config.global, **config_options)
|
1084
|
+
super()
|
1085
|
+
# Config options
|
1086
|
+
@host = host
|
1087
|
+
@config = Config.new(config, **config_options)
|
1088
|
+
@port = port || (ssl ? SSL_PORT : PORT)
|
1089
|
+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
|
1090
|
+
|
1091
|
+
# Basic Client State
|
1092
|
+
@utf8_strings = false
|
1093
|
+
@debug_output_bol = true
|
1094
|
+
@exception = nil
|
1095
|
+
@greeting = nil
|
1096
|
+
@capabilities = nil
|
1097
|
+
@tls_verified = false
|
1098
|
+
@connection_state = ConnectionState::NotAuthenticated.new
|
1099
|
+
|
1100
|
+
# Client Protocol Receiver
|
1101
|
+
@parser = ResponseParser.new(config: @config)
|
1102
|
+
@responses = Hash.new {|h, k| h[k] = [] }
|
1103
|
+
@response_handlers = []
|
1104
|
+
@receiver_thread = nil
|
1105
|
+
@receiver_thread_exception = nil
|
1106
|
+
@receiver_thread_terminating = false
|
1107
|
+
response_handlers&.each do add_response_handler(_1) end
|
1108
|
+
|
1109
|
+
# Client Protocol Sender (including state for currently running commands)
|
1110
|
+
@tag_prefix = "RUBY"
|
1111
|
+
@tagno = 0
|
1112
|
+
@tagged_responses = {}
|
1113
|
+
@tagged_response_arrival = new_cond
|
1114
|
+
@continued_command_tag = nil
|
1115
|
+
@continuation_request_arrival = new_cond
|
1116
|
+
@continuation_request_exception = nil
|
1117
|
+
@idle_done_cond = nil
|
1118
|
+
@logout_command_tag = nil
|
1119
|
+
|
1120
|
+
# Connection
|
1121
|
+
@sock = tcp_socket(@host, @port)
|
1122
|
+
@reader = ResponseReader.new(self, @sock)
|
1123
|
+
start_tls_session if ssl_ctx
|
1124
|
+
start_imap_connection
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
# Returns true after the TLS negotiation has completed and the remote
|
1128
|
+
# hostname has been verified. Returns false when TLS has been established
|
1129
|
+
# but peer verification was disabled.
|
1130
|
+
def tls_verified?; @tls_verified end
|
1131
|
+
|
765
1132
|
# Disconnects from the server.
|
766
1133
|
#
|
767
|
-
#
|
1134
|
+
# Waits for receiver thread to close before returning. Slow or stuck
|
1135
|
+
# response handlers can cause #disconnect to hang until they complete.
|
1136
|
+
#
|
1137
|
+
# Related: #logout, #logout!
|
768
1138
|
def disconnect
|
1139
|
+
in_logout_state = try_state_logout?
|
769
1140
|
return if disconnected?
|
770
1141
|
begin
|
771
|
-
|
772
|
-
# try to call SSL::SSLSocket#io.
|
773
|
-
@sock.io.shutdown
|
774
|
-
rescue NoMethodError
|
775
|
-
# @sock is not an SSL::SSLSocket.
|
776
|
-
@sock.shutdown
|
777
|
-
end
|
1142
|
+
@sock.to_io.shutdown
|
778
1143
|
rescue Errno::ENOTCONN
|
779
1144
|
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
|
780
1145
|
rescue Exception => e
|
781
1146
|
@receiver_thread.raise(e)
|
782
1147
|
end
|
1148
|
+
@sock.close
|
783
1149
|
@receiver_thread.join
|
784
|
-
synchronize do
|
785
|
-
@sock.close
|
786
|
-
end
|
787
1150
|
raise e if e
|
1151
|
+
ensure
|
1152
|
+
# Try again after shutting down the receiver thread. With no reciever
|
1153
|
+
# left to wait for, any remaining locks should be _very_ brief.
|
1154
|
+
state_logout! unless in_logout_state
|
788
1155
|
end
|
789
1156
|
|
790
1157
|
# Returns true if disconnected from the server.
|
@@ -794,62 +1161,123 @@ module Net
|
|
794
1161
|
return @sock.closed?
|
795
1162
|
end
|
796
1163
|
|
797
|
-
#
|
798
|
-
#
|
799
|
-
#
|
1164
|
+
# Returns whether the server supports a given +capability+. When available,
|
1165
|
+
# cached #capabilities are used without sending a new #capability command to
|
1166
|
+
# the server.
|
800
1167
|
#
|
801
|
-
#
|
802
|
-
#
|
803
|
-
# of all standard capabilities, and their reference RFCs.
|
1168
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
1169
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
804
1170
|
#
|
805
|
-
#
|
806
|
-
#
|
807
|
-
#
|
808
|
-
|
809
|
-
|
810
|
-
|
1171
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
1172
|
+
#
|
1173
|
+
# Related: #auth_capable?, #capabilities, #capability, #enable
|
1174
|
+
def capable?(capability) capabilities.include? capability.to_s.upcase end
|
1175
|
+
alias capability? capable?
|
1176
|
+
|
1177
|
+
# Returns the server capabilities. When available, cached capabilities are
|
1178
|
+
# used without sending a new #capability command to the server.
|
1179
|
+
#
|
1180
|
+
# To ensure a case-insensitive comparison, #capable? can be used instead.
|
1181
|
+
#
|
1182
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
1183
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
1184
|
+
#
|
1185
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
1186
|
+
#
|
1187
|
+
# Related: #capable?, #auth_capable?, #auth_mechanisms, #capability, #enable
|
1188
|
+
def capabilities
|
1189
|
+
@capabilities || capability
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
# Returns the #authenticate mechanisms that the server claims to support.
|
1193
|
+
# These are derived from the #capabilities with an <tt>AUTH=</tt> prefix.
|
1194
|
+
#
|
1195
|
+
# This may be different when the connection is cleartext or using TLS. Most
|
1196
|
+
# servers will drop all <tt>AUTH=</tt> mechanisms from #capabilities after
|
1197
|
+
# the connection has authenticated.
|
1198
|
+
#
|
1199
|
+
# imap = Net::IMAP.new(hostname, ssl: false)
|
1200
|
+
# imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
|
1201
|
+
# imap.auth_mechanisms # => []
|
1202
|
+
#
|
1203
|
+
# imap.starttls
|
1204
|
+
# imap.capabilities # => ["IMAP4REV1", "AUTH=PLAIN", "AUTH=XOAUTH2",
|
1205
|
+
# # "AUTH=OAUTHBEARER"]
|
1206
|
+
# imap.auth_mechanisms # => ["PLAIN", "XOAUTH2", "OAUTHBEARER"]
|
1207
|
+
#
|
1208
|
+
# imap.authenticate("XOAUTH2", username, oauth2_access_token)
|
1209
|
+
# imap.auth_mechanisms # => []
|
1210
|
+
#
|
1211
|
+
# Related: #authenticate, #auth_capable?, #capabilities
|
1212
|
+
def auth_mechanisms
|
1213
|
+
capabilities
|
1214
|
+
.grep(/\AAUTH=/i)
|
1215
|
+
.map { _1.delete_prefix("AUTH=") }
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
# Returns whether the server supports a given SASL +mechanism+ for use with
|
1219
|
+
# the #authenticate command. The +mechanism+ is supported when
|
1220
|
+
# #capabilities includes <tt>"AUTH=#{mechanism.to_s.upcase}"</tt>. When
|
1221
|
+
# available, cached capabilities are used without sending a new #capability
|
1222
|
+
# command to the server.
|
811
1223
|
#
|
812
|
-
#
|
813
|
-
#
|
1224
|
+
# imap.capable? "AUTH=PLAIN" # => true
|
1225
|
+
# imap.auth_capable? "PLAIN" # => true
|
1226
|
+
# imap.auth_capable? "blurdybloop" # => false
|
814
1227
|
#
|
815
|
-
#
|
1228
|
+
# Related: #authenticate, #auth_mechanisms, #capable?, #capabilities
|
1229
|
+
def auth_capable?(mechanism)
|
1230
|
+
capable? "AUTH=#{mechanism}"
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
# Returns whether capabilities have been cached. When true, #capable? and
|
1234
|
+
# #capabilities don't require sending a #capability command to the server.
|
816
1235
|
#
|
817
|
-
#
|
818
|
-
# All IMAP4rev1 servers must _implement_ the +STARTTLS+,
|
819
|
-
# <tt>AUTH=PLAIN</tt>, and +LOGINDISABLED+ capabilities, and clients must
|
820
|
-
# respect their presence or absence. See the capabilites requirements on
|
821
|
-
# #starttls, #login, and #authenticate.
|
1236
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
822
1237
|
#
|
823
|
-
#
|
1238
|
+
# Related: #capable?, #capability, #clear_cached_capabilities
|
1239
|
+
def capabilities_cached?
|
1240
|
+
!!@capabilities
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
# Clears capabilities that have been remembered by the Net::IMAP client.
|
1244
|
+
# This forces a #capability command to be sent the next time a #capabilities
|
1245
|
+
# query method is called.
|
824
1246
|
#
|
825
|
-
#
|
826
|
-
#
|
827
|
-
#
|
828
|
-
# compatible behavior, such as response codes or mailbox attributes, may
|
829
|
-
# be sent at any time.
|
1247
|
+
# Net::IMAP automatically discards its cached capabilities when they can
|
1248
|
+
# change. Explicitly calling this _should_ be unnecessary for well-behaved
|
1249
|
+
# servers.
|
830
1250
|
#
|
831
|
-
#
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
1251
|
+
# Related: #capable?, #capability, #capabilities_cached?
|
1252
|
+
def clear_cached_capabilities
|
1253
|
+
synchronize do
|
1254
|
+
clear_responses("CAPABILITY")
|
1255
|
+
@capabilities = nil
|
1256
|
+
end
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
# Sends a {CAPABILITY command [IMAP4rev1 §6.1.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.1.1]
|
1260
|
+
# and returns an array of capabilities that are supported by the server.
|
1261
|
+
# The result is stored for use by #capable? and #capabilities.
|
836
1262
|
#
|
837
|
-
#
|
1263
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
1264
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
838
1265
|
#
|
839
|
-
#
|
840
|
-
#
|
841
|
-
#
|
842
|
-
# #
|
1266
|
+
# Net::IMAP automatically stores and discards capability data according to
|
1267
|
+
# the requirements and recommendations in
|
1268
|
+
# {IMAP4rev2 §6.1.1}[https://www.rfc-editor.org/rfc/rfc9051#section-6.1.1],
|
1269
|
+
# {§6.2}[https://www.rfc-editor.org/rfc/rfc9051#section-6.2], and
|
1270
|
+
# {§7.1}[https://www.rfc-editor.org/rfc/rfc9051#section-7.1].
|
1271
|
+
# Use #capable?, #auth_capable?, or #capabilities to this cache and avoid
|
1272
|
+
# sending the #capability command unnecessarily.
|
843
1273
|
#
|
844
|
-
#
|
845
|
-
# #authenticate. The OK TaggedResponse to #login and #authenticate may
|
846
|
-
# include +CAPABILITY+ response code data, but the TaggedResponse for
|
847
|
-
# #starttls is sent clear-text and cannot be trusted.
|
1274
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
848
1275
|
#
|
1276
|
+
# Related: #capable?, #auth_capable?, #capability, #enable
|
849
1277
|
def capability
|
850
1278
|
synchronize do
|
851
1279
|
send_command("CAPABILITY")
|
852
|
-
|
1280
|
+
@capabilities = clear_responses("CAPABILITY").last.freeze
|
853
1281
|
end
|
854
1282
|
end
|
855
1283
|
|
@@ -860,8 +1288,7 @@ module Net
|
|
860
1288
|
# Note that the user should first check if the server supports the ID
|
861
1289
|
# capability. For example:
|
862
1290
|
#
|
863
|
-
#
|
864
|
-
# if capabilities.include?("ID")
|
1291
|
+
# if capable?(:ID)
|
865
1292
|
# id = imap.id(
|
866
1293
|
# name: "my IMAP client (ruby)",
|
867
1294
|
# version: MyIMAP::VERSION,
|
@@ -870,16 +1297,16 @@ module Net
|
|
870
1297
|
# )
|
871
1298
|
# end
|
872
1299
|
#
|
873
|
-
# See [ID[https://
|
1300
|
+
# See [ID[https://www.rfc-editor.org/rfc/rfc2971]] for field definitions.
|
874
1301
|
#
|
875
|
-
#
|
1302
|
+
# ==== Capabilities
|
876
1303
|
#
|
877
1304
|
# The server's capabilities must include +ID+
|
878
|
-
# [RFC2971[https://
|
1305
|
+
# [RFC2971[https://www.rfc-editor.org/rfc/rfc2971]].
|
879
1306
|
def id(client_id=nil)
|
880
1307
|
synchronize do
|
881
1308
|
send_command("ID", ClientID.new(client_id))
|
882
|
-
|
1309
|
+
clear_responses("ID").last
|
883
1310
|
end
|
884
1311
|
end
|
885
1312
|
|
@@ -888,7 +1315,7 @@ module Net
|
|
888
1315
|
#
|
889
1316
|
# This allows the server to send unsolicited untagged EXPUNGE #responses,
|
890
1317
|
# but does not execute any client request. \IMAP servers are permitted to
|
891
|
-
# send unsolicited untagged responses at any time, except for
|
1318
|
+
# send unsolicited untagged responses at any time, except for +EXPUNGE+:
|
892
1319
|
#
|
893
1320
|
# * +EXPUNGE+ can only be sent while a command is in progress.
|
894
1321
|
# * +EXPUNGE+ must _not_ be sent during #fetch, #store, or #search.
|
@@ -903,150 +1330,200 @@ module Net
|
|
903
1330
|
# to inform the command to inform the server that the client is done with
|
904
1331
|
# the connection.
|
905
1332
|
#
|
906
|
-
# Related: #disconnect
|
1333
|
+
# Related: #disconnect, #logout!
|
907
1334
|
def logout
|
908
1335
|
send_command("LOGOUT")
|
909
1336
|
end
|
910
1337
|
|
1338
|
+
# Calls #logout then, after receiving the TaggedResponse for the +LOGOUT+,
|
1339
|
+
# calls #disconnect. Returns the TaggedResponse from +LOGOUT+. Returns
|
1340
|
+
# +nil+ when the client is already disconnected, in contrast to #logout
|
1341
|
+
# which raises an exception.
|
1342
|
+
#
|
1343
|
+
# If #logout raises a StandardError, a warning will be printed but the
|
1344
|
+
# exception will not be re-raised.
|
1345
|
+
#
|
1346
|
+
# This is useful in situations where the connection must be dropped, for
|
1347
|
+
# example for security or after tests. If logout errors need to be handled,
|
1348
|
+
# use #logout and #disconnect instead.
|
1349
|
+
#
|
1350
|
+
# Related: #logout, #disconnect
|
1351
|
+
def logout!
|
1352
|
+
logout unless disconnected?
|
1353
|
+
rescue => ex
|
1354
|
+
warn "%s during <Net::IMAP %s:%s> logout!: %s" % [
|
1355
|
+
ex.class, host, port, ex
|
1356
|
+
]
|
1357
|
+
ensure
|
1358
|
+
disconnect
|
1359
|
+
end
|
1360
|
+
|
911
1361
|
# Sends a {STARTTLS command [IMAP4rev1 §6.2.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.1]
|
912
1362
|
# to start a TLS session.
|
913
1363
|
#
|
914
|
-
# Any +options+ are forwarded to
|
1364
|
+
# Any +options+ are forwarded directly to
|
1365
|
+
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
|
1366
|
+
# the keys are names of attribute assignment methods on
|
1367
|
+
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
|
1368
|
+
#
|
1369
|
+
# See DeprecatedClientOptions#starttls for deprecated arguments.
|
915
1370
|
#
|
916
1371
|
# This method returns after TLS negotiation and hostname verification are
|
917
1372
|
# both successful. Any error indicates that the connection has not been
|
918
1373
|
# secured.
|
919
1374
|
#
|
1375
|
+
# After the server agrees to start a TLS connection, this method waits up to
|
1376
|
+
# {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
|
1377
|
+
# +Net::OpenTimeout+.
|
1378
|
+
#
|
920
1379
|
# *Note:*
|
921
1380
|
# >>>
|
922
1381
|
# Any #response_handlers added before STARTTLS should be aware that the
|
923
1382
|
# TaggedResponse to STARTTLS is sent clear-text, _before_ TLS negotiation.
|
924
|
-
# TLS
|
1383
|
+
# TLS starts immediately _after_ that response. Any response code sent
|
1384
|
+
# with the response (e.g. CAPABILITY) is insecure and cannot be trusted.
|
925
1385
|
#
|
926
1386
|
# Related: Net::IMAP.new, #login, #authenticate
|
927
1387
|
#
|
928
|
-
#
|
929
|
-
#
|
930
|
-
#
|
1388
|
+
# ==== Capability
|
1389
|
+
# Clients should not call #starttls unless the server advertises the
|
1390
|
+
# +STARTTLS+ capability.
|
931
1391
|
#
|
932
1392
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
933
|
-
# Cached capabilities
|
934
|
-
#
|
935
|
-
# The TaggedResponse to #starttls is sent clear-text, so the server <em>must
|
936
|
-
# *not*</em> send capabilities in the #starttls response and clients <em>must
|
937
|
-
# not</em> use them if they are sent. Servers will generally send an
|
938
|
-
# unsolicited untagged response immeditely _after_ #starttls completes.
|
1393
|
+
# Cached #capabilities will be cleared when this method completes.
|
939
1394
|
#
|
940
|
-
def starttls(options
|
941
|
-
|
1395
|
+
def starttls(**options)
|
1396
|
+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
1397
|
+
error = nil
|
1398
|
+
ok = send_command("STARTTLS") do |resp|
|
942
1399
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
options = create_ssl_params(certs, verify)
|
947
|
-
rescue NoMethodError
|
948
|
-
end
|
949
|
-
start_tls_session(options)
|
1400
|
+
clear_cached_capabilities
|
1401
|
+
clear_responses
|
1402
|
+
start_tls_session
|
950
1403
|
end
|
1404
|
+
rescue Exception => error
|
1405
|
+
raise # note that the error backtrace is in the receiver_thread
|
1406
|
+
end
|
1407
|
+
if error
|
1408
|
+
disconnect
|
1409
|
+
raise error
|
951
1410
|
end
|
1411
|
+
ok
|
952
1412
|
end
|
953
1413
|
|
954
1414
|
# :call-seq:
|
955
|
-
# authenticate(mechanism,
|
956
|
-
# authenticate(mech, *creds, **props) {|prop, auth| val } -> ok_resp
|
957
|
-
# authenticate(mechanism, authnid, credentials, authzid=nil) -> ok_resp
|
958
|
-
# authenticate(mechanism, **properties) -> ok_resp
|
959
|
-
# authenticate(mechanism) {|propname, authctx| prop_value } -> ok_resp
|
1415
|
+
# authenticate(mechanism, *, sasl_ir: config.sasl_ir, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
|
960
1416
|
#
|
961
1417
|
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
|
962
1418
|
# to authenticate the client. If successful, the connection enters the
|
963
1419
|
# "_authenticated_" state.
|
964
1420
|
#
|
965
1421
|
# +mechanism+ is the name of the \SASL authentication mechanism to be used.
|
966
|
-
# All other arguments are forwarded to the authenticator for the requested
|
967
|
-
# mechanism. The listed call signatures are suggestions. <em>The
|
968
|
-
# documentation for each individual mechanism must be consulted for its
|
969
|
-
# specific parameters.</em>
|
970
1422
|
#
|
971
|
-
#
|
1423
|
+
# +sasl_ir+ allows or disallows sending an "initial response" (see the
|
1424
|
+
# +SASL-IR+ capability, below). Defaults to the #config value for
|
1425
|
+
# {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
|
972
1426
|
#
|
973
|
-
#
|
1427
|
+
# The +registry+ kwarg can be used to select the mechanism implementation
|
1428
|
+
# from a custom registry. See SASL.authenticator and SASL::Authenticators.
|
974
1429
|
#
|
975
|
-
#
|
1430
|
+
# All other arguments are forwarded to the registered SASL authenticator for
|
1431
|
+
# the requested mechanism. <em>The documentation for each individual
|
1432
|
+
# mechanism must be consulted for its specific parameters.</em>
|
976
1433
|
#
|
977
|
-
#
|
978
|
-
# Login using clear-text username and password.
|
1434
|
+
# Related: #login, #starttls, #auth_capable?, #auth_mechanisms
|
979
1435
|
#
|
980
|
-
#
|
981
|
-
# Login using a username and OAuth2 access token.
|
982
|
-
# Non-standard and obsoleted by +OAUTHBEARER+, but widely
|
983
|
-
# supported.
|
1436
|
+
# ==== Mechanisms
|
984
1437
|
#
|
985
|
-
#
|
986
|
-
#
|
987
|
-
#
|
1438
|
+
# Each mechanism has different properties and requirements. Please consult
|
1439
|
+
# the documentation for the specific mechanisms you are using:
|
1440
|
+
#
|
1441
|
+
# +ANONYMOUS+::
|
1442
|
+
# See AnonymousAuthenticator[rdoc-ref:Net::IMAP::SASL::AnonymousAuthenticator].
|
1443
|
+
#
|
1444
|
+
# Allows the user to gain access to public services or resources without
|
1445
|
+
# authenticating or disclosing an identity.
|
1446
|
+
#
|
1447
|
+
# +EXTERNAL+::
|
1448
|
+
# See ExternalAuthenticator[rdoc-ref:Net::IMAP::SASL::ExternalAuthenticator].
|
1449
|
+
#
|
1450
|
+
# Authenticates using already established credentials, such as a TLS
|
1451
|
+
# certificate or IPsec.
|
1452
|
+
#
|
1453
|
+
# +OAUTHBEARER+::
|
1454
|
+
# See OAuthBearerAuthenticator[rdoc-ref:Net::IMAP::SASL::OAuthBearerAuthenticator].
|
1455
|
+
#
|
1456
|
+
# Login using an OAuth2 Bearer token. This is the standard mechanism
|
1457
|
+
# for using OAuth2 with \SASL, but it is not yet deployed as widely as
|
1458
|
+
# +XOAUTH2+.
|
1459
|
+
#
|
1460
|
+
# +PLAIN+::
|
1461
|
+
# See PlainAuthenticator[rdoc-ref:Net::IMAP::SASL::PlainAuthenticator].
|
988
1462
|
#
|
989
|
-
#
|
1463
|
+
# Login using clear-text username and password.
|
990
1464
|
#
|
991
|
-
#
|
1465
|
+
# +SCRAM-SHA-1+::
|
1466
|
+
# +SCRAM-SHA-256+::
|
1467
|
+
# See ScramAuthenticator[rdoc-ref:Net::IMAP::SASL::ScramAuthenticator].
|
992
1468
|
#
|
993
|
-
#
|
1469
|
+
# Login by username and password. The password is not sent to the
|
1470
|
+
# server but is used in a salted challenge/response exchange.
|
1471
|
+
# +SCRAM-SHA-1+ and +SCRAM-SHA-256+ are directly supported by
|
1472
|
+
# Net::IMAP::SASL. New authenticators can easily be added for any other
|
1473
|
+
# <tt>SCRAM-*</tt> mechanism if the digest algorithm is supported by
|
1474
|
+
# OpenSSL::Digest.
|
994
1475
|
#
|
995
|
-
#
|
1476
|
+
# +XOAUTH2+::
|
1477
|
+
# See XOAuth2Authenticator[rdoc-ref:Net::IMAP::SASL::XOAuth2Authenticator].
|
996
1478
|
#
|
997
|
-
#
|
998
|
-
#
|
1479
|
+
# Login using a username and an OAuth2 access token. Non-standard and
|
1480
|
+
# obsoleted by +OAUTHBEARER+, but widely supported.
|
1481
|
+
#
|
1482
|
+
# See the {SASL mechanism
|
999
1483
|
# registry}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
1000
|
-
# for
|
1484
|
+
# for a list of all SASL mechanisms and their specifications. To register
|
1485
|
+
# new authenticators, see Authenticators.
|
1001
1486
|
#
|
1002
|
-
# =====
|
1487
|
+
# ===== Deprecated mechanisms
|
1003
1488
|
#
|
1004
|
-
#
|
1005
|
-
#
|
1489
|
+
# <em>Obsolete mechanisms should be avoided, but are still available for
|
1490
|
+
# backwards compatibility. See</em> Net::IMAP::SASL@Deprecated+mechanisms.
|
1491
|
+
# <em>Using a deprecated mechanism will print a warning.</em>
|
1006
1492
|
#
|
1007
|
-
#
|
1008
|
-
#
|
1009
|
-
#
|
1010
|
-
#
|
1011
|
-
#
|
1012
|
-
#
|
1013
|
-
#
|
1014
|
-
#
|
1015
|
-
#
|
1016
|
-
#
|
1017
|
-
#
|
1018
|
-
# creds = {
|
1019
|
-
# authcid: username,
|
1020
|
-
# password: proc { password ||= ui.prompt_for_password },
|
1021
|
-
# oauth2_token: proc { accesstok ||= kms.fresh_access_token },
|
1022
|
-
# }
|
1023
|
-
# capa = imap.capability
|
1024
|
-
# if capa.include? "AUTH=OAUTHBEARER"
|
1025
|
-
# imap.authenticate "OAUTHBEARER", **creds # authcid, oauth2_token
|
1026
|
-
# elsif capa.include? "AUTH=XOAUTH2"
|
1027
|
-
# imap.authenticate "XOAUTH2", **creds # authcid, oauth2_token
|
1028
|
-
# elsif capa.include? "AUTH=SCRAM-SHA-256"
|
1029
|
-
# imap.authenticate "SCRAM-SHA-256", **creds # authcid, password
|
1030
|
-
# elsif capa.include? "AUTH=PLAIN"
|
1031
|
-
# imap.authenticate "PLAIN", **creds # authcid, password
|
1032
|
-
# elsif capa.include? "AUTH=DIGEST-MD5"
|
1033
|
-
# imap.authenticate "DIGEST-MD5", **creds # authcid, password
|
1034
|
-
# elsif capa.include? "LOGINDISABLED"
|
1035
|
-
# raise "the server has disabled login"
|
1036
|
-
# else
|
1493
|
+
# ==== Capabilities
|
1494
|
+
#
|
1495
|
+
# <tt>"AUTH=#{mechanism}"</tt> capabilities indicate server support for
|
1496
|
+
# mechanisms. Use #auth_capable? or #auth_mechanisms to check for support
|
1497
|
+
# before using a particular mechanism.
|
1498
|
+
#
|
1499
|
+
# if imap.auth_capable? "XOAUTH2"
|
1500
|
+
# imap.authenticate "XOAUTH2", username, oauth2_access_token
|
1501
|
+
# elsif imap.auth_capable? "PLAIN"
|
1502
|
+
# imap.authenticate "PLAIN", username, password
|
1503
|
+
# elsif !imap.capability? "LOGINDISABLED"
|
1037
1504
|
# imap.login username, password
|
1505
|
+
# else
|
1506
|
+
# raise "No acceptable authentication mechanism is available"
|
1038
1507
|
# end
|
1039
1508
|
#
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1509
|
+
# Although servers should list all supported \SASL mechanisms, they may
|
1510
|
+
# allow authentication with an unlisted +mechanism+.
|
1511
|
+
#
|
1512
|
+
# If [SASL-IR[https://www.rfc-editor.org/rfc/rfc4959.html]] is supported
|
1513
|
+
# and the appropriate <tt>"AUTH=#{mechanism}"</tt> capability is present,
|
1514
|
+
# an "initial response" may be sent as an argument to the +AUTHENTICATE+
|
1515
|
+
# command, saving a round-trip. The SASL exchange allows for server
|
1516
|
+
# challenges and client responses, but many mechanisms expect the client to
|
1517
|
+
# "respond" first. The initial response will only be sent for
|
1518
|
+
# "client-first" mechanisms.
|
1519
|
+
#
|
1520
|
+
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1521
|
+
# Previously cached #capabilities will be cleared when this method
|
1522
|
+
# completes. If the TaggedResponse to #authenticate includes updated
|
1523
|
+
# capabilities, they will be cached.
|
1524
|
+
def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
|
1525
|
+
sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
|
1526
|
+
.tap do state_authenticated! _1 end
|
1050
1527
|
end
|
1051
1528
|
|
1052
1529
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1054,16 +1531,21 @@ module Net
|
|
1054
1531
|
# this +user+. If successful, the connection enters the "_authenticated_"
|
1055
1532
|
# state.
|
1056
1533
|
#
|
1057
|
-
# Using #authenticate
|
1058
|
-
#
|
1534
|
+
# Using #authenticate {should be
|
1535
|
+
# preferred}[https://www.rfc-editor.org/rfc/rfc9051.html#name-login-command]
|
1536
|
+
# over #login. The LOGIN command is not the same as #authenticate with the
|
1537
|
+
# "LOGIN" +mechanism+.
|
1059
1538
|
#
|
1060
1539
|
# A Net::IMAP::NoResponseError is raised if authentication fails.
|
1061
1540
|
#
|
1062
1541
|
# Related: #authenticate, #starttls
|
1063
1542
|
#
|
1064
1543
|
# ==== Capabilities
|
1065
|
-
#
|
1066
|
-
#
|
1544
|
+
#
|
1545
|
+
# An IMAP client MUST NOT call #login when the server advertises the
|
1546
|
+
# +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
|
1547
|
+
# LoginDisabledError when that capability is present. See
|
1548
|
+
# Config#enforce_logindisabled.
|
1067
1549
|
#
|
1068
1550
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1069
1551
|
# Cached capabilities _must_ be invalidated after this method completes.
|
@@ -1071,35 +1553,54 @@ module Net
|
|
1071
1553
|
# ResponseCode.
|
1072
1554
|
#
|
1073
1555
|
def login(user, password)
|
1556
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
1557
|
+
raise LoginDisabledError
|
1558
|
+
end
|
1074
1559
|
send_command("LOGIN", user, password)
|
1560
|
+
.tap do state_authenticated! _1 end
|
1075
1561
|
end
|
1076
1562
|
|
1077
1563
|
# Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
|
1078
1564
|
# to select a +mailbox+ so that messages in the +mailbox+ can be accessed.
|
1079
1565
|
#
|
1080
1566
|
# After you have selected a mailbox, you may retrieve the number of items in
|
1081
|
-
# that mailbox from <tt>imap.responses
|
1082
|
-
# recent messages from <tt>imap.responses
|
1083
|
-
# these values can change if new messages arrive during a session
|
1084
|
-
# existing messages are expunged; see #add_response_handler for a
|
1085
|
-
# detect these events.
|
1567
|
+
# that mailbox from <tt>imap.responses("EXISTS", &:last)</tt>, and the
|
1568
|
+
# number of recent messages from <tt>imap.responses("RECENT", &:last)</tt>.
|
1569
|
+
# Note that these values can change if new messages arrive during a session
|
1570
|
+
# or when existing messages are expunged; see #add_response_handler for a
|
1571
|
+
# way to detect these events.
|
1572
|
+
#
|
1573
|
+
# When the +condstore+ keyword argument is true, the server is told to
|
1574
|
+
# enable the extension. If +mailbox+ supports persistence of mod-sequences,
|
1575
|
+
# the +HIGHESTMODSEQ+ ResponseCode will be sent as an untagged response to
|
1576
|
+
# #select and all `FETCH` responses will include FetchData#modseq.
|
1577
|
+
# Otherwise, the +NOMODSEQ+ ResponseCode will be sent.
|
1086
1578
|
#
|
1087
1579
|
# A Net::IMAP::NoResponseError is raised if the mailbox does not
|
1088
1580
|
# exist or is for some reason non-selectable.
|
1089
1581
|
#
|
1090
1582
|
# Related: #examine
|
1091
1583
|
#
|
1092
|
-
#
|
1584
|
+
# ==== Capabilities
|
1093
1585
|
#
|
1094
1586
|
# If [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] is supported,
|
1095
1587
|
# the server may return an untagged "NO" response with a "UIDNOTSTICKY"
|
1096
1588
|
# response code indicating that the mailstore does not support persistent
|
1097
1589
|
# UIDs:
|
1098
|
-
#
|
1099
|
-
|
1590
|
+
# imap.responses("NO", &:last)&.code&.name == "UIDNOTSTICKY"
|
1591
|
+
#
|
1592
|
+
# If [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html]] is supported,
|
1593
|
+
# the +condstore+ keyword parameter may be used.
|
1594
|
+
# imap.select("mbox", condstore: true)
|
1595
|
+
# modseq = imap.responses("HIGHESTMODSEQ", &:last)
|
1596
|
+
def select(mailbox, condstore: false)
|
1597
|
+
args = ["SELECT", mailbox]
|
1598
|
+
args << ["CONDSTORE"] if condstore
|
1100
1599
|
synchronize do
|
1600
|
+
state_unselected! # implicitly closes current mailbox
|
1101
1601
|
@responses.clear
|
1102
|
-
send_command(
|
1602
|
+
send_command(*args)
|
1603
|
+
.tap do state_selected! end
|
1103
1604
|
end
|
1104
1605
|
end
|
1105
1606
|
|
@@ -1112,10 +1613,14 @@ module Net
|
|
1112
1613
|
# exist or is for some reason non-examinable.
|
1113
1614
|
#
|
1114
1615
|
# Related: #select
|
1115
|
-
def examine(mailbox)
|
1616
|
+
def examine(mailbox, condstore: false)
|
1617
|
+
args = ["EXAMINE", mailbox]
|
1618
|
+
args << ["CONDSTORE"] if condstore
|
1116
1619
|
synchronize do
|
1620
|
+
state_unselected! # implicitly closes current mailbox
|
1117
1621
|
@responses.clear
|
1118
|
-
send_command(
|
1622
|
+
send_command(*args)
|
1623
|
+
.tap do state_selected! end
|
1119
1624
|
end
|
1120
1625
|
end
|
1121
1626
|
|
@@ -1185,10 +1690,10 @@ module Net
|
|
1185
1690
|
# to the client. +refname+ provides a context (for instance, a base
|
1186
1691
|
# directory in a directory-based mailbox hierarchy). +mailbox+ specifies a
|
1187
1692
|
# mailbox or (via wildcards) mailboxes under that context. Two wildcards
|
1188
|
-
# may be used in +mailbox+:
|
1189
|
-
# the hierarchy delimiter (for instance,
|
1190
|
-
# directory-based mailbox hierarchy); and
|
1191
|
-
# *except* the hierarchy delimiter.
|
1693
|
+
# may be used in +mailbox+: <tt>"*"</tt>, which matches all characters
|
1694
|
+
# *including* the hierarchy delimiter (for instance, "/" on a UNIX-hosted
|
1695
|
+
# directory-based mailbox hierarchy); and <tt>"%"</tt>, which matches all
|
1696
|
+
# characters *except* the hierarchy delimiter.
|
1192
1697
|
#
|
1193
1698
|
# If +refname+ is empty, +mailbox+ is used directly to determine
|
1194
1699
|
# which mailboxes to match. If +mailbox+ is empty, the root
|
@@ -1198,7 +1703,7 @@ module Net
|
|
1198
1703
|
#
|
1199
1704
|
# Related: #lsub, MailboxList
|
1200
1705
|
#
|
1201
|
-
#
|
1706
|
+
# ==== For example:
|
1202
1707
|
#
|
1203
1708
|
# imap.create("foo/bar")
|
1204
1709
|
# imap.create("foo/baz")
|
@@ -1213,7 +1718,7 @@ module Net
|
|
1213
1718
|
def list(refname, mailbox)
|
1214
1719
|
synchronize do
|
1215
1720
|
send_command("LIST", refname, mailbox)
|
1216
|
-
|
1721
|
+
clear_responses("LIST")
|
1217
1722
|
end
|
1218
1723
|
end
|
1219
1724
|
|
@@ -1236,23 +1741,22 @@ module Net
|
|
1236
1741
|
# servers, then folder creation (and listing, moving, etc) can lead to
|
1237
1742
|
# errors.
|
1238
1743
|
#
|
1239
|
-
# From RFC2342:
|
1240
|
-
#
|
1241
|
-
# Although typically a server will support only a single Personal
|
1744
|
+
# From RFC2342[https://www.rfc-editor.org/rfc/rfc2342]:
|
1745
|
+
# >>>
|
1746
|
+
# <em>Although typically a server will support only a single Personal
|
1242
1747
|
# Namespace, and a single Other User's Namespace, circumstances exist
|
1243
1748
|
# where there MAY be multiples of these, and a client MUST be prepared
|
1244
1749
|
# for them. If a client is configured such that it is required to create
|
1245
1750
|
# a certain mailbox, there can be circumstances where it is unclear which
|
1246
1751
|
# Personal Namespaces it should create the mailbox in. In these
|
1247
1752
|
# situations a client SHOULD let the user select which namespaces to
|
1248
|
-
# create the mailbox in
|
1753
|
+
# create the mailbox in.</em>
|
1249
1754
|
#
|
1250
1755
|
# Related: #list, Namespaces, Namespace
|
1251
1756
|
#
|
1252
|
-
#
|
1757
|
+
# ==== For example:
|
1253
1758
|
#
|
1254
|
-
#
|
1255
|
-
# if capabilities.include?("NAMESPACE")
|
1759
|
+
# if capable?("NAMESPACE")
|
1256
1760
|
# namespaces = imap.namespace
|
1257
1761
|
# if namespace = namespaces.personal.first
|
1258
1762
|
# prefix = namespace.prefix # e.g. "" or "INBOX."
|
@@ -1264,14 +1768,14 @@ module Net
|
|
1264
1768
|
# end
|
1265
1769
|
# end
|
1266
1770
|
#
|
1267
|
-
#
|
1771
|
+
# ==== Capabilities
|
1268
1772
|
#
|
1269
|
-
# The server's capabilities must include +NAMESPACE+
|
1270
|
-
# [RFC2342[https://
|
1773
|
+
# The server's capabilities must include either +IMAP4rev2+ or +NAMESPACE+
|
1774
|
+
# [RFC2342[https://www.rfc-editor.org/rfc/rfc2342]].
|
1271
1775
|
def namespace
|
1272
1776
|
synchronize do
|
1273
1777
|
send_command("NAMESPACE")
|
1274
|
-
|
1778
|
+
clear_responses("NAMESPACE").last
|
1275
1779
|
end
|
1276
1780
|
end
|
1277
1781
|
|
@@ -1303,7 +1807,7 @@ module Net
|
|
1303
1807
|
#
|
1304
1808
|
# Related: #list, MailboxList
|
1305
1809
|
#
|
1306
|
-
#
|
1810
|
+
# ==== Capabilities
|
1307
1811
|
#
|
1308
1812
|
# The server's capabilities must include +XLIST+,
|
1309
1813
|
# a deprecated Gmail extension (replaced by +SPECIAL-USE+).
|
@@ -1315,7 +1819,7 @@ module Net
|
|
1315
1819
|
def xlist(refname, mailbox)
|
1316
1820
|
synchronize do
|
1317
1821
|
send_command("XLIST", refname, mailbox)
|
1318
|
-
|
1822
|
+
clear_responses("XLIST")
|
1319
1823
|
end
|
1320
1824
|
end
|
1321
1825
|
|
@@ -1326,16 +1830,16 @@ module Net
|
|
1326
1830
|
#
|
1327
1831
|
# Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
|
1328
1832
|
#
|
1329
|
-
#
|
1833
|
+
# ==== Capabilities
|
1330
1834
|
#
|
1331
1835
|
# The server's capabilities must include +QUOTA+
|
1332
|
-
# [RFC2087[https://
|
1836
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1333
1837
|
def getquotaroot(mailbox)
|
1334
1838
|
synchronize do
|
1335
1839
|
send_command("GETQUOTAROOT", mailbox)
|
1336
1840
|
result = []
|
1337
|
-
result.concat(
|
1338
|
-
result.concat(
|
1841
|
+
result.concat(clear_responses("QUOTAROOT"))
|
1842
|
+
result.concat(clear_responses("QUOTA"))
|
1339
1843
|
return result
|
1340
1844
|
end
|
1341
1845
|
end
|
@@ -1347,14 +1851,14 @@ module Net
|
|
1347
1851
|
#
|
1348
1852
|
# Related: #getquotaroot, #setquota, MailboxQuota
|
1349
1853
|
#
|
1350
|
-
#
|
1854
|
+
# ==== Capabilities
|
1351
1855
|
#
|
1352
1856
|
# The server's capabilities must include +QUOTA+
|
1353
|
-
# [RFC2087[https://
|
1857
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1354
1858
|
def getquota(mailbox)
|
1355
1859
|
synchronize do
|
1356
1860
|
send_command("GETQUOTA", mailbox)
|
1357
|
-
|
1861
|
+
clear_responses("QUOTA")
|
1358
1862
|
end
|
1359
1863
|
end
|
1360
1864
|
|
@@ -1365,10 +1869,10 @@ module Net
|
|
1365
1869
|
#
|
1366
1870
|
# Related: #getquota, #getquotaroot
|
1367
1871
|
#
|
1368
|
-
#
|
1872
|
+
# ==== Capabilities
|
1369
1873
|
#
|
1370
1874
|
# The server's capabilities must include +QUOTA+
|
1371
|
-
# [RFC2087[https://
|
1875
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1372
1876
|
def setquota(mailbox, quota)
|
1373
1877
|
if quota.nil?
|
1374
1878
|
data = '()'
|
@@ -1385,10 +1889,10 @@ module Net
|
|
1385
1889
|
#
|
1386
1890
|
# Related: #getacl
|
1387
1891
|
#
|
1388
|
-
#
|
1892
|
+
# ==== Capabilities
|
1389
1893
|
#
|
1390
1894
|
# The server's capabilities must include +ACL+
|
1391
|
-
# [RFC4314[https://
|
1895
|
+
# [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
|
1392
1896
|
def setacl(mailbox, user, rights)
|
1393
1897
|
if rights.nil?
|
1394
1898
|
send_command("SETACL", mailbox, user, "")
|
@@ -1403,14 +1907,14 @@ module Net
|
|
1403
1907
|
#
|
1404
1908
|
# Related: #setacl, MailboxACLItem
|
1405
1909
|
#
|
1406
|
-
#
|
1910
|
+
# ==== Capabilities
|
1407
1911
|
#
|
1408
1912
|
# The server's capabilities must include +ACL+
|
1409
|
-
# [RFC4314[https://
|
1913
|
+
# [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
|
1410
1914
|
def getacl(mailbox)
|
1411
1915
|
synchronize do
|
1412
1916
|
send_command("GETACL", mailbox)
|
1413
|
-
|
1917
|
+
clear_responses("ACL").last
|
1414
1918
|
end
|
1415
1919
|
end
|
1416
1920
|
|
@@ -1425,31 +1929,74 @@ module Net
|
|
1425
1929
|
def lsub(refname, mailbox)
|
1426
1930
|
synchronize do
|
1427
1931
|
send_command("LSUB", refname, mailbox)
|
1428
|
-
|
1932
|
+
clear_responses("LSUB")
|
1429
1933
|
end
|
1430
1934
|
end
|
1431
1935
|
|
1432
|
-
# Sends a {STATUS
|
1936
|
+
# Sends a {STATUS command [IMAP4rev1 §6.3.10]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.10]
|
1433
1937
|
# and returns the status of the indicated +mailbox+. +attr+ is a list of one
|
1434
|
-
# or more attributes whose statuses are to be requested.
|
1435
|
-
#
|
1938
|
+
# or more attributes whose statuses are to be requested.
|
1939
|
+
#
|
1940
|
+
# The return value is a hash of attributes. Most status attributes return
|
1941
|
+
# integer values, but some return other value types (documented below).
|
1942
|
+
#
|
1943
|
+
# A Net::IMAP::NoResponseError is raised if status values
|
1944
|
+
# for +mailbox+ cannot be returned; for instance, because it
|
1945
|
+
# does not exist.
|
1946
|
+
#
|
1947
|
+
# ==== Supported attributes
|
1948
|
+
#
|
1949
|
+
# +MESSAGES+:: The number of messages in the mailbox.
|
1950
|
+
#
|
1951
|
+
# +UIDNEXT+:: The next unique identifier value of the mailbox.
|
1952
|
+
#
|
1953
|
+
# +UIDVALIDITY+:: The unique identifier validity value of the mailbox.
|
1954
|
+
#
|
1955
|
+
# +UNSEEN+:: The number of messages without the <tt>\Seen</tt> flag.
|
1956
|
+
#
|
1957
|
+
# +DELETED+:: The number of messages with the <tt>\Deleted</tt> flag.
|
1958
|
+
#
|
1959
|
+
# +SIZE+::
|
1960
|
+
# The approximate size of the mailbox---must be greater than or equal to
|
1961
|
+
# the sum of all messages' +RFC822.SIZE+ fetch item values.
|
1962
|
+
#
|
1963
|
+
# +HIGHESTMODSEQ+::
|
1964
|
+
# The highest mod-sequence value of all messages in the mailbox. See
|
1965
|
+
# +CONDSTORE+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
1966
|
+
#
|
1967
|
+
# +MAILBOXID+::
|
1968
|
+
# A server-allocated unique _string_ identifier for the mailbox. See
|
1969
|
+
# +OBJECTID+ {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html].
|
1970
|
+
#
|
1971
|
+
# +RECENT+::
|
1972
|
+
# The number of messages with the <tt>\Recent</tt> flag.
|
1973
|
+
# _NOTE:_ +RECENT+ was removed from IMAP4rev2.
|
1436
1974
|
#
|
1437
|
-
#
|
1438
|
-
#
|
1439
|
-
# UNSEEN:: the number of unseen messages in the mailbox.
|
1975
|
+
# Unsupported attributes may be requested. The attribute value will be
|
1976
|
+
# either an Integer or an ExtensionData object.
|
1440
1977
|
#
|
1441
|
-
#
|
1978
|
+
# ==== For example:
|
1442
1979
|
#
|
1443
1980
|
# p imap.status("inbox", ["MESSAGES", "RECENT"])
|
1444
1981
|
# #=> {"RECENT"=>0, "MESSAGES"=>44}
|
1445
1982
|
#
|
1446
|
-
#
|
1447
|
-
#
|
1448
|
-
#
|
1983
|
+
# ==== Capabilities
|
1984
|
+
#
|
1985
|
+
# +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
|
1986
|
+
# <tt>STATUS=SIZE</tt>
|
1987
|
+
# {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
|
1988
|
+
#
|
1989
|
+
# +DELETED+ requires the server's capabilities to include +IMAP4rev2+.
|
1990
|
+
#
|
1991
|
+
# +HIGHESTMODSEQ+ requires the server's capabilities to include +CONDSTORE+
|
1992
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
1993
|
+
#
|
1994
|
+
# +MAILBOXID+ requires the server's capabilities to include +OBJECTID+
|
1995
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html].
|
1449
1996
|
def status(mailbox, attr)
|
1450
1997
|
synchronize do
|
1451
1998
|
send_command("STATUS", mailbox, attr)
|
1452
|
-
|
1999
|
+
clear_responses("STATUS").last&.attr
|
1453
2000
|
end
|
1454
2001
|
end
|
1455
2002
|
|
@@ -1473,7 +2020,7 @@ module Net
|
|
1473
2020
|
# not exist (it is not created automatically), or if the flags,
|
1474
2021
|
# date_time, or message arguments contain errors.
|
1475
2022
|
#
|
1476
|
-
#
|
2023
|
+
# ==== Capabilities
|
1477
2024
|
#
|
1478
2025
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
1479
2026
|
# supported and the destination supports persistent UIDs, the server's
|
@@ -1512,6 +2059,7 @@ module Net
|
|
1512
2059
|
# Related: #unselect
|
1513
2060
|
def close
|
1514
2061
|
send_command("CLOSE")
|
2062
|
+
.tap do state_authenticated! end
|
1515
2063
|
end
|
1516
2064
|
|
1517
2065
|
# Sends an {UNSELECT command [RFC3691 §2]}[https://www.rfc-editor.org/rfc/rfc3691#section-3]
|
@@ -1522,153 +2070,544 @@ module Net
|
|
1522
2070
|
#
|
1523
2071
|
# Related: #close
|
1524
2072
|
#
|
1525
|
-
#
|
2073
|
+
# ==== Capabilities
|
1526
2074
|
#
|
1527
|
-
# The server's capabilities must include +UNSELECT+
|
1528
|
-
# [RFC3691[https://
|
2075
|
+
# The server's capabilities must include either +IMAP4rev2+ or +UNSELECT+
|
2076
|
+
# [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
|
1529
2077
|
def unselect
|
1530
2078
|
send_command("UNSELECT")
|
2079
|
+
.tap do state_authenticated! end
|
1531
2080
|
end
|
1532
2081
|
|
2082
|
+
# call-seq:
|
2083
|
+
# expunge -> array of message sequence numbers
|
2084
|
+
# expunge -> VanishedData of UIDs
|
2085
|
+
#
|
1533
2086
|
# Sends an {EXPUNGE command [IMAP4rev1 §6.4.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.3]
|
1534
|
-
#
|
1535
|
-
# selected mailbox
|
2087
|
+
# to permanently remove all messages with the +\Deleted+ flag from the
|
2088
|
+
# currently selected mailbox.
|
2089
|
+
#
|
2090
|
+
# Returns either an array of expunged message <em>sequence numbers</em> or
|
2091
|
+
# (when the appropriate capability is enabled) VanishedData of expunged
|
2092
|
+
# UIDs. Previously unhandled +EXPUNGE+ or +VANISHED+ responses are merged
|
2093
|
+
# with the direct response to this command. <tt>VANISHED (EARLIER)</tt>
|
2094
|
+
# responses will _not_ be merged.
|
2095
|
+
#
|
2096
|
+
# When no messages have been expunged, an empty array is returned,
|
2097
|
+
# regardless of which extensions are enabled. In a future release, an empty
|
2098
|
+
# VanishedData may be returned, based on the currently enabled extensions.
|
1536
2099
|
#
|
1537
2100
|
# Related: #uid_expunge
|
2101
|
+
#
|
2102
|
+
# ==== Capabilities
|
2103
|
+
#
|
2104
|
+
# When either QRESYNC[https://www.rfc-editor.org/rfc/rfc7162] or
|
2105
|
+
# UIDONLY[https://www.rfc-editor.org/rfc/rfc9586] are enabled, #expunge
|
2106
|
+
# returns VanishedData, which contains UIDs---<em>not message sequence
|
2107
|
+
# numbers</em>.
|
1538
2108
|
def expunge
|
1539
|
-
|
1540
|
-
send_command("EXPUNGE")
|
1541
|
-
return @responses.delete("EXPUNGE")
|
1542
|
-
end
|
2109
|
+
expunge_internal("EXPUNGE")
|
1543
2110
|
end
|
1544
2111
|
|
2112
|
+
# call-seq:
|
2113
|
+
# uid_expunge{uid_set) -> array of message sequence numbers
|
2114
|
+
# uid_expunge{uid_set) -> VanishedData of UIDs
|
2115
|
+
#
|
1545
2116
|
# Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
|
1546
2117
|
# {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
|
1547
2118
|
# to permanently remove all messages that have both the <tt>\\Deleted</tt>
|
1548
2119
|
# flag set and a UID that is included in +uid_set+.
|
1549
2120
|
#
|
2121
|
+
# Returns the same result type as #expunge.
|
2122
|
+
#
|
1550
2123
|
# By using #uid_expunge instead of #expunge when resynchronizing with
|
1551
2124
|
# the server, the client can ensure that it does not inadvertantly
|
1552
2125
|
# remove any messages that have been marked as <tt>\\Deleted</tt> by other
|
1553
2126
|
# clients between the time that the client was last connected and
|
1554
2127
|
# the time the client resynchronizes.
|
1555
2128
|
#
|
1556
|
-
# *Note:*
|
1557
|
-
# >>>
|
1558
|
-
# Although the command takes a set of UIDs for its argument, the
|
1559
|
-
# server still returns regular EXPUNGE responses, which contain
|
1560
|
-
# a <em>sequence number</em>. These will be deleted from
|
1561
|
-
# #responses and this method returns them as an array of
|
1562
|
-
# <em>sequence number</em> integers.
|
1563
|
-
#
|
1564
2129
|
# Related: #expunge
|
1565
2130
|
#
|
1566
|
-
#
|
2131
|
+
# ==== Capabilities
|
1567
2132
|
#
|
1568
|
-
# The server's capabilities must include +UIDPLUS+
|
2133
|
+
# The server's capabilities must include either +IMAP4rev2+ or +UIDPLUS+
|
1569
2134
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
2135
|
+
#
|
2136
|
+
# Otherwise, #uid_expunge is updated by extensions in the same way as
|
2137
|
+
# #expunge.
|
1570
2138
|
def uid_expunge(uid_set)
|
1571
|
-
|
1572
|
-
send_command("UID EXPUNGE", MessageSet.new(uid_set))
|
1573
|
-
return @responses.delete("EXPUNGE")
|
1574
|
-
end
|
2139
|
+
expunge_internal("UID EXPUNGE", SequenceSet.new(uid_set))
|
1575
2140
|
end
|
1576
2141
|
|
2142
|
+
# :call-seq:
|
2143
|
+
# search(criteria, charset = nil) -> result
|
2144
|
+
# search(criteria, charset: nil, return: nil) -> result
|
2145
|
+
#
|
1577
2146
|
# Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
|
1578
|
-
# to search the mailbox for messages that match the given
|
1579
|
-
#
|
1580
|
-
#
|
1581
|
-
#
|
2147
|
+
# to search the mailbox for messages that match the given search +criteria+,
|
2148
|
+
# and returns either a SearchResult or an ESearchResult. SearchResult
|
2149
|
+
# inherits from Array (for backward compatibility) but adds
|
2150
|
+
# SearchResult#modseq when the +CONDSTORE+ capability has been enabled.
|
2151
|
+
# ESearchResult also implements {#to_a}[rdoc-ref:ESearchResult#to_a], for
|
2152
|
+
# compatibility with SearchResult.
|
2153
|
+
#
|
2154
|
+
# +criteria+ is one or more search keys and their arguments, which may be
|
2155
|
+
# provided as an array or a string.
|
2156
|
+
# See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
|
2157
|
+
# and {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
|
2158
|
+
#
|
2159
|
+
# +return+ options control what kind of information is returned about
|
2160
|
+
# messages matching the search +criteria+. Specifying +return+ should force
|
2161
|
+
# the server to return an ESearchResult instead of a SearchResult, but some
|
2162
|
+
# servers disobey this requirement. <em>Requires an extended search
|
2163
|
+
# capability, such as +ESEARCH+ or +IMAP4rev2+.</em>
|
2164
|
+
# See {"Argument translation"}[rdoc-ref:#search@Argument+translation] and
|
2165
|
+
# {"Supported return options"}[rdoc-ref:#search@Supported+return+options],
|
2166
|
+
# below.
|
2167
|
+
#
|
2168
|
+
# +charset+ is the name of the {registered character
|
2169
|
+
# set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
2170
|
+
# used by strings in the search +criteria+. When +charset+ isn't specified,
|
2171
|
+
# either <tt>"US-ASCII"</tt> or <tt>"UTF-8"</tt> is assumed, depending on
|
2172
|
+
# the server's capabilities.
|
2173
|
+
#
|
2174
|
+
# _NOTE:_ Return options and charset may be sent as part of +criteria+. Do
|
2175
|
+
# not use the +return+ or +charset+ arguments when either return options or
|
2176
|
+
# charset are embedded in +criteria+.
|
1582
2177
|
#
|
1583
2178
|
# Related: #uid_search
|
1584
2179
|
#
|
1585
|
-
#
|
2180
|
+
# ==== For example:
|
2181
|
+
#
|
2182
|
+
# imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
|
2183
|
+
# #=> [1, 6, 7, 8]
|
1586
2184
|
#
|
1587
|
-
#
|
2185
|
+
# The following assumes the server supports +ESEARCH+ and +CONDSTORE+:
|
2186
|
+
#
|
2187
|
+
# result = imap.uid_search(["UID", 12345.., "MODSEQ", 620_162_338],
|
2188
|
+
# return: %w(all count min max))
|
2189
|
+
# # => #<data Net::IMAP::ESearchResult tag="RUBY0123", uid=true,
|
2190
|
+
# # data=[["ALL", Net::IMAP::SequenceSet["12346:12349,22222:22230"]],
|
2191
|
+
# # ["COUNT", 13], ["MIN", 12346], ["MAX", 22230],
|
2192
|
+
# # ["MODSEQ", 917162488]]>
|
2193
|
+
# result.to_a # => [12346, 12347, 12348, 12349, 22222, 22223, 22224,
|
2194
|
+
# # 22225, 22226, 22227, 22228, 22229, 22230]
|
2195
|
+
# result.uid? # => true
|
2196
|
+
# result.count # => 13
|
2197
|
+
# result.min # => 12346
|
2198
|
+
# result.max # => 22230
|
2199
|
+
# result.modseq # => 917162488
|
2200
|
+
#
|
2201
|
+
# Using +return+ options to limit the result to only min, max, and count:
|
2202
|
+
#
|
2203
|
+
# result = imap.uid_search(["UID", 12345..,], return: %w(count min max))
|
2204
|
+
# # => #<data Net::IMAP::ESearchResult tag="RUBY0124", uid=true,
|
2205
|
+
# # data=[["COUNT", 13], ["MIN", 12346], ["MAX", 22230]]>
|
2206
|
+
# result.to_a # => []
|
2207
|
+
# result.count # => 13
|
2208
|
+
# result.min # => 12346
|
2209
|
+
# result.max # => 22230
|
2210
|
+
#
|
2211
|
+
# Return options and charset may be sent as keyword args or embedded in the
|
2212
|
+
# +criteria+ arg, but they must be in the correct order: <tt>"RETURN (...)
|
2213
|
+
# CHARSET ... criteria..."</tt>. The following searches
|
2214
|
+
# send the exact same command to the server:
|
2215
|
+
#
|
2216
|
+
# # Return options and charset as keyword arguments (preferred)
|
2217
|
+
# imap.search(%w(OR UNSEEN FLAGGED), return: %w(MIN MAX), charset: "UTF-8")
|
2218
|
+
# # Embedding return and charset in the criteria array
|
2219
|
+
# imap.search(["RETURN", %w(MIN MAX), "CHARSET", "UTF-8", *%w(OR UNSEEN FLAGGED)])
|
2220
|
+
# # Embedding return and charset in the criteria string
|
2221
|
+
# imap.search("RETURN (MIN MAX) CHARSET UTF-8 OR UNSEEN FLAGGED")
|
2222
|
+
#
|
2223
|
+
# Sending charset as the second positional argument is supported for
|
2224
|
+
# backward compatibility. Future versions may print a deprecation warning:
|
2225
|
+
# imap.search(%w(OR UNSEEN FLAGGED), "UTF-8", return: %w(MIN MAX))
|
2226
|
+
#
|
2227
|
+
# ==== Argument translation
|
2228
|
+
#
|
2229
|
+
# [+return+ options]
|
2230
|
+
# Must be an Array. Return option names may be either strings or symbols.
|
2231
|
+
# +Range+ elements which begin and end with negative integers are encoded
|
2232
|
+
# for use with +PARTIAL+--any other ranges are converted to SequenceSet.
|
2233
|
+
# Unlike +criteria+, other return option arguments are not automatically
|
2234
|
+
# converted to SequenceSet.
|
2235
|
+
#
|
2236
|
+
# [When +criteria+ is an Array]
|
2237
|
+
# When the array begins with <tt>"RETURN"</tt> (case insensitive), the
|
2238
|
+
# second array element is translated like the +return+ parameter (as
|
2239
|
+
# described above).
|
2240
|
+
#
|
2241
|
+
# Every other member is a +SEARCH+ command argument:
|
2242
|
+
# [SequenceSet]
|
2243
|
+
# Encoded as an \IMAP +sequence-set+ with SequenceSet#valid_string.
|
2244
|
+
# [Set, Range, <tt>-1</tt>, +:*+, responds to +#to_sequence_set+]
|
2245
|
+
# Converted to SequenceSet for validation and encoding.
|
2246
|
+
# [nested sequence-set +Array+]
|
2247
|
+
# When every element in a nested array is one of the above types, a
|
2248
|
+
# positive +Integer+, a sequence-set formatted +String+, or a deeply
|
2249
|
+
# nested +Array+ of these same types, the array will be converted to
|
2250
|
+
# SequenceSet for validation and encoding.
|
2251
|
+
# [Any other nested +Array+]
|
2252
|
+
# Otherwise, a nested array is encoded as a parenthesized list, to
|
2253
|
+
# combine multiple search keys (e.g., for use with +OR+ and +NOT+).
|
2254
|
+
# [+String+]
|
2255
|
+
# Sent verbatim when it is a valid \IMAP +atom+, and encoded as an \IMAP
|
2256
|
+
# +quoted+ or +literal+ string otherwise. Every standard search key
|
2257
|
+
# name is a valid \IMAP +atom+ and every standard search key string
|
2258
|
+
# argument is an +astring+ which may be encoded as +atom+, +quoted+, or
|
2259
|
+
# +literal+.
|
2260
|
+
#
|
2261
|
+
# *Note:* <tt>*</tt> is not a valid \IMAP +atom+ character. Any string
|
2262
|
+
# containing <tt>*</tt> will be encoded as a +quoted+ string, _not_ a
|
2263
|
+
# +sequence-set+.
|
2264
|
+
# [+Integer+ (except for <tt>-1</tt>)]
|
2265
|
+
# Encoded using +#to_s+.
|
2266
|
+
# [+Date+]
|
2267
|
+
# Encoded as an \IMAP date (see ::encode_date).
|
2268
|
+
#
|
2269
|
+
# [When +criteria+ is a String]
|
2270
|
+
# +criteria+ will be sent directly to the server <em>without any
|
2271
|
+
# validation or encoding</em>.
|
2272
|
+
#
|
2273
|
+
# <em>*WARNING:* This is vulnerable to injection attacks when external
|
2274
|
+
# inputs are used.</em>
|
2275
|
+
#
|
2276
|
+
# ==== Supported return options
|
2277
|
+
#
|
2278
|
+
# For full definitions of the standard return options and return data, see
|
2279
|
+
# the relevant RFCs.
|
2280
|
+
#
|
2281
|
+
# [+ALL+]
|
2282
|
+
# Returns ESearchResult#all with a SequenceSet of all matching sequence
|
2283
|
+
# numbers or UIDs. This is the default, when return options are empty.
|
2284
|
+
#
|
2285
|
+
# For compatibility with SearchResult, ESearchResult#to_a returns an
|
2286
|
+
# Array of message sequence numbers or UIDs.
|
2287
|
+
#
|
2288
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2289
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2290
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2291
|
+
#
|
2292
|
+
# [+COUNT+]
|
2293
|
+
# Returns ESearchResult#count with the number of matching messages.
|
2294
|
+
#
|
2295
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2296
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2297
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2298
|
+
#
|
2299
|
+
# [+MAX+]
|
2300
|
+
# Returns ESearchResult#max with the highest matching sequence number or
|
2301
|
+
# UID.
|
2302
|
+
#
|
2303
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2304
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2305
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2306
|
+
#
|
2307
|
+
# [+MIN+]
|
2308
|
+
# Returns ESearchResult#min with the lowest matching sequence number or
|
2309
|
+
# UID.
|
2310
|
+
#
|
2311
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2312
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2313
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2314
|
+
#
|
2315
|
+
# [+PARTIAL+ _range_]
|
2316
|
+
# Returns ESearchResult#partial with a SequenceSet of a subset of
|
2317
|
+
# matching sequence numbers or UIDs, as selected by _range_. As with
|
2318
|
+
# sequence numbers, the first result is +1+: <tt>1..500</tt> selects the
|
2319
|
+
# first 500 search results (in mailbox order), <tt>501..1000</tt> the
|
2320
|
+
# second 500, and so on. _range_ may also be negative: <tt>-500..-1</tt>
|
2321
|
+
# selects the last 500 search results.
|
2322
|
+
#
|
2323
|
+
# <em>Requires either the <tt>CONTEXT=SEARCH</tt> or +PARTIAL+ capabability.</em>
|
2324
|
+
# {[RFC5267]}[https://rfc-editor.org/rfc/rfc5267]
|
2325
|
+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
|
2326
|
+
#
|
2327
|
+
# ===== +MODSEQ+ return data
|
2328
|
+
#
|
2329
|
+
# ESearchResult#modseq return data does not have a corresponding return
|
2330
|
+
# option. Instead, it is returned if the +MODSEQ+ search key is used or
|
2331
|
+
# when the +CONDSTORE+ extension is enabled for the selected mailbox.
|
2332
|
+
# See [{RFC4731 §3.2}[https://www.rfc-editor.org/rfc/rfc4731#section-3.2]]
|
2333
|
+
# or [{RFC7162 §2.1.5}[https://www.rfc-editor.org/rfc/rfc7162#section-3.1.5]].
|
2334
|
+
#
|
2335
|
+
# ===== +RFC4466+ compatible extensions
|
2336
|
+
#
|
2337
|
+
# {RFC4466 §2.6}[https://www.rfc-editor.org/rfc/rfc4466.html#section-2.6]
|
2338
|
+
# defines standard syntax for search extensions. Net::IMAP allows sending
|
2339
|
+
# unsupported search return options and will parse unsupported search
|
2340
|
+
# extensions' return values into ExtensionData. Please note that this is an
|
2341
|
+
# intentionally _unstable_ API. Future releases may return different
|
2342
|
+
# (incompatible) objects, <em>without deprecation or warning</em>.
|
2343
|
+
#
|
2344
|
+
# ==== Search keys
|
2345
|
+
#
|
2346
|
+
# For full definitions of the standard search +criteria+,
|
1588
2347
|
# see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
|
1589
2348
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
1590
2349
|
# in addition to documentation for
|
1591
|
-
# any
|
1592
|
-
# reported by #capability which may define additional search filters, e.g:
|
2350
|
+
# any #capabilities which may define additional search filters, such as
|
1593
2351
|
# +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
|
1594
|
-
# +SAVEDATE+.
|
2352
|
+
# +SAVEDATE+.
|
1595
2353
|
#
|
1596
|
-
#
|
1597
|
-
#
|
1598
|
-
#
|
2354
|
+
# With the exception of <em>sequence-set</em> and <em>parenthesized
|
2355
|
+
# list</em>, all search keys are composed of prefix label with zero or more
|
2356
|
+
# arguments. The number and type of arguments is specific to each search
|
2357
|
+
# key.
|
1599
2358
|
#
|
1600
|
-
#
|
1601
|
-
# <b><date></b>. The date argument has a format similar
|
1602
|
-
# to <tt>8-Aug-2002</tt>, and can be formatted using
|
1603
|
-
# Net::IMAP.format_date.
|
2359
|
+
# ===== Search keys that match all messages
|
1604
2360
|
#
|
1605
|
-
#
|
2361
|
+
# [+ALL+]
|
2362
|
+
# The default initial key. Matches every message in the mailbox.
|
1606
2363
|
#
|
1607
|
-
#
|
2364
|
+
# [+SAVEDATESUPPORTED+]
|
2365
|
+
# Matches every message in the mailbox when the mailbox supports the save
|
2366
|
+
# date attribute. Otherwise, it matches no messages.
|
1608
2367
|
#
|
1609
|
-
#
|
2368
|
+
# <em>Requires +SAVEDATE+ capability</em>.
|
2369
|
+
# {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
|
1610
2370
|
#
|
1611
|
-
#
|
2371
|
+
# ===== Sequence set search keys
|
1612
2372
|
#
|
1613
|
-
#
|
2373
|
+
# [_sequence-set_]
|
2374
|
+
# Matches messages with message sequence numbers in _sequence-set_.
|
1614
2375
|
#
|
1615
|
-
#
|
2376
|
+
# _Note:_ this search key has no label.
|
1616
2377
|
#
|
1617
|
-
#
|
1618
|
-
#
|
2378
|
+
# <em>+UIDONLY+ must *not* be enabled.</em>
|
2379
|
+
# {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.html]
|
1619
2380
|
#
|
1620
|
-
#
|
2381
|
+
# [+UID+ _sequence-set_]
|
2382
|
+
# Matches messages with a UID in _sequence-set_.
|
1621
2383
|
#
|
1622
|
-
#
|
2384
|
+
# ===== Compound search keys
|
1623
2385
|
#
|
1624
|
-
#
|
2386
|
+
# [(_search-key_ _search-key_...)]
|
2387
|
+
# Combines one or more _search-key_ arguments to match
|
2388
|
+
# messages which match all contained search keys. Useful for +OR+, +NOT+,
|
2389
|
+
# and other search keys with _search-key_ arguments.
|
1625
2390
|
#
|
1626
|
-
#
|
2391
|
+
# _Note:_ this search key has no label.
|
1627
2392
|
#
|
1628
|
-
#
|
1629
|
-
#
|
2393
|
+
# [+OR+ _search-key_ _search-key_]
|
2394
|
+
# Matches messages which match either _search-key_ argument.
|
1630
2395
|
#
|
1631
|
-
|
1632
|
-
|
1633
|
-
end
|
1634
|
-
|
1635
|
-
# Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1636
|
-
# to search the mailbox for messages that match the given searching
|
1637
|
-
# criteria, and returns unique identifiers (<tt>UID</tt>s).
|
2396
|
+
# [+NOT+ _search-key_]
|
2397
|
+
# Matches messages which do not match _search-key_.
|
1638
2398
|
#
|
1639
|
-
#
|
1640
|
-
|
1641
|
-
return search_internal("UID SEARCH", keys, charset)
|
1642
|
-
end
|
1643
|
-
|
1644
|
-
# Sends a {FETCH command [IMAP4rev1 §6.4.5]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.5]
|
1645
|
-
# to retrieve data associated with a message in the mailbox.
|
2399
|
+
# [+FUZZY+ _search-key_]
|
2400
|
+
# Uses fuzzy matching for the specified search key.
|
1646
2401
|
#
|
1647
|
-
#
|
1648
|
-
#
|
1649
|
-
# where -1 represents a '*' for use in range notation like 100..-1
|
1650
|
-
# being interpreted as '100:*'. Beware that the +exclude_end?+
|
1651
|
-
# property of a Range object is ignored, and the contents of a
|
1652
|
-
# range are independent of the order of the range endpoints as per
|
1653
|
-
# the protocol specification, so 1...5, 5..1 and 5...1 are all
|
1654
|
-
# equivalent to 1..5.
|
2402
|
+
# <em>Requires <tt>SEARCH=FUZZY</tt> capability.</em>
|
2403
|
+
# {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
|
1655
2404
|
#
|
1656
|
-
#
|
1657
|
-
# for FetchData for a list of valid attributes.
|
2405
|
+
# ===== Flags search keys
|
1658
2406
|
#
|
1659
|
-
#
|
1660
|
-
#
|
2407
|
+
# [+ANSWERED+, +UNANSWERED+]
|
2408
|
+
# Matches messages with or without the <tt>\\Answered</tt> flag.
|
2409
|
+
# [+DELETED+, +UNDELETED+]
|
2410
|
+
# Matches messages with or without the <tt>\\Deleted</tt> flag.
|
2411
|
+
# [+DRAFT+, +UNDRAFT+]
|
2412
|
+
# Matches messages with or without the <tt>\\Draft</tt> flag.
|
2413
|
+
# [+FLAGGED+, +UNFLAGGED+]
|
2414
|
+
# Matches messages with or without the <tt>\\Flagged</tt> flag.
|
2415
|
+
# [+SEEN+, +UNSEEN+]
|
2416
|
+
# Matches messages with or without the <tt>\\Seen</tt> flag.
|
2417
|
+
# [+KEYWORD+ _keyword_, +UNKEYWORD+ _keyword_]
|
2418
|
+
# Matches messages with or without the specified _keyword_.
|
1661
2419
|
#
|
1662
|
-
#
|
2420
|
+
# [+RECENT+, +UNRECENT+]
|
2421
|
+
# Matches messages with or without the <tt>\\Recent</tt> flag.
|
1663
2422
|
#
|
1664
|
-
#
|
2423
|
+
# *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
|
2424
|
+
# [+NEW+]
|
2425
|
+
# Equivalent to <tt>(RECENT UNSEEN)</tt>.
|
1665
2426
|
#
|
1666
|
-
#
|
1667
|
-
#
|
1668
|
-
#
|
1669
|
-
#
|
1670
|
-
#
|
1671
|
-
#
|
2427
|
+
# *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
|
2428
|
+
#
|
2429
|
+
# ===== Header field substring search keys
|
2430
|
+
#
|
2431
|
+
# [+BCC+ _substring_]
|
2432
|
+
# Matches when _substring_ is in the envelope's +BCC+ field.
|
2433
|
+
# [+CC+ _substring_]
|
2434
|
+
# Matches when _substring_ is in the envelope's +CC+ field.
|
2435
|
+
# [+FROM+ _substring_]
|
2436
|
+
# Matches when _substring_ is in the envelope's +FROM+ field.
|
2437
|
+
# [+SUBJECT+ _substring_]
|
2438
|
+
# Matches when _substring_ is in the envelope's +SUBJECT+ field.
|
2439
|
+
# [+TO+ _substring_]
|
2440
|
+
# Matches when _substring_ is in the envelope's +TO+ field.
|
2441
|
+
#
|
2442
|
+
# [+HEADER+ _field_ _substring_]
|
2443
|
+
# Matches when _substring_ is in the specified header _field_.
|
2444
|
+
#
|
2445
|
+
# ===== Body text search keys
|
2446
|
+
# [+BODY+ _string_]
|
2447
|
+
# Matches when _string_ is in the body of the message.
|
2448
|
+
# Does not match on header fields.
|
2449
|
+
#
|
2450
|
+
# The server _may_ use flexible matching, rather than simple substring
|
2451
|
+
# matches. For example, this may use stemming or match only full words.
|
2452
|
+
#
|
2453
|
+
# [+TEXT+ _string_]
|
2454
|
+
# Matches when _string_ is in the header or body of the message.
|
2455
|
+
#
|
2456
|
+
# The server _may_ use flexible matching, rather than simple substring
|
2457
|
+
# matches. For example, this may use stemming or match only full words.
|
2458
|
+
#
|
2459
|
+
# ===== Date/Time search keys
|
2460
|
+
#
|
2461
|
+
# [+SENTBEFORE+ _date_]
|
2462
|
+
# [+SENTON+ _date_]
|
2463
|
+
# [+SENTSINCE+ _date_]
|
2464
|
+
# Matches when the +Date+ header is earlier than, on, or later than _date_.
|
2465
|
+
#
|
2466
|
+
# [+BEFORE+ _date_]
|
2467
|
+
# [+ON+ _date_]
|
2468
|
+
# [+SINCE+ _date_]
|
2469
|
+
# Matches when the +INTERNALDATE+ is earlier than, on, or later than
|
2470
|
+
# _date_.
|
2471
|
+
#
|
2472
|
+
# [+OLDER+ _interval_]
|
2473
|
+
# [+YOUNGER+ _interval_]
|
2474
|
+
# Matches when the +INTERNALDATE+ is more/less than _interval_ seconds ago.
|
2475
|
+
#
|
2476
|
+
# <em>Requires +WITHIN+ capability</em>.
|
2477
|
+
# {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
|
2478
|
+
#
|
2479
|
+
# [+SAVEDBEFORE+ _date_]
|
2480
|
+
# [+SAVEDON+ _date_]
|
2481
|
+
# [+SAVEDSINCE+ _date_]
|
2482
|
+
# Matches when the save date is earlier than, on, or later than _date_.
|
2483
|
+
#
|
2484
|
+
# <em>Requires +SAVEDATE+ capability.</em>
|
2485
|
+
# {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
|
2486
|
+
#
|
2487
|
+
# ===== Other message attribute search keys
|
2488
|
+
#
|
2489
|
+
# [+SMALLER+ _bytes_]
|
2490
|
+
# [+LARGER+ _bytes_]
|
2491
|
+
# Matches when +RFC822.SIZE+ is smaller or larger than _bytes_.
|
2492
|
+
#
|
2493
|
+
# [+ANNOTATION+ _entry_ _attr_ _value_]
|
2494
|
+
# Matches messages that have annotations with entries matching _entry_,
|
2495
|
+
# attributes matching _attr_, and _value_ in the attribute's values.
|
2496
|
+
#
|
2497
|
+
# <em>Requires +ANNOTATE-EXPERIMENT-1+ capability</em>.
|
2498
|
+
# {[RFC5257]}[https://www.rfc-editor.org/rfc/rfc5257.html].
|
2499
|
+
#
|
2500
|
+
# [+FILTER+ _filter_]
|
2501
|
+
# References a _filter_ that is stored on the server and matches all
|
2502
|
+
# messages which would be matched by that filter's search criteria.
|
2503
|
+
#
|
2504
|
+
# <em>Requires +FILTERS+ capability</em>.
|
2505
|
+
# {[RFC5466]}[https://www.rfc-editor.org/rfc/rfc5466.html#section-3.1]
|
2506
|
+
#
|
2507
|
+
# [+MODSEQ+ _modseq_]
|
2508
|
+
# Matches when +MODSEQ+ is greater than or equal to _modseq_.
|
2509
|
+
#
|
2510
|
+
# <em>Requires +CONDSTORE+ capability</em>.
|
2511
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
|
2512
|
+
#
|
2513
|
+
# [+MODSEQ+ _entry_ _entry-type_ _modseq_]
|
2514
|
+
# Matches when a specific metadata _entry_ has been updated since
|
2515
|
+
# _modseq_.
|
2516
|
+
#
|
2517
|
+
# For flags, the corresponding _entry_ name is
|
2518
|
+
# <tt>"/flags/#{flag_name}"</tt>, where _flag_name_ includes the
|
2519
|
+
# <tt>\\</tt> prefix. _entry-type_ can be one of <tt>"shared"</tt>,
|
2520
|
+
# <tt>"priv"</tt> (private), or <tt>"all"</tt>.
|
2521
|
+
#
|
2522
|
+
# <em>Requires +CONDSTORE+ capability</em>.
|
2523
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
|
2524
|
+
#
|
2525
|
+
# [+EMAILID+ _objectid_]
|
2526
|
+
# [+THREADID+ _objectid_]
|
2527
|
+
# Matches when +EMAILID+/+THREADID+ is equal to _objectid_
|
2528
|
+
# (substring matches are not supported).
|
2529
|
+
#
|
2530
|
+
# <em>Requires +OBJECTID+ capability</em>.
|
2531
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-6]
|
2532
|
+
#
|
2533
|
+
# ==== Capabilities
|
2534
|
+
#
|
2535
|
+
# Return options should only be specified when the server supports
|
2536
|
+
# +IMAP4rev2+ or an extension that allows them, such as +ESEARCH+
|
2537
|
+
# [RFC4731[https://rfc-editor.org/rfc/rfc4731#section-3.1]].
|
2538
|
+
#
|
2539
|
+
# When +IMAP4rev2+ is enabled, or when the server supports +IMAP4rev2+ but
|
2540
|
+
# not +IMAP4rev1+, ESearchResult is always returned instead of SearchResult.
|
2541
|
+
#
|
2542
|
+
# If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
|
2543
|
+
# and enabled for the selected mailbox, a non-empty SearchResult will
|
2544
|
+
# include a +MODSEQ+ value.
|
2545
|
+
# imap.select("mbox", condstore: true)
|
2546
|
+
# result = imap.search(["SUBJECT", "hi there", "not", "new"])
|
2547
|
+
# #=> Net::IMAP::SearchResult[1, 6, 7, 8, modseq: 5594]
|
2548
|
+
# result.modseq # => 5594
|
2549
|
+
#
|
2550
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2551
|
+
# the +SEARCH+ command is prohibited. Use #uid_search instead.
|
2552
|
+
def search(...)
|
2553
|
+
search_internal("SEARCH", ...)
|
2554
|
+
end
|
2555
|
+
|
2556
|
+
# :call-seq:
|
2557
|
+
# uid_search(criteria, charset = nil) -> result
|
2558
|
+
# uid_search(criteria, charset: nil, return: nil) -> result
|
2559
|
+
#
|
2560
|
+
# Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
2561
|
+
# to search the mailbox for messages that match the given searching
|
2562
|
+
# criteria, and returns unique identifiers (<tt>UID</tt>s).
|
2563
|
+
#
|
2564
|
+
# Returns a SearchResult object. SearchResult inherits from Array (for
|
2565
|
+
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
2566
|
+
# capability has been enabled.
|
2567
|
+
#
|
2568
|
+
# See #search for documentation of parameters.
|
2569
|
+
#
|
2570
|
+
# ==== Capabilities
|
2571
|
+
#
|
2572
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2573
|
+
# #uid_search must be used instead of #search, and the <tt><message
|
2574
|
+
# set></tt> search criterion is prohibited. Use +ALL+ or <tt>UID
|
2575
|
+
# sequence-set</tt> instead.
|
2576
|
+
#
|
2577
|
+
# Otherwise, #uid_search is updated by extensions in the same way as
|
2578
|
+
# #search.
|
2579
|
+
def uid_search(...)
|
2580
|
+
search_internal("UID SEARCH", ...)
|
2581
|
+
end
|
2582
|
+
|
2583
|
+
# :call-seq:
|
2584
|
+
# fetch(set, attr, changedsince: nil) -> array of FetchData
|
2585
|
+
#
|
2586
|
+
# Sends a {FETCH command [IMAP4rev1 §6.4.5]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.5]
|
2587
|
+
# to retrieve data associated with a message in the mailbox.
|
2588
|
+
#
|
2589
|
+
# +set+ is the message sequence numbers to fetch, and may be any valid input
|
2590
|
+
# to {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
|
2591
|
+
# (For UIDs, use #uid_fetch instead.)
|
2592
|
+
#
|
2593
|
+
# +attr+ is a list of attributes to fetch; see FetchStruct documentation for
|
2594
|
+
# a list of supported attributes.
|
2595
|
+
#
|
2596
|
+
# +changedsince+ is an optional integer mod-sequence. It limits results to
|
2597
|
+
# messages with a mod-sequence greater than +changedsince+.
|
2598
|
+
#
|
2599
|
+
# The return value is an array of FetchData.
|
2600
|
+
#
|
2601
|
+
# Related: #uid_fetch, FetchData
|
2602
|
+
#
|
2603
|
+
# ==== For example:
|
2604
|
+
#
|
2605
|
+
# p imap.fetch(6..8, "UID")
|
2606
|
+
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
|
2607
|
+
# #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\
|
2608
|
+
# #<Net::IMAP::FetchData seqno=8, attr={"UID"=>100}>]
|
2609
|
+
# p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]")
|
2610
|
+
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"BODY[HEADER.FIELDS (SUBJECT)]"=>"Subject: test\r\n\r\n"}>]
|
1672
2611
|
# data = imap.uid_fetch(98, ["RFC822.SIZE", "INTERNALDATE"])[0]
|
1673
2612
|
# p data.seqno
|
1674
2613
|
# #=> 6
|
@@ -1678,48 +2617,138 @@ module Net
|
|
1678
2617
|
# #=> "12-Oct-2000 22:40:59 +0900"
|
1679
2618
|
# p data.attr["UID"]
|
1680
2619
|
# #=> 98
|
1681
|
-
|
1682
|
-
|
2620
|
+
#
|
2621
|
+
# ==== Capabilities
|
2622
|
+
#
|
2623
|
+
# Many extensions define new message +attr+ names. See FetchStruct for a
|
2624
|
+
# list of supported extension fields.
|
2625
|
+
#
|
2626
|
+
# The server's capabilities must include +CONDSTORE+
|
2627
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
|
2628
|
+
# +changedsince+ argument. Using +changedsince+ implicitly enables the
|
2629
|
+
# +CONDSTORE+ extension.
|
2630
|
+
#
|
2631
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2632
|
+
# +FETCH+ command is prohibited. Use #uid_fetch instead.
|
2633
|
+
def fetch(...)
|
2634
|
+
fetch_internal("FETCH", ...)
|
1683
2635
|
end
|
1684
2636
|
|
2637
|
+
# :call-seq:
|
2638
|
+
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData (or UIDFetchData)
|
2639
|
+
#
|
1685
2640
|
# Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1686
2641
|
# to retrieve data associated with a message in the mailbox.
|
1687
2642
|
#
|
1688
|
-
#
|
1689
|
-
#
|
2643
|
+
# +set+ is the message UIDs to fetch, and may be any valid input to
|
2644
|
+
# {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
|
2645
|
+
# (For message sequence numbers, use #fetch instead.)
|
1690
2646
|
#
|
2647
|
+
# +attr+ behaves the same as with #fetch.
|
1691
2648
|
# >>>
|
1692
2649
|
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
|
1693
2650
|
# part of any +FETCH+ response caused by a +UID+ command, regardless of
|
1694
2651
|
# whether a +UID+ was specified as a message data item to the +FETCH+.
|
1695
2652
|
#
|
2653
|
+
# +changedsince+ (optional) behaves the same as with #fetch.
|
2654
|
+
#
|
2655
|
+
# +partial+ is an optional range to limit the number of results returned.
|
2656
|
+
# It's useful when +set+ contains an unknown number of messages.
|
2657
|
+
# <tt>1..500</tt> returns the first 500 messages in +set+ (in mailbox
|
2658
|
+
# order), <tt>501..1000</tt> the second 500, and so on. +partial+ may also
|
2659
|
+
# be negative: <tt>-500..-1</tt> selects the last 500 messages in +set+.
|
2660
|
+
# <em>Requires the +PARTIAL+ capabability.</em>
|
2661
|
+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
|
2662
|
+
#
|
2663
|
+
# For example:
|
2664
|
+
#
|
2665
|
+
# # Without partial, the size of the results may be unknown beforehand:
|
2666
|
+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
|
2667
|
+
# # ... maybe wait for a long time ... and allocate a lot of memory ...
|
2668
|
+
# results.size # => 0..2**32-1
|
2669
|
+
# process results # may also take a long time and use a lot of memory...
|
2670
|
+
#
|
2671
|
+
# # Using partial, the results may be paginated:
|
2672
|
+
# loop do
|
2673
|
+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
|
2674
|
+
# partial: 1..500)
|
2675
|
+
# # fetch should return quickly and allocate little memory
|
2676
|
+
# results.size # => 0..500
|
2677
|
+
# break if results.empty?
|
2678
|
+
# next_uid_to_fetch = results.last.uid + 1
|
2679
|
+
# process results
|
2680
|
+
# end
|
2681
|
+
#
|
1696
2682
|
# Related: #fetch, FetchData
|
1697
|
-
|
1698
|
-
|
2683
|
+
#
|
2684
|
+
# ==== Capabilities
|
2685
|
+
#
|
2686
|
+
# The server's capabilities must include +PARTIAL+
|
2687
|
+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394] in order to use the
|
2688
|
+
# +partial+ argument.
|
2689
|
+
#
|
2690
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2691
|
+
# #uid_fetch must be used instead of #fetch, and UIDFetchData will be
|
2692
|
+
# returned instead of FetchData.
|
2693
|
+
#
|
2694
|
+
# Otherwise, #uid_fetch is updated by extensions in the same way as #fetch.
|
2695
|
+
def uid_fetch(...)
|
2696
|
+
fetch_internal("UID FETCH", ...)
|
1699
2697
|
end
|
1700
2698
|
|
2699
|
+
# :call-seq:
|
2700
|
+
# store(set, attr, value, unchangedsince: nil) -> array of FetchData
|
2701
|
+
#
|
1701
2702
|
# Sends a {STORE command [IMAP4rev1 §6.4.6]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.6]
|
1702
2703
|
# to alter data associated with messages in the mailbox, in particular their
|
1703
|
-
# flags.
|
1704
|
-
#
|
1705
|
-
#
|
1706
|
-
#
|
1707
|
-
#
|
2704
|
+
# flags.
|
2705
|
+
#
|
2706
|
+
# +set+ is a number, an array of numbers, or a Range object. Each number is
|
2707
|
+
# a message sequence number.
|
2708
|
+
#
|
2709
|
+
# +attr+ is the name of a data item to store. The semantics of +value+
|
2710
|
+
# varies based on +attr+:
|
2711
|
+
# * When +attr+ is <tt>"FLAGS"</tt>, the flags in +value+ replace the
|
2712
|
+
# message's flag list.
|
2713
|
+
# * When +attr+ is <tt>"+FLAGS"</tt>, the flags in +value+ are added to
|
2714
|
+
# the flags for the message.
|
2715
|
+
# * When +attr+ is <tt>"-FLAGS"</tt>, the flags in +value+ are removed
|
2716
|
+
# from the message.
|
2717
|
+
#
|
2718
|
+
# +unchangedsince+ is an optional integer mod-sequence. It prohibits any
|
2719
|
+
# changes to messages with +mod-sequence+ greater than the specified
|
2720
|
+
# +unchangedsince+ value. A SequenceSet of any messages that fail this
|
2721
|
+
# check will be returned in a +MODIFIED+ ResponseCode.
|
1708
2722
|
#
|
1709
|
-
# The return value is an array of FetchData
|
2723
|
+
# The return value is an array of FetchData.
|
1710
2724
|
#
|
1711
2725
|
# Related: #uid_store
|
1712
2726
|
#
|
1713
|
-
#
|
2727
|
+
# ==== For example:
|
1714
2728
|
#
|
1715
2729
|
# p imap.store(6..8, "+FLAGS", [:Deleted])
|
1716
|
-
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
1717
|
-
# #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
2730
|
+
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
2731
|
+
# #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
1718
2732
|
# #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
|
1719
|
-
|
1720
|
-
|
2733
|
+
#
|
2734
|
+
# ==== Capabilities
|
2735
|
+
#
|
2736
|
+
# Extensions may define new data items to be used with #store.
|
2737
|
+
#
|
2738
|
+
# The server's capabilities must include +CONDSTORE+
|
2739
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
|
2740
|
+
# +unchangedsince+ argument. Using +unchangedsince+ implicitly enables the
|
2741
|
+
# +CONDSTORE+ extension.
|
2742
|
+
#
|
2743
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2744
|
+
# +STORE+ command is prohibited. Use #uid_store instead.
|
2745
|
+
def store(set, attr, flags, unchangedsince: nil)
|
2746
|
+
store_internal("STORE", set, attr, flags, unchangedsince: unchangedsince)
|
1721
2747
|
end
|
1722
2748
|
|
2749
|
+
# :call-seq:
|
2750
|
+
# uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData (or UIDFetchData)
|
2751
|
+
#
|
1723
2752
|
# Sends a {UID STORE command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1724
2753
|
# to alter data associated with messages in the mailbox, in particular their
|
1725
2754
|
# flags.
|
@@ -1728,8 +2757,16 @@ module Net
|
|
1728
2757
|
# message sequence numbers.
|
1729
2758
|
#
|
1730
2759
|
# Related: #store
|
1731
|
-
|
1732
|
-
|
2760
|
+
#
|
2761
|
+
# ==== Capabilities
|
2762
|
+
#
|
2763
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2764
|
+
# #uid_store must be used instead of #store, and UIDFetchData will be
|
2765
|
+
# returned instead of FetchData.
|
2766
|
+
#
|
2767
|
+
# Otherwise, #uid_store is updated by extensions in the same way as #store.
|
2768
|
+
def uid_store(set, attr, flags, unchangedsince: nil)
|
2769
|
+
store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
|
1733
2770
|
end
|
1734
2771
|
|
1735
2772
|
# Sends a {COPY command [IMAP4rev1 §6.4.7]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.7]
|
@@ -1739,13 +2776,16 @@ module Net
|
|
1739
2776
|
#
|
1740
2777
|
# Related: #uid_copy
|
1741
2778
|
#
|
1742
|
-
#
|
2779
|
+
# ==== Capabilities
|
1743
2780
|
#
|
1744
2781
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
1745
2782
|
# supported, the server's response should include a +COPYUID+ response code
|
1746
2783
|
# with UIDPlusData. This will report the UIDVALIDITY of the destination
|
1747
2784
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
1748
2785
|
# the moved messages.
|
2786
|
+
#
|
2787
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2788
|
+
# +COPY+ command is prohibited. Use #uid_copy instead.
|
1749
2789
|
def copy(set, mailbox)
|
1750
2790
|
copy_internal("COPY", set, mailbox)
|
1751
2791
|
end
|
@@ -1756,9 +2796,12 @@ module Net
|
|
1756
2796
|
#
|
1757
2797
|
# Similar to #copy, but +set+ contains unique identifiers.
|
1758
2798
|
#
|
1759
|
-
#
|
2799
|
+
# ==== Capabilities
|
1760
2800
|
#
|
1761
|
-
#
|
2801
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] in enabled,
|
2802
|
+
# #uid_copy must be used instead of #copy.
|
2803
|
+
#
|
2804
|
+
# Otherwise, #uid_copy is updated by extensions in the same way as #copy.
|
1762
2805
|
def uid_copy(set, mailbox)
|
1763
2806
|
copy_internal("UID COPY", set, mailbox)
|
1764
2807
|
end
|
@@ -1771,10 +2814,10 @@ module Net
|
|
1771
2814
|
#
|
1772
2815
|
# Related: #uid_move
|
1773
2816
|
#
|
1774
|
-
#
|
2817
|
+
# ==== Capabilities
|
1775
2818
|
#
|
1776
|
-
# The server's capabilities must include +MOVE+
|
1777
|
-
# [RFC6851[https://
|
2819
|
+
# The server's capabilities must include either +IMAP4rev2+ or +MOVE+
|
2820
|
+
# [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
|
1778
2821
|
#
|
1779
2822
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
1780
2823
|
# supported, the server's response should include a +COPYUID+ response code
|
@@ -1782,6 +2825,8 @@ module Net
|
|
1782
2825
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
1783
2826
|
# the moved messages.
|
1784
2827
|
#
|
2828
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2829
|
+
# +MOVE+ command is prohibited. Use #uid_move instead.
|
1785
2830
|
def move(set, mailbox)
|
1786
2831
|
copy_internal("MOVE", set, mailbox)
|
1787
2832
|
end
|
@@ -1795,11 +2840,15 @@ module Net
|
|
1795
2840
|
#
|
1796
2841
|
# Related: #move
|
1797
2842
|
#
|
1798
|
-
#
|
2843
|
+
# ==== Capabilities
|
2844
|
+
#
|
2845
|
+
# The server's capabilities must include either +IMAP4rev2+ or +MOVE+
|
2846
|
+
# [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
|
1799
2847
|
#
|
1800
|
-
#
|
1801
|
-
#
|
1802
|
-
#
|
2848
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2849
|
+
# #uid_move must be used instead of #move.
|
2850
|
+
#
|
2851
|
+
# Otherwise, #uid_move is updated by extensions in the same way as #move.
|
1803
2852
|
def uid_move(set, mailbox)
|
1804
2853
|
copy_internal("UID MOVE", set, mailbox)
|
1805
2854
|
end
|
@@ -1815,17 +2864,17 @@ module Net
|
|
1815
2864
|
#
|
1816
2865
|
# Related: #uid_sort, #search, #uid_search, #thread, #uid_thread
|
1817
2866
|
#
|
1818
|
-
#
|
2867
|
+
# ==== For example:
|
1819
2868
|
#
|
1820
2869
|
# p imap.sort(["FROM"], ["ALL"], "US-ASCII")
|
1821
2870
|
# #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
|
1822
2871
|
# p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
|
1823
2872
|
# #=> [6, 7, 8, 1]
|
1824
2873
|
#
|
1825
|
-
#
|
2874
|
+
# ==== Capabilities
|
1826
2875
|
#
|
1827
2876
|
# The server's capabilities must include +SORT+
|
1828
|
-
# [RFC5256[https://
|
2877
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1829
2878
|
def sort(sort_keys, search_keys, charset)
|
1830
2879
|
return sort_internal("SORT", sort_keys, search_keys, charset)
|
1831
2880
|
end
|
@@ -1837,10 +2886,10 @@ module Net
|
|
1837
2886
|
#
|
1838
2887
|
# Related: #sort, #search, #uid_search, #thread, #uid_thread
|
1839
2888
|
#
|
1840
|
-
#
|
2889
|
+
# ==== Capabilities
|
1841
2890
|
#
|
1842
2891
|
# The server's capabilities must include +SORT+
|
1843
|
-
# [RFC5256[https://
|
2892
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1844
2893
|
def uid_sort(sort_keys, search_keys, charset)
|
1845
2894
|
return sort_internal("UID SORT", sort_keys, search_keys, charset)
|
1846
2895
|
end
|
@@ -1862,10 +2911,10 @@ module Net
|
|
1862
2911
|
#
|
1863
2912
|
# Related: #uid_thread, #search, #uid_search, #sort, #uid_sort
|
1864
2913
|
#
|
1865
|
-
#
|
2914
|
+
# ==== Capabilities
|
1866
2915
|
#
|
1867
2916
|
# The server's capabilities must include +THREAD+
|
1868
|
-
# [RFC5256[https://
|
2917
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1869
2918
|
def thread(algorithm, search_keys, charset)
|
1870
2919
|
return thread_internal("THREAD", algorithm, search_keys, charset)
|
1871
2920
|
end
|
@@ -1876,14 +2925,112 @@ module Net
|
|
1876
2925
|
#
|
1877
2926
|
# Related: #thread, #search, #uid_search, #sort, #uid_sort
|
1878
2927
|
#
|
1879
|
-
#
|
2928
|
+
# ==== Capabilities
|
1880
2929
|
#
|
1881
2930
|
# The server's capabilities must include +THREAD+
|
1882
|
-
# [RFC5256[https://
|
2931
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1883
2932
|
def uid_thread(algorithm, search_keys, charset)
|
1884
2933
|
return thread_internal("UID THREAD", algorithm, search_keys, charset)
|
1885
2934
|
end
|
1886
2935
|
|
2936
|
+
# Sends an {ENABLE command [RFC5161 §3.2]}[https://www.rfc-editor.org/rfc/rfc5161#section-3.1]
|
2937
|
+
# {[IMAP4rev2 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.3.1]
|
2938
|
+
# to enable the specified server +capabilities+. Each capability may be an
|
2939
|
+
# array, string, or symbol. Returns a list of the capabilities that were
|
2940
|
+
# enabled.
|
2941
|
+
#
|
2942
|
+
# The +ENABLE+ command is only valid in the _authenticated_ state, before
|
2943
|
+
# any mailbox is selected.
|
2944
|
+
#
|
2945
|
+
# Related: #capable?, #capabilities, #capability
|
2946
|
+
#
|
2947
|
+
# ==== Capabilities
|
2948
|
+
#
|
2949
|
+
# The server's capabilities must include
|
2950
|
+
# +ENABLE+ [RFC5161[https://www.rfc-editor.org/rfc/rfc5161]]
|
2951
|
+
# or +IMAP4REV2+ [RFC9051[https://www.rfc-editor.org/rfc/rfc9051]].
|
2952
|
+
#
|
2953
|
+
# Additionally, the server capabilities must include a capability matching
|
2954
|
+
# each enabled extension (usually the same name as the enabled extension).
|
2955
|
+
# The following capabilities may be enabled:
|
2956
|
+
#
|
2957
|
+
# [+CONDSTORE+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html]]
|
2958
|
+
#
|
2959
|
+
# Updates various commands to return +CONDSTORE+ extension responses. It
|
2960
|
+
# is not necessary to explicitly enable +CONDSTORE+—using any of the
|
2961
|
+
# command parameters defined by the extension will implicitly enable it.
|
2962
|
+
# See {[RFC7162 §3.1]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1].
|
2963
|
+
#
|
2964
|
+
# [+:utf8+ --- an alias for <tt>"UTF8=ACCEPT"</tt>]
|
2965
|
+
#
|
2966
|
+
# In a future release, <tt>enable(:utf8)</tt> will enable either
|
2967
|
+
# <tt>"UTF8=ACCEPT"</tt> or <tt>"IMAP4rev2"</tt>, depending on server
|
2968
|
+
# capabilities.
|
2969
|
+
#
|
2970
|
+
# [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
|
2971
|
+
#
|
2972
|
+
# The server's capabilities must include <tt>UTF8=ACCEPT</tt> _or_
|
2973
|
+
# <tt>UTF8=ONLY</tt>.
|
2974
|
+
#
|
2975
|
+
# This allows the server to send strings encoded as UTF-8 which might
|
2976
|
+
# otherwise need to use a 7-bit encoding, such as {modified
|
2977
|
+
# UTF-7}[::decode_utf7] for mailbox names, or RFC2047 encoded-words for
|
2978
|
+
# message headers.
|
2979
|
+
#
|
2980
|
+
# *Note:* <em>A future update may set string encodings slightly
|
2981
|
+
# differently</em>, e.g: "US-ASCII" when UTF-8 is not enabled, and "UTF-8"
|
2982
|
+
# when it is. Currently, the encoding of strings sent as "quoted" or
|
2983
|
+
# "text" will _always_ be "UTF-8", even when only ASCII characters are
|
2984
|
+
# used (e.g. "Subject: Agenda") And currently, string "literals" sent
|
2985
|
+
# by the server will always have an "ASCII-8BIT" (binary)
|
2986
|
+
# encoding, even if they generally contain UTF-8 data, if they are
|
2987
|
+
# text at all.
|
2988
|
+
#
|
2989
|
+
# [<tt>"UTF8=ONLY"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
|
2990
|
+
#
|
2991
|
+
# A server that reports the <tt>UTF8=ONLY</tt> capability _requires_ that
|
2992
|
+
# the client <tt>enable("UTF8=ACCEPT")</tt> before any mailboxes may be
|
2993
|
+
# selected. For convenience, <tt>enable("UTF8=ONLY")</tt> is aliased to
|
2994
|
+
# <tt>enable("UTF8=ACCEPT")</tt>.
|
2995
|
+
#
|
2996
|
+
# [+UIDONLY+ {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.pdf]]
|
2997
|
+
#
|
2998
|
+
# When UIDONLY is enabled, the #fetch, #store, #search, #copy, and #move
|
2999
|
+
# commands are prohibited and result in a tagged BAD response. Clients
|
3000
|
+
# should instead use uid_fetch, uid_store, uid_search, uid_copy, or
|
3001
|
+
# uid_move, respectively. All +FETCH+ responses that would be returned are
|
3002
|
+
# replaced by +UIDFETCH+ responses. All +EXPUNGED+ responses that would be
|
3003
|
+
# returned are replaced by +VANISHED+ responses. The "<sequence set>"
|
3004
|
+
# uid_search criterion is prohibited.
|
3005
|
+
#
|
3006
|
+
# ===== Unsupported capabilities
|
3007
|
+
#
|
3008
|
+
# *Note:* Some extensions that use ENABLE permit the server to send syntax
|
3009
|
+
# that Net::IMAP cannot parse, which may raise an exception and disconnect.
|
3010
|
+
# Some extensions may work, but the support may be incomplete, untested, or
|
3011
|
+
# experimental.
|
3012
|
+
#
|
3013
|
+
# Until a capability is documented here as supported, enabling it may result
|
3014
|
+
# in undocumented behavior and a future release may update with incompatible
|
3015
|
+
# behavior <em>without warning or deprecation</em>.
|
3016
|
+
#
|
3017
|
+
# <em>Caution is advised.</em>
|
3018
|
+
#
|
3019
|
+
def enable(*capabilities)
|
3020
|
+
capabilities = capabilities
|
3021
|
+
.flatten
|
3022
|
+
.map {|e| ENABLE_ALIASES[e] || e }
|
3023
|
+
.uniq
|
3024
|
+
.join(' ')
|
3025
|
+
synchronize do
|
3026
|
+
send_command("ENABLE #{capabilities}")
|
3027
|
+
result = clear_responses("ENABLED").last || []
|
3028
|
+
@utf8_strings ||= result.include? "UTF8=ACCEPT"
|
3029
|
+
@utf8_strings ||= result.include? "IMAP4REV2"
|
3030
|
+
result
|
3031
|
+
end
|
3032
|
+
end
|
3033
|
+
|
1887
3034
|
# Sends an {IDLE command [RFC2177 §3]}[https://www.rfc-editor.org/rfc/rfc6851#section-3]
|
1888
3035
|
# {[IMAP4rev2 §6.3.13]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.3.13]
|
1889
3036
|
# that waits for notifications of new or expunged messages. Yields
|
@@ -1896,17 +3043,23 @@ module Net
|
|
1896
3043
|
# checks the connection for each 60 seconds.
|
1897
3044
|
#
|
1898
3045
|
# loop do
|
1899
|
-
# imap.idle(60) do |
|
1900
|
-
#
|
3046
|
+
# imap.idle(60) do |response|
|
3047
|
+
# do_something_with(response)
|
3048
|
+
# imap.idle_done if some_condition?(response)
|
1901
3049
|
# end
|
1902
3050
|
# end
|
1903
3051
|
#
|
3052
|
+
# Returns the server's response to indicate the IDLE state has ended.
|
3053
|
+
# Returns +nil+ if the server does not respond to #idle_done within
|
3054
|
+
# {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
|
3055
|
+
# seconds.
|
3056
|
+
#
|
1904
3057
|
# Related: #idle_done, #noop, #check
|
1905
3058
|
#
|
1906
|
-
#
|
3059
|
+
# ==== Capabilities
|
1907
3060
|
#
|
1908
|
-
# The server's capabilities must include +IDLE+
|
1909
|
-
# [RFC2177[https://
|
3061
|
+
# The server's capabilities must include either +IMAP4rev2+ or +IDLE+
|
3062
|
+
# [RFC2177[https://www.rfc-editor.org/rfc/rfc2177]].
|
1910
3063
|
def idle(timeout = nil, &response_handler)
|
1911
3064
|
raise LocalJumpError, "no block given" unless response_handler
|
1912
3065
|
|
@@ -1925,10 +3078,10 @@ module Net
|
|
1925
3078
|
raise @exception || Net::IMAP::Error.new("connection closed")
|
1926
3079
|
end
|
1927
3080
|
ensure
|
3081
|
+
remove_response_handler(response_handler)
|
1928
3082
|
unless @receiver_thread_terminating
|
1929
|
-
remove_response_handler(response_handler)
|
1930
3083
|
put_string("DONE#{CRLF}")
|
1931
|
-
response = get_tagged_response(tag, "IDLE",
|
3084
|
+
response = get_tagged_response(tag, "IDLE", idle_response_timeout)
|
1932
3085
|
end
|
1933
3086
|
end
|
1934
3087
|
end
|
@@ -1936,7 +3089,11 @@ module Net
|
|
1936
3089
|
return response
|
1937
3090
|
end
|
1938
3091
|
|
1939
|
-
# Leaves IDLE.
|
3092
|
+
# Leaves IDLE, allowing #idle to return.
|
3093
|
+
#
|
3094
|
+
# If the server does not respond within
|
3095
|
+
# {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
|
3096
|
+
# seconds, #idle will return +nil+.
|
1940
3097
|
#
|
1941
3098
|
# Related: #idle
|
1942
3099
|
def idle_done
|
@@ -1948,6 +3105,196 @@ module Net
|
|
1948
3105
|
end
|
1949
3106
|
end
|
1950
3107
|
|
3108
|
+
RESPONSES_DEPRECATION_MSG =
|
3109
|
+
"Pass a type or block to #responses, " \
|
3110
|
+
"set config.responses_without_block to :frozen_dup " \
|
3111
|
+
"or :silence_deprecation_warning, " \
|
3112
|
+
"or use #extract_responses or #clear_responses."
|
3113
|
+
private_constant :RESPONSES_DEPRECATION_MSG
|
3114
|
+
|
3115
|
+
# :call-seq:
|
3116
|
+
# responses -> hash of {String => Array} (see config.responses_without_block)
|
3117
|
+
# responses(type) -> frozen array
|
3118
|
+
# responses {|hash| ...} -> block result
|
3119
|
+
# responses(type) {|array| ...} -> block result
|
3120
|
+
#
|
3121
|
+
# Yields or returns unhandled server responses. Unhandled responses are
|
3122
|
+
# stored in a hash, with arrays of UntaggedResponse#data keyed by
|
3123
|
+
# UntaggedResponse#name and <em>non-+nil+</em> untagged ResponseCode#data
|
3124
|
+
# keyed by ResponseCode#name.
|
3125
|
+
#
|
3126
|
+
# When a block is given, yields unhandled responses and returns the block's
|
3127
|
+
# result. Without a block, returns the unhandled responses.
|
3128
|
+
#
|
3129
|
+
# [With +type+]
|
3130
|
+
# Yield or return only the array of responses for that +type+.
|
3131
|
+
# When no block is given, the returned array is a frozen copy.
|
3132
|
+
# [Without +type+]
|
3133
|
+
# Yield or return the entire responses hash.
|
3134
|
+
#
|
3135
|
+
# When no block is given, the behavior is determined by
|
3136
|
+
# Config#responses_without_block:
|
3137
|
+
# >>>
|
3138
|
+
# [+:silence_deprecation_warning+ <em>(original behavior)</em>]
|
3139
|
+
# Returns the mutable responses hash (without any warnings).
|
3140
|
+
# <em>This is not thread-safe.</em>
|
3141
|
+
#
|
3142
|
+
# [+:warn+ <em>(default since +v0.5+)</em>]
|
3143
|
+
# Prints a warning and returns the mutable responses hash.
|
3144
|
+
# <em>This is not thread-safe.</em>
|
3145
|
+
#
|
3146
|
+
# [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
|
3147
|
+
# Returns a frozen copy of the unhandled responses hash, with frozen
|
3148
|
+
# array values.
|
3149
|
+
#
|
3150
|
+
# [+:raise+]
|
3151
|
+
# Raise an +ArgumentError+ with the deprecation warning.
|
3152
|
+
#
|
3153
|
+
# For example:
|
3154
|
+
#
|
3155
|
+
# imap.select("inbox")
|
3156
|
+
# p imap.responses("EXISTS").last
|
3157
|
+
# #=> 2
|
3158
|
+
# p imap.responses("UIDNEXT", &:last)
|
3159
|
+
# #=> 123456
|
3160
|
+
# p imap.responses("UIDVALIDITY", &:last)
|
3161
|
+
# #=> 968263756
|
3162
|
+
# p imap.responses {|responses|
|
3163
|
+
# {
|
3164
|
+
# exists: responses.delete("EXISTS").last,
|
3165
|
+
# uidnext: responses.delete("UIDNEXT").last,
|
3166
|
+
# uidvalidity: responses.delete("UIDVALIDITY").last,
|
3167
|
+
# }
|
3168
|
+
# }
|
3169
|
+
# #=> {:exists=>2, :uidnext=>123456, :uidvalidity=>968263756}
|
3170
|
+
# # "EXISTS", "UIDNEXT", and "UIDVALIDITY" have been removed:
|
3171
|
+
# p imap.responses(&:keys)
|
3172
|
+
# #=> ["FLAGS", "OK", "PERMANENTFLAGS", "RECENT", "HIGHESTMODSEQ"]
|
3173
|
+
#
|
3174
|
+
# Related: #extract_responses, #clear_responses, #response_handlers, #greeting
|
3175
|
+
#
|
3176
|
+
# ==== Thread safety
|
3177
|
+
# >>>
|
3178
|
+
# *Note:* Access to the responses hash is synchronized for thread-safety.
|
3179
|
+
# The receiver thread and response_handlers cannot process new responses
|
3180
|
+
# until the block completes. Accessing either the response hash or its
|
3181
|
+
# response type arrays outside of the block is unsafe. They can be safely
|
3182
|
+
# updated inside the block. Consider using #clear_responses or
|
3183
|
+
# #extract_responses instead.
|
3184
|
+
#
|
3185
|
+
# Net::IMAP will add and remove responses from the responses hash and its
|
3186
|
+
# array values, in the calling threads for commands and in the receiver
|
3187
|
+
# thread, but will not modify any responses after adding them to the
|
3188
|
+
# responses hash.
|
3189
|
+
#
|
3190
|
+
# ==== Clearing responses
|
3191
|
+
#
|
3192
|
+
# Previously unhandled responses are automatically cleared before entering a
|
3193
|
+
# mailbox with #select or #examine. Long-lived connections can receive many
|
3194
|
+
# unhandled server responses, which must be pruned or they will continually
|
3195
|
+
# consume more memory. Update or clear the responses hash or arrays inside
|
3196
|
+
# the block, or remove responses with #extract_responses, #clear_responses,
|
3197
|
+
# or #add_response_handler.
|
3198
|
+
#
|
3199
|
+
# ==== Missing responses
|
3200
|
+
#
|
3201
|
+
# Only non-+nil+ data is stored. Many important response codes have no data
|
3202
|
+
# of their own, but are used as "tags" on the ResponseText object they are
|
3203
|
+
# attached to. ResponseText will be accessible by its response types:
|
3204
|
+
# "+OK+", "+NO+", "+BAD+", "+BYE+", or "+PREAUTH+".
|
3205
|
+
#
|
3206
|
+
# TaggedResponse#data is not saved to #responses, nor is any
|
3207
|
+
# ResponseCode#data on tagged responses. Although some command methods do
|
3208
|
+
# return the TaggedResponse directly, #add_response_handler must be used to
|
3209
|
+
# handle all response codes.
|
3210
|
+
def responses(type = nil)
|
3211
|
+
if block_given?
|
3212
|
+
synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
|
3213
|
+
elsif type
|
3214
|
+
synchronize { @responses[type.to_s.upcase].dup.freeze }
|
3215
|
+
else
|
3216
|
+
case config.responses_without_block
|
3217
|
+
when :raise
|
3218
|
+
raise ArgumentError, RESPONSES_DEPRECATION_MSG
|
3219
|
+
when :warn
|
3220
|
+
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
3221
|
+
when :frozen_dup
|
3222
|
+
synchronize {
|
3223
|
+
responses = @responses.transform_values(&:freeze)
|
3224
|
+
responses.default_proc = nil
|
3225
|
+
responses.default = [].freeze
|
3226
|
+
return responses.freeze
|
3227
|
+
}
|
3228
|
+
end
|
3229
|
+
@responses
|
3230
|
+
end
|
3231
|
+
end
|
3232
|
+
|
3233
|
+
# :call-seq:
|
3234
|
+
# clear_responses -> hash
|
3235
|
+
# clear_responses(type) -> array
|
3236
|
+
#
|
3237
|
+
# Clears and returns the unhandled #responses hash or the unhandled
|
3238
|
+
# responses array for a single response +type+.
|
3239
|
+
#
|
3240
|
+
# Clearing responses is synchronized with other threads. The lock is
|
3241
|
+
# released before returning.
|
3242
|
+
#
|
3243
|
+
# Related: #extract_responses, #responses, #response_handlers
|
3244
|
+
def clear_responses(type = nil)
|
3245
|
+
synchronize {
|
3246
|
+
if type
|
3247
|
+
@responses.delete(type) || []
|
3248
|
+
else
|
3249
|
+
@responses.dup.transform_values(&:freeze)
|
3250
|
+
.tap { _1.default = [].freeze }
|
3251
|
+
.tap { @responses.clear }
|
3252
|
+
end
|
3253
|
+
}
|
3254
|
+
.freeze
|
3255
|
+
end
|
3256
|
+
|
3257
|
+
# :call-seq:
|
3258
|
+
# extract_responses(type) {|response| ... } -> array
|
3259
|
+
#
|
3260
|
+
# Yields all of the unhandled #responses for a single response +type+.
|
3261
|
+
# Removes and returns the responses for which the block returns a true
|
3262
|
+
# value.
|
3263
|
+
#
|
3264
|
+
# Extracting responses is synchronized with other threads. The lock is
|
3265
|
+
# released before returning.
|
3266
|
+
#
|
3267
|
+
# Related: #responses, #clear_responses
|
3268
|
+
def extract_responses(type)
|
3269
|
+
type = String.try_convert(type) or
|
3270
|
+
raise ArgumentError, "type must be a string"
|
3271
|
+
raise ArgumentError, "must provide a block" unless block_given?
|
3272
|
+
extracted = []
|
3273
|
+
responses(type) do |all|
|
3274
|
+
all.reject! do |response|
|
3275
|
+
extracted << response if yield response
|
3276
|
+
end
|
3277
|
+
end
|
3278
|
+
extracted
|
3279
|
+
end
|
3280
|
+
|
3281
|
+
# Returns all response handlers, including those that are added internally
|
3282
|
+
# by commands. Each response handler will be called with every new
|
3283
|
+
# UntaggedResponse, TaggedResponse, and ContinuationRequest.
|
3284
|
+
#
|
3285
|
+
# Response handlers are called with a mutex inside the receiver thread. New
|
3286
|
+
# responses cannot be processed and commands from other threads must wait
|
3287
|
+
# until all response_handlers return. An exception will shut-down the
|
3288
|
+
# receiver thread and close the connection.
|
3289
|
+
#
|
3290
|
+
# For thread-safety, the returned array is a frozen copy of the internal
|
3291
|
+
# array.
|
3292
|
+
#
|
3293
|
+
# Related: #add_response_handler, #remove_response_handler
|
3294
|
+
def response_handlers
|
3295
|
+
synchronize { @response_handlers.clone.freeze }
|
3296
|
+
end
|
3297
|
+
|
1951
3298
|
# Adds a response handler. For example, to detect when
|
1952
3299
|
# the server sends a new EXISTS response (which normally
|
1953
3300
|
# indicates new messages being added to the mailbox),
|
@@ -1960,14 +3307,25 @@ module Net
|
|
1960
3307
|
# end
|
1961
3308
|
# }
|
1962
3309
|
#
|
3310
|
+
# Response handlers can also be added when the client is created before the
|
3311
|
+
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
3312
|
+
# This ensures every server response is handled, including the #greeting.
|
3313
|
+
#
|
3314
|
+
# Related: #remove_response_handler, #response_handlers
|
1963
3315
|
def add_response_handler(handler = nil, &block)
|
1964
3316
|
raise ArgumentError, "two Procs are passed" if handler && block
|
1965
|
-
|
3317
|
+
synchronize do
|
3318
|
+
@response_handlers.push(block || handler)
|
3319
|
+
end
|
1966
3320
|
end
|
1967
3321
|
|
1968
3322
|
# Removes the response handler.
|
3323
|
+
#
|
3324
|
+
# Related: #add_response_handler, #response_handlers
|
1969
3325
|
def remove_response_handler(handler)
|
1970
|
-
|
3326
|
+
synchronize do
|
3327
|
+
@response_handlers.delete(handler)
|
3328
|
+
end
|
1971
3329
|
end
|
1972
3330
|
|
1973
3331
|
private
|
@@ -1976,105 +3334,44 @@ module Net
|
|
1976
3334
|
PORT = 143 # :nodoc:
|
1977
3335
|
SSL_PORT = 993 # :nodoc:
|
1978
3336
|
|
1979
|
-
|
1980
|
-
|
1981
|
-
|
1982
|
-
|
1983
|
-
|
1984
|
-
|
1985
|
-
|
1986
|
-
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
# idle_response_timeout:: Seconds to wait until an IDLE response is received
|
1998
|
-
#
|
1999
|
-
# The most common errors are:
|
2000
|
-
#
|
2001
|
-
# Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
|
2002
|
-
# firewall.
|
2003
|
-
# Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
|
2004
|
-
# being dropped by an intervening firewall).
|
2005
|
-
# Errno::ENETUNREACH:: There is no route to that network.
|
2006
|
-
# SocketError:: Hostname not known or other socket error.
|
2007
|
-
# Net::IMAP::ByeResponseError:: The connected to the host was successful, but
|
2008
|
-
# it immediately said goodbye.
|
2009
|
-
def initialize(host, port_or_options = {},
|
2010
|
-
usessl = false, certs = nil, verify = true)
|
2011
|
-
super()
|
2012
|
-
@host = host
|
2013
|
-
begin
|
2014
|
-
options = port_or_options.to_hash
|
2015
|
-
rescue NoMethodError
|
2016
|
-
# for backward compatibility
|
2017
|
-
options = {}
|
2018
|
-
options[:port] = port_or_options
|
2019
|
-
if usessl
|
2020
|
-
options[:ssl] = create_ssl_params(certs, verify)
|
2021
|
-
end
|
3337
|
+
def start_imap_connection
|
3338
|
+
@greeting = get_server_greeting
|
3339
|
+
@capabilities = capabilities_from_resp_code @greeting
|
3340
|
+
@response_handlers.each do |handler| handler.call(@greeting) end
|
3341
|
+
@receiver_thread = start_receiver_thread
|
3342
|
+
rescue Exception
|
3343
|
+
state_logout!
|
3344
|
+
@sock.close
|
3345
|
+
raise
|
3346
|
+
end
|
3347
|
+
|
3348
|
+
def get_server_greeting
|
3349
|
+
greeting = get_response
|
3350
|
+
raise Error, "No server greeting - connection closed" unless greeting
|
3351
|
+
record_untagged_response_code greeting
|
3352
|
+
case greeting.name
|
3353
|
+
when "PREAUTH" then state_authenticated!
|
3354
|
+
when "BYE" then state_logout!; raise ByeResponseError, greeting
|
2022
3355
|
end
|
2023
|
-
|
2024
|
-
|
2025
|
-
@tagno = 0
|
2026
|
-
@open_timeout = options[:open_timeout] || 30
|
2027
|
-
@idle_response_timeout = options[:idle_response_timeout] || 5
|
2028
|
-
@parser = ResponseParser.new
|
2029
|
-
@sock = tcp_socket(@host, @port)
|
2030
|
-
begin
|
2031
|
-
if options[:ssl]
|
2032
|
-
start_tls_session(options[:ssl])
|
2033
|
-
@usessl = true
|
2034
|
-
else
|
2035
|
-
@usessl = false
|
2036
|
-
end
|
2037
|
-
@responses = Hash.new([].freeze)
|
2038
|
-
@tagged_responses = {}
|
2039
|
-
@response_handlers = []
|
2040
|
-
@tagged_response_arrival = new_cond
|
2041
|
-
@continued_command_tag = nil
|
2042
|
-
@continuation_request_arrival = new_cond
|
2043
|
-
@continuation_request_exception = nil
|
2044
|
-
@idle_done_cond = nil
|
2045
|
-
@logout_command_tag = nil
|
2046
|
-
@debug_output_bol = true
|
2047
|
-
@exception = nil
|
2048
|
-
|
2049
|
-
@greeting = get_response
|
2050
|
-
if @greeting.nil?
|
2051
|
-
raise Error, "connection closed"
|
2052
|
-
end
|
2053
|
-
if @greeting.name == "BYE"
|
2054
|
-
raise ByeResponseError, @greeting
|
2055
|
-
end
|
3356
|
+
greeting
|
3357
|
+
end
|
2056
3358
|
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
}
|
2064
|
-
@receiver_thread_terminating = false
|
2065
|
-
rescue Exception
|
2066
|
-
@sock.close
|
2067
|
-
raise
|
3359
|
+
def start_receiver_thread
|
3360
|
+
Thread.start do
|
3361
|
+
receive_responses
|
3362
|
+
rescue Exception => ex
|
3363
|
+
@receiver_thread_exception = ex
|
3364
|
+
# don't exit the thread with an exception
|
2068
3365
|
end
|
2069
3366
|
end
|
2070
3367
|
|
2071
3368
|
def tcp_socket(host, port)
|
2072
|
-
s = Socket.tcp(host, port, :connect_timeout =>
|
3369
|
+
s = Socket.tcp(host, port, :connect_timeout => open_timeout)
|
2073
3370
|
s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
|
2074
3371
|
s
|
2075
3372
|
rescue Errno::ETIMEDOUT
|
2076
3373
|
raise Net::OpenTimeout, "Timeout to open TCP connection to " +
|
2077
|
-
"#{host}:#{port} (exceeds #{
|
3374
|
+
"#{host}:#{port} (exceeds #{open_timeout} seconds)"
|
2078
3375
|
end
|
2079
3376
|
|
2080
3377
|
def receive_responses
|
@@ -2087,6 +3384,7 @@ module Net
|
|
2087
3384
|
resp = get_response
|
2088
3385
|
rescue Exception => e
|
2089
3386
|
synchronize do
|
3387
|
+
state_logout!
|
2090
3388
|
@sock.close
|
2091
3389
|
@exception = e
|
2092
3390
|
end
|
@@ -2106,6 +3404,7 @@ module Net
|
|
2106
3404
|
@tagged_response_arrival.broadcast
|
2107
3405
|
case resp.tag
|
2108
3406
|
when @logout_command_tag
|
3407
|
+
state_logout!
|
2109
3408
|
return
|
2110
3409
|
when @continued_command_tag
|
2111
3410
|
@continuation_request_exception =
|
@@ -2113,12 +3412,9 @@ module Net
|
|
2113
3412
|
@continuation_request_arrival.signal
|
2114
3413
|
end
|
2115
3414
|
when UntaggedResponse
|
2116
|
-
|
2117
|
-
if resp.data.instance_of?(ResponseText) &&
|
2118
|
-
(code = resp.data.code)
|
2119
|
-
record_response(code.name, code.data)
|
2120
|
-
end
|
3415
|
+
record_untagged_response(resp)
|
2121
3416
|
if resp.name == "BYE" && @logout_command_tag.nil?
|
3417
|
+
state_logout!
|
2122
3418
|
@sock.close
|
2123
3419
|
@exception = ByeResponseError.new(resp)
|
2124
3420
|
connection_closed = true
|
@@ -2126,6 +3422,7 @@ module Net
|
|
2126
3422
|
when ContinuationRequest
|
2127
3423
|
@continuation_request_arrival.signal
|
2128
3424
|
end
|
3425
|
+
state_unselected! if resp in {data: {code: {name: "CLOSED"}}}
|
2129
3426
|
@response_handlers.each do |handler|
|
2130
3427
|
handler.call(resp)
|
2131
3428
|
end
|
@@ -2146,6 +3443,8 @@ module Net
|
|
2146
3443
|
@idle_done_cond.signal
|
2147
3444
|
end
|
2148
3445
|
end
|
3446
|
+
ensure
|
3447
|
+
state_logout!
|
2149
3448
|
end
|
2150
3449
|
|
2151
3450
|
def get_tagged_response(tag, cmd, timeout = nil)
|
@@ -2171,35 +3470,54 @@ module Net
|
|
2171
3470
|
when /\A(?:BAD)\z/ni
|
2172
3471
|
raise BadResponseError, resp
|
2173
3472
|
else
|
2174
|
-
|
3473
|
+
disconnect
|
3474
|
+
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw.chomp]
|
2175
3475
|
end
|
2176
3476
|
end
|
2177
3477
|
|
2178
3478
|
def get_response
|
2179
|
-
buff =
|
2180
|
-
while true
|
2181
|
-
s = @sock.gets(CRLF)
|
2182
|
-
break unless s
|
2183
|
-
buff.concat(s)
|
2184
|
-
if /\{(\d+)\}\r\n/n =~ s
|
2185
|
-
s = @sock.read($1.to_i)
|
2186
|
-
buff.concat(s)
|
2187
|
-
else
|
2188
|
-
break
|
2189
|
-
end
|
2190
|
-
end
|
3479
|
+
buff = @reader.read_response_buffer
|
2191
3480
|
return nil if buff.length == 0
|
2192
|
-
if
|
2193
|
-
|
2194
|
-
end
|
2195
|
-
return @parser.parse(buff)
|
3481
|
+
$stderr.print(buff.gsub(/^/n, "S: ")) if config.debug?
|
3482
|
+
@parser.parse(buff)
|
2196
3483
|
end
|
2197
3484
|
|
2198
|
-
|
2199
|
-
|
2200
|
-
|
3485
|
+
#############################
|
3486
|
+
# built-in response handlers
|
3487
|
+
|
3488
|
+
# store name => [..., data]
|
3489
|
+
def record_untagged_response(resp)
|
3490
|
+
@responses[resp.name] << resp.data
|
3491
|
+
record_untagged_response_code resp
|
3492
|
+
end
|
3493
|
+
|
3494
|
+
# store code.name => [..., code.data]
|
3495
|
+
def record_untagged_response_code(resp)
|
3496
|
+
return unless resp.data.is_a?(ResponseText)
|
3497
|
+
return unless (code = resp.data.code)
|
3498
|
+
@responses[code.name] << code.data
|
3499
|
+
end
|
3500
|
+
|
3501
|
+
# NOTE: only call this for greeting, login, and authenticate
|
3502
|
+
def capabilities_from_resp_code(resp)
|
3503
|
+
return unless %w[PREAUTH OK].any? { _1.casecmp? resp.name }
|
3504
|
+
return unless (code = resp.data.code)
|
3505
|
+
return unless code.name.casecmp?("CAPABILITY")
|
3506
|
+
code.data.freeze
|
3507
|
+
end
|
3508
|
+
|
3509
|
+
#############################
|
3510
|
+
|
3511
|
+
# Calls send_command, yielding the text of each ContinuationRequest and
|
3512
|
+
# responding with each block result. Returns TaggedResponse. Raises
|
3513
|
+
# NoResponseError or BadResponseError.
|
3514
|
+
def send_command_with_continuations(cmd, *args)
|
3515
|
+
send_command(cmd, *args) do |server_response|
|
3516
|
+
if server_response.instance_of?(ContinuationRequest)
|
3517
|
+
client_response = yield server_response.data.text
|
3518
|
+
put_string(client_response + CRLF)
|
3519
|
+
end
|
2201
3520
|
end
|
2202
|
-
@responses[name].push(data)
|
2203
3521
|
end
|
2204
3522
|
|
2205
3523
|
def send_command(cmd, *args, &block)
|
@@ -2237,12 +3555,12 @@ module Net
|
|
2237
3555
|
|
2238
3556
|
def put_string(str)
|
2239
3557
|
@sock.print(str)
|
2240
|
-
if
|
3558
|
+
if config.debug?
|
2241
3559
|
if @debug_output_bol
|
2242
3560
|
$stderr.print("C: ")
|
2243
3561
|
end
|
2244
|
-
$stderr.print(str.gsub(/\n
|
2245
|
-
if /\
|
3562
|
+
$stderr.print(str.gsub(/\n/n) { $'.empty? ? $& : "\nC: " })
|
3563
|
+
if /\n\z/n.match(str)
|
2246
3564
|
@debug_output_bol = true
|
2247
3565
|
else
|
2248
3566
|
@debug_output_bol = false
|
@@ -2250,23 +3568,119 @@ module Net
|
|
2250
3568
|
end
|
2251
3569
|
end
|
2252
3570
|
|
2253
|
-
def
|
2254
|
-
if
|
2255
|
-
|
3571
|
+
def enforce_logindisabled?
|
3572
|
+
if config.enforce_logindisabled == :when_capabilities_cached
|
3573
|
+
capabilities_cached?
|
2256
3574
|
else
|
2257
|
-
|
3575
|
+
config.enforce_logindisabled
|
3576
|
+
end
|
3577
|
+
end
|
3578
|
+
|
3579
|
+
def expunge_internal(...)
|
3580
|
+
synchronize do
|
3581
|
+
send_command(...)
|
3582
|
+
expunged_array = clear_responses("EXPUNGE")
|
3583
|
+
vanished_array = extract_responses("VANISHED") { !_1.earlier? }
|
3584
|
+
if vanished_array.empty?
|
3585
|
+
expunged_array
|
3586
|
+
elsif vanished_array.length == 1
|
3587
|
+
vanished_array.first
|
3588
|
+
else
|
3589
|
+
merged_uids = SequenceSet[*vanished_array.map(&:uids)]
|
3590
|
+
VanishedData[uids: merged_uids, earlier: false]
|
3591
|
+
end
|
3592
|
+
end
|
3593
|
+
end
|
3594
|
+
|
3595
|
+
RETURN_WHOLE = /\ARETURN\z/i
|
3596
|
+
RETURN_START = /\ARETURN\b/i
|
3597
|
+
private_constant :RETURN_WHOLE, :RETURN_START
|
3598
|
+
|
3599
|
+
def search_args(keys, charset_arg = nil, return: nil, charset: nil)
|
3600
|
+
{return:} => {return: return_kw}
|
3601
|
+
case [return_kw, keys]
|
3602
|
+
in [nil, Array[RETURN_WHOLE, return_opts, *keys]]
|
3603
|
+
return_opts = convert_return_opts(return_opts)
|
3604
|
+
esearch = true
|
3605
|
+
in [nil => return_opts, RETURN_START]
|
3606
|
+
esearch = true
|
3607
|
+
in [nil => return_opts, keys]
|
3608
|
+
esearch = false
|
3609
|
+
in [_, Array[RETURN_WHOLE, _, *] | RETURN_START]
|
3610
|
+
raise ArgumentError, "conflicting return options"
|
3611
|
+
in [_, Array[RETURN_WHOLE, _, *]] # workaround for https://bugs.ruby-lang.org/issues/20956
|
3612
|
+
raise ArgumentError, "conflicting return options"
|
3613
|
+
in [_, RETURN_START] # workaround for https://bugs.ruby-lang.org/issues/20956
|
3614
|
+
raise ArgumentError, "conflicting return options"
|
3615
|
+
in [return_opts, keys]
|
3616
|
+
return_opts = convert_return_opts(return_opts)
|
3617
|
+
esearch = true
|
2258
3618
|
end
|
3619
|
+
if charset && charset_arg
|
3620
|
+
raise ArgumentError, "multiple charset arguments"
|
3621
|
+
end
|
3622
|
+
charset ||= charset_arg
|
3623
|
+
# NOTE: not handling combined RETURN and CHARSET for raw strings
|
3624
|
+
if charset && keys in /\ACHARSET\b/i | Array[/\ACHARSET\z/i, *]
|
3625
|
+
raise ArgumentError, "multiple charset arguments"
|
3626
|
+
end
|
3627
|
+
args = normalize_searching_criteria(keys)
|
3628
|
+
args.prepend("CHARSET", charset) if charset
|
3629
|
+
args.prepend("RETURN", return_opts) if return_opts
|
3630
|
+
return args, esearch
|
3631
|
+
end
|
3632
|
+
|
3633
|
+
def convert_return_opts(unconverted)
|
3634
|
+
return_opts = Array.try_convert(unconverted) or
|
3635
|
+
raise TypeError, "expected return options to be Array, got %s" % [
|
3636
|
+
unconverted.class
|
3637
|
+
]
|
3638
|
+
return_opts.map {|opt|
|
3639
|
+
case opt
|
3640
|
+
when Symbol then opt.to_s
|
3641
|
+
when PartialRange::Negative then PartialRange[opt]
|
3642
|
+
when Range then SequenceSet[opt]
|
3643
|
+
else opt
|
3644
|
+
end
|
3645
|
+
}
|
3646
|
+
end
|
3647
|
+
|
3648
|
+
def search_internal(cmd, ...)
|
3649
|
+
args, esearch = search_args(...)
|
2259
3650
|
synchronize do
|
2260
|
-
|
2261
|
-
|
3651
|
+
tagged = send_command(cmd, *args)
|
3652
|
+
tag = tagged.tag
|
3653
|
+
# Only the last ESEARCH or SEARCH is used. Excess results are ignored.
|
3654
|
+
esearch_result = extract_responses("ESEARCH") {|response|
|
3655
|
+
response in ESearchResult(tag: ^tag)
|
3656
|
+
}.last
|
3657
|
+
search_result = clear_responses("SEARCH").last
|
3658
|
+
if esearch_result
|
3659
|
+
# silently ignore SEARCH results, if any
|
3660
|
+
esearch_result
|
3661
|
+
elsif search_result
|
3662
|
+
# warn EXPECTED_ESEARCH_RESULT if esearch
|
3663
|
+
search_result
|
3664
|
+
elsif esearch
|
3665
|
+
# warn NO_SEARCH_RESPONSE
|
3666
|
+
ESearchResult[tag:, uid: cmd.start_with?("UID ")]
|
2262
3667
|
else
|
2263
|
-
|
3668
|
+
# warn NO_SEARCH_RESPONSE
|
3669
|
+
SearchResult[]
|
2264
3670
|
end
|
2265
|
-
return @responses.delete("SEARCH")[-1]
|
2266
3671
|
end
|
2267
3672
|
end
|
2268
3673
|
|
2269
|
-
def fetch_internal(cmd, set, attr, mod = nil)
|
3674
|
+
def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
|
3675
|
+
set = SequenceSet[set]
|
3676
|
+
if partial
|
3677
|
+
mod ||= []
|
3678
|
+
mod << "PARTIAL" << PartialRange[partial]
|
3679
|
+
end
|
3680
|
+
if changedsince
|
3681
|
+
mod ||= []
|
3682
|
+
mod << "CHANGEDSINCE" << Integer(changedsince)
|
3683
|
+
end
|
2270
3684
|
case attr
|
2271
3685
|
when String then
|
2272
3686
|
attr = RawData.new(attr)
|
@@ -2276,118 +3690,172 @@ module Net
|
|
2276
3690
|
}
|
2277
3691
|
end
|
2278
3692
|
|
2279
|
-
|
2280
|
-
|
2281
|
-
|
2282
|
-
send_command(cmd, MessageSet.new(set), attr, mod)
|
2283
|
-
else
|
2284
|
-
send_command(cmd, MessageSet.new(set), attr)
|
2285
|
-
end
|
2286
|
-
return @responses.delete("FETCH")
|
2287
|
-
end
|
3693
|
+
args = [cmd, set, attr]
|
3694
|
+
args << mod if mod
|
3695
|
+
send_command_returning_fetch_results(*args)
|
2288
3696
|
end
|
2289
3697
|
|
2290
|
-
def store_internal(cmd, set, attr, flags)
|
2291
|
-
if attr.instance_of?(String)
|
2292
|
-
|
2293
|
-
|
3698
|
+
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
3699
|
+
attr = RawData.new(attr) if attr.instance_of?(String)
|
3700
|
+
args = [SequenceSet.new(set)]
|
3701
|
+
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
3702
|
+
args << attr << flags
|
3703
|
+
send_command_returning_fetch_results(cmd, *args)
|
3704
|
+
end
|
3705
|
+
|
3706
|
+
def send_command_returning_fetch_results(...)
|
2294
3707
|
synchronize do
|
2295
|
-
|
2296
|
-
|
2297
|
-
|
3708
|
+
clear_responses("FETCH")
|
3709
|
+
clear_responses("UIDFETCH")
|
3710
|
+
send_command(...)
|
3711
|
+
fetches = clear_responses("FETCH")
|
3712
|
+
uidfetches = clear_responses("UIDFETCH")
|
3713
|
+
uidfetches.any? ? uidfetches : fetches
|
2298
3714
|
end
|
2299
3715
|
end
|
2300
3716
|
|
2301
3717
|
def copy_internal(cmd, set, mailbox)
|
2302
|
-
send_command(cmd,
|
3718
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
2303
3719
|
end
|
2304
3720
|
|
2305
3721
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
2306
|
-
|
2307
|
-
search_keys = [RawData.new(search_keys)]
|
2308
|
-
else
|
2309
|
-
normalize_searching_criteria(search_keys)
|
2310
|
-
end
|
2311
|
-
normalize_searching_criteria(search_keys)
|
3722
|
+
search_keys = normalize_searching_criteria(search_keys)
|
2312
3723
|
synchronize do
|
2313
3724
|
send_command(cmd, sort_keys, charset, *search_keys)
|
2314
|
-
|
3725
|
+
clear_responses("SORT").last || []
|
2315
3726
|
end
|
2316
3727
|
end
|
2317
3728
|
|
2318
3729
|
def thread_internal(cmd, algorithm, search_keys, charset)
|
2319
|
-
|
2320
|
-
|
2321
|
-
|
2322
|
-
|
3730
|
+
search_keys = normalize_searching_criteria(search_keys)
|
3731
|
+
synchronize do
|
3732
|
+
send_command(cmd, algorithm, charset, *search_keys)
|
3733
|
+
clear_responses("THREAD").last || []
|
2323
3734
|
end
|
2324
|
-
normalize_searching_criteria(search_keys)
|
2325
|
-
send_command(cmd, algorithm, charset, *search_keys)
|
2326
|
-
return @responses.delete("THREAD")[-1]
|
2327
3735
|
end
|
2328
3736
|
|
2329
|
-
def normalize_searching_criteria(
|
2330
|
-
|
2331
|
-
|
2332
|
-
|
2333
|
-
|
3737
|
+
def normalize_searching_criteria(criteria)
|
3738
|
+
return [RawData.new(criteria)] if criteria.is_a?(String)
|
3739
|
+
criteria.map {|i|
|
3740
|
+
if coerce_search_arg_to_seqset?(i)
|
3741
|
+
SequenceSet[i]
|
2334
3742
|
else
|
2335
3743
|
i
|
2336
3744
|
end
|
3745
|
+
}
|
3746
|
+
end
|
3747
|
+
|
3748
|
+
def coerce_search_arg_to_seqset?(obj)
|
3749
|
+
case obj
|
3750
|
+
when Set, -1, :* then true
|
3751
|
+
when Range then true
|
3752
|
+
when Array then obj.all? { coerce_search_array_arg_to_seqset? _1 }
|
3753
|
+
else obj.respond_to?(:to_sequence_set)
|
2337
3754
|
end
|
2338
3755
|
end
|
2339
3756
|
|
2340
|
-
def
|
2341
|
-
|
2342
|
-
|
2343
|
-
|
2344
|
-
|
2345
|
-
|
2346
|
-
params[:ca_path] = certs
|
2347
|
-
end
|
3757
|
+
def coerce_search_array_arg_to_seqset?(obj)
|
3758
|
+
case obj
|
3759
|
+
when Integer then obj.positive? || obj == -1
|
3760
|
+
when String then ResponseParser::Patterns::SEQUENCE_SET_STR.match?(obj.b)
|
3761
|
+
else
|
3762
|
+
coerce_search_arg_to_seqset?(obj)
|
2348
3763
|
end
|
2349
|
-
|
2350
|
-
|
3764
|
+
end
|
3765
|
+
|
3766
|
+
def build_ssl_ctx(ssl)
|
3767
|
+
if ssl
|
3768
|
+
params = (Hash.try_convert(ssl) || {}).freeze
|
3769
|
+
context = SSLContext.new
|
3770
|
+
context.set_params(params)
|
3771
|
+
if defined?(VerifyCallbackProc)
|
3772
|
+
context.verify_callback = VerifyCallbackProc
|
3773
|
+
end
|
3774
|
+
context.freeze
|
3775
|
+
[params, context]
|
2351
3776
|
else
|
2352
|
-
|
3777
|
+
false
|
2353
3778
|
end
|
2354
|
-
return params
|
2355
3779
|
end
|
2356
3780
|
|
2357
|
-
def start_tls_session
|
2358
|
-
unless defined?(OpenSSL::SSL)
|
2359
|
-
|
3781
|
+
def start_tls_session
|
3782
|
+
raise "SSL extension not installed" unless defined?(OpenSSL::SSL)
|
3783
|
+
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
3784
|
+
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
3785
|
+
@sock = SSLSocket.new(@sock, ssl_ctx)
|
3786
|
+
@reader = ResponseReader.new(self, @sock)
|
3787
|
+
@sock.sync_close = true
|
3788
|
+
@sock.hostname = @host if @sock.respond_to? :hostname=
|
3789
|
+
ssl_socket_connect(@sock, open_timeout)
|
3790
|
+
if ssl_ctx.verify_mode != VERIFY_NONE
|
3791
|
+
@sock.post_connection_check(@host)
|
3792
|
+
@tls_verified = true
|
2360
3793
|
end
|
2361
|
-
|
2362
|
-
|
3794
|
+
end
|
3795
|
+
|
3796
|
+
def state_authenticated!(resp = nil)
|
3797
|
+
synchronize do
|
3798
|
+
@capabilities = capabilities_from_resp_code resp if resp
|
3799
|
+
@connection_state = ConnectionState::Authenticated.new
|
2363
3800
|
end
|
2364
|
-
|
2365
|
-
|
2366
|
-
|
2367
|
-
|
3801
|
+
end
|
3802
|
+
|
3803
|
+
def state_selected!
|
3804
|
+
synchronize do
|
3805
|
+
@connection_state = ConnectionState::Selected.new
|
2368
3806
|
end
|
2369
|
-
|
2370
|
-
|
2371
|
-
|
2372
|
-
|
3807
|
+
end
|
3808
|
+
|
3809
|
+
def state_unselected!
|
3810
|
+
synchronize do
|
3811
|
+
state_authenticated! if connection_state.to_sym == :selected
|
2373
3812
|
end
|
2374
|
-
|
2375
|
-
|
2376
|
-
|
2377
|
-
|
2378
|
-
|
2379
|
-
|
3813
|
+
end
|
3814
|
+
|
3815
|
+
def state_logout!
|
3816
|
+
return true if connection_state in [:logout, *]
|
3817
|
+
synchronize do
|
3818
|
+
return true if connection_state in [:logout, *]
|
3819
|
+
@connection_state = ConnectionState::Logout.new
|
2380
3820
|
end
|
2381
3821
|
end
|
2382
3822
|
|
3823
|
+
# don't wait to aqcuire the lock
|
3824
|
+
def try_state_logout?
|
3825
|
+
return true if connection_state in [:logout, *]
|
3826
|
+
return false unless acquired_lock = mon_try_enter
|
3827
|
+
state_logout!
|
3828
|
+
true
|
3829
|
+
ensure
|
3830
|
+
mon_exit if acquired_lock
|
3831
|
+
end
|
3832
|
+
|
3833
|
+
def sasl_adapter
|
3834
|
+
SASLAdapter.new(self, &method(:send_command_with_continuations))
|
3835
|
+
end
|
3836
|
+
|
3837
|
+
#--
|
3838
|
+
# We could get the saslprep method by extending the SASLprep module
|
3839
|
+
# directly. It's done indirectly, so SASLprep can be lazily autoloaded,
|
3840
|
+
# because most users won't need it.
|
3841
|
+
#++
|
3842
|
+
# Delegates to Net::IMAP::StringPrep::SASLprep#saslprep.
|
3843
|
+
def self.saslprep(string, **opts)
|
3844
|
+
Net::IMAP::StringPrep::SASLprep.saslprep(string, **opts)
|
3845
|
+
end
|
3846
|
+
|
2383
3847
|
end
|
2384
3848
|
end
|
2385
3849
|
|
2386
3850
|
require_relative "imap/errors"
|
3851
|
+
require_relative "imap/config"
|
2387
3852
|
require_relative "imap/command_data"
|
2388
3853
|
require_relative "imap/data_encoding"
|
3854
|
+
require_relative "imap/data_lite"
|
2389
3855
|
require_relative "imap/flags"
|
2390
3856
|
require_relative "imap/response_data"
|
2391
3857
|
require_relative "imap/response_parser"
|
2392
3858
|
require_relative "imap/authenticators"
|
2393
|
-
|
3859
|
+
|
3860
|
+
require_relative "imap/deprecated_client_options"
|
3861
|
+
Net::IMAP.prepend Net::IMAP::DeprecatedClientOptions
|