net-imap 0.3.7 → 0.4.5
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.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/workflows/pages.yml +46 -0
- data/.github/workflows/test.yml +5 -12
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/README.md +15 -4
- data/Rakefile +0 -7
- data/docs/styles.css +0 -12
- data/lib/net/imap/authenticators.rb +26 -57
- data/lib/net/imap/command_data.rb +13 -6
- data/lib/net/imap/data_encoding.rb +14 -2
- data/lib/net/imap/deprecated_client_options.rb +139 -0
- data/lib/net/imap/errors.rb +20 -0
- data/lib/net/imap/fetch_data.rb +518 -0
- data/lib/net/imap/response_data.rb +116 -252
- data/lib/net/imap/response_parser/parser_utils.rb +240 -0
- data/lib/net/imap/response_parser.rb +1535 -1003
- data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
- data/lib/net/imap/sasl/authentication_exchange.rb +107 -0
- data/lib/net/imap/sasl/authenticators.rb +118 -0
- data/lib/net/imap/sasl/client_adapter.rb +72 -0
- data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +21 -11
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +180 -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} +25 -16
- 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 +45 -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 +144 -43
- data/lib/net/imap/sasl_adapter.rb +21 -0
- data/lib/net/imap/sequence_set.rb +67 -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.rb +1055 -612
- data/net-imap.gemspec +4 -3
- data/rakelib/benchmarks.rake +91 -0
- data/rakelib/saslprep.rake +4 -4
- data/rakelib/string_prep_tables_generator.rb +82 -60
- metadata +31 -13
- 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://tools.ietf.org/html/rfc3501]
|
29
|
+
# and {IMAP4rev2 [RFC9051]}[https://tools.ietf.org/html/rfc9051].
|
32
30
|
#
|
33
31
|
# == \IMAP Overview
|
34
32
|
#
|
@@ -77,31 +75,22 @@ module Net
|
|
77
75
|
# UIDs have to be reassigned. An \IMAP client thus cannot
|
78
76
|
# rearrange message orders.
|
79
77
|
#
|
80
|
-
# ===
|
78
|
+
# === Examples of Usage
|
81
79
|
#
|
82
|
-
#
|
83
|
-
# #capability. Users of the class must check for required capabilities before
|
84
|
-
# issuing commands. Special care should be taken to follow all #capability
|
85
|
-
# requirements for #starttls, #login, and #authenticate.
|
86
|
-
#
|
87
|
-
# See the #capability method for more information.
|
88
|
-
#
|
89
|
-
# == Examples of Usage
|
90
|
-
#
|
91
|
-
# === List sender and subject of all recent messages in the default mailbox
|
80
|
+
# ==== List sender and subject of all recent messages in the default mailbox
|
92
81
|
#
|
93
82
|
# imap = Net::IMAP.new('mail.example.com')
|
94
|
-
# imap.authenticate('
|
83
|
+
# imap.authenticate('PLAIN', 'joe_user', 'joes_password')
|
95
84
|
# imap.examine('INBOX')
|
96
85
|
# imap.search(["RECENT"]).each do |message_id|
|
97
86
|
# envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
|
98
87
|
# puts "#{envelope.from[0].name}: \t#{envelope.subject}"
|
99
88
|
# end
|
100
89
|
#
|
101
|
-
#
|
90
|
+
# ==== Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
|
102
91
|
#
|
103
92
|
# imap = Net::IMAP.new('mail.example.com')
|
104
|
-
# imap.authenticate('
|
93
|
+
# imap.authenticate('PLAIN', 'joe_user', 'joes_password')
|
105
94
|
# imap.select('Mail/sent-mail')
|
106
95
|
# if not imap.list('Mail/', 'sent-apr03')
|
107
96
|
# imap.create('Mail/sent-apr03')
|
@@ -112,12 +101,96 @@ module Net
|
|
112
101
|
# end
|
113
102
|
# imap.expunge
|
114
103
|
#
|
104
|
+
# == Capabilities
|
105
|
+
#
|
106
|
+
# Most Net::IMAP methods do not _currently_ modify their behaviour according
|
107
|
+
# to the server's advertised #capabilities. Users of this class must check
|
108
|
+
# that the server is capable of extension commands or command arguments before
|
109
|
+
# sending them. Special care should be taken to follow the #capabilities
|
110
|
+
# requirements for #starttls, #login, and #authenticate.
|
111
|
+
#
|
112
|
+
# See #capable?, #auth_capable?, #capabilities, #auth_mechanisms to discover
|
113
|
+
# server capabilities. For relevant capability requirements, see the
|
114
|
+
# documentation on each \IMAP command.
|
115
|
+
#
|
116
|
+
# imap = Net::IMAP.new("mail.example.com")
|
117
|
+
# imap.capable?(:IMAP4rev1) or raise "Not an IMAP4rev1 server"
|
118
|
+
# imap.capable?(:starttls) or raise "Cannot start TLS"
|
119
|
+
# imap.starttls
|
120
|
+
#
|
121
|
+
# if imap.auth_capable?("PLAIN")
|
122
|
+
# imap.authenticate "PLAIN", username, password
|
123
|
+
# elsif !imap.capability?("LOGINDISABLED")
|
124
|
+
# imap.login username, password
|
125
|
+
# else
|
126
|
+
# raise "No acceptable authentication mechanisms"
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# # Support for "UTF8=ACCEPT" implies support for "ENABLE"
|
130
|
+
# imap.enable :utf8 if imap.capable?("UTF8=ACCEPT")
|
131
|
+
#
|
132
|
+
# namespaces = imap.namespace if imap.capable?(:namespace)
|
133
|
+
# mbox_prefix = namespaces&.personal&.first&.prefix || ""
|
134
|
+
# mbox_delim = namespaces&.personal&.first&.delim || "/"
|
135
|
+
# mbox_path = prefix + %w[path to my mailbox].join(delim)
|
136
|
+
# imap.create mbox_path
|
137
|
+
#
|
138
|
+
# === Basic IMAP4rev1 capabilities
|
139
|
+
#
|
140
|
+
# IMAP4rev1 servers must advertise +IMAP4rev1+ in their capabilities list.
|
141
|
+
# IMAP4rev1 servers must _implement_ the +STARTTLS+, <tt>AUTH=PLAIN</tt>,
|
142
|
+
# and +LOGINDISABLED+ capabilities. See #starttls, #login, and #authenticate
|
143
|
+
# for the implications of these capabilities.
|
144
|
+
#
|
145
|
+
# === Caching +CAPABILITY+ responses
|
146
|
+
#
|
147
|
+
# Net::IMAP automatically stores and discards capability data according to the
|
148
|
+
# the requirements and recommendations in
|
149
|
+
# {IMAP4rev2 §6.1.1}[https://www.rfc-editor.org/rfc/rfc9051#section-6.1.1],
|
150
|
+
# {§6.2}[https://www.rfc-editor.org/rfc/rfc9051#section-6.2], and
|
151
|
+
# {§7.1}[https://www.rfc-editor.org/rfc/rfc9051#section-7.1].
|
152
|
+
# Use #capable?, #auth_capable?, or #capabilities to use this cache and avoid
|
153
|
+
# sending the #capability command unnecessarily.
|
154
|
+
#
|
155
|
+
# The server may advertise its initial capabilities using the +CAPABILITY+
|
156
|
+
# ResponseCode in a +PREAUTH+ or +OK+ #greeting. When TLS has started
|
157
|
+
# (#starttls) and after authentication (#login or #authenticate), the server's
|
158
|
+
# capabilities may change and cached capabilities are discarded. The server
|
159
|
+
# may send updated capabilities with an +OK+ TaggedResponse to #login or
|
160
|
+
# #authenticate, and these will be cached by Net::IMAP. But the
|
161
|
+
# TaggedResponse to #starttls MUST be ignored--it is sent before TLS starts
|
162
|
+
# and is unprotected.
|
163
|
+
#
|
164
|
+
# When storing capability values to variables, be careful that they are
|
165
|
+
# discarded or reset appropriately, especially following #starttls.
|
166
|
+
#
|
167
|
+
# === Using IMAP4rev1 extensions
|
168
|
+
#
|
169
|
+
# See the {IANA IMAP4 capabilities
|
170
|
+
# registry}[http://www.iana.org/assignments/imap4-capabilities] for a list of
|
171
|
+
# all standard capabilities, and their reference RFCs.
|
172
|
+
#
|
173
|
+
# IMAP4rev1 servers must not activate behavior that is incompatible with the
|
174
|
+
# base specification until an explicit client action invokes a capability,
|
175
|
+
# e.g. sending a command or command argument specific to that capability.
|
176
|
+
# Servers may send data with backward compatible behavior, such as response
|
177
|
+
# codes or mailbox attributes, at any time without client action.
|
178
|
+
#
|
179
|
+
# Invoking capabilities which are unknown to Net::IMAP may cause unexpected
|
180
|
+
# behavior and errors. For example, ResponseParseError is raised when
|
181
|
+
# unknown response syntax is received. Invoking commands or command
|
182
|
+
# parameters that are unsupported by the server may raise NoResponseError,
|
183
|
+
# BadResponseError, or cause other unexpected behavior.
|
184
|
+
#
|
185
|
+
# Some capabilities must be explicitly activated using the #enable command.
|
186
|
+
# See #enable for details.
|
187
|
+
#
|
115
188
|
# == Thread Safety
|
116
189
|
#
|
117
190
|
# Net::IMAP supports concurrent threads. For example,
|
118
191
|
#
|
119
192
|
# imap = Net::IMAP.new("imap.foo.net", "imap2")
|
120
|
-
# imap.authenticate("
|
193
|
+
# imap.authenticate("scram-md5", "bar", "password")
|
121
194
|
# imap.select("inbox")
|
122
195
|
# fetch_thread = Thread.start { imap.fetch(1..-1, "UID") }
|
123
196
|
# search_result = imap.search(["BODY", "hello"])
|
@@ -173,24 +246,54 @@ module Net
|
|
173
246
|
# == What's here?
|
174
247
|
#
|
175
248
|
# * {Connection control}[rdoc-ref:Net::IMAP@Connection+control+methods]
|
176
|
-
# * {
|
177
|
-
# * {...for any state}[rdoc-ref:Net::IMAP@IMAP+commands+for+any+state]
|
178
|
-
# * {...for the "not authenticated" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Not+Authenticated-22+state]
|
179
|
-
# * {...for the "authenticated" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Authenticated-22+state]
|
180
|
-
# * {...for the "selected" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Selected-22+state]
|
181
|
-
# * {...for the "logout" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Logout-22+state]
|
182
|
-
# * {Supported IMAP extensions}[rdoc-ref:Net::IMAP@Supported+IMAP+extensions]
|
249
|
+
# * {Server capabilities}[rdoc-ref:Net::IMAP@Server+capabilities]
|
183
250
|
# * {Handling server responses}[rdoc-ref:Net::IMAP@Handling+server+responses]
|
251
|
+
# * {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands]
|
252
|
+
# * {for any state}[rdoc-ref:Net::IMAP@Any+state]
|
253
|
+
# * {for the "not authenticated" state}[rdoc-ref:Net::IMAP@Not+Authenticated+state]
|
254
|
+
# * {for the "authenticated" state}[rdoc-ref:Net::IMAP@Authenticated+state]
|
255
|
+
# * {for the "selected" state}[rdoc-ref:Net::IMAP@Selected+state]
|
256
|
+
# * {for the "logout" state}[rdoc-ref:Net::IMAP@Logout+state]
|
257
|
+
# * {IMAP extension support}[rdoc-ref:Net::IMAP@IMAP+extension+support]
|
184
258
|
#
|
185
259
|
# === Connection control methods
|
186
260
|
#
|
187
|
-
# - Net::IMAP.new:
|
188
|
-
# successful server greeting before
|
261
|
+
# - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
|
262
|
+
# waits for a successful server greeting before the method returns.
|
189
263
|
# - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
|
190
264
|
# - #logout: Tells the server to end the session. Enters the "_logout_" state.
|
191
265
|
# - #disconnect: Disconnects the connection (without sending #logout first).
|
192
266
|
# - #disconnected?: True if the connection has been closed.
|
193
267
|
#
|
268
|
+
# === Server capabilities
|
269
|
+
#
|
270
|
+
# - #capable?: Returns whether the server supports a given capability.
|
271
|
+
# - #capabilities: Returns the server's capabilities as an array of strings.
|
272
|
+
# - #auth_capable?: Returns whether the server advertises support for a given
|
273
|
+
# SASL mechanism, for use with #authenticate.
|
274
|
+
# - #auth_mechanisms: Returns the #authenticate SASL mechanisms which
|
275
|
+
# the server claims to support as an array of strings.
|
276
|
+
# - #clear_cached_capabilities: Clears cached capabilities.
|
277
|
+
#
|
278
|
+
# <em>The capabilities cache is automatically cleared after completing
|
279
|
+
# #starttls, #login, or #authenticate.</em>
|
280
|
+
# - #capability: Sends the +CAPABILITY+ command and returns the #capabilities.
|
281
|
+
#
|
282
|
+
# <em>In general, #capable? should be used rather than explicitly sending a
|
283
|
+
# +CAPABILITY+ command to the server.</em>
|
284
|
+
#
|
285
|
+
# === Handling server responses
|
286
|
+
#
|
287
|
+
# - #greeting: The server's initial untagged response, which can indicate a
|
288
|
+
# pre-authenticated connection.
|
289
|
+
# - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
|
290
|
+
# ResponseCode#data.
|
291
|
+
# - #clear_responses: Deletes unhandled data from #responses and returns it.
|
292
|
+
# - #add_response_handler: Add a block to be called inside the receiver thread
|
293
|
+
# with every server response.
|
294
|
+
# - #response_handlers: Returns the list of response handlers.
|
295
|
+
# - #remove_response_handler: Remove a previously added response handler.
|
296
|
+
#
|
194
297
|
# === Core \IMAP commands
|
195
298
|
#
|
196
299
|
# The following commands are defined either by
|
@@ -199,69 +302,48 @@ module Net
|
|
199
302
|
# [IDLE[https://tools.ietf.org/html/rfc2177]],
|
200
303
|
# [NAMESPACE[https://tools.ietf.org/html/rfc2342]],
|
201
304
|
# [UNSELECT[https://tools.ietf.org/html/rfc3691]],
|
202
|
-
|
203
|
-
# TODO: [ENABLE[https://tools.ietf.org/html/rfc5161]],
|
204
|
-
# TODO: [LIST-EXTENDED[https://tools.ietf.org/html/rfc5258]],
|
205
|
-
# TODO: [LIST-STATUS[https://tools.ietf.org/html/rfc5819]],
|
206
|
-
#++
|
305
|
+
# [ENABLE[https://tools.ietf.org/html/rfc5161]],
|
207
306
|
# [MOVE[https://tools.ietf.org/html/rfc6851]].
|
208
307
|
# These extensions are widely supported by modern IMAP4rev1 servers and have
|
209
308
|
# all been integrated into [IMAP4rev2[https://tools.ietf.org/html/rfc9051]].
|
210
|
-
# <em
|
211
|
-
#
|
212
|
-
|
213
|
-
# TODO: When IMAP4rev2 is supported, add the following to the each of the
|
214
|
-
# appropriate commands below.
|
215
|
-
# Note:: CHECK has been removed from IMAP4rev2.
|
216
|
-
# Note:: LSUB is obsoleted by +LIST-EXTENDED and has been removed from IMAP4rev2.
|
217
|
-
# <em>Some arguments require the +LIST-EXTENDED+ or +IMAP4rev2+ capability.</em>
|
218
|
-
# <em>Requires either the +ENABLE+ or +IMAP4rev2+ capability.</em>
|
219
|
-
# <em>Requires either the +NAMESPACE+ or +IMAP4rev2+ capability.</em>
|
220
|
-
# <em>Requires either the +IDLE+ or +IMAP4rev2+ capability.</em>
|
221
|
-
# <em>Requires either the +UNSELECT+ or +IMAP4rev2+ capability.</em>
|
222
|
-
# <em>Requires either the +UIDPLUS+ or +IMAP4rev2+ capability.</em>
|
223
|
-
# <em>Requires either the +MOVE+ or +IMAP4rev2+ capability.</em>
|
224
|
-
#++
|
225
|
-
#
|
226
|
-
# ==== \IMAP commands for any state
|
309
|
+
# <em>*NOTE:* Net::IMAP doesn't support IMAP4rev2 yet.</em>
|
310
|
+
#
|
311
|
+
# ==== Any state
|
227
312
|
#
|
228
313
|
# - #capability: Returns the server's capabilities as an array of strings.
|
229
314
|
#
|
230
|
-
# <em>
|
231
|
-
#
|
315
|
+
# <em>In general, #capable? should be used rather than explicitly sending a
|
316
|
+
# +CAPABILITY+ command to the server.</em>
|
232
317
|
# - #noop: Allows the server to send unsolicited untagged #responses.
|
233
318
|
# - #logout: Tells the server to end the session. Enters the "_logout_" state.
|
234
319
|
#
|
235
|
-
# ====
|
320
|
+
# ==== Not Authenticated state
|
236
321
|
#
|
237
|
-
# In addition to the
|
238
|
-
# the "<em>not authenticated</em>" state:
|
322
|
+
# In addition to the commands for any state, the following commands are valid
|
323
|
+
# in the "<em>not authenticated</em>" state:
|
239
324
|
#
|
240
325
|
# - #starttls: Upgrades a clear-text connection to use TLS.
|
241
326
|
#
|
242
327
|
# <em>Requires the +STARTTLS+ capability.</em>
|
243
|
-
# - #authenticate: Identifies the client to the server using
|
244
|
-
# mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
245
|
-
# Enters the "_authenticated_" state.
|
328
|
+
# - #authenticate: Identifies the client to the server using the given
|
329
|
+
# {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
330
|
+
# and credentials. Enters the "_authenticated_" state.
|
246
331
|
#
|
247
|
-
# <em>
|
248
|
-
#
|
332
|
+
# <em>The server should list <tt>"AUTH=#{mechanism}"</tt> capabilities for
|
333
|
+
# supported mechanisms.</em>
|
249
334
|
# - #login: Identifies the client to the server using a plain text password.
|
250
335
|
# Using #authenticate is generally preferred. Enters the "_authenticated_"
|
251
336
|
# state.
|
252
337
|
#
|
253
338
|
# <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
|
254
339
|
#
|
255
|
-
# ====
|
256
|
-
#
|
257
|
-
# In addition to the universal commands, the following commands are valid in
|
258
|
-
# the "_authenticated_" state:
|
340
|
+
# ==== Authenticated state
|
259
341
|
#
|
260
|
-
|
261
|
-
#
|
342
|
+
# In addition to the commands for any state, the following commands are valid
|
343
|
+
# in the "_authenticated_" state:
|
262
344
|
#
|
263
|
-
#
|
264
|
-
|
345
|
+
# - #enable: Enables backwards incompatible server extensions.
|
346
|
+
# <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
|
265
347
|
# - #select: Open a mailbox and enter the "_selected_" state.
|
266
348
|
# - #examine: Open a mailbox read-only, and enter the "_selected_" state.
|
267
349
|
# - #create: Creates a new mailbox.
|
@@ -271,37 +353,31 @@ module Net
|
|
271
353
|
# - #unsubscribe: Removes a mailbox from the "subscribed" set.
|
272
354
|
# - #list: Returns names and attributes of mailboxes matching a given pattern.
|
273
355
|
# - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
|
274
|
-
#
|
275
|
-
# <em>Requires the +NAMESPACE+ capability.</em>
|
356
|
+
# <em>Requires the +NAMESPACE+ or +IMAP4rev2+ capability.</em>
|
276
357
|
# - #status: Returns mailbox information, e.g. message count, unseen message
|
277
358
|
# count, +UIDVALIDITY+ and +UIDNEXT+.
|
278
359
|
# - #append: Appends a message to the end of a mailbox.
|
279
360
|
# - #idle: Allows the server to send updates to the client, without the client
|
280
361
|
# needing to poll using #noop.
|
362
|
+
# <em>Requires the +IDLE+ or +IMAP4rev2+ capability.</em>
|
363
|
+
# - *Obsolete* #lsub: <em>Replaced by <tt>LIST-EXTENDED</tt> and removed from
|
364
|
+
# +IMAP4rev2+.</em> Lists mailboxes in the "subscribed" set.
|
281
365
|
#
|
282
|
-
# <em
|
283
|
-
# - #lsub: Lists mailboxes the user has declared "active" or "subscribed".
|
284
|
-
#--
|
285
|
-
# <em>Replaced by</em> <tt>LIST-EXTENDED</tt> <em>and removed from</em>
|
286
|
-
# +IMAP4rev2+. <em>However, Net::IMAP hasn't implemented</em>
|
287
|
-
# <tt>LIST-EXTENDED</tt> _yet_.
|
288
|
-
#++
|
366
|
+
# <em>*Note:* Net::IMAP hasn't implemented <tt>LIST-EXTENDED</tt> yet.</em>
|
289
367
|
#
|
290
|
-
# ====
|
368
|
+
# ==== Selected state
|
291
369
|
#
|
292
|
-
# In addition to the
|
293
|
-
# following commands are valid in the "_selected_" state:
|
370
|
+
# In addition to the commands for any state and the "_authenticated_"
|
371
|
+
# commands, the following commands are valid in the "_selected_" state:
|
294
372
|
#
|
295
373
|
# - #close: Closes the mailbox and returns to the "_authenticated_" state,
|
296
374
|
# expunging deleted messages, unless the mailbox was opened as read-only.
|
297
375
|
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
|
298
376
|
# without expunging any messages.
|
299
|
-
#
|
300
|
-
# <em>Requires the +UNSELECT+ capability.</em>
|
377
|
+
# <em>Requires the +UNSELECT+ or +IMAP4rev2+ capability.</em>
|
301
378
|
# - #expunge: Permanently removes messages which have the Deleted flag set.
|
302
|
-
# - #uid_expunge: Restricts
|
303
|
-
#
|
304
|
-
# <em>Requires the +UIDPLUS+ capability.</em>
|
379
|
+
# - #uid_expunge: Restricts expunge to only remove the specified UIDs.
|
380
|
+
# <em>Requires the +UIDPLUS+ or +IMAP4rev2+ capability.</em>
|
305
381
|
# - #search, #uid_search: Returns sequence numbers or UIDs of messages that
|
306
382
|
# match the given searching criteria.
|
307
383
|
# - #fetch, #uid_fetch: Returns data associated with a set of messages,
|
@@ -311,45 +387,35 @@ module Net
|
|
311
387
|
# specified destination mailbox.
|
312
388
|
# - #move, #uid_move: Moves the specified messages to the end of the
|
313
389
|
# specified destination mailbox, expunging them from the current mailbox.
|
390
|
+
# <em>Requires the +MOVE+ or +IMAP4rev2+ capability.</em>
|
391
|
+
# - #check: <em>*Obsolete:* removed from +IMAP4rev2+.</em>
|
392
|
+
# Can be replaced with #noop or #idle.
|
314
393
|
#
|
315
|
-
#
|
316
|
-
# - #check: Mostly obsolete. Can be replaced with #noop or #idle.
|
317
|
-
#--
|
318
|
-
# <em>Removed from IMAP4rev2.</em>
|
319
|
-
#++
|
320
|
-
#
|
321
|
-
# ==== \IMAP commands for the "Logout" state
|
394
|
+
# ==== Logout state
|
322
395
|
#
|
323
|
-
# No \IMAP commands are valid in the
|
396
|
+
# No \IMAP commands are valid in the "_logout_" state. If the socket is still
|
324
397
|
# open, Net::IMAP will close it after receiving server confirmation.
|
325
398
|
# Exceptions will be raised by \IMAP commands that have already started and
|
326
399
|
# are waiting for a response, as well as any that are called after logout.
|
327
400
|
#
|
328
|
-
# ===
|
401
|
+
# === \IMAP extension support
|
329
402
|
#
|
330
403
|
# ==== RFC9051: +IMAP4rev2+
|
331
404
|
#
|
332
|
-
# Although IMAP4rev2[https://tools.ietf.org/html/rfc9051] is
|
333
|
-
# yet
|
334
|
-
#
|
335
|
-
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
343
|
-
#
|
344
|
-
#
|
345
|
-
#
|
346
|
-
# implicitly supported, but we can do better: Response codes: RFC5530, etc
|
347
|
-
# implicitly supported, but we can do better: <tt>STATUS=SIZE</tt>
|
348
|
-
# implicitly supported, but we can do better: <tt>STATUS DELETED</tt>
|
349
|
-
#++
|
350
|
-
# Commands for these extensions are included with the {Core IMAP
|
351
|
-
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above. Other supported
|
352
|
-
# extensons are listed below.
|
405
|
+
# Although IMAP4rev2[https://tools.ietf.org/html/rfc9051] is not supported
|
406
|
+
# yet, Net::IMAP supports several extensions that have been folded into it:
|
407
|
+
# +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+, and
|
408
|
+
# the fetch side of +BINARY+.
|
409
|
+
# Commands for these extensions are listed with the {Core IMAP
|
410
|
+
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
|
411
|
+
#
|
412
|
+
# >>>
|
413
|
+
# <em>The following are folded into +IMAP4rev2+ but are currently
|
414
|
+
# unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
|
415
|
+
# extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
|
416
|
+
# +LITERAL-+, and +SPECIAL-USE+. The following extensions are implicitly
|
417
|
+
# supported, but will be updated with more direct support: RFC5530 response
|
418
|
+
# codes, <tt>STATUS=SIZE</tt>, and <tt>STATUS=DELETED</tt>.</em>
|
353
419
|
#
|
354
420
|
# ==== RFC2087: +QUOTA+
|
355
421
|
# - #getquota: returns the resource usage and limits for a quota root
|
@@ -358,92 +424,56 @@ module Net
|
|
358
424
|
# - #setquota: sets the resource limits for a given quota root.
|
359
425
|
#
|
360
426
|
# ==== RFC2177: +IDLE+
|
361
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051]
|
362
|
-
#
|
427
|
+
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
428
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
363
429
|
# - #idle: Allows the server to send updates to the client, without the client
|
364
430
|
# needing to poll using #noop.
|
365
431
|
#
|
366
432
|
# ==== RFC2342: +NAMESPACE+
|
367
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051]
|
368
|
-
#
|
433
|
+
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
434
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
369
435
|
# - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
|
370
436
|
#
|
371
437
|
# ==== RFC2971: +ID+
|
372
438
|
# - #id: exchanges client and server implementation information.
|
373
439
|
#
|
374
|
-
#--
|
375
|
-
# ==== RFC3502: +MULTIAPPEND+
|
376
|
-
# TODO...
|
377
|
-
#++
|
378
|
-
#
|
379
|
-
#--
|
380
440
|
# ==== RFC3516: +BINARY+
|
381
|
-
#
|
382
|
-
|
441
|
+
# The fetch side of +BINARY+ has been folded into
|
442
|
+
# IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
443
|
+
# - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
|
444
|
+
# +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
|
445
|
+
#
|
446
|
+
# >>>
|
447
|
+
# *NOTE:* The binary extension the #append command is _not_ supported yet.
|
383
448
|
#
|
384
449
|
# ==== RFC3691: +UNSELECT+
|
385
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051]
|
386
|
-
#
|
450
|
+
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
451
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
387
452
|
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
|
388
453
|
# without expunging any messages.
|
389
454
|
#
|
390
455
|
# ==== RFC4314: +ACL+
|
391
456
|
# - #getacl: lists the authenticated user's access rights to a mailbox.
|
392
457
|
# - #setacl: sets the access rights for a user on a mailbox
|
393
|
-
|
394
|
-
#
|
395
|
-
#++
|
396
|
-
# - *_Note:_* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
|
458
|
+
# >>>
|
459
|
+
# *NOTE:* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
|
397
460
|
#
|
398
461
|
# ==== RFC4315: +UIDPLUS+
|
399
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051]
|
400
|
-
#
|
462
|
+
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
463
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
401
464
|
# - #uid_expunge: Restricts #expunge to only remove the specified UIDs.
|
402
465
|
# - Updates #select, #examine with the +UIDNOTSTICKY+ ResponseCode
|
403
466
|
# - Updates #append with the +APPENDUID+ ResponseCode
|
404
467
|
# - Updates #copy, #move with the +COPYUID+ ResponseCode
|
405
468
|
#
|
406
|
-
#--
|
407
|
-
# ==== RFC4466: Collected Extensions to IMAP4 ABNF
|
408
|
-
# TODO...
|
409
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051], this RFC updates
|
410
|
-
# the protocol to enable new optional parameters to many commands: #select,
|
411
|
-
# #examine, #create, #rename, #fetch, #uid_fetch, #store, #uid_store, #search,
|
412
|
-
# #uid_search, and #append. However, specific parameters are not defined.
|
413
|
-
# Extensions to these commands use this syntax whenever possible. Net::IMAP
|
414
|
-
# may be partially compatible with extensions to these commands, even without
|
415
|
-
# any explicit support.
|
416
|
-
#++
|
417
|
-
#
|
418
|
-
#--
|
419
|
-
# ==== RFC4731 +ESEARCH+
|
420
|
-
# TODO...
|
421
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
422
|
-
# - Updates #search, #uid_search to accept result options: +MIN+, +MAX+,
|
423
|
-
# +ALL+, +COUNT+, and to return ExtendedSearchData.
|
424
|
-
#++
|
425
|
-
#
|
426
|
-
#--
|
427
469
|
# ==== RFC4959: +SASL-IR+
|
428
|
-
# TODO...
|
429
470
|
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
430
|
-
# - Updates #authenticate to
|
431
|
-
#++
|
432
|
-
#
|
433
|
-
#--
|
434
|
-
# ==== RFC4978: COMPRESS=DEFLATE
|
435
|
-
# TODO...
|
436
|
-
#++
|
471
|
+
# - Updates #authenticate with the option to send an initial response.
|
437
472
|
#
|
438
|
-
|
439
|
-
#
|
440
|
-
#
|
441
|
-
#
|
442
|
-
# - Updates #search, #uid_search with the +SAVE+ result option.
|
443
|
-
# - Updates #copy, #uid_copy, #fetch, #uid_fetch, #move, #uid_move, #search,
|
444
|
-
# #uid_search, #store, #uid_store, and #uid_expunge with ability to
|
445
|
-
# reference the saved result of a previous #search or #uid_search command.
|
446
|
-
#++
|
473
|
+
# ==== RFC5161: +ENABLE+
|
474
|
+
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
475
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
476
|
+
# - #enable: Enables backwards incompatible server extensions.
|
447
477
|
#
|
448
478
|
# ==== RFC5256: +SORT+
|
449
479
|
# - #sort, #uid_sort: An alternate version of #search or #uid_search which
|
@@ -453,75 +483,35 @@ module Net
|
|
453
483
|
# which arranges the results into ordered groups or threads according to a
|
454
484
|
# chosen algorithm.
|
455
485
|
#
|
456
|
-
|
457
|
-
#
|
458
|
-
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
462
|
-
# even without any explicit support.
|
463
|
-
# - Updates #list to accept selection options: +SUBSCRIBED+, +REMOTE+, and
|
464
|
-
# +RECURSIVEMATCH+, and return options: +SUBSCRIBED+ and +CHILDREN+.
|
465
|
-
#++
|
466
|
-
#
|
467
|
-
#--
|
468
|
-
# ==== RFC5819 +LIST-STATUS+
|
469
|
-
# TODO...
|
470
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
471
|
-
# - Updates #list with +STATUS+ return option.
|
472
|
-
#++
|
473
|
-
#
|
474
|
-
# ==== +XLIST+ (non-standard, deprecated)
|
486
|
+
# ==== +X-GM-EXT-1+
|
487
|
+
# +X-GM-EXT-1+ is a non-standard Gmail extension. See {Google's
|
488
|
+
# documentation}[https://developers.google.com/gmail/imap/imap-extensions].
|
489
|
+
# - Updates #fetch and #uid_fetch with support for +X-GM-MSGID+ (unique
|
490
|
+
# message ID), +X-GM-THRID+ (thread ID), and +X-GM-LABELS+ (Gmail labels).
|
491
|
+
# - Updates #search with the +X-GM-RAW+ search attribute.
|
475
492
|
# - #xlist: replaced by +SPECIAL-USE+ attributes in #list responses.
|
476
493
|
#
|
477
|
-
|
478
|
-
#
|
479
|
-
# TODO...
|
480
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
481
|
-
# - Updates #list with the +SPECIAL-USE+ selection and return options.
|
482
|
-
#++
|
494
|
+
# *NOTE:* The +OBJECTID+ extension should replace +X-GM-MSGID+ and
|
495
|
+
# +X-GM-THRID+, but Gmail does not support it (as of 2023-11-10).
|
483
496
|
#
|
484
497
|
# ==== RFC6851: +MOVE+
|
485
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051]
|
486
|
-
#
|
498
|
+
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
499
|
+
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
487
500
|
# - #move, #uid_move: Moves the specified messages to the end of the
|
488
501
|
# specified destination mailbox, expunging them from the current mailbox.
|
489
502
|
#
|
490
|
-
|
491
|
-
# ==== RFC6855: UTF8=ACCEPT
|
492
|
-
# TODO...
|
493
|
-
# ==== RFC6855: UTF8=ONLY
|
494
|
-
# TODO...
|
495
|
-
#++
|
496
|
-
#
|
497
|
-
#--
|
498
|
-
# ==== RFC7888: <tt>LITERAL+</tt>, +LITERAL-+
|
499
|
-
# TODO...
|
500
|
-
# ==== RFC7162: +QRESYNC+
|
501
|
-
# TODO...
|
502
|
-
# ==== RFC7162: +CONDSTORE+
|
503
|
-
# TODO...
|
504
|
-
# ==== RFC8474: +OBJECTID+
|
505
|
-
# TODO...
|
506
|
-
# ==== RFC9208: +QUOTA+
|
507
|
-
# TODO...
|
508
|
-
#++
|
503
|
+
# ==== RFC6855: <tt>UTF8=ACCEPT</tt>, <tt>UTF8=ONLY</tt>
|
509
504
|
#
|
510
|
-
#
|
511
|
-
#
|
512
|
-
# - #greeting: The server's initial untagged response, which can indicate a
|
513
|
-
# pre-authenticated connection.
|
514
|
-
# - #responses: A hash with arrays of unhandled <em>non-+nil+</em>
|
515
|
-
# UntaggedResponse and ResponseCode +#data+, keyed by +#name+.
|
516
|
-
# - #add_response_handler: Add a block to be called inside the receiver thread
|
517
|
-
# with every server response.
|
518
|
-
# - #remove_response_handler: Remove a previously added response handler.
|
505
|
+
# - See #enable for information about support for UTF-8 string encoding.
|
519
506
|
#
|
507
|
+
# ==== RFC8474: +OBJECTID+
|
508
|
+
# - Adds +MAILBOXID+ ResponseCode to #create tagged response.
|
509
|
+
# - Adds +MAILBOXID+ ResponseCode to #select and #examine untagged response.
|
510
|
+
# - Updates #fetch and #uid_fetch with the +EMAILID+ and +THREADID+ items.
|
511
|
+
# See FetchData#emailid and FetchData#emailid.
|
512
|
+
# - Updates #status with support for the +MAILBOXID+ status attribute.
|
520
513
|
#
|
521
514
|
# == References
|
522
|
-
#--
|
523
|
-
# TODO: Consider moving references list to REFERENCES.md or REFERENCES.rdoc.
|
524
|
-
#++
|
525
515
|
#
|
526
516
|
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
|
527
517
|
# Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - \VERSION 4rev1",
|
@@ -622,27 +612,21 @@ module Net
|
|
622
612
|
# RFC 1864, DOI 10.17487/RFC1864, October 1995,
|
623
613
|
# <https://www.rfc-editor.org/info/rfc1864>.
|
624
614
|
#
|
625
|
-
|
626
|
-
#
|
615
|
+
# [RFC3503[https://tools.ietf.org/html/rfc3503]]::
|
616
|
+
# Melnikov, A., "Message Disposition Notification (MDN)
|
617
|
+
# profile for Internet Message Access Protocol (IMAP)",
|
618
|
+
# RFC 3503, DOI 10.17487/RFC3503, March 2003,
|
619
|
+
# <https://www.rfc-editor.org/info/rfc3503>.
|
627
620
|
#
|
628
|
-
#
|
629
|
-
# Melnikov, A., "Message Disposition Notification (MDN)
|
630
|
-
# profile for Internet Message Access Protocol (IMAP)",
|
631
|
-
# RFC 3503, DOI 10.17487/RFC3503, March 2003,
|
632
|
-
# <https://www.rfc-editor.org/info/rfc3503>.
|
633
|
-
#++
|
621
|
+
# === \IMAP Extensions
|
634
622
|
#
|
635
|
-
# === Supported \IMAP Extensions
|
636
|
-
#
|
637
|
-
# [QUOTA[https://tools.ietf.org/html/rfc2087]]::
|
638
|
-
# Myers, J., "IMAP4 QUOTA extension", RFC 2087, DOI 10.17487/RFC2087,
|
639
|
-
# January 1997, <https://www.rfc-editor.org/info/rfc2087>.
|
640
|
-
#--
|
641
|
-
# TODO: test compatibility with updated QUOTA extension:
|
642
623
|
# [QUOTA[https://tools.ietf.org/html/rfc9208]]::
|
643
624
|
# Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
|
644
625
|
# March 2022, <https://www.rfc-editor.org/info/rfc9208>.
|
645
|
-
|
626
|
+
#
|
627
|
+
# <em>Note: obsoletes</em>
|
628
|
+
# RFC-2087[https://tools.ietf.org/html/rfc2087]<em> (January 1997)</em>.
|
629
|
+
# <em>Net::IMAP does not fully support the RFC9208 updates yet.</em>
|
646
630
|
# [IDLE[https://tools.ietf.org/html/rfc2177]]::
|
647
631
|
# Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
|
648
632
|
# June 1997, <https://www.rfc-editor.org/info/rfc2177>.
|
@@ -652,6 +636,10 @@ module Net
|
|
652
636
|
# [ID[https://tools.ietf.org/html/rfc2971]]::
|
653
637
|
# Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
|
654
638
|
# October 2000, <https://www.rfc-editor.org/info/rfc2971>.
|
639
|
+
# [BINARY[https://tools.ietf.org/html/rfc3516]]::
|
640
|
+
# Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
|
641
|
+
# DOI 10.17487/RFC3516, April 2003,
|
642
|
+
# <https://www.rfc-editor.org/info/rfc3516>.
|
655
643
|
# [ACL[https://tools.ietf.org/html/rfc4314]]::
|
656
644
|
# Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
|
657
645
|
# DOI 10.17487/RFC4314, December 2005,
|
@@ -675,31 +663,44 @@ module Net
|
|
675
663
|
# Gulbrandsen, A. and N. Freed, Ed., "Internet Message Access Protocol
|
676
664
|
# (\IMAP) - MOVE Extension", RFC 6851, DOI 10.17487/RFC6851, January 2013,
|
677
665
|
# <https://www.rfc-editor.org/info/rfc6851>.
|
666
|
+
# [UTF8=ACCEPT[https://tools.ietf.org/html/rfc6855]]::
|
667
|
+
# [UTF8=ONLY[https://tools.ietf.org/html/rfc6855]]::
|
668
|
+
# Resnick, P., Ed., Newman, C., Ed., and S. Shen, Ed.,
|
669
|
+
# "IMAP Support for UTF-8", RFC 6855, DOI 10.17487/RFC6855, March 2013,
|
670
|
+
# <https://www.rfc-editor.org/info/rfc6855>.
|
678
671
|
#
|
679
672
|
# === IANA registries
|
680
|
-
#
|
681
673
|
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
|
682
674
|
# * {IMAP Response Codes}[https://www.iana.org/assignments/imap-response-codes/imap-response-codes.xhtml]
|
683
675
|
# * {IMAP Mailbox Name Attributes}[https://www.iana.org/assignments/imap-mailbox-name-attributes/imap-mailbox-name-attributes.xhtml]
|
684
676
|
# * {IMAP and JMAP Keywords}[https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml]
|
685
677
|
# * {IMAP Threading Algorithms}[https://www.iana.org/assignments/imap-threading-algorithms/imap-threading-algorithms.xhtml]
|
686
|
-
#--
|
687
|
-
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
688
|
-
# * [{LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
|
689
|
-
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
|
690
|
-
# * {IMAP ANNOTATE Extension Entries and Attributes}[https://www.iana.org/assignments/imap-annotate-extension/imap-annotate-extension.xhtml]
|
691
|
-
# * {IMAP URLAUTH Access Identifiers and Prefixes}[https://www.iana.org/assignments/urlauth-access-ids/urlauth-access-ids.xhtml]
|
692
|
-
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
693
|
-
#++
|
694
678
|
# * {SASL Mechanisms and SASL SCRAM Family Mechanisms}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
695
679
|
# * {Service Name and Transport Protocol Port Number Registry}[https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml]:
|
696
680
|
# +imap+: tcp/143, +imaps+: tcp/993
|
697
681
|
# * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
|
698
682
|
# +imap+
|
699
683
|
# * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
684
|
+
# ===== For currently unsupported features:
|
685
|
+
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
686
|
+
# * {LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
|
687
|
+
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
|
688
|
+
# * {IMAP ANNOTATE Extension Entries and Attributes}[https://www.iana.org/assignments/imap-annotate-extension/imap-annotate-extension.xhtml]
|
689
|
+
# * {IMAP URLAUTH Access Identifiers and Prefixes}[https://www.iana.org/assignments/urlauth-access-ids/urlauth-access-ids.xhtml]
|
690
|
+
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
700
691
|
#
|
701
692
|
class IMAP < Protocol
|
702
|
-
VERSION = "0.
|
693
|
+
VERSION = "0.4.5"
|
694
|
+
|
695
|
+
# Aliases for supported capabilities, to be used with the #enable command.
|
696
|
+
ENABLE_ALIASES = {
|
697
|
+
utf8: "UTF8=ACCEPT",
|
698
|
+
"UTF8=ONLY" => "UTF8=ACCEPT",
|
699
|
+
}.freeze
|
700
|
+
|
701
|
+
autoload :SASL, File.expand_path("imap/sasl", __dir__)
|
702
|
+
autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
|
703
|
+
autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
|
703
704
|
|
704
705
|
include MonitorMixin
|
705
706
|
if defined?(OpenSSL::SSL)
|
@@ -707,35 +708,6 @@ module Net
|
|
707
708
|
include SSL
|
708
709
|
end
|
709
710
|
|
710
|
-
# Returns the initial greeting the server, an UntaggedResponse.
|
711
|
-
attr_reader :greeting
|
712
|
-
|
713
|
-
# Returns a hash with arrays of unhandled <em>non-+nil+</em>
|
714
|
-
# UntaggedResponse#data keyed by UntaggedResponse#name, and
|
715
|
-
# ResponseCode#data keyed by ResponseCode#name.
|
716
|
-
#
|
717
|
-
# For example:
|
718
|
-
#
|
719
|
-
# imap.select("inbox")
|
720
|
-
# p imap.responses["EXISTS"][-1]
|
721
|
-
# #=> 2
|
722
|
-
# p imap.responses["UIDVALIDITY"][-1]
|
723
|
-
# #=> 968263756
|
724
|
-
attr_reader :responses
|
725
|
-
|
726
|
-
# Returns all response handlers.
|
727
|
-
attr_reader :response_handlers
|
728
|
-
|
729
|
-
# Seconds to wait until a connection is opened.
|
730
|
-
# If the IMAP object cannot open a connection within this time,
|
731
|
-
# it raises a Net::OpenTimeout exception. The default value is 30 seconds.
|
732
|
-
attr_reader :open_timeout
|
733
|
-
|
734
|
-
# Seconds to wait until an IDLE response is received.
|
735
|
-
attr_reader :idle_response_timeout
|
736
|
-
|
737
|
-
attr_accessor :client_thread # :nodoc:
|
738
|
-
|
739
711
|
# Returns the debug mode.
|
740
712
|
def self.debug
|
741
713
|
return @@debug
|
@@ -762,9 +734,175 @@ module Net
|
|
762
734
|
alias default_ssl_port default_tls_port
|
763
735
|
end
|
764
736
|
|
737
|
+
# Returns the initial greeting the server, an UntaggedResponse.
|
738
|
+
attr_reader :greeting
|
739
|
+
|
740
|
+
# Seconds to wait until a connection is opened.
|
741
|
+
# If the IMAP object cannot open a connection within this time,
|
742
|
+
# it raises a Net::OpenTimeout exception. The default value is 30 seconds.
|
743
|
+
attr_reader :open_timeout
|
744
|
+
|
745
|
+
# Seconds to wait until an IDLE response is received.
|
746
|
+
attr_reader :idle_response_timeout
|
747
|
+
|
748
|
+
# The hostname this client connected to
|
749
|
+
attr_reader :host
|
750
|
+
|
751
|
+
# The port this client connected to
|
752
|
+
attr_reader :port
|
753
|
+
|
754
|
+
# Returns the
|
755
|
+
# {SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]
|
756
|
+
# used by the SSLSocket when TLS is attempted, even when the TLS handshake
|
757
|
+
# is unsuccessful. The context object will be frozen.
|
758
|
+
#
|
759
|
+
# Returns +nil+ for a plaintext connection.
|
760
|
+
attr_reader :ssl_ctx
|
761
|
+
|
762
|
+
# Returns the parameters that were sent to #ssl_ctx
|
763
|
+
# {set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params]
|
764
|
+
# when the connection tries to use TLS (even when unsuccessful).
|
765
|
+
#
|
766
|
+
# Returns +false+ for a plaintext connection.
|
767
|
+
attr_reader :ssl_ctx_params
|
768
|
+
|
769
|
+
# Creates a new Net::IMAP object and connects it to the specified
|
770
|
+
# +host+.
|
771
|
+
#
|
772
|
+
# ==== Options
|
773
|
+
#
|
774
|
+
# Accepts the following options:
|
775
|
+
#
|
776
|
+
# [port]
|
777
|
+
# Port number. Defaults to 993 when +ssl+ is truthy, and 143 otherwise.
|
778
|
+
#
|
779
|
+
# [ssl]
|
780
|
+
# If +true+, the connection will use TLS with the default params set by
|
781
|
+
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params].
|
782
|
+
# If +ssl+ is a hash, it's passed to
|
783
|
+
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
|
784
|
+
# the keys are names of attribute assignment methods on
|
785
|
+
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
|
786
|
+
#
|
787
|
+
# [open_timeout]
|
788
|
+
# Seconds to wait until a connection is opened
|
789
|
+
# [idle_response_timeout]
|
790
|
+
# Seconds to wait until an IDLE response is received
|
791
|
+
#
|
792
|
+
# See DeprecatedClientOptions.new for deprecated arguments.
|
793
|
+
#
|
794
|
+
# ==== Examples
|
795
|
+
#
|
796
|
+
# Connect to cleartext port 143 at mail.example.com and recieve the server greeting:
|
797
|
+
# imap = Net::IMAP.new('mail.example.com', ssl: false) # => #<Net::IMAP:0x00007f79b0872bd0>
|
798
|
+
# imap.port => 143
|
799
|
+
# imap.tls_verified? => false
|
800
|
+
# imap.greeting => name: ("OK" | "PREAUTH") => status
|
801
|
+
# status # => "OK"
|
802
|
+
# # The client is connected in the "Not Authenticated" state.
|
803
|
+
#
|
804
|
+
# Connect with TLS to port 993
|
805
|
+
# imap = Net::IMAP.new('mail.example.com', ssl: true) # => #<Net::IMAP:0x00007f79b0872bd0>
|
806
|
+
# imap.port => 993
|
807
|
+
# imap.tls_verified? => true
|
808
|
+
# imap.greeting => name: (/OK/i | /PREAUTH/i) => status
|
809
|
+
# case status
|
810
|
+
# in /OK/i
|
811
|
+
# # The client is connected in the "Not Authenticated" state.
|
812
|
+
# imap.authenticate("PLAIN", "joe_user", "joes_password")
|
813
|
+
# in /PREAUTH/i
|
814
|
+
# # The client is connected in the "Authenticated" state.
|
815
|
+
# end
|
816
|
+
#
|
817
|
+
# Connect with prior authentication, for example using an SSL certificate:
|
818
|
+
# ssl_ctx_params = {
|
819
|
+
# cert: OpenSSL::X509::Certificate.new(File.read("client.crt")),
|
820
|
+
# key: OpenSSL::PKey::EC.new(File.read('client.key')),
|
821
|
+
# extra_chain_cert: [
|
822
|
+
# OpenSSL::X509::Certificate.new(File.read("intermediate.crt")),
|
823
|
+
# ],
|
824
|
+
# }
|
825
|
+
# imap = Net::IMAP.new('mail.example.com', ssl: ssl_ctx_params)
|
826
|
+
# imap.port => 993
|
827
|
+
# imap.tls_verified? => true
|
828
|
+
# imap.greeting => name: "PREAUTH"
|
829
|
+
# # The client is connected in the "Authenticated" state.
|
830
|
+
#
|
831
|
+
# ==== Exceptions
|
832
|
+
#
|
833
|
+
# The most common errors are:
|
834
|
+
#
|
835
|
+
# [Errno::ECONNREFUSED]
|
836
|
+
# Connection refused by +host+ or an intervening firewall.
|
837
|
+
# [Errno::ETIMEDOUT]
|
838
|
+
# Connection timed out (possibly due to packets being dropped by an
|
839
|
+
# intervening firewall).
|
840
|
+
# [Errno::ENETUNREACH]
|
841
|
+
# There is no route to that network.
|
842
|
+
# [SocketError]
|
843
|
+
# Hostname not known or other socket error.
|
844
|
+
# [Net::IMAP::ByeResponseError]
|
845
|
+
# Connected to the host successfully, but it immediately said goodbye.
|
846
|
+
#
|
847
|
+
def initialize(host, port: nil, ssl: nil,
|
848
|
+
open_timeout: 30, idle_response_timeout: 5)
|
849
|
+
super()
|
850
|
+
# Config options
|
851
|
+
@host = host
|
852
|
+
@port = port || (ssl ? SSL_PORT : PORT)
|
853
|
+
@open_timeout = Integer(open_timeout)
|
854
|
+
@idle_response_timeout = Integer(idle_response_timeout)
|
855
|
+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
|
856
|
+
|
857
|
+
# Basic Client State
|
858
|
+
@utf8_strings = false
|
859
|
+
@debug_output_bol = true
|
860
|
+
@exception = nil
|
861
|
+
@greeting = nil
|
862
|
+
@capabilities = nil
|
863
|
+
|
864
|
+
# Client Protocol Reciever
|
865
|
+
@parser = ResponseParser.new
|
866
|
+
@responses = Hash.new {|h, k| h[k] = [] }
|
867
|
+
@response_handlers = []
|
868
|
+
@receiver_thread = nil
|
869
|
+
@receiver_thread_exception = nil
|
870
|
+
@receiver_thread_terminating = false
|
871
|
+
|
872
|
+
# Client Protocol Sender (including state for currently running commands)
|
873
|
+
@tag_prefix = "RUBY"
|
874
|
+
@tagno = 0
|
875
|
+
@tagged_responses = {}
|
876
|
+
@tagged_response_arrival = new_cond
|
877
|
+
@continued_command_tag = nil
|
878
|
+
@continuation_request_arrival = new_cond
|
879
|
+
@continuation_request_exception = nil
|
880
|
+
@idle_done_cond = nil
|
881
|
+
@logout_command_tag = nil
|
882
|
+
|
883
|
+
# Connection
|
884
|
+
@tls_verified = false
|
885
|
+
@sock = tcp_socket(@host, @port)
|
886
|
+
start_tls_session if ssl_ctx
|
887
|
+
start_imap_connection
|
888
|
+
|
889
|
+
# DEPRECATED: to remove in next version
|
890
|
+
@client_thread = Thread.current
|
891
|
+
end
|
892
|
+
|
893
|
+
# Returns true after the TLS negotiation has completed and the remote
|
894
|
+
# hostname has been verified. Returns false when TLS has been established
|
895
|
+
# but peer verification was disabled.
|
896
|
+
def tls_verified?; @tls_verified end
|
897
|
+
|
898
|
+
def client_thread # :nodoc:
|
899
|
+
warn "Net::IMAP#client_thread is deprecated and will be removed soon."
|
900
|
+
@client_thread
|
901
|
+
end
|
902
|
+
|
765
903
|
# Disconnects from the server.
|
766
904
|
#
|
767
|
-
# Related: #logout
|
905
|
+
# Related: #logout, #logout!
|
768
906
|
def disconnect
|
769
907
|
return if disconnected?
|
770
908
|
begin
|
@@ -794,62 +932,123 @@ module Net
|
|
794
932
|
return @sock.closed?
|
795
933
|
end
|
796
934
|
|
797
|
-
#
|
798
|
-
#
|
799
|
-
#
|
935
|
+
# Returns whether the server supports a given +capability+. When available,
|
936
|
+
# cached #capabilities are used without sending a new #capability command to
|
937
|
+
# the server.
|
800
938
|
#
|
801
|
-
#
|
802
|
-
#
|
803
|
-
# of all standard capabilities, and their reference RFCs.
|
939
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
940
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
804
941
|
#
|
805
|
-
#
|
806
|
-
#
|
807
|
-
#
|
808
|
-
|
809
|
-
|
810
|
-
|
942
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
943
|
+
#
|
944
|
+
# Related: #auth_capable?, #capabilities, #capability, #enable
|
945
|
+
def capable?(capability) capabilities.include? capability.to_s.upcase end
|
946
|
+
alias capability? capable?
|
947
|
+
|
948
|
+
# Returns the server capabilities. When available, cached capabilities are
|
949
|
+
# used without sending a new #capability command to the server.
|
950
|
+
#
|
951
|
+
# To ensure a case-insensitive comparison, #capable? can be used instead.
|
952
|
+
#
|
953
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
954
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
955
|
+
#
|
956
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
957
|
+
#
|
958
|
+
# Related: #capable?, #auth_capable?, #auth_mechanisms, #capability, #enable
|
959
|
+
def capabilities
|
960
|
+
@capabilities || capability
|
961
|
+
end
|
962
|
+
|
963
|
+
# Returns the #authenticate mechanisms that the server claims to support.
|
964
|
+
# These are derived from the #capabilities with an <tt>AUTH=</tt> prefix.
|
965
|
+
#
|
966
|
+
# This may be different when the connection is cleartext or using TLS. Most
|
967
|
+
# servers will drop all <tt>AUTH=</tt> mechanisms from #capabilities after
|
968
|
+
# the connection has authenticated.
|
969
|
+
#
|
970
|
+
# imap = Net::IMAP.new(hostname, ssl: false)
|
971
|
+
# imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
|
972
|
+
# imap.auth_mechanisms # => []
|
973
|
+
#
|
974
|
+
# imap.starttls
|
975
|
+
# imap.capabilities # => ["IMAP4REV1", "AUTH=PLAIN", "AUTH=XOAUTH2",
|
976
|
+
# # "AUTH=OAUTHBEARER"]
|
977
|
+
# imap.auth_mechanisms # => ["PLAIN", "XOAUTH2", "OAUTHBEARER"]
|
978
|
+
#
|
979
|
+
# imap.authenticate("XOAUTH2", username, oauth2_access_token)
|
980
|
+
# imap.auth_mechanisms # => []
|
981
|
+
#
|
982
|
+
# Related: #authenticate, #auth_capable?, #capabilities
|
983
|
+
def auth_mechanisms
|
984
|
+
capabilities
|
985
|
+
.grep(/\AAUTH=/i)
|
986
|
+
.map { _1.delete_prefix("AUTH=") }
|
987
|
+
end
|
988
|
+
|
989
|
+
# Returns whether the server supports a given SASL +mechanism+ for use with
|
990
|
+
# the #authenticate command. The +mechanism+ is supported when
|
991
|
+
# #capabilities includes <tt>"AUTH=#{mechanism.to_s.upcase}"</tt>. When
|
992
|
+
# available, cached capabilities are used without sending a new #capability
|
993
|
+
# command to the server.
|
811
994
|
#
|
812
|
-
#
|
813
|
-
#
|
995
|
+
# imap.capable? "AUTH=PLAIN" # => true
|
996
|
+
# imap.auth_capable? "PLAIN" # => true
|
997
|
+
# imap.auth_capable? "blurdybloop" # => false
|
814
998
|
#
|
815
|
-
#
|
999
|
+
# Related: #authenticate, #auth_mechanisms, #capable?, #capabilities
|
1000
|
+
def auth_capable?(mechanism)
|
1001
|
+
capable? "AUTH=#{mechanism}"
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
# Returns whether capabilities have been cached. When true, #capable? and
|
1005
|
+
# #capabilities don't require sending a #capability command to the server.
|
816
1006
|
#
|
817
|
-
#
|
818
|
-
# All IMAP4rev1 servers must _implement_ the +STARTTLS+,
|
819
|
-
# <tt>AUTH=PLAIN</tt>, and +LOGINDISABLED+ capabilities, and clients must
|
820
|
-
# respect their presence or absence. See the capabilites requirements on
|
821
|
-
# #starttls, #login, and #authenticate.
|
1007
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
822
1008
|
#
|
823
|
-
#
|
1009
|
+
# Related: #capable?, #capability, #clear_cached_capabilities
|
1010
|
+
def capabilities_cached?
|
1011
|
+
!!@capabilities
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# Clears capabilities that have been remembered by the Net::IMAP client.
|
1015
|
+
# This forces a #capability command to be sent the next time a #capabilities
|
1016
|
+
# query method is called.
|
824
1017
|
#
|
825
|
-
#
|
826
|
-
#
|
827
|
-
#
|
828
|
-
# compatible behavior, such as response codes or mailbox attributes, may
|
829
|
-
# be sent at any time.
|
1018
|
+
# Net::IMAP automatically discards its cached capabilities when they can
|
1019
|
+
# change. Explicitly calling this _should_ be unnecessary for well-behaved
|
1020
|
+
# servers.
|
830
1021
|
#
|
831
|
-
#
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
1022
|
+
# Related: #capable?, #capability, #capabilities_cached?
|
1023
|
+
def clear_cached_capabilities
|
1024
|
+
synchronize do
|
1025
|
+
clear_responses("CAPABILITY")
|
1026
|
+
@capabilities = nil
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
# Sends a {CAPABILITY command [IMAP4rev1 §6.1.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.1.1]
|
1031
|
+
# and returns an array of capabilities that are supported by the server.
|
1032
|
+
# The result is stored for use by #capable? and #capabilities.
|
836
1033
|
#
|
837
|
-
#
|
1034
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
1035
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
838
1036
|
#
|
839
|
-
#
|
840
|
-
#
|
841
|
-
#
|
842
|
-
# #
|
1037
|
+
# Net::IMAP automatically stores and discards capability data according to
|
1038
|
+
# the requirements and recommendations in
|
1039
|
+
# {IMAP4rev2 §6.1.1}[https://www.rfc-editor.org/rfc/rfc9051#section-6.1.1],
|
1040
|
+
# {§6.2}[https://www.rfc-editor.org/rfc/rfc9051#section-6.2], and
|
1041
|
+
# {§7.1}[https://www.rfc-editor.org/rfc/rfc9051#section-7.1].
|
1042
|
+
# Use #capable?, #auth_capable?, or #capabilities to this cache and avoid
|
1043
|
+
# sending the #capability command unnecessarily.
|
843
1044
|
#
|
844
|
-
#
|
845
|
-
# #authenticate. The OK TaggedResponse to #login and #authenticate may
|
846
|
-
# include +CAPABILITY+ response code data, but the TaggedResponse for
|
847
|
-
# #starttls is sent clear-text and cannot be trusted.
|
1045
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
848
1046
|
#
|
1047
|
+
# Related: #capable?, #auth_capable?, #capability, #enable
|
849
1048
|
def capability
|
850
1049
|
synchronize do
|
851
1050
|
send_command("CAPABILITY")
|
852
|
-
|
1051
|
+
@capabilities = clear_responses("CAPABILITY").last.freeze
|
853
1052
|
end
|
854
1053
|
end
|
855
1054
|
|
@@ -860,8 +1059,7 @@ module Net
|
|
860
1059
|
# Note that the user should first check if the server supports the ID
|
861
1060
|
# capability. For example:
|
862
1061
|
#
|
863
|
-
#
|
864
|
-
# if capabilities.include?("ID")
|
1062
|
+
# if capable?(:ID)
|
865
1063
|
# id = imap.id(
|
866
1064
|
# name: "my IMAP client (ruby)",
|
867
1065
|
# version: MyIMAP::VERSION,
|
@@ -875,11 +1073,11 @@ module Net
|
|
875
1073
|
# ===== Capabilities
|
876
1074
|
#
|
877
1075
|
# The server's capabilities must include +ID+
|
878
|
-
# [RFC2971[https://tools.ietf.org/html/rfc2971]]
|
1076
|
+
# [RFC2971[https://tools.ietf.org/html/rfc2971]].
|
879
1077
|
def id(client_id=nil)
|
880
1078
|
synchronize do
|
881
1079
|
send_command("ID", ClientID.new(client_id))
|
882
|
-
|
1080
|
+
clear_responses("ID").last
|
883
1081
|
end
|
884
1082
|
end
|
885
1083
|
|
@@ -888,7 +1086,7 @@ module Net
|
|
888
1086
|
#
|
889
1087
|
# This allows the server to send unsolicited untagged EXPUNGE #responses,
|
890
1088
|
# but does not execute any client request. \IMAP servers are permitted to
|
891
|
-
# send unsolicited untagged responses at any time, except for
|
1089
|
+
# send unsolicited untagged responses at any time, except for +EXPUNGE+:
|
892
1090
|
#
|
893
1091
|
# * +EXPUNGE+ can only be sent while a command is in progress.
|
894
1092
|
# * +EXPUNGE+ must _not_ be sent during #fetch, #store, or #search.
|
@@ -903,15 +1101,43 @@ module Net
|
|
903
1101
|
# to inform the command to inform the server that the client is done with
|
904
1102
|
# the connection.
|
905
1103
|
#
|
906
|
-
# Related: #disconnect
|
1104
|
+
# Related: #disconnect, #logout!
|
907
1105
|
def logout
|
908
1106
|
send_command("LOGOUT")
|
909
1107
|
end
|
910
1108
|
|
1109
|
+
# Calls #logout then, after receiving the TaggedResponse for the +LOGOUT+,
|
1110
|
+
# calls #disconnect. Returns the TaggedResponse from +LOGOUT+. Returns
|
1111
|
+
# +nil+ when the client is already disconnected, in contrast to #logout
|
1112
|
+
# which raises an exception.
|
1113
|
+
#
|
1114
|
+
# If #logout raises a StandardError, a warning will be printed but the
|
1115
|
+
# exception will not be re-raised.
|
1116
|
+
#
|
1117
|
+
# This is useful in situations where the connection must be dropped, for
|
1118
|
+
# example for security or after tests. If logout errors need to be handled,
|
1119
|
+
# use #logout and #disconnect instead.
|
1120
|
+
#
|
1121
|
+
# Related: #logout, #disconnect
|
1122
|
+
def logout!
|
1123
|
+
logout unless disconnected?
|
1124
|
+
rescue => ex
|
1125
|
+
warn "%s during <Net::IMAP %s:%s> logout!: %s" % [
|
1126
|
+
ex.class, host, port, ex
|
1127
|
+
]
|
1128
|
+
ensure
|
1129
|
+
disconnect
|
1130
|
+
end
|
1131
|
+
|
911
1132
|
# Sends a {STARTTLS command [IMAP4rev1 §6.2.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.1]
|
912
1133
|
# to start a TLS session.
|
913
1134
|
#
|
914
|
-
# Any +options+ are forwarded to
|
1135
|
+
# Any +options+ are forwarded directly to
|
1136
|
+
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
|
1137
|
+
# the keys are names of attribute assignment methods on
|
1138
|
+
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
|
1139
|
+
#
|
1140
|
+
# See DeprecatedClientOptions#starttls for deprecated arguments.
|
915
1141
|
#
|
916
1142
|
# This method returns after TLS negotiation and hostname verification are
|
917
1143
|
# both successful. Any error indicates that the connection has not been
|
@@ -921,132 +1147,156 @@ module Net
|
|
921
1147
|
# >>>
|
922
1148
|
# Any #response_handlers added before STARTTLS should be aware that the
|
923
1149
|
# TaggedResponse to STARTTLS is sent clear-text, _before_ TLS negotiation.
|
924
|
-
# TLS
|
1150
|
+
# TLS starts immediately _after_ that response. Any response code sent
|
1151
|
+
# with the response (e.g. CAPABILITY) is insecure and cannot be trusted.
|
925
1152
|
#
|
926
1153
|
# Related: Net::IMAP.new, #login, #authenticate
|
927
1154
|
#
|
928
1155
|
# ===== Capability
|
929
|
-
#
|
930
|
-
#
|
1156
|
+
# Clients should not call #starttls unless the server advertises the
|
1157
|
+
# +STARTTLS+ capability.
|
931
1158
|
#
|
932
1159
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
933
|
-
# Cached capabilities
|
934
|
-
#
|
935
|
-
# The TaggedResponse to #starttls is sent clear-text, so the server <em>must
|
936
|
-
# *not*</em> send capabilities in the #starttls response and clients <em>must
|
937
|
-
# not</em> use them if they are sent. Servers will generally send an
|
938
|
-
# unsolicited untagged response immeditely _after_ #starttls completes.
|
1160
|
+
# Cached #capabilities will be cleared when this method completes.
|
939
1161
|
#
|
940
|
-
def starttls(options
|
1162
|
+
def starttls(**options)
|
1163
|
+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
941
1164
|
send_command("STARTTLS") do |resp|
|
942
1165
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
options = create_ssl_params(certs, verify)
|
947
|
-
rescue NoMethodError
|
948
|
-
end
|
949
|
-
start_tls_session(options)
|
1166
|
+
clear_cached_capabilities
|
1167
|
+
clear_responses
|
1168
|
+
start_tls_session
|
950
1169
|
end
|
951
1170
|
end
|
952
1171
|
end
|
953
1172
|
|
954
1173
|
# :call-seq:
|
955
|
-
# authenticate(mechanism,
|
956
|
-
# authenticate(mech, *creds, **props) {|prop, auth| val } -> ok_resp
|
957
|
-
# authenticate(mechanism, authnid, credentials, authzid=nil) -> ok_resp
|
958
|
-
# authenticate(mechanism, **properties) -> ok_resp
|
959
|
-
# authenticate(mechanism) {|propname, authctx| prop_value } -> ok_resp
|
1174
|
+
# authenticate(mechanism, *, sasl_ir: true, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
|
960
1175
|
#
|
961
1176
|
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
|
962
1177
|
# to authenticate the client. If successful, the connection enters the
|
963
1178
|
# "_authenticated_" state.
|
964
1179
|
#
|
965
1180
|
# +mechanism+ is the name of the \SASL authentication mechanism to be used.
|
966
|
-
# All other arguments are forwarded to the authenticator for the requested
|
967
|
-
# mechanism. The listed call signatures are suggestions. <em>The
|
968
|
-
# documentation for each individual mechanism must be consulted for its
|
969
|
-
# specific parameters.</em>
|
970
1181
|
#
|
971
|
-
#
|
1182
|
+
# +sasl_ir+ allows or disallows sending an "initial response" (see the
|
1183
|
+
# +SASL-IR+ capability, below).
|
972
1184
|
#
|
973
|
-
#
|
1185
|
+
# All other arguments are forwarded to the registered SASL authenticator for
|
1186
|
+
# the requested mechanism. <em>The documentation for each individual
|
1187
|
+
# mechanism must be consulted for its specific parameters.</em>
|
974
1188
|
#
|
975
|
-
#
|
1189
|
+
# Related: #login, #starttls, #auth_capable?, #auth_mechanisms
|
976
1190
|
#
|
977
|
-
#
|
978
|
-
# Login using clear-text username and password.
|
1191
|
+
# ==== Mechanisms
|
979
1192
|
#
|
980
|
-
#
|
981
|
-
#
|
982
|
-
# Non-standard and obsoleted by +OAUTHBEARER+, but widely
|
983
|
-
# supported.
|
1193
|
+
# Each mechanism has different properties and requirements. Please consult
|
1194
|
+
# the documentation for the specific mechanisms you are using:
|
984
1195
|
#
|
985
|
-
#
|
986
|
-
#
|
987
|
-
#
|
1196
|
+
# +ANONYMOUS+::
|
1197
|
+
# See AnonymousAuthenticator[rdoc-ref:Net::IMAP::SASL::AnonymousAuthenticator].
|
1198
|
+
#
|
1199
|
+
# Allows the user to gain access to public services or resources without
|
1200
|
+
# authenticating or disclosing an identity.
|
1201
|
+
#
|
1202
|
+
# +EXTERNAL+::
|
1203
|
+
# See ExternalAuthenticator[rdoc-ref:Net::IMAP::SASL::ExternalAuthenticator].
|
1204
|
+
#
|
1205
|
+
# Authenticates using already established credentials, such as a TLS
|
1206
|
+
# certificate or IPsec.
|
988
1207
|
#
|
989
|
-
#
|
1208
|
+
# +OAUTHBEARER+::
|
1209
|
+
# See OAuthBearerAuthenticator[rdoc-ref:Net::IMAP::SASL::OAuthBearerAuthenticator].
|
990
1210
|
#
|
991
|
-
#
|
1211
|
+
# Login using an OAuth2 Bearer token. This is the standard mechanism
|
1212
|
+
# for using OAuth2 with \SASL, but it is not yet deployed as widely as
|
1213
|
+
# +XOAUTH2+.
|
992
1214
|
#
|
993
|
-
#
|
1215
|
+
# +PLAIN+::
|
1216
|
+
# See PlainAuthenticator[rdoc-ref:Net::IMAP::SASL::PlainAuthenticator].
|
994
1217
|
#
|
995
|
-
#
|
1218
|
+
# Login using clear-text username and password.
|
996
1219
|
#
|
997
|
-
#
|
998
|
-
#
|
1220
|
+
# +SCRAM-SHA-1+::
|
1221
|
+
# +SCRAM-SHA-256+::
|
1222
|
+
# See ScramAuthenticator[rdoc-ref:Net::IMAP::SASL::ScramAuthenticator].
|
1223
|
+
#
|
1224
|
+
# Login by username and password. The password is not sent to the
|
1225
|
+
# server but is used in a salted challenge/response exchange.
|
1226
|
+
# +SCRAM-SHA-1+ and +SCRAM-SHA-256+ are directly supported by
|
1227
|
+
# Net::IMAP::SASL. New authenticators can easily be added for any other
|
1228
|
+
# <tt>SCRAM-*</tt> mechanism if the digest algorithm is supported by
|
1229
|
+
# OpenSSL::Digest.
|
1230
|
+
#
|
1231
|
+
# +XOAUTH2+::
|
1232
|
+
# See XOAuth2Authenticator[rdoc-ref:Net::IMAP::SASL::XOAuth2Authenticator].
|
1233
|
+
#
|
1234
|
+
# Login using a username and an OAuth2 access token. Non-standard and
|
1235
|
+
# obsoleted by +OAUTHBEARER+, but widely supported.
|
1236
|
+
#
|
1237
|
+
# See the {SASL mechanism
|
999
1238
|
# registry}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
1000
|
-
# for
|
1239
|
+
# for a list of all SASL mechanisms and their specifications. To register
|
1240
|
+
# new authenticators, see Authenticators.
|
1001
1241
|
#
|
1002
|
-
# =====
|
1242
|
+
# ===== Deprecated mechanisms
|
1003
1243
|
#
|
1004
|
-
#
|
1005
|
-
#
|
1244
|
+
# <em>Obsolete mechanisms should be avoided, but are still available for
|
1245
|
+
# backwards compatibility. See</em> Net::IMAP::SASL@Deprecated+mechanisms.
|
1246
|
+
# <em>Using a deprecated mechanism will print a warning.</em>
|
1006
1247
|
#
|
1007
|
-
#
|
1008
|
-
#
|
1009
|
-
#
|
1010
|
-
#
|
1011
|
-
#
|
1012
|
-
#
|
1013
|
-
#
|
1014
|
-
#
|
1015
|
-
#
|
1016
|
-
#
|
1017
|
-
#
|
1018
|
-
# creds = {
|
1019
|
-
# authcid: username,
|
1020
|
-
# password: proc { password ||= ui.prompt_for_password },
|
1021
|
-
# oauth2_token: proc { accesstok ||= kms.fresh_access_token },
|
1022
|
-
# }
|
1023
|
-
# capa = imap.capability
|
1024
|
-
# if capa.include? "AUTH=OAUTHBEARER"
|
1025
|
-
# imap.authenticate "OAUTHBEARER", **creds # authcid, oauth2_token
|
1026
|
-
# elsif capa.include? "AUTH=XOAUTH2"
|
1027
|
-
# imap.authenticate "XOAUTH2", **creds # authcid, oauth2_token
|
1028
|
-
# elsif capa.include? "AUTH=SCRAM-SHA-256"
|
1029
|
-
# imap.authenticate "SCRAM-SHA-256", **creds # authcid, password
|
1030
|
-
# elsif capa.include? "AUTH=PLAIN"
|
1031
|
-
# imap.authenticate "PLAIN", **creds # authcid, password
|
1032
|
-
# elsif capa.include? "AUTH=DIGEST-MD5"
|
1033
|
-
# imap.authenticate "DIGEST-MD5", **creds # authcid, password
|
1034
|
-
# elsif capa.include? "LOGINDISABLED"
|
1035
|
-
# raise "the server has disabled login"
|
1036
|
-
# else
|
1248
|
+
# ==== Capabilities
|
1249
|
+
#
|
1250
|
+
# <tt>"AUTH=#{mechanism}"</tt> capabilities indicate server support for
|
1251
|
+
# mechanisms. Use #auth_capable? or #auth_mechanisms to check for support
|
1252
|
+
# before using a particular mechanism.
|
1253
|
+
#
|
1254
|
+
# if imap.auth_capable? "XOAUTH2"
|
1255
|
+
# imap.authenticate "XOAUTH2", username, oauth2_access_token
|
1256
|
+
# elsif imap.auth_capable? "PLAIN"
|
1257
|
+
# imap.authenticate "PLAIN", username, password
|
1258
|
+
# elsif !imap.capability? "LOGINDISABLED"
|
1037
1259
|
# imap.login username, password
|
1260
|
+
# else
|
1261
|
+
# raise "No acceptable authentication mechanism is available"
|
1038
1262
|
# end
|
1039
1263
|
#
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1264
|
+
# Although servers should list all supported \SASL mechanisms, they may
|
1265
|
+
# allow authentication with an unlisted +mechanism+.
|
1266
|
+
#
|
1267
|
+
# If [SASL-IR[https://www.rfc-editor.org/rfc/rfc4959.html]] is supported
|
1268
|
+
# and the appropriate <tt>"AUTH=#{mechanism}"</tt> capability is present,
|
1269
|
+
# an "initial response" may be sent as an argument to the +AUTHENTICATE+
|
1270
|
+
# command, saving a round-trip. The SASL exchange allows for server
|
1271
|
+
# challenges and client responses, but many mechanisms expect the client to
|
1272
|
+
# "respond" first. The initial response will only be sent for
|
1273
|
+
# "client-first" mechanisms.
|
1274
|
+
#
|
1275
|
+
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1276
|
+
# Previously cached #capabilities will be cleared when this method
|
1277
|
+
# completes. If the TaggedResponse to #authenticate includes updated
|
1278
|
+
# capabilities, they will be cached.
|
1279
|
+
def authenticate(mechanism, *creds, sasl_ir: true, **props, &callback)
|
1280
|
+
mechanism = mechanism.to_s.tr("_", "-").upcase
|
1281
|
+
authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
|
1282
|
+
cmdargs = ["AUTHENTICATE", mechanism]
|
1283
|
+
if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
|
1284
|
+
authenticator.respond_to?(:initial_response?) &&
|
1285
|
+
authenticator.initial_response?
|
1286
|
+
response = authenticator.process(nil)
|
1287
|
+
cmdargs << (response.empty? ? "=" : [response].pack("m0"))
|
1288
|
+
end
|
1289
|
+
result = send_command_with_continuations(*cmdargs) {|data|
|
1290
|
+
challenge = data.unpack1("m")
|
1291
|
+
response = authenticator.process challenge
|
1292
|
+
[response].pack("m0")
|
1293
|
+
}
|
1294
|
+
if authenticator.respond_to?(:done?) && !authenticator.done?
|
1295
|
+
logout!
|
1296
|
+
raise SASL::AuthenticationIncomplete, result
|
1049
1297
|
end
|
1298
|
+
@capabilities = capabilities_from_resp_code result
|
1299
|
+
result
|
1050
1300
|
end
|
1051
1301
|
|
1052
1302
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1054,16 +1304,25 @@ module Net
|
|
1054
1304
|
# this +user+. If successful, the connection enters the "_authenticated_"
|
1055
1305
|
# state.
|
1056
1306
|
#
|
1057
|
-
# Using #authenticate
|
1058
|
-
#
|
1307
|
+
# Using #authenticate {should be
|
1308
|
+
# preferred}[https://www.rfc-editor.org/rfc/rfc9051.html#name-login-command]
|
1309
|
+
# over #login. The LOGIN command is not the same as #authenticate with the
|
1310
|
+
# "LOGIN" +mechanism+.
|
1059
1311
|
#
|
1060
1312
|
# A Net::IMAP::NoResponseError is raised if authentication fails.
|
1061
1313
|
#
|
1062
1314
|
# Related: #authenticate, #starttls
|
1063
1315
|
#
|
1064
|
-
#
|
1065
|
-
#
|
1066
|
-
#
|
1316
|
+
# ===== Capabilities
|
1317
|
+
#
|
1318
|
+
# An IMAP client MUST NOT call #login when the server advertises the
|
1319
|
+
# +LOGINDISABLED+ capability.
|
1320
|
+
#
|
1321
|
+
# if imap.capability? "LOGINDISABLED"
|
1322
|
+
# raise "Remote server has disabled the login command"
|
1323
|
+
# else
|
1324
|
+
# imap.login username, password
|
1325
|
+
# end
|
1067
1326
|
#
|
1068
1327
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1069
1328
|
# Cached capabilities _must_ be invalidated after this method completes.
|
@@ -1072,17 +1331,18 @@ module Net
|
|
1072
1331
|
#
|
1073
1332
|
def login(user, password)
|
1074
1333
|
send_command("LOGIN", user, password)
|
1334
|
+
.tap { @capabilities = capabilities_from_resp_code _1 }
|
1075
1335
|
end
|
1076
1336
|
|
1077
1337
|
# Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
|
1078
1338
|
# to select a +mailbox+ so that messages in the +mailbox+ can be accessed.
|
1079
1339
|
#
|
1080
1340
|
# After you have selected a mailbox, you may retrieve the number of items in
|
1081
|
-
# that mailbox from <tt>imap.responses
|
1082
|
-
# recent messages from <tt>imap.responses
|
1083
|
-
# these values can change if new messages arrive during a session
|
1084
|
-
# existing messages are expunged; see #add_response_handler for a
|
1085
|
-
# detect these events.
|
1341
|
+
# that mailbox from <tt>imap.responses("EXISTS", &:last)</tt>, and the
|
1342
|
+
# number of recent messages from <tt>imap.responses("RECENT", &:last)</tt>.
|
1343
|
+
# Note that these values can change if new messages arrive during a session
|
1344
|
+
# or when existing messages are expunged; see #add_response_handler for a
|
1345
|
+
# way to detect these events.
|
1086
1346
|
#
|
1087
1347
|
# A Net::IMAP::NoResponseError is raised if the mailbox does not
|
1088
1348
|
# exist or is for some reason non-selectable.
|
@@ -1095,7 +1355,7 @@ module Net
|
|
1095
1355
|
# the server may return an untagged "NO" response with a "UIDNOTSTICKY"
|
1096
1356
|
# response code indicating that the mailstore does not support persistent
|
1097
1357
|
# UIDs:
|
1098
|
-
#
|
1358
|
+
# imap.responses("NO", &:last)&.code&.name == "UIDNOTSTICKY"
|
1099
1359
|
def select(mailbox)
|
1100
1360
|
synchronize do
|
1101
1361
|
@responses.clear
|
@@ -1185,10 +1445,10 @@ module Net
|
|
1185
1445
|
# to the client. +refname+ provides a context (for instance, a base
|
1186
1446
|
# directory in a directory-based mailbox hierarchy). +mailbox+ specifies a
|
1187
1447
|
# mailbox or (via wildcards) mailboxes under that context. Two wildcards
|
1188
|
-
# may be used in +mailbox+:
|
1189
|
-
# the hierarchy delimiter (for instance,
|
1190
|
-
# directory-based mailbox hierarchy); and
|
1191
|
-
# *except* the hierarchy delimiter.
|
1448
|
+
# may be used in +mailbox+: <tt>"*"</tt>, which matches all characters
|
1449
|
+
# *including* the hierarchy delimiter (for instance, "/" on a UNIX-hosted
|
1450
|
+
# directory-based mailbox hierarchy); and <tt>"%"</tt>, which matches all
|
1451
|
+
# characters *except* the hierarchy delimiter.
|
1192
1452
|
#
|
1193
1453
|
# If +refname+ is empty, +mailbox+ is used directly to determine
|
1194
1454
|
# which mailboxes to match. If +mailbox+ is empty, the root
|
@@ -1213,7 +1473,7 @@ module Net
|
|
1213
1473
|
def list(refname, mailbox)
|
1214
1474
|
synchronize do
|
1215
1475
|
send_command("LIST", refname, mailbox)
|
1216
|
-
|
1476
|
+
clear_responses("LIST")
|
1217
1477
|
end
|
1218
1478
|
end
|
1219
1479
|
|
@@ -1236,23 +1496,22 @@ module Net
|
|
1236
1496
|
# servers, then folder creation (and listing, moving, etc) can lead to
|
1237
1497
|
# errors.
|
1238
1498
|
#
|
1239
|
-
# From RFC2342:
|
1240
|
-
#
|
1241
|
-
# Although typically a server will support only a single Personal
|
1499
|
+
# From RFC2342[https://tools.ietf.org/html/rfc2342]:
|
1500
|
+
# >>>
|
1501
|
+
# <em>Although typically a server will support only a single Personal
|
1242
1502
|
# Namespace, and a single Other User's Namespace, circumstances exist
|
1243
1503
|
# where there MAY be multiples of these, and a client MUST be prepared
|
1244
1504
|
# for them. If a client is configured such that it is required to create
|
1245
1505
|
# a certain mailbox, there can be circumstances where it is unclear which
|
1246
1506
|
# Personal Namespaces it should create the mailbox in. In these
|
1247
1507
|
# situations a client SHOULD let the user select which namespaces to
|
1248
|
-
# create the mailbox in
|
1508
|
+
# create the mailbox in.</em>
|
1249
1509
|
#
|
1250
1510
|
# Related: #list, Namespaces, Namespace
|
1251
1511
|
#
|
1252
1512
|
# ===== For example:
|
1253
1513
|
#
|
1254
|
-
#
|
1255
|
-
# if capabilities.include?("NAMESPACE")
|
1514
|
+
# if capable?("NAMESPACE")
|
1256
1515
|
# namespaces = imap.namespace
|
1257
1516
|
# if namespace = namespaces.personal.first
|
1258
1517
|
# prefix = namespace.prefix # e.g. "" or "INBOX."
|
@@ -1271,7 +1530,7 @@ module Net
|
|
1271
1530
|
def namespace
|
1272
1531
|
synchronize do
|
1273
1532
|
send_command("NAMESPACE")
|
1274
|
-
|
1533
|
+
clear_responses("NAMESPACE").last
|
1275
1534
|
end
|
1276
1535
|
end
|
1277
1536
|
|
@@ -1315,7 +1574,7 @@ module Net
|
|
1315
1574
|
def xlist(refname, mailbox)
|
1316
1575
|
synchronize do
|
1317
1576
|
send_command("XLIST", refname, mailbox)
|
1318
|
-
|
1577
|
+
clear_responses("XLIST")
|
1319
1578
|
end
|
1320
1579
|
end
|
1321
1580
|
|
@@ -1334,8 +1593,8 @@ module Net
|
|
1334
1593
|
synchronize do
|
1335
1594
|
send_command("GETQUOTAROOT", mailbox)
|
1336
1595
|
result = []
|
1337
|
-
result.concat(
|
1338
|
-
result.concat(
|
1596
|
+
result.concat(clear_responses("QUOTAROOT"))
|
1597
|
+
result.concat(clear_responses("QUOTA"))
|
1339
1598
|
return result
|
1340
1599
|
end
|
1341
1600
|
end
|
@@ -1354,7 +1613,7 @@ module Net
|
|
1354
1613
|
def getquota(mailbox)
|
1355
1614
|
synchronize do
|
1356
1615
|
send_command("GETQUOTA", mailbox)
|
1357
|
-
|
1616
|
+
clear_responses("QUOTA")
|
1358
1617
|
end
|
1359
1618
|
end
|
1360
1619
|
|
@@ -1410,7 +1669,7 @@ module Net
|
|
1410
1669
|
def getacl(mailbox)
|
1411
1670
|
synchronize do
|
1412
1671
|
send_command("GETACL", mailbox)
|
1413
|
-
|
1672
|
+
clear_responses("ACL").last
|
1414
1673
|
end
|
1415
1674
|
end
|
1416
1675
|
|
@@ -1425,31 +1684,62 @@ module Net
|
|
1425
1684
|
def lsub(refname, mailbox)
|
1426
1685
|
synchronize do
|
1427
1686
|
send_command("LSUB", refname, mailbox)
|
1428
|
-
|
1687
|
+
clear_responses("LSUB")
|
1429
1688
|
end
|
1430
1689
|
end
|
1431
1690
|
|
1432
1691
|
# Sends a {STATUS commands [IMAP4rev1 §6.3.10]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.10]
|
1433
1692
|
# and returns the status of the indicated +mailbox+. +attr+ is a list of one
|
1434
|
-
# or more attributes whose statuses are to be requested.
|
1435
|
-
#
|
1693
|
+
# or more attributes whose statuses are to be requested.
|
1694
|
+
#
|
1695
|
+
# The return value is a hash of attributes.
|
1696
|
+
#
|
1697
|
+
# A Net::IMAP::NoResponseError is raised if status values
|
1698
|
+
# for +mailbox+ cannot be returned; for instance, because it
|
1699
|
+
# does not exist.
|
1700
|
+
#
|
1701
|
+
# ===== Supported attributes:
|
1702
|
+
#
|
1703
|
+
# +MESSAGES+:: The number of messages in the mailbox.
|
1704
|
+
#
|
1705
|
+
# +UIDNEXT+:: The next unique identifier value of the mailbox.
|
1706
|
+
#
|
1707
|
+
# +UIDVALIDITY+:: The unique identifier validity value of the mailbox.
|
1436
1708
|
#
|
1437
|
-
#
|
1438
|
-
# RECENT:: the number of recent messages in the mailbox.
|
1439
|
-
# UNSEEN:: the number of unseen messages in the mailbox.
|
1709
|
+
# +UNSEEN+:: The number of messages without the <tt>\Seen</tt> flag.
|
1440
1710
|
#
|
1441
|
-
# The
|
1711
|
+
# +DELETED+:: The number of messages with the <tt>\Deleted</tt> flag.
|
1712
|
+
#
|
1713
|
+
# +SIZE+::
|
1714
|
+
# The approximate size of the mailbox---must be greater than or equal to
|
1715
|
+
# the sum of all messages' +RFC822.SIZE+ fetch item values.
|
1716
|
+
#
|
1717
|
+
# +MAILBOXID+::
|
1718
|
+
# A server-allocated unique identifier for the mailbox.
|
1719
|
+
# See +OBJECTID+
|
1720
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-4].
|
1721
|
+
#
|
1722
|
+
# +RECENT+::
|
1723
|
+
# The number of messages with the <tt>\Recent</tt> flag.
|
1724
|
+
# _NOTE:_ +RECENT+ was removed from IMAP4rev2.
|
1725
|
+
#
|
1726
|
+
# ===== For example:
|
1442
1727
|
#
|
1443
1728
|
# p imap.status("inbox", ["MESSAGES", "RECENT"])
|
1444
1729
|
# #=> {"RECENT"=>0, "MESSAGES"=>44}
|
1445
1730
|
#
|
1446
|
-
#
|
1447
|
-
#
|
1448
|
-
#
|
1731
|
+
# ===== Capabilities
|
1732
|
+
#
|
1733
|
+
# +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
|
1734
|
+
# <tt>STATUS=SIZE</tt>
|
1735
|
+
# {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
|
1736
|
+
#
|
1737
|
+
# +MAILBOXID+ requires the server's capabilities to include +OBJECTID+
|
1738
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html].
|
1449
1739
|
def status(mailbox, attr)
|
1450
1740
|
synchronize do
|
1451
1741
|
send_command("STATUS", mailbox, attr)
|
1452
|
-
|
1742
|
+
clear_responses("STATUS").last&.attr
|
1453
1743
|
end
|
1454
1744
|
end
|
1455
1745
|
|
@@ -1538,7 +1828,7 @@ module Net
|
|
1538
1828
|
def expunge
|
1539
1829
|
synchronize do
|
1540
1830
|
send_command("EXPUNGE")
|
1541
|
-
|
1831
|
+
clear_responses("EXPUNGE")
|
1542
1832
|
end
|
1543
1833
|
end
|
1544
1834
|
|
@@ -1570,7 +1860,7 @@ module Net
|
|
1570
1860
|
def uid_expunge(uid_set)
|
1571
1861
|
synchronize do
|
1572
1862
|
send_command("UID EXPUNGE", MessageSet.new(uid_set))
|
1573
|
-
|
1863
|
+
clear_responses("EXPUNGE")
|
1574
1864
|
end
|
1575
1865
|
end
|
1576
1866
|
|
@@ -1589,7 +1879,7 @@ module Net
|
|
1589
1879
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
1590
1880
|
# in addition to documentation for
|
1591
1881
|
# any [CAPABILITIES[https://www.iana.org/assignments/imap-capabilities/imap-capabilities.xhtml]]
|
1592
|
-
# reported by #
|
1882
|
+
# reported by #capabilities which may define additional search filters, e.g:
|
1593
1883
|
# +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
|
1594
1884
|
# +SAVEDATE+. The following are some common search criteria:
|
1595
1885
|
#
|
@@ -1656,8 +1946,7 @@ module Net
|
|
1656
1946
|
# +attr+ is a list of attributes to fetch; see the documentation
|
1657
1947
|
# for FetchData for a list of valid attributes.
|
1658
1948
|
#
|
1659
|
-
# The return value is an array of FetchData
|
1660
|
-
# (instead of an empty array) if there is no matching message.
|
1949
|
+
# The return value is an array of FetchData.
|
1661
1950
|
#
|
1662
1951
|
# Related: #uid_search, FetchData
|
1663
1952
|
#
|
@@ -1702,11 +1991,11 @@ module Net
|
|
1702
1991
|
# to alter data associated with messages in the mailbox, in particular their
|
1703
1992
|
# flags. The +set+ parameter is a number, an array of numbers, or a Range
|
1704
1993
|
# object. Each number is a message sequence number. +attr+ is the name of a
|
1705
|
-
# data item to store:
|
1706
|
-
# provided one,
|
1707
|
-
# remove them. +flags+ is a list of flags.
|
1994
|
+
# data item to store: <tt>"FLAGS"</tt> will replace the message's flag list
|
1995
|
+
# with the provided one, <tt>"+FLAGS"</tt> will add the provided flags, and
|
1996
|
+
# <tt>"-FLAGS"</tt> will remove them. +flags+ is a list of flags.
|
1708
1997
|
#
|
1709
|
-
# The return value is an array of FetchData
|
1998
|
+
# The return value is an array of FetchData.
|
1710
1999
|
#
|
1711
2000
|
# Related: #uid_store
|
1712
2001
|
#
|
@@ -1884,6 +2173,87 @@ module Net
|
|
1884
2173
|
return thread_internal("UID THREAD", algorithm, search_keys, charset)
|
1885
2174
|
end
|
1886
2175
|
|
2176
|
+
# Sends an {ENABLE command [RFC5161 §3.2]}[https://www.rfc-editor.org/rfc/rfc5161#section-3.1]
|
2177
|
+
# {[IMAP4rev2 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.3.1]
|
2178
|
+
# to enable the specified server +capabilities+. Each capability may be an
|
2179
|
+
# array, string, or symbol. Returns a list of the capabilities that were
|
2180
|
+
# enabled.
|
2181
|
+
#
|
2182
|
+
# The +ENABLE+ command is only valid in the _authenticated_ state, before
|
2183
|
+
# any mailbox is selected.
|
2184
|
+
#
|
2185
|
+
# Related: #capable?, #capabilities, #capability
|
2186
|
+
#
|
2187
|
+
# ===== Capabilities
|
2188
|
+
#
|
2189
|
+
# The server's capabilities must include
|
2190
|
+
# +ENABLE+ [RFC5161[https://tools.ietf.org/html/rfc5161]]
|
2191
|
+
# or +IMAP4REV2+ [RFC9051[https://tools.ietf.org/html/rfc9051]].
|
2192
|
+
#
|
2193
|
+
# Additionally, the server capabilities must include a capability matching
|
2194
|
+
# each enabled extension (usually the same name as the enabled extension).
|
2195
|
+
# The following capabilities may be enabled:
|
2196
|
+
#
|
2197
|
+
# [+:utf8+ --- an alias for <tt>"UTF8=ACCEPT"</tt>]
|
2198
|
+
#
|
2199
|
+
# In a future release, <tt>enable(:utf8)</tt> will enable either
|
2200
|
+
# <tt>"UTF8=ACCEPT"</tt> or <tt>"IMAP4rev2"</tt>, depending on server
|
2201
|
+
# capabilities.
|
2202
|
+
#
|
2203
|
+
# [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://tools.ietf.org/html/rfc6855]]]
|
2204
|
+
#
|
2205
|
+
# The server's capabilities must include <tt>UTF8=ACCEPT</tt> _or_
|
2206
|
+
# <tt>UTF8=ONLY</tt>.
|
2207
|
+
#
|
2208
|
+
# This allows the server to send strings encoded as UTF-8 which might
|
2209
|
+
# otherwise need to use a 7-bit encoding, such as {modified
|
2210
|
+
# UTF-7}[::decode_utf7] for mailbox names, or RFC2047 encoded-words for
|
2211
|
+
# message headers.
|
2212
|
+
#
|
2213
|
+
# *Note:* <em>A future update may set string encodings slightly
|
2214
|
+
# differently</em>, e.g: "US-ASCII" when UTF-8 is not enabled, and "UTF-8"
|
2215
|
+
# when it is. Currently, the encoding of strings sent as "quoted" or
|
2216
|
+
# "text" will _always_ be "UTF-8", even when only ASCII characters are
|
2217
|
+
# used (e.g. "Subject: Agenda") And currently, string "literals" sent
|
2218
|
+
# by the server will always have an "ASCII-8BIT" (binary)
|
2219
|
+
# encoding, even if they generally contain UTF-8 data, if they are
|
2220
|
+
# text at all.
|
2221
|
+
#
|
2222
|
+
# [<tt>"UTF8=ONLY"</tt> [RFC6855[https://tools.ietf.org/html/rfc6855]]]
|
2223
|
+
#
|
2224
|
+
# A server that reports the <tt>UTF8=ONLY</tt> capability _requires_ that
|
2225
|
+
# the client <tt>enable("UTF8=ACCEPT")</tt> before any mailboxes may be
|
2226
|
+
# selected. For convenience, <tt>enable("UTF8=ONLY")</tt> is aliased to
|
2227
|
+
# <tt>enable("UTF8=ACCEPT")</tt>.
|
2228
|
+
#
|
2229
|
+
# ===== Unsupported capabilities
|
2230
|
+
#
|
2231
|
+
# *Note:* Some extensions that use ENABLE permit the server to send syntax
|
2232
|
+
# that Net::IMAP cannot parse, which may raise an exception and disconnect.
|
2233
|
+
# Some extensions may work, but the support may be incomplete, untested, or
|
2234
|
+
# experimental.
|
2235
|
+
#
|
2236
|
+
# Until a capability is documented here as supported, enabling it may result
|
2237
|
+
# in undocumented behavior and a future release may update with incompatible
|
2238
|
+
# behavior <em>without warning or deprecation</em>.
|
2239
|
+
#
|
2240
|
+
# <em>Caution is advised.</em>
|
2241
|
+
#
|
2242
|
+
def enable(*capabilities)
|
2243
|
+
capabilities = capabilities
|
2244
|
+
.flatten
|
2245
|
+
.map {|e| ENABLE_ALIASES[e] || e }
|
2246
|
+
.uniq
|
2247
|
+
.join(' ')
|
2248
|
+
synchronize do
|
2249
|
+
send_command("ENABLE #{capabilities}")
|
2250
|
+
result = clear_responses("ENABLED").last || []
|
2251
|
+
@utf8_strings ||= result.include? "UTF8=ACCEPT"
|
2252
|
+
@utf8_strings ||= result.include? "IMAP4REV2"
|
2253
|
+
result
|
2254
|
+
end
|
2255
|
+
end
|
2256
|
+
|
1887
2257
|
# Sends an {IDLE command [RFC2177 §3]}[https://www.rfc-editor.org/rfc/rfc6851#section-3]
|
1888
2258
|
# {[IMAP4rev2 §6.3.13]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.3.13]
|
1889
2259
|
# that waits for notifications of new or expunged messages. Yields
|
@@ -1948,6 +2318,104 @@ module Net
|
|
1948
2318
|
end
|
1949
2319
|
end
|
1950
2320
|
|
2321
|
+
# :call-seq:
|
2322
|
+
# responses {|hash| ...} -> block result
|
2323
|
+
# responses(type) {|array| ...} -> block result
|
2324
|
+
#
|
2325
|
+
# Yields unhandled responses and returns the result of the block.
|
2326
|
+
#
|
2327
|
+
# Unhandled responses are stored in a hash, with arrays of
|
2328
|
+
# <em>non-+nil+</em> UntaggedResponse#data keyed by UntaggedResponse#name
|
2329
|
+
# and ResponseCode#data keyed by ResponseCode#name. Call without +type+ to
|
2330
|
+
# yield the entire responses hash. Call with +type+ to yield only the array
|
2331
|
+
# of responses for that type.
|
2332
|
+
#
|
2333
|
+
# For example:
|
2334
|
+
#
|
2335
|
+
# imap.select("inbox")
|
2336
|
+
# p imap.responses("EXISTS", &:last)
|
2337
|
+
# #=> 2
|
2338
|
+
# p imap.responses("UIDVALIDITY", &:last)
|
2339
|
+
# #=> 968263756
|
2340
|
+
#
|
2341
|
+
# >>>
|
2342
|
+
# *Note:* Access to the responses hash is synchronized for thread-safety.
|
2343
|
+
# The receiver thread and response_handlers cannot process new responses
|
2344
|
+
# until the block completes. Accessing either the response hash or its
|
2345
|
+
# response type arrays outside of the block is unsafe.
|
2346
|
+
#
|
2347
|
+
# Calling without a block is unsafe and deprecated. Future releases will
|
2348
|
+
# raise ArgumentError unless a block is given.
|
2349
|
+
#
|
2350
|
+
# Previously unhandled responses are automatically cleared before entering a
|
2351
|
+
# mailbox with #select or #examine. Long-lived connections can receive many
|
2352
|
+
# unhandled server responses, which must be pruned or they will continually
|
2353
|
+
# consume more memory. Update or clear the responses hash or arrays inside
|
2354
|
+
# the block, or use #clear_responses.
|
2355
|
+
#
|
2356
|
+
# Only non-+nil+ data is stored. Many important response codes have no data
|
2357
|
+
# of their own, but are used as "tags" on the ResponseText object they are
|
2358
|
+
# attached to. ResponseText will be accessible by its response types:
|
2359
|
+
# "+OK+", "+NO+", "+BAD+", "+BYE+", or "+PREAUTH+".
|
2360
|
+
#
|
2361
|
+
# TaggedResponse#data is not saved to #responses, nor is any
|
2362
|
+
# ResponseCode#data on tagged responses. Although some command methods do
|
2363
|
+
# return the TaggedResponse directly, #add_response_handler must be used to
|
2364
|
+
# handle all response codes.
|
2365
|
+
#
|
2366
|
+
# Related: #clear_responses, #response_handlers, #greeting
|
2367
|
+
def responses(type = nil)
|
2368
|
+
if block_given?
|
2369
|
+
synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
|
2370
|
+
elsif type
|
2371
|
+
raise ArgumentError, "Pass a block or use #clear_responses"
|
2372
|
+
else
|
2373
|
+
# warn("DEPRECATED: pass a block or use #clear_responses", uplevel: 1)
|
2374
|
+
@responses
|
2375
|
+
end
|
2376
|
+
end
|
2377
|
+
|
2378
|
+
# :call-seq:
|
2379
|
+
# clear_responses -> hash
|
2380
|
+
# clear_responses(type) -> array
|
2381
|
+
#
|
2382
|
+
# Clears and returns the unhandled #responses hash or the unhandled
|
2383
|
+
# responses array for a single response +type+.
|
2384
|
+
#
|
2385
|
+
# Clearing responses is synchronized with other threads. The lock is
|
2386
|
+
# released before returning.
|
2387
|
+
#
|
2388
|
+
# Related: #responses, #response_handlers
|
2389
|
+
def clear_responses(type = nil)
|
2390
|
+
synchronize {
|
2391
|
+
if type
|
2392
|
+
@responses.delete(type) || []
|
2393
|
+
else
|
2394
|
+
@responses.dup.transform_values(&:freeze)
|
2395
|
+
.tap { _1.default = [].freeze }
|
2396
|
+
.tap { @responses.clear }
|
2397
|
+
end
|
2398
|
+
}
|
2399
|
+
.freeze
|
2400
|
+
end
|
2401
|
+
|
2402
|
+
# Returns all response handlers, including those that are added internally
|
2403
|
+
# by commands. Each response handler will be called with every new
|
2404
|
+
# UntaggedResponse, TaggedResponse, and ContinuationRequest.
|
2405
|
+
#
|
2406
|
+
# Response handlers are called with a mutex inside the receiver thread. New
|
2407
|
+
# responses cannot be processed and commands from other threads must wait
|
2408
|
+
# until all response_handlers return. An exception will shut-down the
|
2409
|
+
# receiver thread and close the connection.
|
2410
|
+
#
|
2411
|
+
# For thread-safety, the returned array is a frozen copy of the internal
|
2412
|
+
# array.
|
2413
|
+
#
|
2414
|
+
# Related: #add_response_handler, #remove_response_handler
|
2415
|
+
def response_handlers
|
2416
|
+
synchronize { @response_handlers.clone.freeze }
|
2417
|
+
end
|
2418
|
+
|
1951
2419
|
# Adds a response handler. For example, to detect when
|
1952
2420
|
# the server sends a new EXISTS response (which normally
|
1953
2421
|
# indicates new messages being added to the mailbox),
|
@@ -1960,14 +2428,21 @@ module Net
|
|
1960
2428
|
# end
|
1961
2429
|
# }
|
1962
2430
|
#
|
2431
|
+
# Related: #remove_response_handler, #response_handlers
|
1963
2432
|
def add_response_handler(handler = nil, &block)
|
1964
2433
|
raise ArgumentError, "two Procs are passed" if handler && block
|
1965
|
-
|
2434
|
+
synchronize do
|
2435
|
+
@response_handlers.push(block || handler)
|
2436
|
+
end
|
1966
2437
|
end
|
1967
2438
|
|
1968
2439
|
# Removes the response handler.
|
2440
|
+
#
|
2441
|
+
# Related: #add_response_handler, #response_handlers
|
1969
2442
|
def remove_response_handler(handler)
|
1970
|
-
|
2443
|
+
synchronize do
|
2444
|
+
@response_handlers.delete(handler)
|
2445
|
+
end
|
1971
2446
|
end
|
1972
2447
|
|
1973
2448
|
private
|
@@ -1978,93 +2453,29 @@ module Net
|
|
1978
2453
|
|
1979
2454
|
@@debug = false
|
1980
2455
|
|
1981
|
-
|
1982
|
-
|
1983
|
-
|
1984
|
-
|
1985
|
-
|
1986
|
-
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
|
1998
|
-
|
1999
|
-
|
2000
|
-
|
2001
|
-
|
2002
|
-
|
2003
|
-
|
2004
|
-
# being dropped by an intervening firewall).
|
2005
|
-
# Errno::ENETUNREACH:: There is no route to that network.
|
2006
|
-
# SocketError:: Hostname not known or other socket error.
|
2007
|
-
# Net::IMAP::ByeResponseError:: The connected to the host was successful, but
|
2008
|
-
# it immediately said goodbye.
|
2009
|
-
def initialize(host, port_or_options = {},
|
2010
|
-
usessl = false, certs = nil, verify = true)
|
2011
|
-
super()
|
2012
|
-
@host = host
|
2013
|
-
begin
|
2014
|
-
options = port_or_options.to_hash
|
2015
|
-
rescue NoMethodError
|
2016
|
-
# for backward compatibility
|
2017
|
-
options = {}
|
2018
|
-
options[:port] = port_or_options
|
2019
|
-
if usessl
|
2020
|
-
options[:ssl] = create_ssl_params(certs, verify)
|
2021
|
-
end
|
2022
|
-
end
|
2023
|
-
@port = options[:port] || (options[:ssl] ? SSL_PORT : PORT)
|
2024
|
-
@tag_prefix = "RUBY"
|
2025
|
-
@tagno = 0
|
2026
|
-
@open_timeout = options[:open_timeout] || 30
|
2027
|
-
@idle_response_timeout = options[:idle_response_timeout] || 5
|
2028
|
-
@parser = ResponseParser.new
|
2029
|
-
@sock = tcp_socket(@host, @port)
|
2030
|
-
begin
|
2031
|
-
if options[:ssl]
|
2032
|
-
start_tls_session(options[:ssl])
|
2033
|
-
@usessl = true
|
2034
|
-
else
|
2035
|
-
@usessl = false
|
2036
|
-
end
|
2037
|
-
@responses = Hash.new([].freeze)
|
2038
|
-
@tagged_responses = {}
|
2039
|
-
@response_handlers = []
|
2040
|
-
@tagged_response_arrival = new_cond
|
2041
|
-
@continued_command_tag = nil
|
2042
|
-
@continuation_request_arrival = new_cond
|
2043
|
-
@continuation_request_exception = nil
|
2044
|
-
@idle_done_cond = nil
|
2045
|
-
@logout_command_tag = nil
|
2046
|
-
@debug_output_bol = true
|
2047
|
-
@exception = nil
|
2048
|
-
|
2049
|
-
@greeting = get_response
|
2050
|
-
if @greeting.nil?
|
2051
|
-
raise Error, "connection closed"
|
2052
|
-
end
|
2053
|
-
if @greeting.name == "BYE"
|
2054
|
-
raise ByeResponseError, @greeting
|
2055
|
-
end
|
2056
|
-
|
2057
|
-
@client_thread = Thread.current
|
2058
|
-
@receiver_thread = Thread.start {
|
2059
|
-
begin
|
2060
|
-
receive_responses
|
2061
|
-
rescue Exception
|
2062
|
-
end
|
2063
|
-
}
|
2064
|
-
@receiver_thread_terminating = false
|
2065
|
-
rescue Exception
|
2066
|
-
@sock.close
|
2067
|
-
raise
|
2456
|
+
def start_imap_connection
|
2457
|
+
@greeting = get_server_greeting
|
2458
|
+
@capabilities = capabilities_from_resp_code @greeting
|
2459
|
+
@receiver_thread = start_receiver_thread
|
2460
|
+
rescue Exception
|
2461
|
+
@sock.close
|
2462
|
+
raise
|
2463
|
+
end
|
2464
|
+
|
2465
|
+
def get_server_greeting
|
2466
|
+
greeting = get_response
|
2467
|
+
raise Error, "No server greeting - connection closed" unless greeting
|
2468
|
+
record_untagged_response_code greeting
|
2469
|
+
raise ByeResponseError, greeting if greeting.name == "BYE"
|
2470
|
+
greeting
|
2471
|
+
end
|
2472
|
+
|
2473
|
+
def start_receiver_thread
|
2474
|
+
Thread.start do
|
2475
|
+
receive_responses
|
2476
|
+
rescue Exception => ex
|
2477
|
+
@receiver_thread_exception = ex
|
2478
|
+
# don't exit the thread with an exception
|
2068
2479
|
end
|
2069
2480
|
end
|
2070
2481
|
|
@@ -2113,11 +2524,7 @@ module Net
|
|
2113
2524
|
@continuation_request_arrival.signal
|
2114
2525
|
end
|
2115
2526
|
when UntaggedResponse
|
2116
|
-
|
2117
|
-
if resp.data.instance_of?(ResponseText) &&
|
2118
|
-
(code = resp.data.code)
|
2119
|
-
record_response(code.name, code.data)
|
2120
|
-
end
|
2527
|
+
record_untagged_response(resp)
|
2121
2528
|
if resp.name == "BYE" && @logout_command_tag.nil?
|
2122
2529
|
@sock.close
|
2123
2530
|
@exception = ByeResponseError.new(resp)
|
@@ -2171,7 +2578,8 @@ module Net
|
|
2171
2578
|
when /\A(?:BAD)\z/ni
|
2172
2579
|
raise BadResponseError, resp
|
2173
2580
|
else
|
2174
|
-
|
2581
|
+
disconnect
|
2582
|
+
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw.chomp]
|
2175
2583
|
end
|
2176
2584
|
end
|
2177
2585
|
|
@@ -2195,11 +2603,42 @@ module Net
|
|
2195
2603
|
return @parser.parse(buff)
|
2196
2604
|
end
|
2197
2605
|
|
2198
|
-
|
2199
|
-
|
2200
|
-
|
2606
|
+
#############################
|
2607
|
+
# built-in response handlers
|
2608
|
+
|
2609
|
+
# store name => [..., data]
|
2610
|
+
def record_untagged_response(resp)
|
2611
|
+
@responses[resp.name] << resp.data
|
2612
|
+
record_untagged_response_code resp
|
2613
|
+
end
|
2614
|
+
|
2615
|
+
# store code.name => [..., code.data]
|
2616
|
+
def record_untagged_response_code(resp)
|
2617
|
+
return unless resp.data.is_a?(ResponseText)
|
2618
|
+
return unless (code = resp.data.code)
|
2619
|
+
@responses[code.name] << code.data
|
2620
|
+
end
|
2621
|
+
|
2622
|
+
# NOTE: only call this for greeting, login, and authenticate
|
2623
|
+
def capabilities_from_resp_code(resp)
|
2624
|
+
return unless %w[PREAUTH OK].any? { _1.casecmp? resp.name }
|
2625
|
+
return unless (code = resp.data.code)
|
2626
|
+
return unless code.name.casecmp?("CAPABILITY")
|
2627
|
+
code.data.freeze
|
2628
|
+
end
|
2629
|
+
|
2630
|
+
#############################
|
2631
|
+
|
2632
|
+
# Calls send_command, yielding the text of each ContinuationRequest and
|
2633
|
+
# responding with each block result. Returns TaggedResponse. Raises
|
2634
|
+
# NoResponseError or BadResponseError.
|
2635
|
+
def send_command_with_continuations(cmd, *args)
|
2636
|
+
send_command(cmd, *args) do |server_response|
|
2637
|
+
if server_response.instance_of?(ContinuationRequest)
|
2638
|
+
client_response = yield server_response.data.text
|
2639
|
+
put_string(client_response + CRLF)
|
2640
|
+
end
|
2201
2641
|
end
|
2202
|
-
@responses[name].push(data)
|
2203
2642
|
end
|
2204
2643
|
|
2205
2644
|
def send_command(cmd, *args, &block)
|
@@ -2241,8 +2680,8 @@ module Net
|
|
2241
2680
|
if @debug_output_bol
|
2242
2681
|
$stderr.print("C: ")
|
2243
2682
|
end
|
2244
|
-
$stderr.print(str.gsub(/\n
|
2245
|
-
if /\
|
2683
|
+
$stderr.print(str.gsub(/\n/n) { $'.empty? ? $& : "\nC: " })
|
2684
|
+
if /\n\z/n.match(str)
|
2246
2685
|
@debug_output_bol = true
|
2247
2686
|
else
|
2248
2687
|
@debug_output_bol = false
|
@@ -2262,7 +2701,7 @@ module Net
|
|
2262
2701
|
else
|
2263
2702
|
send_command(cmd, *keys)
|
2264
2703
|
end
|
2265
|
-
|
2704
|
+
clear_responses("SEARCH").last || []
|
2266
2705
|
end
|
2267
2706
|
end
|
2268
2707
|
|
@@ -2277,13 +2716,13 @@ module Net
|
|
2277
2716
|
end
|
2278
2717
|
|
2279
2718
|
synchronize do
|
2280
|
-
|
2719
|
+
clear_responses("FETCH")
|
2281
2720
|
if mod
|
2282
2721
|
send_command(cmd, MessageSet.new(set), attr, mod)
|
2283
2722
|
else
|
2284
2723
|
send_command(cmd, MessageSet.new(set), attr)
|
2285
2724
|
end
|
2286
|
-
|
2725
|
+
clear_responses("FETCH")
|
2287
2726
|
end
|
2288
2727
|
end
|
2289
2728
|
|
@@ -2292,9 +2731,9 @@ module Net
|
|
2292
2731
|
attr = RawData.new(attr)
|
2293
2732
|
end
|
2294
2733
|
synchronize do
|
2295
|
-
|
2734
|
+
clear_responses("FETCH")
|
2296
2735
|
send_command(cmd, MessageSet.new(set), attr, flags)
|
2297
|
-
|
2736
|
+
clear_responses("FETCH")
|
2298
2737
|
end
|
2299
2738
|
end
|
2300
2739
|
|
@@ -2311,7 +2750,7 @@ module Net
|
|
2311
2750
|
normalize_searching_criteria(search_keys)
|
2312
2751
|
synchronize do
|
2313
2752
|
send_command(cmd, sort_keys, charset, *search_keys)
|
2314
|
-
|
2753
|
+
clear_responses("SORT").last || []
|
2315
2754
|
end
|
2316
2755
|
end
|
2317
2756
|
|
@@ -2322,8 +2761,10 @@ module Net
|
|
2322
2761
|
normalize_searching_criteria(search_keys)
|
2323
2762
|
end
|
2324
2763
|
normalize_searching_criteria(search_keys)
|
2325
|
-
|
2326
|
-
|
2764
|
+
synchronize do
|
2765
|
+
send_command(cmd, algorithm, charset, *search_keys)
|
2766
|
+
clear_responses("THREAD").last || []
|
2767
|
+
end
|
2327
2768
|
end
|
2328
2769
|
|
2329
2770
|
def normalize_searching_criteria(keys)
|
@@ -2337,49 +2778,49 @@ module Net
|
|
2337
2778
|
end
|
2338
2779
|
end
|
2339
2780
|
|
2340
|
-
def
|
2341
|
-
|
2342
|
-
|
2343
|
-
|
2344
|
-
|
2345
|
-
|
2346
|
-
|
2781
|
+
def build_ssl_ctx(ssl)
|
2782
|
+
if ssl
|
2783
|
+
params = (Hash.try_convert(ssl) || {}).freeze
|
2784
|
+
context = SSLContext.new
|
2785
|
+
context.set_params(params)
|
2786
|
+
if defined?(VerifyCallbackProc)
|
2787
|
+
context.verify_callback = VerifyCallbackProc
|
2347
2788
|
end
|
2348
|
-
|
2349
|
-
|
2350
|
-
params[:verify_mode] = VERIFY_PEER
|
2789
|
+
context.freeze
|
2790
|
+
[params, context]
|
2351
2791
|
else
|
2352
|
-
|
2792
|
+
false
|
2353
2793
|
end
|
2354
|
-
return params
|
2355
2794
|
end
|
2356
2795
|
|
2357
|
-
def start_tls_session
|
2358
|
-
unless defined?(OpenSSL::SSL)
|
2359
|
-
|
2360
|
-
|
2361
|
-
|
2362
|
-
raise RuntimeError, "already using SSL"
|
2363
|
-
end
|
2364
|
-
begin
|
2365
|
-
params = params.to_hash
|
2366
|
-
rescue NoMethodError
|
2367
|
-
params = {}
|
2368
|
-
end
|
2369
|
-
context = SSLContext.new
|
2370
|
-
context.set_params(params)
|
2371
|
-
if defined?(VerifyCallbackProc)
|
2372
|
-
context.verify_callback = VerifyCallbackProc
|
2373
|
-
end
|
2374
|
-
@sock = SSLSocket.new(@sock, context)
|
2796
|
+
def start_tls_session
|
2797
|
+
raise "SSL extension not installed" unless defined?(OpenSSL::SSL)
|
2798
|
+
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
2799
|
+
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
2800
|
+
@sock = SSLSocket.new(@sock, ssl_ctx)
|
2375
2801
|
@sock.sync_close = true
|
2376
2802
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
2377
2803
|
ssl_socket_connect(@sock, @open_timeout)
|
2378
|
-
if
|
2804
|
+
if ssl_ctx.verify_mode != VERIFY_NONE
|
2379
2805
|
@sock.post_connection_check(@host)
|
2806
|
+
@tls_verified = true
|
2380
2807
|
end
|
2381
2808
|
end
|
2382
2809
|
|
2810
|
+
def sasl_adapter
|
2811
|
+
SASLAdapter.new(self, &method(:send_command_with_continuations))
|
2812
|
+
end
|
2813
|
+
|
2814
|
+
#--
|
2815
|
+
# We could get the saslprep method by extending the SASLprep module
|
2816
|
+
# directly. It's done indirectly, so SASLprep can be lazily autoloaded,
|
2817
|
+
# because most users won't need it.
|
2818
|
+
#++
|
2819
|
+
# Delegates to Net::IMAP::StringPrep::SASLprep#saslprep.
|
2820
|
+
def self.saslprep(string, **opts)
|
2821
|
+
Net::IMAP::StringPrep::SASLprep.saslprep(string, **opts)
|
2822
|
+
end
|
2823
|
+
|
2383
2824
|
end
|
2384
2825
|
end
|
2385
2826
|
|
@@ -2390,4 +2831,6 @@ require_relative "imap/flags"
|
|
2390
2831
|
require_relative "imap/response_data"
|
2391
2832
|
require_relative "imap/response_parser"
|
2392
2833
|
require_relative "imap/authenticators"
|
2393
|
-
|
2834
|
+
|
2835
|
+
require_relative "imap/deprecated_client_options"
|
2836
|
+
Net::IMAP.prepend Net::IMAP::DeprecatedClientOptions
|