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