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