net-imap 0.3.9 → 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.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- 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 -35
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +597 -0
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +250 -440
- data/lib/net/imap/response_parser/parser_utils.rb +245 -0
- data/lib/net/imap/response_parser.rb +1873 -1210
- data/lib/net/imap/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 +2109 -924
- 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 -47
- 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/response_reader.rb +0 -75
- data/lib/net/imap/sasl/saslprep.rb +0 -55
- data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
- data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
data/lib/net/imap.rb
CHANGED
@@ -24,11 +24,9 @@ end
|
|
24
24
|
module Net
|
25
25
|
|
26
26
|
# Net::IMAP implements Internet Message Access Protocol (\IMAP) client
|
27
|
-
# functionality. The protocol is described
|
28
|
-
#
|
29
|
-
|
30
|
-
# TODO: and [IMAP4rev2[https://tools.ietf.org/html/rfc9051]].
|
31
|
-
#++
|
27
|
+
# functionality. The protocol is described
|
28
|
+
# in {IMAP4rev1 [RFC3501]}[https://www.rfc-editor.org/rfc/rfc3501]
|
29
|
+
# and {IMAP4rev2 [RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051].
|
32
30
|
#
|
33
31
|
# == \IMAP Overview
|
34
32
|
#
|
@@ -45,16 +43,10 @@ module Net
|
|
45
43
|
# To work on the messages within a mailbox, the client must
|
46
44
|
# first select that mailbox, using either #select or #examine
|
47
45
|
# (for read-only access). Once the client has successfully
|
48
|
-
# selected a mailbox, they enter the
|
46
|
+
# selected a mailbox, they enter the "_selected_" state, and that
|
49
47
|
# mailbox becomes the _current_ mailbox, on which mail-item
|
50
48
|
# related commands implicitly operate.
|
51
49
|
#
|
52
|
-
# === Connection state
|
53
|
-
#
|
54
|
-
# Once an IMAP connection is established, the connection is in one of four
|
55
|
-
# states: <tt>not authenticated</tt>, +authenticated+, +selected+, and
|
56
|
-
# +logout+. Most commands are valid only in certain states.
|
57
|
-
#
|
58
50
|
# === Sequence numbers and UIDs
|
59
51
|
#
|
60
52
|
# Messages have two sorts of identifiers: message sequence
|
@@ -83,31 +75,22 @@ module Net
|
|
83
75
|
# UIDs have to be reassigned. An \IMAP client thus cannot
|
84
76
|
# rearrange message orders.
|
85
77
|
#
|
86
|
-
# ===
|
87
|
-
#
|
88
|
-
# Net::IMAP <em>does not modify its behavior</em> according to server
|
89
|
-
# #capability. Users of the class must check for required capabilities before
|
90
|
-
# issuing commands. Special care should be taken to follow all #capability
|
91
|
-
# requirements for #starttls, #login, and #authenticate.
|
92
|
-
#
|
93
|
-
# See the #capability method for more information.
|
94
|
-
#
|
95
|
-
# == Examples of Usage
|
78
|
+
# === Examples of Usage
|
96
79
|
#
|
97
|
-
#
|
80
|
+
# ==== List sender and subject of all recent messages in the default mailbox
|
98
81
|
#
|
99
82
|
# imap = Net::IMAP.new('mail.example.com')
|
100
|
-
# imap.authenticate('
|
83
|
+
# imap.authenticate('PLAIN', 'joe_user', 'joes_password')
|
101
84
|
# imap.examine('INBOX')
|
102
85
|
# imap.search(["RECENT"]).each do |message_id|
|
103
86
|
# envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
|
104
87
|
# puts "#{envelope.from[0].name}: \t#{envelope.subject}"
|
105
88
|
# end
|
106
89
|
#
|
107
|
-
#
|
90
|
+
# ==== Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
|
108
91
|
#
|
109
92
|
# imap = Net::IMAP.new('mail.example.com')
|
110
|
-
# imap.authenticate('
|
93
|
+
# imap.authenticate('PLAIN', 'joe_user', 'joes_password')
|
111
94
|
# imap.select('Mail/sent-mail')
|
112
95
|
# if not imap.list('Mail/', 'sent-apr03')
|
113
96
|
# imap.create('Mail/sent-apr03')
|
@@ -118,12 +101,96 @@ module Net
|
|
118
101
|
# end
|
119
102
|
# imap.expunge
|
120
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
|
+
#
|
121
188
|
# == Thread Safety
|
122
189
|
#
|
123
190
|
# Net::IMAP supports concurrent threads. For example,
|
124
191
|
#
|
125
192
|
# imap = Net::IMAP.new("imap.foo.net", "imap2")
|
126
|
-
# imap.authenticate("
|
193
|
+
# imap.authenticate("scram-md5", "bar", "password")
|
127
194
|
# imap.select("inbox")
|
128
195
|
# fetch_thread = Thread.start { imap.fetch(1..-1, "UID") }
|
129
196
|
# search_result = imap.search(["BODY", "hello"])
|
@@ -132,41 +199,6 @@ module Net
|
|
132
199
|
#
|
133
200
|
# This script invokes the FETCH command and the SEARCH command concurrently.
|
134
201
|
#
|
135
|
-
# When running multiple commands, care must be taken to avoid ambiguity. For
|
136
|
-
# example, SEARCH responses are ambiguous about which command they are
|
137
|
-
# responding to, so search commands should not run simultaneously, unless the
|
138
|
-
# server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
|
139
|
-
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
|
140
|
-
# §5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
|
141
|
-
# other examples of command sequences which should not be pipelined.
|
142
|
-
#
|
143
|
-
# == Unbounded memory use
|
144
|
-
#
|
145
|
-
# Net::IMAP reads server responses in a separate receiver thread per client.
|
146
|
-
# Unhandled response data is saved to #responses, and response_handlers run
|
147
|
-
# inside the receiver thread. See the list of methods for {handling server
|
148
|
-
# responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
|
149
|
-
#
|
150
|
-
# Because the receiver thread continuously reads and saves new responses, some
|
151
|
-
# scenarios must be careful to avoid unbounded memory use:
|
152
|
-
#
|
153
|
-
# * Commands such as #list or #fetch can have an enormous number of responses.
|
154
|
-
# * Commands such as #fetch can result in an enormous size per response.
|
155
|
-
# * Long-lived connections will gradually accumulate unsolicited server
|
156
|
-
# responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
|
157
|
-
# * A buggy or untrusted server could send inappropriate responses, which
|
158
|
-
# could be very numerous, very large, and very rapid.
|
159
|
-
#
|
160
|
-
# Use paginated or limited versions of commands whenever possible.
|
161
|
-
#
|
162
|
-
# Use #max_response_size to impose a limit on incoming server responses
|
163
|
-
# as they are being read. <em>This is especially important for untrusted
|
164
|
-
# servers.</em>
|
165
|
-
#
|
166
|
-
# Use #add_response_handler to handle responses after each one is received.
|
167
|
-
# Use the +response_handlers+ argument to ::new to assign response handlers
|
168
|
-
# before the receiver thread is started.
|
169
|
-
#
|
170
202
|
# == Errors
|
171
203
|
#
|
172
204
|
# An \IMAP server can send three different types of responses to indicate
|
@@ -214,96 +246,108 @@ module Net
|
|
214
246
|
# == What's here?
|
215
247
|
#
|
216
248
|
# * {Connection control}[rdoc-ref:Net::IMAP@Connection+control+methods]
|
217
|
-
# * {
|
218
|
-
# * {...for any state}[rdoc-ref:Net::IMAP@IMAP+commands+for+any+state]
|
219
|
-
# * {...for the "not authenticated" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Not+Authenticated-22+state]
|
220
|
-
# * {...for the "authenticated" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Authenticated-22+state]
|
221
|
-
# * {...for the "selected" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Selected-22+state]
|
222
|
-
# * {...for the "logout" state}[rdoc-ref:Net::IMAP@IMAP+commands+for+the+-22Logout-22+state]
|
223
|
-
# * {Supported IMAP extensions}[rdoc-ref:Net::IMAP@Supported+IMAP+extensions]
|
249
|
+
# * {Server capabilities}[rdoc-ref:Net::IMAP@Server+capabilities]
|
224
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]
|
225
258
|
#
|
226
259
|
# === Connection control methods
|
227
260
|
#
|
228
|
-
# - Net::IMAP.new:
|
229
|
-
# 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.
|
230
263
|
# - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
|
231
|
-
# - #logout: Tells the server to end the session.
|
264
|
+
# - #logout: Tells the server to end the session. Enters the "_logout_" state.
|
232
265
|
# - #disconnect: Disconnects the connection (without sending #logout first).
|
233
266
|
# - #disconnected?: True if the connection has been closed.
|
234
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
|
+
#
|
235
299
|
# === Core \IMAP commands
|
236
300
|
#
|
237
301
|
# The following commands are defined either by
|
238
|
-
# the [IMAP4rev1[https://
|
302
|
+
# the [IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501]] base specification, or
|
239
303
|
# by one of the following extensions:
|
240
|
-
# [IDLE[https://
|
241
|
-
# [NAMESPACE[https://
|
242
|
-
# [UNSELECT[https://
|
243
|
-
|
244
|
-
#
|
245
|
-
# TODO: [LIST-EXTENDED[https://tools.ietf.org/html/rfc5258]],
|
246
|
-
# TODO: [LIST-STATUS[https://tools.ietf.org/html/rfc5819]],
|
247
|
-
#++
|
248
|
-
# [MOVE[https://tools.ietf.org/html/rfc6851]].
|
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]].
|
249
309
|
# These extensions are widely supported by modern IMAP4rev1 servers and have
|
250
|
-
# all been integrated into [IMAP4rev2[https://
|
251
|
-
# <em
|
252
|
-
#
|
253
|
-
|
254
|
-
# TODO: When IMAP4rev2 is supported, add the following to the each of the
|
255
|
-
# appropriate commands below.
|
256
|
-
# Note:: CHECK has been removed from IMAP4rev2.
|
257
|
-
# Note:: LSUB is obsoleted by +LIST-EXTENDED and has been removed from IMAP4rev2.
|
258
|
-
# <em>Some arguments require the +LIST-EXTENDED+ or +IMAP4rev2+ capability.</em>
|
259
|
-
# <em>Requires either the +ENABLE+ or +IMAP4rev2+ capability.</em>
|
260
|
-
# <em>Requires either the +NAMESPACE+ or +IMAP4rev2+ capability.</em>
|
261
|
-
# <em>Requires either the +IDLE+ or +IMAP4rev2+ capability.</em>
|
262
|
-
# <em>Requires either the +UNSELECT+ or +IMAP4rev2+ capability.</em>
|
263
|
-
# <em>Requires either the +UIDPLUS+ or +IMAP4rev2+ capability.</em>
|
264
|
-
# <em>Requires either the +MOVE+ or +IMAP4rev2+ capability.</em>
|
265
|
-
#++
|
266
|
-
#
|
267
|
-
# ==== \IMAP commands for any state
|
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
|
268
314
|
#
|
269
315
|
# - #capability: Returns the server's capabilities as an array of strings.
|
270
316
|
#
|
271
|
-
# <em>
|
272
|
-
#
|
317
|
+
# <em>In general, #capable? should be used rather than explicitly sending a
|
318
|
+
# +CAPABILITY+ command to the server.</em>
|
273
319
|
# - #noop: Allows the server to send unsolicited untagged #responses.
|
274
|
-
# - #logout: Tells the server to end the session. Enters the
|
320
|
+
# - #logout: Tells the server to end the session. Enters the "_logout_" state.
|
275
321
|
#
|
276
|
-
# ====
|
322
|
+
# ==== Not Authenticated state
|
277
323
|
#
|
278
324
|
# In addition to the commands for any state, the following commands are valid
|
279
|
-
# in the
|
325
|
+
# in the "<em>not authenticated</em>" state:
|
280
326
|
#
|
281
327
|
# - #starttls: Upgrades a clear-text connection to use TLS.
|
282
328
|
#
|
283
329
|
# <em>Requires the +STARTTLS+ capability.</em>
|
284
|
-
# - #authenticate: Identifies the client to the server using the given
|
285
|
-
# mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
286
|
-
# and credentials. Enters the
|
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.
|
287
333
|
#
|
288
|
-
# <em>
|
289
|
-
#
|
334
|
+
# <em>The server should list <tt>"AUTH=#{mechanism}"</tt> capabilities for
|
335
|
+
# supported mechanisms.</em>
|
290
336
|
# - #login: Identifies the client to the server using a plain text password.
|
291
|
-
# Using #authenticate is preferred. Enters the
|
337
|
+
# Using #authenticate is generally preferred. Enters the "_authenticated_"
|
338
|
+
# state.
|
292
339
|
#
|
293
340
|
# <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
|
294
341
|
#
|
295
|
-
# ====
|
342
|
+
# ==== Authenticated state
|
296
343
|
#
|
297
344
|
# In addition to the commands for any state, the following commands are valid
|
298
|
-
# in the
|
299
|
-
#
|
300
|
-
#--
|
301
|
-
# - #enable: <em>Not implemented by Net::IMAP, yet.</em>
|
345
|
+
# in the "_authenticated_" state:
|
302
346
|
#
|
303
|
-
#
|
304
|
-
|
305
|
-
# - #select: Open a mailbox and enter the
|
306
|
-
# - #examine: Open a mailbox read-only, and enter the
|
347
|
+
# - #enable: Enables backwards incompatible server extensions.
|
348
|
+
# <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
|
349
|
+
# - #select: Open a mailbox and enter the "_selected_" state.
|
350
|
+
# - #examine: Open a mailbox read-only, and enter the "_selected_" state.
|
307
351
|
# - #create: Creates a new mailbox.
|
308
352
|
# - #delete: Permanently remove a mailbox.
|
309
353
|
# - #rename: Change the name of a mailbox.
|
@@ -311,37 +355,31 @@ module Net
|
|
311
355
|
# - #unsubscribe: Removes a mailbox from the "subscribed" set.
|
312
356
|
# - #list: Returns names and attributes of mailboxes matching a given pattern.
|
313
357
|
# - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
|
314
|
-
#
|
315
|
-
# <em>Requires the +NAMESPACE+ capability.</em>
|
358
|
+
# <em>Requires the +NAMESPACE+ or +IMAP4rev2+ capability.</em>
|
316
359
|
# - #status: Returns mailbox information, e.g. message count, unseen message
|
317
360
|
# count, +UIDVALIDITY+ and +UIDNEXT+.
|
318
361
|
# - #append: Appends a message to the end of a mailbox.
|
319
362
|
# - #idle: Allows the server to send updates to the client, without the client
|
320
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.
|
321
367
|
#
|
322
|
-
# <em
|
323
|
-
# - #lsub: Lists mailboxes the user has declared "active" or "subscribed".
|
324
|
-
#--
|
325
|
-
# <em>Replaced by</em> <tt>LIST-EXTENDED</tt> <em>and removed from</em>
|
326
|
-
# +IMAP4rev2+. <em>However, Net::IMAP hasn't implemented</em>
|
327
|
-
# <tt>LIST-EXTENDED</tt> _yet_.
|
328
|
-
#++
|
368
|
+
# <em>*Note:* Net::IMAP hasn't implemented <tt>LIST-EXTENDED</tt> yet.</em>
|
329
369
|
#
|
330
|
-
# ====
|
370
|
+
# ==== Selected state
|
331
371
|
#
|
332
|
-
# In addition to the commands for any state and the
|
333
|
-
# commands, the following commands are valid in the
|
372
|
+
# In addition to the commands for any state and the "_authenticated_"
|
373
|
+
# commands, the following commands are valid in the "_selected_" state:
|
334
374
|
#
|
335
|
-
# - #close: Closes the mailbox and returns to the
|
375
|
+
# - #close: Closes the mailbox and returns to the "_authenticated_" state,
|
336
376
|
# expunging deleted messages, unless the mailbox was opened as read-only.
|
337
|
-
# - #unselect: Closes the mailbox and returns to the
|
377
|
+
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
|
338
378
|
# without expunging any messages.
|
339
|
-
#
|
340
|
-
# <em>Requires the +UNSELECT+ capability.</em>
|
379
|
+
# <em>Requires the +UNSELECT+ or +IMAP4rev2+ capability.</em>
|
341
380
|
# - #expunge: Permanently removes messages which have the Deleted flag set.
|
342
|
-
# - #uid_expunge: Restricts
|
343
|
-
#
|
344
|
-
# <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>
|
345
383
|
# - #search, #uid_search: Returns sequence numbers or UIDs of messages that
|
346
384
|
# match the given searching criteria.
|
347
385
|
# - #fetch, #uid_fetch: Returns data associated with a set of messages,
|
@@ -351,45 +389,33 @@ module Net
|
|
351
389
|
# specified destination mailbox.
|
352
390
|
# - #move, #uid_move: Moves the specified messages to the end of the
|
353
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.
|
354
395
|
#
|
355
|
-
#
|
356
|
-
# - #check: Mostly obsolete. Can be replaced with #noop or #idle.
|
357
|
-
#--
|
358
|
-
# <em>Removed from IMAP4rev2.</em>
|
359
|
-
#++
|
396
|
+
# ==== Logout state
|
360
397
|
#
|
361
|
-
#
|
362
|
-
#
|
363
|
-
# No \IMAP commands are valid in the +logout+ state. If the socket is still
|
398
|
+
# No \IMAP commands are valid in the "_logout_" state. If the socket is still
|
364
399
|
# open, Net::IMAP will close it after receiving server confirmation.
|
365
400
|
# Exceptions will be raised by \IMAP commands that have already started and
|
366
401
|
# are waiting for a response, as well as any that are called after logout.
|
367
402
|
#
|
368
|
-
# ===
|
403
|
+
# === \IMAP extension support
|
369
404
|
#
|
370
405
|
# ==== RFC9051: +IMAP4rev2+
|
371
406
|
#
|
372
|
-
# Although IMAP4rev2[https://
|
373
|
-
# yet
|
374
|
-
#
|
375
|
-
|
376
|
-
#
|
377
|
-
#
|
378
|
-
#
|
379
|
-
#
|
380
|
-
#
|
381
|
-
#
|
382
|
-
#
|
383
|
-
#
|
384
|
-
# TODO: +BINARY+ (only the FETCH side)
|
385
|
-
# TODO: +SPECIAL-USE+
|
386
|
-
# implicitly supported, but we can do better: Response codes: RFC5530, etc
|
387
|
-
# implicitly supported, but we can do better: <tt>STATUS=SIZE</tt>
|
388
|
-
# implicitly supported, but we can do better: <tt>STATUS DELETED</tt>
|
389
|
-
#++
|
390
|
-
# Commands for these extensions are included with the {Core IMAP
|
391
|
-
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above. Other supported
|
392
|
-
# extensons are listed below.
|
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>
|
393
419
|
#
|
394
420
|
# ==== RFC2087: +QUOTA+
|
395
421
|
# - #getquota: returns the resource usage and limits for a quota root
|
@@ -398,92 +424,60 @@ module Net
|
|
398
424
|
# - #setquota: sets the resource limits for a given quota root.
|
399
425
|
#
|
400
426
|
# ==== RFC2177: +IDLE+
|
401
|
-
# Folded into IMAP4rev2[https://
|
402
|
-
#
|
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].
|
403
429
|
# - #idle: Allows the server to send updates to the client, without the client
|
404
430
|
# needing to poll using #noop.
|
405
431
|
#
|
406
432
|
# ==== RFC2342: +NAMESPACE+
|
407
|
-
# Folded into IMAP4rev2[https://
|
408
|
-
#
|
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].
|
409
435
|
# - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
|
410
436
|
#
|
411
437
|
# ==== RFC2971: +ID+
|
412
438
|
# - #id: exchanges client and server implementation information.
|
413
439
|
#
|
414
|
-
#--
|
415
|
-
# ==== RFC3502: +MULTIAPPEND+
|
416
|
-
# TODO...
|
417
|
-
#++
|
418
|
-
#
|
419
|
-
#--
|
420
440
|
# ==== RFC3516: +BINARY+
|
421
|
-
#
|
422
|
-
|
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.
|
423
448
|
#
|
424
449
|
# ==== RFC3691: +UNSELECT+
|
425
|
-
# Folded into IMAP4rev2[https://
|
426
|
-
#
|
427
|
-
# - #unselect: Closes the mailbox and returns to the
|
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].
|
452
|
+
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
|
428
453
|
# without expunging any messages.
|
429
454
|
#
|
430
455
|
# ==== RFC4314: +ACL+
|
431
456
|
# - #getacl: lists the authenticated user's access rights to a mailbox.
|
432
457
|
# - #setacl: sets the access rights for a user on a mailbox
|
433
|
-
|
434
|
-
#
|
435
|
-
#++
|
436
|
-
# - *_Note:_* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
|
458
|
+
# >>>
|
459
|
+
# *NOTE:* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
|
437
460
|
#
|
438
461
|
# ==== RFC4315: +UIDPLUS+
|
439
|
-
# Folded into IMAP4rev2[https://
|
440
|
-
#
|
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].
|
441
464
|
# - #uid_expunge: Restricts #expunge to only remove the specified UIDs.
|
442
465
|
# - Updates #select, #examine with the +UIDNOTSTICKY+ ResponseCode
|
443
466
|
# - Updates #append with the +APPENDUID+ ResponseCode
|
444
467
|
# - Updates #copy, #move with the +COPYUID+ ResponseCode
|
445
468
|
#
|
446
|
-
|
447
|
-
#
|
448
|
-
#
|
449
|
-
#
|
450
|
-
# the protocol to enable new optional parameters to many commands: #select,
|
451
|
-
# #examine, #create, #rename, #fetch, #uid_fetch, #store, #uid_store, #search,
|
452
|
-
# #uid_search, and #append. However, specific parameters are not defined.
|
453
|
-
# Extensions to these commands use this syntax whenever possible. Net::IMAP
|
454
|
-
# may be partially compatible with extensions to these commands, even without
|
455
|
-
# any explicit support.
|
456
|
-
#++
|
457
|
-
#
|
458
|
-
#--
|
459
|
-
# ==== RFC4731 +ESEARCH+
|
460
|
-
# TODO...
|
461
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
462
|
-
# - Updates #search, #uid_search to accept result options: +MIN+, +MAX+,
|
463
|
-
# +ALL+, +COUNT+, and to return ExtendedSearchData.
|
464
|
-
#++
|
465
|
-
#
|
466
|
-
#--
|
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
|
+
#
|
467
473
|
# ==== RFC4959: +SASL-IR+
|
468
|
-
#
|
469
|
-
#
|
470
|
-
#
|
471
|
-
|
472
|
-
#
|
473
|
-
|
474
|
-
#
|
475
|
-
# TODO...
|
476
|
-
#++
|
477
|
-
#
|
478
|
-
#--
|
479
|
-
# ==== RFC5182 +SEARCHRES+
|
480
|
-
# TODO...
|
481
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
482
|
-
# - Updates #search, #uid_search with the +SAVE+ result option.
|
483
|
-
# - Updates #copy, #uid_copy, #fetch, #uid_fetch, #move, #uid_move, #search,
|
484
|
-
# #uid_search, #store, #uid_store, and #uid_expunge with ability to
|
485
|
-
# reference the saved result of a previous #search or #uid_search command.
|
486
|
-
#++
|
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.
|
487
481
|
#
|
488
482
|
# ==== RFC5256: +SORT+
|
489
483
|
# - #sort, #uid_sort: An alternate version of #search or #uid_search which
|
@@ -493,75 +487,65 @@ module Net
|
|
493
487
|
# which arranges the results into ordered groups or threads according to a
|
494
488
|
# chosen algorithm.
|
495
489
|
#
|
496
|
-
|
497
|
-
#
|
498
|
-
#
|
499
|
-
#
|
500
|
-
#
|
501
|
-
#
|
502
|
-
# even without any explicit support.
|
503
|
-
# - Updates #list to accept selection options: +SUBSCRIBED+, +REMOTE+, and
|
504
|
-
# +RECURSIVEMATCH+, and return options: +SUBSCRIBED+ and +CHILDREN+.
|
505
|
-
#++
|
506
|
-
#
|
507
|
-
#--
|
508
|
-
# ==== RFC5819 +LIST-STATUS+
|
509
|
-
# TODO...
|
510
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
511
|
-
# - Updates #list with +STATUS+ return option.
|
512
|
-
#++
|
513
|
-
#
|
514
|
-
# ==== +XLIST+ (non-standard, deprecated)
|
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.
|
515
496
|
# - #xlist: replaced by +SPECIAL-USE+ attributes in #list responses.
|
516
497
|
#
|
517
|
-
|
518
|
-
#
|
519
|
-
# TODO...
|
520
|
-
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
521
|
-
# - Updates #list with the +SPECIAL-USE+ selection and return options.
|
522
|
-
#++
|
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).
|
523
500
|
#
|
524
501
|
# ==== RFC6851: +MOVE+
|
525
|
-
# Folded into IMAP4rev2[https://
|
526
|
-
#
|
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].
|
527
504
|
# - #move, #uid_move: Moves the specified messages to the end of the
|
528
505
|
# specified destination mailbox, expunging them from the current mailbox.
|
529
506
|
#
|
530
|
-
|
531
|
-
# ==== RFC6855: UTF8=ACCEPT
|
532
|
-
# TODO...
|
533
|
-
# ==== RFC6855: UTF8=ONLY
|
534
|
-
# TODO...
|
535
|
-
#++
|
536
|
-
#
|
537
|
-
#--
|
538
|
-
# ==== RFC7888: <tt>LITERAL+</tt>, +LITERAL-+
|
539
|
-
# TODO...
|
540
|
-
# ==== RFC7162: +QRESYNC+
|
541
|
-
# TODO...
|
542
|
-
# ==== RFC7162: +CONDSTORE+
|
543
|
-
# TODO...
|
544
|
-
# ==== RFC8474: +OBJECTID+
|
545
|
-
# TODO...
|
546
|
-
# ==== RFC9208: +QUOTA+
|
547
|
-
# TODO...
|
548
|
-
#++
|
507
|
+
# ==== RFC6855: <tt>UTF8=ACCEPT</tt>, <tt>UTF8=ONLY</tt>
|
549
508
|
#
|
550
|
-
#
|
509
|
+
# - See #enable for information about support for UTF-8 string encoding.
|
551
510
|
#
|
552
|
-
#
|
553
|
-
#
|
554
|
-
# - #
|
555
|
-
#
|
556
|
-
# - #
|
557
|
-
#
|
558
|
-
#
|
511
|
+
# ==== RFC7162: +CONDSTORE+
|
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.
|
559
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.
|
560
547
|
#
|
561
548
|
# == References
|
562
|
-
#--
|
563
|
-
# TODO: Consider moving references list to REFERENCES.md or REFERENCES.rdoc.
|
564
|
-
#++
|
565
549
|
#
|
566
550
|
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
|
567
551
|
# Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - \VERSION 4rev1",
|
@@ -591,57 +575,57 @@ module Net
|
|
591
575
|
# Gahrns, M., "IMAP4 Multi-Accessed Mailbox Practice", RFC 2180, DOI
|
592
576
|
# 10.17487/RFC2180, July 1997, <https://www.rfc-editor.org/info/rfc2180>.
|
593
577
|
#
|
594
|
-
# [UTF7[https://
|
578
|
+
# [UTF7[https://www.rfc-editor.org/rfc/rfc2152]]::
|
595
579
|
# Goldsmith, D. and M. Davis, "UTF-7 A Mail-Safe Transformation Format of
|
596
580
|
# Unicode", RFC 2152, DOI 10.17487/RFC2152, May 1997,
|
597
581
|
# <https://www.rfc-editor.org/info/rfc2152>.
|
598
582
|
#
|
599
583
|
# === Message envelope and body structure
|
600
584
|
#
|
601
|
-
# [RFC5322[https://
|
585
|
+
# [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]::
|
602
586
|
# Resnick, P., Ed., "Internet Message Format",
|
603
587
|
# RFC 5322, DOI 10.17487/RFC5322, October 2008,
|
604
588
|
# <https://www.rfc-editor.org/info/rfc5322>.
|
605
589
|
#
|
606
590
|
# <em>Note: obsoletes</em>
|
607
|
-
# RFC-2822[https://
|
608
|
-
# 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>
|
609
593
|
#
|
610
|
-
# [CHARSET[https://
|
594
|
+
# [CHARSET[https://www.rfc-editor.org/rfc/rfc2978]]::
|
611
595
|
# Freed, N. and J. Postel, "IANA Charset Registration Procedures", BCP 19,
|
612
596
|
# RFC 2978, DOI 10.17487/RFC2978, October 2000,
|
613
597
|
# <https://www.rfc-editor.org/info/rfc2978>.
|
614
598
|
#
|
615
|
-
# [DISPOSITION[https://
|
599
|
+
# [DISPOSITION[https://www.rfc-editor.org/rfc/rfc2183]]::
|
616
600
|
# Troost, R., Dorner, S., and K. Moore, Ed., "Communicating Presentation
|
617
601
|
# Information in Internet Messages: The Content-Disposition Header
|
618
602
|
# Field", RFC 2183, DOI 10.17487/RFC2183, August 1997,
|
619
603
|
# <https://www.rfc-editor.org/info/rfc2183>.
|
620
604
|
#
|
621
|
-
# [MIME-IMB[https://
|
605
|
+
# [MIME-IMB[https://www.rfc-editor.org/rfc/rfc2045]]::
|
622
606
|
# Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
|
623
607
|
# (MIME) Part One: Format of Internet Message Bodies",
|
624
608
|
# RFC 2045, DOI 10.17487/RFC2045, November 1996,
|
625
609
|
# <https://www.rfc-editor.org/info/rfc2045>.
|
626
610
|
#
|
627
|
-
# [MIME-IMT[https://
|
611
|
+
# [MIME-IMT[https://www.rfc-editor.org/rfc/rfc2046]]::
|
628
612
|
# Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
|
629
613
|
# (MIME) Part Two: Media Types", RFC 2046, DOI 10.17487/RFC2046,
|
630
614
|
# November 1996, <https://www.rfc-editor.org/info/rfc2046>.
|
631
615
|
#
|
632
|
-
# [MIME-HDRS[https://
|
616
|
+
# [MIME-HDRS[https://www.rfc-editor.org/rfc/rfc2047]]::
|
633
617
|
# Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part Three:
|
634
618
|
# Message Header Extensions for Non-ASCII Text",
|
635
619
|
# RFC 2047, DOI 10.17487/RFC2047, November 1996,
|
636
620
|
# <https://www.rfc-editor.org/info/rfc2047>.
|
637
621
|
#
|
638
|
-
# [RFC2231[https://
|
622
|
+
# [RFC2231[https://www.rfc-editor.org/rfc/rfc2231]]::
|
639
623
|
# Freed, N. and K. Moore, "MIME Parameter Value and Encoded Word
|
640
624
|
# Extensions: Character Sets, Languages, and Continuations",
|
641
625
|
# RFC 2231, DOI 10.17487/RFC2231, November 1997,
|
642
626
|
# <https://www.rfc-editor.org/info/rfc2231>.
|
643
627
|
#
|
644
|
-
# [I18n-HDRS[https://
|
628
|
+
# [I18n-HDRS[https://www.rfc-editor.org/rfc/rfc6532]]::
|
645
629
|
# Yang, A., Steele, S., and N. Freed, "Internationalized Email Headers",
|
646
630
|
# RFC 6532, DOI 10.17487/RFC6532, February 2012,
|
647
631
|
# <https://www.rfc-editor.org/info/rfc6532>.
|
@@ -657,42 +641,40 @@ module Net
|
|
657
641
|
# RFC 2557, DOI 10.17487/RFC2557, March 1999,
|
658
642
|
# <https://www.rfc-editor.org/info/rfc2557>.
|
659
643
|
#
|
660
|
-
# [MD5[https://
|
644
|
+
# [MD5[https://www.rfc-editor.org/rfc/rfc1864]]::
|
661
645
|
# Myers, J. and M. Rose, "The Content-MD5 Header Field",
|
662
646
|
# RFC 1864, DOI 10.17487/RFC1864, October 1995,
|
663
647
|
# <https://www.rfc-editor.org/info/rfc1864>.
|
664
648
|
#
|
665
|
-
|
666
|
-
#
|
667
|
-
#
|
668
|
-
#
|
669
|
-
#
|
670
|
-
# profile for Internet Message Access Protocol (IMAP)",
|
671
|
-
# RFC 3503, DOI 10.17487/RFC3503, March 2003,
|
672
|
-
# <https://www.rfc-editor.org/info/rfc3503>.
|
673
|
-
#++
|
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>.
|
674
654
|
#
|
675
|
-
# ===
|
655
|
+
# === \IMAP Extensions
|
676
656
|
#
|
677
|
-
# [QUOTA[https://
|
678
|
-
# Myers, J., "IMAP4 QUOTA extension", RFC 2087, DOI 10.17487/RFC2087,
|
679
|
-
# January 1997, <https://www.rfc-editor.org/info/rfc2087>.
|
680
|
-
#--
|
681
|
-
# TODO: test compatibility with updated QUOTA extension:
|
682
|
-
# [QUOTA[https://tools.ietf.org/html/rfc9208]]::
|
657
|
+
# [QUOTA[https://www.rfc-editor.org/rfc/rfc9208]]::
|
683
658
|
# Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
|
684
659
|
# March 2022, <https://www.rfc-editor.org/info/rfc9208>.
|
685
|
-
|
686
|
-
#
|
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]]::
|
687
665
|
# Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
|
688
666
|
# June 1997, <https://www.rfc-editor.org/info/rfc2177>.
|
689
|
-
# [NAMESPACE[https://
|
667
|
+
# [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]]::
|
690
668
|
# Gahrns, M. and C. Newman, "IMAP4 Namespace", RFC 2342,
|
691
669
|
# DOI 10.17487/RFC2342, May 1998, <https://www.rfc-editor.org/info/rfc2342>.
|
692
|
-
# [ID[https://
|
670
|
+
# [ID[https://www.rfc-editor.org/rfc/rfc2971]]::
|
693
671
|
# Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
|
694
672
|
# October 2000, <https://www.rfc-editor.org/info/rfc2971>.
|
695
|
-
# [
|
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]]::
|
696
678
|
# Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
|
697
679
|
# DOI 10.17487/RFC4314, December 2005,
|
698
680
|
# <https://www.rfc-editor.org/info/rfc4314>.
|
@@ -700,48 +682,79 @@ module Net
|
|
700
682
|
# Crispin, M., "Internet Message Access Protocol (\IMAP) - UIDPLUS
|
701
683
|
# extension", RFC 4315, DOI 10.17487/RFC4315, December 2005,
|
702
684
|
# <https://www.rfc-editor.org/info/rfc4315>.
|
703
|
-
# [SORT[https://
|
685
|
+
# [SORT[https://www.rfc-editor.org/rfc/rfc5256]]::
|
704
686
|
# Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
|
705
687
|
# THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
|
706
688
|
# <https://www.rfc-editor.org/info/rfc5256>.
|
707
|
-
# [THREAD[https://
|
689
|
+
# [THREAD[https://www.rfc-editor.org/rfc/rfc5256]]::
|
708
690
|
# Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
|
709
691
|
# THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
|
710
692
|
# <https://www.rfc-editor.org/info/rfc5256>.
|
711
693
|
# [RFC5530[https://www.rfc-editor.org/rfc/rfc5530.html]]::
|
712
694
|
# Gulbrandsen, A., "IMAP Response Codes", RFC 5530, DOI 10.17487/RFC5530,
|
713
695
|
# May 2009, <https://www.rfc-editor.org/info/rfc5530>.
|
714
|
-
# [MOVE[https://
|
696
|
+
# [MOVE[https://www.rfc-editor.org/rfc/rfc6851]]::
|
715
697
|
# Gulbrandsen, A. and N. Freed, Ed., "Internet Message Access Protocol
|
716
698
|
# (\IMAP) - MOVE Extension", RFC 6851, DOI 10.17487/RFC6851, January 2013,
|
717
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>.
|
718
725
|
#
|
719
726
|
# === IANA registries
|
720
|
-
#
|
721
727
|
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
|
722
728
|
# * {IMAP Response Codes}[https://www.iana.org/assignments/imap-response-codes/imap-response-codes.xhtml]
|
723
729
|
# * {IMAP Mailbox Name Attributes}[https://www.iana.org/assignments/imap-mailbox-name-attributes/imap-mailbox-name-attributes.xhtml]
|
724
730
|
# * {IMAP and JMAP Keywords}[https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml]
|
725
731
|
# * {IMAP Threading Algorithms}[https://www.iana.org/assignments/imap-threading-algorithms/imap-threading-algorithms.xhtml]
|
726
|
-
#--
|
727
|
-
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
728
|
-
# * [{LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
|
729
|
-
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
|
730
|
-
# * {IMAP ANNOTATE Extension Entries and Attributes}[https://www.iana.org/assignments/imap-annotate-extension/imap-annotate-extension.xhtml]
|
731
|
-
# * {IMAP URLAUTH Access Identifiers and Prefixes}[https://www.iana.org/assignments/urlauth-access-ids/urlauth-access-ids.xhtml]
|
732
|
-
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
733
|
-
#++
|
734
732
|
# * {SASL Mechanisms and SASL SCRAM Family Mechanisms}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
735
733
|
# * {Service Name and Transport Protocol Port Number Registry}[https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml]:
|
736
734
|
# +imap+: tcp/143, +imaps+: tcp/993
|
737
735
|
# * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
|
738
736
|
# +imap+
|
739
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]
|
740
745
|
#
|
741
746
|
class IMAP < Protocol
|
742
|
-
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
|
743
754
|
|
744
|
-
autoload :
|
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__)
|
745
758
|
|
746
759
|
include MonitorMixin
|
747
760
|
if defined?(OpenSSL::SSL)
|
@@ -749,77 +762,15 @@ module Net
|
|
749
762
|
include SSL
|
750
763
|
end
|
751
764
|
|
752
|
-
# Returns the
|
753
|
-
|
754
|
-
|
755
|
-
# Returns a hash with arrays of unhandled <em>non-+nil+</em>
|
756
|
-
# UntaggedResponse#data keyed by UntaggedResponse#name, and
|
757
|
-
# ResponseCode#data keyed by ResponseCode#name.
|
758
|
-
#
|
759
|
-
# For example:
|
760
|
-
#
|
761
|
-
# imap.select("inbox")
|
762
|
-
# p imap.responses["EXISTS"][-1]
|
763
|
-
# #=> 2
|
764
|
-
# p imap.responses["UIDVALIDITY"][-1]
|
765
|
-
# #=> 968263756
|
766
|
-
attr_reader :responses
|
767
|
-
|
768
|
-
# Returns all response handlers.
|
769
|
-
attr_reader :response_handlers
|
770
|
-
|
771
|
-
# Seconds to wait until a connection is opened.
|
772
|
-
# If the IMAP object cannot open a connection within this time,
|
773
|
-
# it raises a Net::OpenTimeout exception. The default value is 30 seconds.
|
774
|
-
attr_reader :open_timeout
|
775
|
-
|
776
|
-
# Seconds to wait until an IDLE response is received.
|
777
|
-
attr_reader :idle_response_timeout
|
778
|
-
|
779
|
-
# The maximum allowed server response size. When +nil+, there is no limit
|
780
|
-
# on response size.
|
781
|
-
#
|
782
|
-
# The default value is _unlimited_ (after +v0.5.8+, the default is 512 MiB).
|
783
|
-
# A _much_ lower value should be used with untrusted servers (for example,
|
784
|
-
# when connecting to a user-provided hostname). When using a lower limit,
|
785
|
-
# message bodies should be fetched in chunks rather than all at once.
|
786
|
-
#
|
787
|
-
# <em>Please Note:</em> this only limits the size per response. It does
|
788
|
-
# not prevent a flood of individual responses and it does not limit how
|
789
|
-
# many unhandled responses may be stored on the responses hash. See
|
790
|
-
# Net::IMAP@Unbounded+memory+use.
|
791
|
-
#
|
792
|
-
# Socket reads are limited to the maximum remaining bytes for the current
|
793
|
-
# response: max_response_size minus the bytes that have already been read.
|
794
|
-
# When the limit is reached, or reading a +literal+ _would_ go over the
|
795
|
-
# limit, ResponseTooLargeError is raised and the connection is closed.
|
796
|
-
# See also #socket_read_limit.
|
797
|
-
#
|
798
|
-
# Note that changes will not take effect immediately, because the receiver
|
799
|
-
# thread may already be waiting for the next response using the previous
|
800
|
-
# value. Net::IMAP#noop can force a response and enforce the new setting
|
801
|
-
# immediately.
|
802
|
-
#
|
803
|
-
# ==== Versioned Defaults
|
804
|
-
#
|
805
|
-
# Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
|
806
|
-
# attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to a config
|
807
|
-
# attribute.</em>
|
808
|
-
#
|
809
|
-
# * original: +nil+ <em>(no limit)</em>
|
810
|
-
# * +0.5+: 512 MiB
|
811
|
-
attr_accessor :max_response_size
|
812
|
-
|
813
|
-
attr_accessor :client_thread # :nodoc:
|
765
|
+
# Returns the global Config object
|
766
|
+
def self.config; Config.global end
|
814
767
|
|
815
|
-
# Returns the debug mode.
|
816
|
-
def self.debug
|
817
|
-
return @@debug
|
818
|
-
end
|
768
|
+
# Returns the global debug mode.
|
769
|
+
def self.debug; config.debug end
|
819
770
|
|
820
|
-
# Sets the debug mode.
|
771
|
+
# Sets the global debug mode.
|
821
772
|
def self.debug=(val)
|
822
|
-
|
773
|
+
config.debug = val
|
823
774
|
end
|
824
775
|
|
825
776
|
# The default port for IMAP connections, port 143
|
@@ -838,9 +789,198 @@ module Net
|
|
838
789
|
alias default_ssl_port default_tls_port
|
839
790
|
end
|
840
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
|
+
|
841
981
|
# Disconnects from the server.
|
842
982
|
#
|
843
|
-
# Related: #logout
|
983
|
+
# Related: #logout, #logout!
|
844
984
|
def disconnect
|
845
985
|
return if disconnected?
|
846
986
|
begin
|
@@ -870,62 +1010,123 @@ module Net
|
|
870
1010
|
return @sock.closed?
|
871
1011
|
end
|
872
1012
|
|
873
|
-
#
|
874
|
-
#
|
875
|
-
#
|
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.
|
876
1016
|
#
|
877
|
-
#
|
878
|
-
#
|
879
|
-
# 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>
|
880
1019
|
#
|
881
|
-
#
|
882
|
-
#
|
883
|
-
#
|
884
|
-
|
885
|
-
|
886
|
-
|
1020
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
1021
|
+
#
|
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.
|
1028
|
+
#
|
1029
|
+
# To ensure a case-insensitive comparison, #capable? can be used instead.
|
1030
|
+
#
|
1031
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
1032
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
1033
|
+
#
|
1034
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
1035
|
+
#
|
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.
|
1043
|
+
#
|
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.
|
887
1047
|
#
|
888
|
-
#
|
889
|
-
#
|
1048
|
+
# imap = Net::IMAP.new(hostname, ssl: false)
|
1049
|
+
# imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
|
1050
|
+
# imap.auth_mechanisms # => []
|
890
1051
|
#
|
891
|
-
#
|
1052
|
+
# imap.starttls
|
1053
|
+
# imap.capabilities # => ["IMAP4REV1", "AUTH=PLAIN", "AUTH=XOAUTH2",
|
1054
|
+
# # "AUTH=OAUTHBEARER"]
|
1055
|
+
# imap.auth_mechanisms # => ["PLAIN", "XOAUTH2", "OAUTHBEARER"]
|
892
1056
|
#
|
893
|
-
#
|
894
|
-
#
|
895
|
-
# <tt>AUTH=PLAIN</tt>, and +LOGINDISABLED+ capabilities, and clients must
|
896
|
-
# respect their presence or absence. See the capabilites requirements on
|
897
|
-
# #starttls, #login, and #authenticate.
|
1057
|
+
# imap.authenticate("XOAUTH2", username, oauth2_access_token)
|
1058
|
+
# imap.auth_mechanisms # => []
|
898
1059
|
#
|
899
|
-
#
|
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.
|
900
1095
|
#
|
901
|
-
#
|
902
|
-
#
|
903
|
-
#
|
904
|
-
# compatible behavior, such as response codes or mailbox attributes, may
|
905
|
-
# be sent at any time.
|
1096
|
+
# Net::IMAP automatically discards its cached capabilities when they can
|
1097
|
+
# change. Explicitly calling this _should_ be unnecessary for well-behaved
|
1098
|
+
# servers.
|
906
1099
|
#
|
907
|
-
#
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
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.
|
912
1111
|
#
|
913
|
-
#
|
1112
|
+
# <em>*NOTE:* Most Net::IMAP methods do not _currently_ modify their
|
1113
|
+
# behaviour according to the server's advertised #capabilities.</em>
|
914
1114
|
#
|
915
|
-
#
|
916
|
-
#
|
917
|
-
#
|
918
|
-
# #
|
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.
|
919
1122
|
#
|
920
|
-
#
|
921
|
-
# #authenticate. The OK TaggedResponse to #login and #authenticate may
|
922
|
-
# include +CAPABILITY+ response code data, but the TaggedResponse for
|
923
|
-
# #starttls is sent clear-text and cannot be trusted.
|
1123
|
+
# See Net::IMAP@Capabilities for more about \IMAP capabilities.
|
924
1124
|
#
|
1125
|
+
# Related: #capable?, #auth_capable?, #capability, #enable
|
925
1126
|
def capability
|
926
1127
|
synchronize do
|
927
1128
|
send_command("CAPABILITY")
|
928
|
-
|
1129
|
+
@capabilities = clear_responses("CAPABILITY").last.freeze
|
929
1130
|
end
|
930
1131
|
end
|
931
1132
|
|
@@ -936,8 +1137,7 @@ module Net
|
|
936
1137
|
# Note that the user should first check if the server supports the ID
|
937
1138
|
# capability. For example:
|
938
1139
|
#
|
939
|
-
#
|
940
|
-
# if capabilities.include?("ID")
|
1140
|
+
# if capable?(:ID)
|
941
1141
|
# id = imap.id(
|
942
1142
|
# name: "my IMAP client (ruby)",
|
943
1143
|
# version: MyIMAP::VERSION,
|
@@ -946,16 +1146,16 @@ module Net
|
|
946
1146
|
# )
|
947
1147
|
# end
|
948
1148
|
#
|
949
|
-
# See [ID[https://
|
1149
|
+
# See [ID[https://www.rfc-editor.org/rfc/rfc2971]] for field definitions.
|
950
1150
|
#
|
951
|
-
#
|
1151
|
+
# ==== Capabilities
|
952
1152
|
#
|
953
1153
|
# The server's capabilities must include +ID+
|
954
|
-
# [RFC2971[https://
|
1154
|
+
# [RFC2971[https://www.rfc-editor.org/rfc/rfc2971]].
|
955
1155
|
def id(client_id=nil)
|
956
1156
|
synchronize do
|
957
1157
|
send_command("ID", ClientID.new(client_id))
|
958
|
-
|
1158
|
+
clear_responses("ID").last
|
959
1159
|
end
|
960
1160
|
end
|
961
1161
|
|
@@ -964,7 +1164,7 @@ module Net
|
|
964
1164
|
#
|
965
1165
|
# This allows the server to send unsolicited untagged EXPUNGE #responses,
|
966
1166
|
# but does not execute any client request. \IMAP servers are permitted to
|
967
|
-
# send unsolicited untagged responses at any time, except for
|
1167
|
+
# send unsolicited untagged responses at any time, except for +EXPUNGE+:
|
968
1168
|
#
|
969
1169
|
# * +EXPUNGE+ can only be sent while a command is in progress.
|
970
1170
|
# * +EXPUNGE+ must _not_ be sent during #fetch, #store, or #search.
|
@@ -979,15 +1179,43 @@ module Net
|
|
979
1179
|
# to inform the command to inform the server that the client is done with
|
980
1180
|
# the connection.
|
981
1181
|
#
|
982
|
-
# Related: #disconnect
|
1182
|
+
# Related: #disconnect, #logout!
|
983
1183
|
def logout
|
984
1184
|
send_command("LOGOUT")
|
985
1185
|
end
|
986
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
|
+
|
987
1210
|
# Sends a {STARTTLS command [IMAP4rev1 §6.2.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.1]
|
988
1211
|
# to start a TLS session.
|
989
1212
|
#
|
990
|
-
# 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.
|
991
1219
|
#
|
992
1220
|
# This method returns after TLS negotiation and hostname verification are
|
993
1221
|
# both successful. Any error indicates that the connection has not been
|
@@ -997,132 +1225,150 @@ module Net
|
|
997
1225
|
# >>>
|
998
1226
|
# Any #response_handlers added before STARTTLS should be aware that the
|
999
1227
|
# TaggedResponse to STARTTLS is sent clear-text, _before_ TLS negotiation.
|
1000
|
-
# 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.
|
1001
1230
|
#
|
1002
1231
|
# Related: Net::IMAP.new, #login, #authenticate
|
1003
1232
|
#
|
1004
|
-
#
|
1005
|
-
#
|
1006
|
-
#
|
1233
|
+
# ==== Capability
|
1234
|
+
# Clients should not call #starttls unless the server advertises the
|
1235
|
+
# +STARTTLS+ capability.
|
1007
1236
|
#
|
1008
1237
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1009
|
-
# Cached capabilities
|
1010
|
-
#
|
1011
|
-
# The TaggedResponse to #starttls is sent clear-text, so the server <em>must
|
1012
|
-
# *not*</em> send capabilities in the #starttls response and clients <em>must
|
1013
|
-
# not</em> use them if they are sent. Servers will generally send an
|
1014
|
-
# unsolicited untagged response immeditely _after_ #starttls completes.
|
1238
|
+
# Cached #capabilities will be cleared when this method completes.
|
1015
1239
|
#
|
1016
|
-
def starttls(options
|
1017
|
-
|
1240
|
+
def starttls(**options)
|
1241
|
+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
1242
|
+
error = nil
|
1243
|
+
ok = send_command("STARTTLS") do |resp|
|
1018
1244
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
options = create_ssl_params(certs, verify)
|
1023
|
-
rescue NoMethodError
|
1024
|
-
end
|
1025
|
-
start_tls_session(options)
|
1245
|
+
clear_cached_capabilities
|
1246
|
+
clear_responses
|
1247
|
+
start_tls_session
|
1026
1248
|
end
|
1249
|
+
rescue Exception => error
|
1250
|
+
raise # note that the error backtrace is in the receiver_thread
|
1251
|
+
end
|
1252
|
+
if error
|
1253
|
+
disconnect
|
1254
|
+
raise error
|
1027
1255
|
end
|
1256
|
+
ok
|
1028
1257
|
end
|
1029
1258
|
|
1030
1259
|
# :call-seq:
|
1031
|
-
# authenticate(mechanism,
|
1032
|
-
# authenticate(mech, *creds, **props) {|prop, auth| val } -> ok_resp
|
1033
|
-
# authenticate(mechanism, authnid, credentials, authzid=nil) -> ok_resp
|
1034
|
-
# authenticate(mechanism, **properties) -> ok_resp
|
1035
|
-
# authenticate(mechanism) {|propname, authctx| prop_value } -> ok_resp
|
1260
|
+
# authenticate(mechanism, *, sasl_ir: config.sasl_ir, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
|
1036
1261
|
#
|
1037
1262
|
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
|
1038
1263
|
# to authenticate the client. If successful, the connection enters the
|
1039
1264
|
# "_authenticated_" state.
|
1040
1265
|
#
|
1041
1266
|
# +mechanism+ is the name of the \SASL authentication mechanism to be used.
|
1042
|
-
# All other arguments are forwarded to the authenticator for the requested
|
1043
|
-
# mechanism. The listed call signatures are suggestions. <em>The
|
1044
|
-
# documentation for each individual mechanism must be consulted for its
|
1045
|
-
# specific parameters.</em>
|
1046
1267
|
#
|
1047
|
-
#
|
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+.
|
1048
1271
|
#
|
1049
|
-
#
|
1272
|
+
# The +registry+ kwarg can be used to select the mechanism implementation
|
1273
|
+
# from a custom registry. See SASL.authenticator and SASL::Authenticators.
|
1050
1274
|
#
|
1051
|
-
#
|
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>
|
1052
1278
|
#
|
1053
|
-
#
|
1054
|
-
# Login using clear-text username and password.
|
1279
|
+
# Related: #login, #starttls, #auth_capable?, #auth_mechanisms
|
1055
1280
|
#
|
1056
|
-
#
|
1057
|
-
# Login using a username and OAuth2 access token.
|
1058
|
-
# Non-standard and obsoleted by +OAUTHBEARER+, but widely
|
1059
|
-
# supported.
|
1281
|
+
# ==== Mechanisms
|
1060
1282
|
#
|
1061
|
-
#
|
1062
|
-
#
|
1063
|
-
#
|
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].
|
1064
1294
|
#
|
1065
|
-
#
|
1295
|
+
# Authenticates using already established credentials, such as a TLS
|
1296
|
+
# certificate or IPsec.
|
1066
1297
|
#
|
1067
|
-
#
|
1298
|
+
# +OAUTHBEARER+::
|
1299
|
+
# See OAuthBearerAuthenticator[rdoc-ref:Net::IMAP::SASL::OAuthBearerAuthenticator].
|
1068
1300
|
#
|
1069
|
-
#
|
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+.
|
1070
1304
|
#
|
1071
|
-
#
|
1305
|
+
# +PLAIN+::
|
1306
|
+
# See PlainAuthenticator[rdoc-ref:Net::IMAP::SASL::PlainAuthenticator].
|
1072
1307
|
#
|
1073
|
-
#
|
1074
|
-
#
|
1308
|
+
# Login using clear-text username and password.
|
1309
|
+
#
|
1310
|
+
# +SCRAM-SHA-1+::
|
1311
|
+
# +SCRAM-SHA-256+::
|
1312
|
+
# See ScramAuthenticator[rdoc-ref:Net::IMAP::SASL::ScramAuthenticator].
|
1313
|
+
#
|
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.
|
1320
|
+
#
|
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
|
1075
1328
|
# registry}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
1076
|
-
# for
|
1329
|
+
# for a list of all SASL mechanisms and their specifications. To register
|
1330
|
+
# new authenticators, see Authenticators.
|
1077
1331
|
#
|
1078
|
-
# =====
|
1332
|
+
# ===== Deprecated mechanisms
|
1079
1333
|
#
|
1080
|
-
#
|
1081
|
-
#
|
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>
|
1082
1337
|
#
|
1083
|
-
#
|
1084
|
-
#
|
1085
|
-
#
|
1086
|
-
#
|
1087
|
-
#
|
1088
|
-
#
|
1089
|
-
#
|
1090
|
-
#
|
1091
|
-
#
|
1092
|
-
#
|
1093
|
-
#
|
1094
|
-
# creds = {
|
1095
|
-
# authcid: username,
|
1096
|
-
# password: proc { password ||= ui.prompt_for_password },
|
1097
|
-
# oauth2_token: proc { accesstok ||= kms.fresh_access_token },
|
1098
|
-
# }
|
1099
|
-
# capa = imap.capability
|
1100
|
-
# if capa.include? "AUTH=OAUTHBEARER"
|
1101
|
-
# imap.authenticate "OAUTHBEARER", **creds # authcid, oauth2_token
|
1102
|
-
# elsif capa.include? "AUTH=XOAUTH2"
|
1103
|
-
# imap.authenticate "XOAUTH2", **creds # authcid, oauth2_token
|
1104
|
-
# elsif capa.include? "AUTH=SCRAM-SHA-256"
|
1105
|
-
# imap.authenticate "SCRAM-SHA-256", **creds # authcid, password
|
1106
|
-
# elsif capa.include? "AUTH=PLAIN"
|
1107
|
-
# imap.authenticate "PLAIN", **creds # authcid, password
|
1108
|
-
# elsif capa.include? "AUTH=DIGEST-MD5"
|
1109
|
-
# imap.authenticate "DIGEST-MD5", **creds # authcid, password
|
1110
|
-
# elsif capa.include? "LOGINDISABLED"
|
1111
|
-
# raise "the server has disabled login"
|
1112
|
-
# else
|
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"
|
1113
1349
|
# imap.login username, password
|
1350
|
+
# else
|
1351
|
+
# raise "No acceptable authentication mechanism is available"
|
1114
1352
|
# end
|
1115
1353
|
#
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
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 }
|
1126
1372
|
end
|
1127
1373
|
|
1128
1374
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1130,16 +1376,21 @@ module Net
|
|
1130
1376
|
# this +user+. If successful, the connection enters the "_authenticated_"
|
1131
1377
|
# state.
|
1132
1378
|
#
|
1133
|
-
# Using #authenticate
|
1134
|
-
#
|
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+.
|
1135
1383
|
#
|
1136
1384
|
# A Net::IMAP::NoResponseError is raised if authentication fails.
|
1137
1385
|
#
|
1138
1386
|
# Related: #authenticate, #starttls
|
1139
1387
|
#
|
1140
1388
|
# ==== Capabilities
|
1141
|
-
#
|
1142
|
-
#
|
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.
|
1143
1394
|
#
|
1144
1395
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1145
1396
|
# Cached capabilities _must_ be invalidated after this method completes.
|
@@ -1147,35 +1398,52 @@ module Net
|
|
1147
1398
|
# ResponseCode.
|
1148
1399
|
#
|
1149
1400
|
def login(user, password)
|
1401
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
1402
|
+
raise LoginDisabledError
|
1403
|
+
end
|
1150
1404
|
send_command("LOGIN", user, password)
|
1405
|
+
.tap { @capabilities = capabilities_from_resp_code _1 }
|
1151
1406
|
end
|
1152
1407
|
|
1153
1408
|
# Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
|
1154
1409
|
# to select a +mailbox+ so that messages in the +mailbox+ can be accessed.
|
1155
1410
|
#
|
1156
1411
|
# After you have selected a mailbox, you may retrieve the number of items in
|
1157
|
-
# that mailbox from <tt>imap.responses
|
1158
|
-
# recent messages from <tt>imap.responses
|
1159
|
-
# these values can change if new messages arrive during a session
|
1160
|
-
# existing messages are expunged; see #add_response_handler for a
|
1161
|
-
# detect these events.
|
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.
|
1162
1423
|
#
|
1163
1424
|
# A Net::IMAP::NoResponseError is raised if the mailbox does not
|
1164
1425
|
# exist or is for some reason non-selectable.
|
1165
1426
|
#
|
1166
1427
|
# Related: #examine
|
1167
1428
|
#
|
1168
|
-
#
|
1429
|
+
# ==== Capabilities
|
1169
1430
|
#
|
1170
1431
|
# If [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] is supported,
|
1171
1432
|
# the server may return an untagged "NO" response with a "UIDNOTSTICKY"
|
1172
1433
|
# response code indicating that the mailstore does not support persistent
|
1173
1434
|
# UIDs:
|
1174
|
-
#
|
1175
|
-
|
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
|
1176
1444
|
synchronize do
|
1177
1445
|
@responses.clear
|
1178
|
-
send_command(
|
1446
|
+
send_command(*args)
|
1179
1447
|
end
|
1180
1448
|
end
|
1181
1449
|
|
@@ -1188,10 +1456,12 @@ module Net
|
|
1188
1456
|
# exist or is for some reason non-examinable.
|
1189
1457
|
#
|
1190
1458
|
# Related: #select
|
1191
|
-
def examine(mailbox)
|
1459
|
+
def examine(mailbox, condstore: false)
|
1460
|
+
args = ["EXAMINE", mailbox]
|
1461
|
+
args << ["CONDSTORE"] if condstore
|
1192
1462
|
synchronize do
|
1193
1463
|
@responses.clear
|
1194
|
-
send_command(
|
1464
|
+
send_command(*args)
|
1195
1465
|
end
|
1196
1466
|
end
|
1197
1467
|
|
@@ -1261,10 +1531,10 @@ module Net
|
|
1261
1531
|
# to the client. +refname+ provides a context (for instance, a base
|
1262
1532
|
# directory in a directory-based mailbox hierarchy). +mailbox+ specifies a
|
1263
1533
|
# mailbox or (via wildcards) mailboxes under that context. Two wildcards
|
1264
|
-
# may be used in +mailbox+:
|
1265
|
-
# the hierarchy delimiter (for instance,
|
1266
|
-
# directory-based mailbox hierarchy); and
|
1267
|
-
# *except* the hierarchy delimiter.
|
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.
|
1268
1538
|
#
|
1269
1539
|
# If +refname+ is empty, +mailbox+ is used directly to determine
|
1270
1540
|
# which mailboxes to match. If +mailbox+ is empty, the root
|
@@ -1274,7 +1544,7 @@ module Net
|
|
1274
1544
|
#
|
1275
1545
|
# Related: #lsub, MailboxList
|
1276
1546
|
#
|
1277
|
-
#
|
1547
|
+
# ==== For example:
|
1278
1548
|
#
|
1279
1549
|
# imap.create("foo/bar")
|
1280
1550
|
# imap.create("foo/baz")
|
@@ -1289,7 +1559,7 @@ module Net
|
|
1289
1559
|
def list(refname, mailbox)
|
1290
1560
|
synchronize do
|
1291
1561
|
send_command("LIST", refname, mailbox)
|
1292
|
-
|
1562
|
+
clear_responses("LIST")
|
1293
1563
|
end
|
1294
1564
|
end
|
1295
1565
|
|
@@ -1312,23 +1582,22 @@ module Net
|
|
1312
1582
|
# servers, then folder creation (and listing, moving, etc) can lead to
|
1313
1583
|
# errors.
|
1314
1584
|
#
|
1315
|
-
# From RFC2342:
|
1316
|
-
#
|
1317
|
-
# 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
|
1318
1588
|
# Namespace, and a single Other User's Namespace, circumstances exist
|
1319
1589
|
# where there MAY be multiples of these, and a client MUST be prepared
|
1320
1590
|
# for them. If a client is configured such that it is required to create
|
1321
1591
|
# a certain mailbox, there can be circumstances where it is unclear which
|
1322
1592
|
# Personal Namespaces it should create the mailbox in. In these
|
1323
1593
|
# situations a client SHOULD let the user select which namespaces to
|
1324
|
-
# create the mailbox in
|
1594
|
+
# create the mailbox in.</em>
|
1325
1595
|
#
|
1326
1596
|
# Related: #list, Namespaces, Namespace
|
1327
1597
|
#
|
1328
|
-
#
|
1598
|
+
# ==== For example:
|
1329
1599
|
#
|
1330
|
-
#
|
1331
|
-
# if capabilities.include?("NAMESPACE")
|
1600
|
+
# if capable?("NAMESPACE")
|
1332
1601
|
# namespaces = imap.namespace
|
1333
1602
|
# if namespace = namespaces.personal.first
|
1334
1603
|
# prefix = namespace.prefix # e.g. "" or "INBOX."
|
@@ -1340,14 +1609,14 @@ module Net
|
|
1340
1609
|
# end
|
1341
1610
|
# end
|
1342
1611
|
#
|
1343
|
-
#
|
1612
|
+
# ==== Capabilities
|
1344
1613
|
#
|
1345
|
-
# The server's capabilities must include +NAMESPACE+
|
1346
|
-
# [RFC2342[https://
|
1614
|
+
# The server's capabilities must include either +IMAP4rev2+ or +NAMESPACE+
|
1615
|
+
# [RFC2342[https://www.rfc-editor.org/rfc/rfc2342]].
|
1347
1616
|
def namespace
|
1348
1617
|
synchronize do
|
1349
1618
|
send_command("NAMESPACE")
|
1350
|
-
|
1619
|
+
clear_responses("NAMESPACE").last
|
1351
1620
|
end
|
1352
1621
|
end
|
1353
1622
|
|
@@ -1379,7 +1648,7 @@ module Net
|
|
1379
1648
|
#
|
1380
1649
|
# Related: #list, MailboxList
|
1381
1650
|
#
|
1382
|
-
#
|
1651
|
+
# ==== Capabilities
|
1383
1652
|
#
|
1384
1653
|
# The server's capabilities must include +XLIST+,
|
1385
1654
|
# a deprecated Gmail extension (replaced by +SPECIAL-USE+).
|
@@ -1391,7 +1660,7 @@ module Net
|
|
1391
1660
|
def xlist(refname, mailbox)
|
1392
1661
|
synchronize do
|
1393
1662
|
send_command("XLIST", refname, mailbox)
|
1394
|
-
|
1663
|
+
clear_responses("XLIST")
|
1395
1664
|
end
|
1396
1665
|
end
|
1397
1666
|
|
@@ -1402,16 +1671,16 @@ module Net
|
|
1402
1671
|
#
|
1403
1672
|
# Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
|
1404
1673
|
#
|
1405
|
-
#
|
1674
|
+
# ==== Capabilities
|
1406
1675
|
#
|
1407
1676
|
# The server's capabilities must include +QUOTA+
|
1408
|
-
# [RFC2087[https://
|
1677
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1409
1678
|
def getquotaroot(mailbox)
|
1410
1679
|
synchronize do
|
1411
1680
|
send_command("GETQUOTAROOT", mailbox)
|
1412
1681
|
result = []
|
1413
|
-
result.concat(
|
1414
|
-
result.concat(
|
1682
|
+
result.concat(clear_responses("QUOTAROOT"))
|
1683
|
+
result.concat(clear_responses("QUOTA"))
|
1415
1684
|
return result
|
1416
1685
|
end
|
1417
1686
|
end
|
@@ -1423,14 +1692,14 @@ module Net
|
|
1423
1692
|
#
|
1424
1693
|
# Related: #getquotaroot, #setquota, MailboxQuota
|
1425
1694
|
#
|
1426
|
-
#
|
1695
|
+
# ==== Capabilities
|
1427
1696
|
#
|
1428
1697
|
# The server's capabilities must include +QUOTA+
|
1429
|
-
# [RFC2087[https://
|
1698
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1430
1699
|
def getquota(mailbox)
|
1431
1700
|
synchronize do
|
1432
1701
|
send_command("GETQUOTA", mailbox)
|
1433
|
-
|
1702
|
+
clear_responses("QUOTA")
|
1434
1703
|
end
|
1435
1704
|
end
|
1436
1705
|
|
@@ -1441,10 +1710,10 @@ module Net
|
|
1441
1710
|
#
|
1442
1711
|
# Related: #getquota, #getquotaroot
|
1443
1712
|
#
|
1444
|
-
#
|
1713
|
+
# ==== Capabilities
|
1445
1714
|
#
|
1446
1715
|
# The server's capabilities must include +QUOTA+
|
1447
|
-
# [RFC2087[https://
|
1716
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1448
1717
|
def setquota(mailbox, quota)
|
1449
1718
|
if quota.nil?
|
1450
1719
|
data = '()'
|
@@ -1461,10 +1730,10 @@ module Net
|
|
1461
1730
|
#
|
1462
1731
|
# Related: #getacl
|
1463
1732
|
#
|
1464
|
-
#
|
1733
|
+
# ==== Capabilities
|
1465
1734
|
#
|
1466
1735
|
# The server's capabilities must include +ACL+
|
1467
|
-
# [RFC4314[https://
|
1736
|
+
# [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
|
1468
1737
|
def setacl(mailbox, user, rights)
|
1469
1738
|
if rights.nil?
|
1470
1739
|
send_command("SETACL", mailbox, user, "")
|
@@ -1479,14 +1748,14 @@ module Net
|
|
1479
1748
|
#
|
1480
1749
|
# Related: #setacl, MailboxACLItem
|
1481
1750
|
#
|
1482
|
-
#
|
1751
|
+
# ==== Capabilities
|
1483
1752
|
#
|
1484
1753
|
# The server's capabilities must include +ACL+
|
1485
|
-
# [RFC4314[https://
|
1754
|
+
# [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
|
1486
1755
|
def getacl(mailbox)
|
1487
1756
|
synchronize do
|
1488
1757
|
send_command("GETACL", mailbox)
|
1489
|
-
|
1758
|
+
clear_responses("ACL").last
|
1490
1759
|
end
|
1491
1760
|
end
|
1492
1761
|
|
@@ -1501,31 +1770,74 @@ module Net
|
|
1501
1770
|
def lsub(refname, mailbox)
|
1502
1771
|
synchronize do
|
1503
1772
|
send_command("LSUB", refname, mailbox)
|
1504
|
-
|
1773
|
+
clear_responses("LSUB")
|
1505
1774
|
end
|
1506
1775
|
end
|
1507
1776
|
|
1508
|
-
# Sends a {STATUS
|
1777
|
+
# Sends a {STATUS command [IMAP4rev1 §6.3.10]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.10]
|
1509
1778
|
# and returns the status of the indicated +mailbox+. +attr+ is a list of one
|
1510
|
-
# or more attributes whose statuses are to be requested.
|
1511
|
-
#
|
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.
|
1512
1799
|
#
|
1513
|
-
#
|
1514
|
-
#
|
1515
|
-
#
|
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.
|
1516
1803
|
#
|
1517
|
-
#
|
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].
|
1807
|
+
#
|
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:
|
1518
1820
|
#
|
1519
1821
|
# p imap.status("inbox", ["MESSAGES", "RECENT"])
|
1520
1822
|
# #=> {"RECENT"=>0, "MESSAGES"=>44}
|
1521
1823
|
#
|
1522
|
-
#
|
1523
|
-
#
|
1524
|
-
#
|
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].
|
1525
1837
|
def status(mailbox, attr)
|
1526
1838
|
synchronize do
|
1527
1839
|
send_command("STATUS", mailbox, attr)
|
1528
|
-
|
1840
|
+
clear_responses("STATUS").last&.attr
|
1529
1841
|
end
|
1530
1842
|
end
|
1531
1843
|
|
@@ -1549,7 +1861,7 @@ module Net
|
|
1549
1861
|
# not exist (it is not created automatically), or if the flags,
|
1550
1862
|
# date_time, or message arguments contain errors.
|
1551
1863
|
#
|
1552
|
-
#
|
1864
|
+
# ==== Capabilities
|
1553
1865
|
#
|
1554
1866
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
1555
1867
|
# supported and the destination supports persistent UIDs, the server's
|
@@ -1598,146 +1910,536 @@ module Net
|
|
1598
1910
|
#
|
1599
1911
|
# Related: #close
|
1600
1912
|
#
|
1601
|
-
#
|
1913
|
+
# ==== Capabilities
|
1602
1914
|
#
|
1603
|
-
# The server's capabilities must include +UNSELECT+
|
1604
|
-
# [RFC3691[https://
|
1915
|
+
# The server's capabilities must include either +IMAP4rev2+ or +UNSELECT+
|
1916
|
+
# [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
|
1605
1917
|
def unselect
|
1606
1918
|
send_command("UNSELECT")
|
1607
1919
|
end
|
1608
1920
|
|
1921
|
+
# call-seq:
|
1922
|
+
# expunge -> array of message sequence numbers
|
1923
|
+
# expunge -> VanishedData of UIDs
|
1924
|
+
#
|
1609
1925
|
# Sends an {EXPUNGE command [IMAP4rev1 §6.4.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.3]
|
1610
|
-
#
|
1611
|
-
# 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.
|
1612
1938
|
#
|
1613
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>.
|
1614
1947
|
def expunge
|
1615
|
-
|
1616
|
-
send_command("EXPUNGE")
|
1617
|
-
return @responses.delete("EXPUNGE")
|
1618
|
-
end
|
1948
|
+
expunge_internal("EXPUNGE")
|
1619
1949
|
end
|
1620
1950
|
|
1951
|
+
# call-seq:
|
1952
|
+
# uid_expunge{uid_set) -> array of message sequence numbers
|
1953
|
+
# uid_expunge{uid_set) -> VanishedData of UIDs
|
1954
|
+
#
|
1621
1955
|
# Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
|
1622
1956
|
# {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
|
1623
1957
|
# to permanently remove all messages that have both the <tt>\\Deleted</tt>
|
1624
1958
|
# flag set and a UID that is included in +uid_set+.
|
1625
1959
|
#
|
1960
|
+
# Returns the same result type as #expunge.
|
1961
|
+
#
|
1626
1962
|
# By using #uid_expunge instead of #expunge when resynchronizing with
|
1627
1963
|
# the server, the client can ensure that it does not inadvertantly
|
1628
1964
|
# remove any messages that have been marked as <tt>\\Deleted</tt> by other
|
1629
1965
|
# clients between the time that the client was last connected and
|
1630
1966
|
# the time the client resynchronizes.
|
1631
1967
|
#
|
1632
|
-
# *Note:*
|
1633
|
-
# >>>
|
1634
|
-
# Although the command takes a set of UIDs for its argument, the
|
1635
|
-
# server still returns regular EXPUNGE responses, which contain
|
1636
|
-
# a <em>sequence number</em>. These will be deleted from
|
1637
|
-
# #responses and this method returns them as an array of
|
1638
|
-
# <em>sequence number</em> integers.
|
1639
|
-
#
|
1640
1968
|
# Related: #expunge
|
1641
1969
|
#
|
1642
|
-
#
|
1970
|
+
# ==== Capabilities
|
1643
1971
|
#
|
1644
|
-
# The server's capabilities must include +UIDPLUS+
|
1972
|
+
# The server's capabilities must include either +IMAP4rev2+ or +UIDPLUS+
|
1645
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.
|
1646
1977
|
def uid_expunge(uid_set)
|
1647
|
-
|
1648
|
-
send_command("UID EXPUNGE", MessageSet.new(uid_set))
|
1649
|
-
return @responses.delete("EXPUNGE")
|
1650
|
-
end
|
1978
|
+
expunge_internal("UID EXPUNGE", SequenceSet.new(uid_set))
|
1651
1979
|
end
|
1652
1980
|
|
1981
|
+
# :call-seq:
|
1982
|
+
# search(criteria, charset = nil) -> result
|
1983
|
+
# search(criteria, charset: nil, return: nil) -> result
|
1984
|
+
#
|
1653
1985
|
# Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
|
1654
|
-
# to search the mailbox for messages that match the given
|
1655
|
-
#
|
1656
|
-
#
|
1657
|
-
#
|
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+.
|
1658
2016
|
#
|
1659
2017
|
# Related: #uid_search
|
1660
2018
|
#
|
1661
|
-
#
|
2019
|
+
# ==== For example:
|
2020
|
+
#
|
2021
|
+
# imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
|
2022
|
+
# #=> [1, 6, 7, 8]
|
1662
2023
|
#
|
1663
|
-
#
|
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+,
|
1664
2186
|
# see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
|
1665
2187
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
1666
2188
|
# in addition to documentation for
|
1667
|
-
# any
|
1668
|
-
# reported by #capability which may define additional search filters, e.g:
|
2189
|
+
# any #capabilities which may define additional search filters, such as
|
1669
2190
|
# +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
|
1670
|
-
# +SAVEDATE+.
|
2191
|
+
# +SAVEDATE+.
|
1671
2192
|
#
|
1672
|
-
#
|
1673
|
-
#
|
1674
|
-
#
|
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.
|
1675
2197
|
#
|
1676
|
-
#
|
1677
|
-
# <b><date></b>. The date argument has a format similar
|
1678
|
-
# to <tt>8-Aug-2002</tt>, and can be formatted using
|
1679
|
-
# Net::IMAP.format_date.
|
2198
|
+
# ===== Search keys that match all messages
|
1680
2199
|
#
|
1681
|
-
#
|
2200
|
+
# [+ALL+]
|
2201
|
+
# The default initial key. Matches every message in the mailbox.
|
1682
2202
|
#
|
1683
|
-
#
|
2203
|
+
# [+SAVEDATESUPPORTED+]
|
2204
|
+
# Matches every message in the mailbox when the mailbox supports the save
|
2205
|
+
# date attribute. Otherwise, it matches no messages.
|
1684
2206
|
#
|
1685
|
-
#
|
2207
|
+
# <em>Requires +SAVEDATE+ capability</em>.
|
2208
|
+
# {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
|
1686
2209
|
#
|
1687
|
-
#
|
2210
|
+
# ===== Sequence set search keys
|
1688
2211
|
#
|
1689
|
-
#
|
2212
|
+
# [_sequence-set_]
|
2213
|
+
# Matches messages with message sequence numbers in _sequence-set_.
|
1690
2214
|
#
|
1691
|
-
#
|
2215
|
+
# _Note:_ this search key has no label.
|
1692
2216
|
#
|
1693
|
-
#
|
1694
|
-
#
|
2217
|
+
# <em>+UIDONLY+ must *not* be enabled.</em>
|
2218
|
+
# {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.html]
|
1695
2219
|
#
|
1696
|
-
#
|
2220
|
+
# [+UID+ _sequence-set_]
|
2221
|
+
# Matches messages with a UID in _sequence-set_.
|
1697
2222
|
#
|
1698
|
-
#
|
2223
|
+
# ===== Compound search keys
|
1699
2224
|
#
|
1700
|
-
#
|
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.
|
1701
2229
|
#
|
1702
|
-
#
|
2230
|
+
# _Note:_ this search key has no label.
|
1703
2231
|
#
|
1704
|
-
#
|
1705
|
-
#
|
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].
|
1706
2243
|
#
|
1707
|
-
|
1708
|
-
|
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
|
2269
|
+
#
|
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", ...)
|
1709
2393
|
end
|
1710
2394
|
|
2395
|
+
# :call-seq:
|
2396
|
+
# uid_search(criteria, charset = nil) -> result
|
2397
|
+
# uid_search(criteria, charset: nil, return: nil) -> result
|
2398
|
+
#
|
1711
2399
|
# Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1712
2400
|
# to search the mailbox for messages that match the given searching
|
1713
2401
|
# criteria, and returns unique identifiers (<tt>UID</tt>s).
|
1714
2402
|
#
|
1715
|
-
#
|
1716
|
-
|
1717
|
-
|
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", ...)
|
1718
2420
|
end
|
1719
2421
|
|
2422
|
+
# :call-seq:
|
2423
|
+
# fetch(set, attr, changedsince: nil) -> array of FetchData
|
2424
|
+
#
|
1720
2425
|
# Sends a {FETCH command [IMAP4rev1 §6.4.5]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.5]
|
1721
2426
|
# to retrieve data associated with a message in the mailbox.
|
1722
2427
|
#
|
1723
|
-
#
|
1724
|
-
#
|
1725
|
-
#
|
1726
|
-
# being interpreted as '100:*'. Beware that the +exclude_end?+
|
1727
|
-
# property of a Range object is ignored, and the contents of a
|
1728
|
-
# range are independent of the order of the range endpoints as per
|
1729
|
-
# the protocol specification, so 1...5, 5..1 and 5...1 are all
|
1730
|
-
# equivalent to 1..5.
|
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.)
|
1731
2431
|
#
|
1732
|
-
# +attr+ is a list of attributes to fetch; see
|
1733
|
-
#
|
2432
|
+
# +attr+ is a list of attributes to fetch; see FetchStruct documentation for
|
2433
|
+
# a list of supported attributes.
|
1734
2434
|
#
|
1735
|
-
#
|
1736
|
-
#
|
2435
|
+
# +changedsince+ is an optional integer mod-sequence. It limits results to
|
2436
|
+
# messages with a mod-sequence greater than +changedsince+.
|
1737
2437
|
#
|
1738
|
-
#
|
2438
|
+
# The return value is an array of FetchData.
|
1739
2439
|
#
|
1740
|
-
#
|
2440
|
+
# Related: #uid_fetch, FetchData
|
2441
|
+
#
|
2442
|
+
# ==== For example:
|
1741
2443
|
#
|
1742
2444
|
# p imap.fetch(6..8, "UID")
|
1743
2445
|
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
|
@@ -1754,48 +2456,138 @@ module Net
|
|
1754
2456
|
# #=> "12-Oct-2000 22:40:59 +0900"
|
1755
2457
|
# p data.attr["UID"]
|
1756
2458
|
# #=> 98
|
1757
|
-
|
1758
|
-
|
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", ...)
|
1759
2474
|
end
|
1760
2475
|
|
2476
|
+
# :call-seq:
|
2477
|
+
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData (or UIDFetchData)
|
2478
|
+
#
|
1761
2479
|
# Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1762
2480
|
# to retrieve data associated with a message in the mailbox.
|
1763
2481
|
#
|
1764
|
-
#
|
1765
|
-
#
|
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.)
|
1766
2485
|
#
|
2486
|
+
# +attr+ behaves the same as with #fetch.
|
1767
2487
|
# >>>
|
1768
2488
|
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
|
1769
2489
|
# part of any +FETCH+ response caused by a +UID+ command, regardless of
|
1770
2490
|
# whether a +UID+ was specified as a message data item to the +FETCH+.
|
1771
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
|
+
#
|
1772
2521
|
# Related: #fetch, FetchData
|
1773
|
-
|
1774
|
-
|
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", ...)
|
1775
2536
|
end
|
1776
2537
|
|
2538
|
+
# :call-seq:
|
2539
|
+
# store(set, attr, value, unchangedsince: nil) -> array of FetchData
|
2540
|
+
#
|
1777
2541
|
# Sends a {STORE command [IMAP4rev1 §6.4.6]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.6]
|
1778
2542
|
# to alter data associated with messages in the mailbox, in particular their
|
1779
|
-
# flags.
|
1780
|
-
#
|
1781
|
-
#
|
1782
|
-
#
|
1783
|
-
#
|
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.
|
1784
2556
|
#
|
1785
|
-
#
|
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.
|
1786
2563
|
#
|
1787
2564
|
# Related: #uid_store
|
1788
2565
|
#
|
1789
|
-
#
|
2566
|
+
# ==== For example:
|
1790
2567
|
#
|
1791
2568
|
# p imap.store(6..8, "+FLAGS", [:Deleted])
|
1792
|
-
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
1793
|
-
# #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
2569
|
+
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
2570
|
+
# #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
1794
2571
|
# #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
|
1795
|
-
|
1796
|
-
|
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)
|
1797
2586
|
end
|
1798
2587
|
|
2588
|
+
# :call-seq:
|
2589
|
+
# uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData (or UIDFetchData)
|
2590
|
+
#
|
1799
2591
|
# Sends a {UID STORE command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1800
2592
|
# to alter data associated with messages in the mailbox, in particular their
|
1801
2593
|
# flags.
|
@@ -1804,8 +2596,16 @@ module Net
|
|
1804
2596
|
# message sequence numbers.
|
1805
2597
|
#
|
1806
2598
|
# Related: #store
|
1807
|
-
|
1808
|
-
|
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)
|
1809
2609
|
end
|
1810
2610
|
|
1811
2611
|
# Sends a {COPY command [IMAP4rev1 §6.4.7]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.7]
|
@@ -1815,13 +2615,16 @@ module Net
|
|
1815
2615
|
#
|
1816
2616
|
# Related: #uid_copy
|
1817
2617
|
#
|
1818
|
-
#
|
2618
|
+
# ==== Capabilities
|
1819
2619
|
#
|
1820
2620
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
1821
2621
|
# supported, the server's response should include a +COPYUID+ response code
|
1822
2622
|
# with UIDPlusData. This will report the UIDVALIDITY of the destination
|
1823
2623
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
1824
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.
|
1825
2628
|
def copy(set, mailbox)
|
1826
2629
|
copy_internal("COPY", set, mailbox)
|
1827
2630
|
end
|
@@ -1832,9 +2635,12 @@ module Net
|
|
1832
2635
|
#
|
1833
2636
|
# Similar to #copy, but +set+ contains unique identifiers.
|
1834
2637
|
#
|
1835
|
-
#
|
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.
|
1836
2642
|
#
|
1837
|
-
#
|
2643
|
+
# Otherwise, #uid_copy is updated by extensions in the same way as #copy.
|
1838
2644
|
def uid_copy(set, mailbox)
|
1839
2645
|
copy_internal("UID COPY", set, mailbox)
|
1840
2646
|
end
|
@@ -1847,10 +2653,10 @@ module Net
|
|
1847
2653
|
#
|
1848
2654
|
# Related: #uid_move
|
1849
2655
|
#
|
1850
|
-
#
|
2656
|
+
# ==== Capabilities
|
1851
2657
|
#
|
1852
|
-
# The server's capabilities must include +MOVE+
|
1853
|
-
# [RFC6851[https://
|
2658
|
+
# The server's capabilities must include either +IMAP4rev2+ or +MOVE+
|
2659
|
+
# [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
|
1854
2660
|
#
|
1855
2661
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
1856
2662
|
# supported, the server's response should include a +COPYUID+ response code
|
@@ -1858,6 +2664,8 @@ module Net
|
|
1858
2664
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
1859
2665
|
# the moved messages.
|
1860
2666
|
#
|
2667
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2668
|
+
# +MOVE+ command is prohibited. Use #uid_move instead.
|
1861
2669
|
def move(set, mailbox)
|
1862
2670
|
copy_internal("MOVE", set, mailbox)
|
1863
2671
|
end
|
@@ -1871,11 +2679,15 @@ module Net
|
|
1871
2679
|
#
|
1872
2680
|
# Related: #move
|
1873
2681
|
#
|
1874
|
-
#
|
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.
|
1875
2689
|
#
|
1876
|
-
#
|
1877
|
-
# [RFC6851[https://tools.ietf.org/html/rfc6851]]. +UIDPLUS+ also affects
|
1878
|
-
# #uid_move the same way it affects #move.
|
2690
|
+
# Otherwise, #uid_move is updated by extensions in the same way as #move.
|
1879
2691
|
def uid_move(set, mailbox)
|
1880
2692
|
copy_internal("UID MOVE", set, mailbox)
|
1881
2693
|
end
|
@@ -1891,17 +2703,17 @@ module Net
|
|
1891
2703
|
#
|
1892
2704
|
# Related: #uid_sort, #search, #uid_search, #thread, #uid_thread
|
1893
2705
|
#
|
1894
|
-
#
|
2706
|
+
# ==== For example:
|
1895
2707
|
#
|
1896
2708
|
# p imap.sort(["FROM"], ["ALL"], "US-ASCII")
|
1897
2709
|
# #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
|
1898
2710
|
# p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
|
1899
2711
|
# #=> [6, 7, 8, 1]
|
1900
2712
|
#
|
1901
|
-
#
|
2713
|
+
# ==== Capabilities
|
1902
2714
|
#
|
1903
2715
|
# The server's capabilities must include +SORT+
|
1904
|
-
# [RFC5256[https://
|
2716
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1905
2717
|
def sort(sort_keys, search_keys, charset)
|
1906
2718
|
return sort_internal("SORT", sort_keys, search_keys, charset)
|
1907
2719
|
end
|
@@ -1913,10 +2725,10 @@ module Net
|
|
1913
2725
|
#
|
1914
2726
|
# Related: #sort, #search, #uid_search, #thread, #uid_thread
|
1915
2727
|
#
|
1916
|
-
#
|
2728
|
+
# ==== Capabilities
|
1917
2729
|
#
|
1918
2730
|
# The server's capabilities must include +SORT+
|
1919
|
-
# [RFC5256[https://
|
2731
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1920
2732
|
def uid_sort(sort_keys, search_keys, charset)
|
1921
2733
|
return sort_internal("UID SORT", sort_keys, search_keys, charset)
|
1922
2734
|
end
|
@@ -1938,10 +2750,10 @@ module Net
|
|
1938
2750
|
#
|
1939
2751
|
# Related: #uid_thread, #search, #uid_search, #sort, #uid_sort
|
1940
2752
|
#
|
1941
|
-
#
|
2753
|
+
# ==== Capabilities
|
1942
2754
|
#
|
1943
2755
|
# The server's capabilities must include +THREAD+
|
1944
|
-
# [RFC5256[https://
|
2756
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1945
2757
|
def thread(algorithm, search_keys, charset)
|
1946
2758
|
return thread_internal("THREAD", algorithm, search_keys, charset)
|
1947
2759
|
end
|
@@ -1952,14 +2764,112 @@ module Net
|
|
1952
2764
|
#
|
1953
2765
|
# Related: #thread, #search, #uid_search, #sort, #uid_sort
|
1954
2766
|
#
|
1955
|
-
#
|
2767
|
+
# ==== Capabilities
|
1956
2768
|
#
|
1957
2769
|
# The server's capabilities must include +THREAD+
|
1958
|
-
# [RFC5256[https://
|
2770
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
1959
2771
|
def uid_thread(algorithm, search_keys, charset)
|
1960
2772
|
return thread_internal("UID THREAD", algorithm, search_keys, charset)
|
1961
2773
|
end
|
1962
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
|
+
|
1963
2873
|
# Sends an {IDLE command [RFC2177 §3]}[https://www.rfc-editor.org/rfc/rfc6851#section-3]
|
1964
2874
|
# {[IMAP4rev2 §6.3.13]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.3.13]
|
1965
2875
|
# that waits for notifications of new or expunged messages. Yields
|
@@ -1972,17 +2882,23 @@ module Net
|
|
1972
2882
|
# checks the connection for each 60 seconds.
|
1973
2883
|
#
|
1974
2884
|
# loop do
|
1975
|
-
# imap.idle(60) do |
|
1976
|
-
#
|
2885
|
+
# imap.idle(60) do |response|
|
2886
|
+
# do_something_with(response)
|
2887
|
+
# imap.idle_done if some_condition?(response)
|
1977
2888
|
# end
|
1978
2889
|
# end
|
1979
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
|
+
#
|
1980
2896
|
# Related: #idle_done, #noop, #check
|
1981
2897
|
#
|
1982
|
-
#
|
2898
|
+
# ==== Capabilities
|
1983
2899
|
#
|
1984
|
-
# The server's capabilities must include +IDLE+
|
1985
|
-
# [RFC2177[https://
|
2900
|
+
# The server's capabilities must include either +IMAP4rev2+ or +IDLE+
|
2901
|
+
# [RFC2177[https://www.rfc-editor.org/rfc/rfc2177]].
|
1986
2902
|
def idle(timeout = nil, &response_handler)
|
1987
2903
|
raise LocalJumpError, "no block given" unless response_handler
|
1988
2904
|
|
@@ -2004,7 +2920,7 @@ module Net
|
|
2004
2920
|
unless @receiver_thread_terminating
|
2005
2921
|
remove_response_handler(response_handler)
|
2006
2922
|
put_string("DONE#{CRLF}")
|
2007
|
-
response = get_tagged_response(tag, "IDLE",
|
2923
|
+
response = get_tagged_response(tag, "IDLE", idle_response_timeout)
|
2008
2924
|
end
|
2009
2925
|
end
|
2010
2926
|
end
|
@@ -2012,7 +2928,11 @@ module Net
|
|
2012
2928
|
return response
|
2013
2929
|
end
|
2014
2930
|
|
2015
|
-
# 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+.
|
2016
2936
|
#
|
2017
2937
|
# Related: #idle
|
2018
2938
|
def idle_done
|
@@ -2024,6 +2944,196 @@ module Net
|
|
2024
2944
|
end
|
2025
2945
|
end
|
2026
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
|
+
|
2027
3137
|
# Adds a response handler. For example, to detect when
|
2028
3138
|
# the server sends a new EXISTS response (which normally
|
2029
3139
|
# indicates new messages being added to the mailbox),
|
@@ -2036,19 +3146,21 @@ module Net
|
|
2036
3146
|
# end
|
2037
3147
|
# }
|
2038
3148
|
#
|
2039
|
-
# Response handlers can also be added when the client is created before the
|
2040
|
-
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
2041
|
-
# This ensures every server response is handled, including the #greeting.
|
2042
|
-
#
|
2043
3149
|
# Related: #remove_response_handler, #response_handlers
|
2044
3150
|
def add_response_handler(handler = nil, &block)
|
2045
3151
|
raise ArgumentError, "two Procs are passed" if handler && block
|
2046
|
-
|
3152
|
+
synchronize do
|
3153
|
+
@response_handlers.push(block || handler)
|
3154
|
+
end
|
2047
3155
|
end
|
2048
3156
|
|
2049
3157
|
# Removes the response handler.
|
3158
|
+
#
|
3159
|
+
# Related: #add_response_handler, #response_handlers
|
2050
3160
|
def remove_response_handler(handler)
|
2051
|
-
|
3161
|
+
synchronize do
|
3162
|
+
@response_handlers.delete(handler)
|
3163
|
+
end
|
2052
3164
|
end
|
2053
3165
|
|
2054
3166
|
private
|
@@ -2057,116 +3169,39 @@ module Net
|
|
2057
3169
|
PORT = 143 # :nodoc:
|
2058
3170
|
SSL_PORT = 993 # :nodoc:
|
2059
3171
|
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
# but all other responses are handled in the receiver
|
2084
|
-
# thread.
|
2085
|
-
# max_response_size:: See #max_response_size.
|
2086
|
-
#
|
2087
|
-
# The most common errors are:
|
2088
|
-
#
|
2089
|
-
# Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
|
2090
|
-
# firewall.
|
2091
|
-
# Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
|
2092
|
-
# being dropped by an intervening firewall).
|
2093
|
-
# Errno::ENETUNREACH:: There is no route to that network.
|
2094
|
-
# SocketError:: Hostname not known or other socket error.
|
2095
|
-
# Net::IMAP::ByeResponseError:: The connected to the host was successful, but
|
2096
|
-
# it immediately said goodbye.
|
2097
|
-
def initialize(host, port_or_options = {},
|
2098
|
-
usessl = false, certs = nil, verify = true)
|
2099
|
-
super()
|
2100
|
-
@host = host
|
2101
|
-
begin
|
2102
|
-
options = port_or_options.to_hash
|
2103
|
-
rescue NoMethodError
|
2104
|
-
# for backward compatibility
|
2105
|
-
options = {}
|
2106
|
-
options[:port] = port_or_options
|
2107
|
-
if usessl
|
2108
|
-
options[:ssl] = create_ssl_params(certs, verify)
|
2109
|
-
end
|
2110
|
-
end
|
2111
|
-
@port = options[:port] || (options[:ssl] ? SSL_PORT : PORT)
|
2112
|
-
@tag_prefix = "RUBY"
|
2113
|
-
@tagno = 0
|
2114
|
-
@open_timeout = options[:open_timeout] || 30
|
2115
|
-
@idle_response_timeout = options[:idle_response_timeout] || 5
|
2116
|
-
@max_response_size = options[:max_response_size]
|
2117
|
-
@parser = ResponseParser.new
|
2118
|
-
@sock = tcp_socket(@host, @port)
|
2119
|
-
@reader = ResponseReader.new(self, @sock)
|
2120
|
-
begin
|
2121
|
-
if options[:ssl]
|
2122
|
-
start_tls_session(options[:ssl])
|
2123
|
-
@usessl = true
|
2124
|
-
else
|
2125
|
-
@usessl = false
|
2126
|
-
end
|
2127
|
-
@responses = Hash.new([].freeze)
|
2128
|
-
@tagged_responses = {}
|
2129
|
-
@response_handlers = []
|
2130
|
-
options[:response_handlers]&.each do |h| add_response_handler(h) end
|
2131
|
-
@tagged_response_arrival = new_cond
|
2132
|
-
@continued_command_tag = nil
|
2133
|
-
@continuation_request_arrival = new_cond
|
2134
|
-
@continuation_request_exception = nil
|
2135
|
-
@idle_done_cond = nil
|
2136
|
-
@logout_command_tag = nil
|
2137
|
-
@debug_output_bol = true
|
2138
|
-
@exception = nil
|
2139
|
-
|
2140
|
-
@greeting = get_response
|
2141
|
-
if @greeting.nil?
|
2142
|
-
raise Error, "connection closed"
|
2143
|
-
end
|
2144
|
-
if @greeting.name == "BYE"
|
2145
|
-
raise ByeResponseError, @greeting
|
2146
|
-
end
|
2147
|
-
@response_handlers.each do |handler| handler.call(@greeting) end
|
2148
|
-
|
2149
|
-
@client_thread = Thread.current
|
2150
|
-
@receiver_thread = Thread.start {
|
2151
|
-
begin
|
2152
|
-
receive_responses
|
2153
|
-
rescue Exception
|
2154
|
-
end
|
2155
|
-
}
|
2156
|
-
@receiver_thread_terminating = false
|
2157
|
-
rescue Exception
|
2158
|
-
@sock.close
|
2159
|
-
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
|
2160
3195
|
end
|
2161
3196
|
end
|
2162
3197
|
|
2163
3198
|
def tcp_socket(host, port)
|
2164
|
-
s = Socket.tcp(host, port, :connect_timeout =>
|
3199
|
+
s = Socket.tcp(host, port, :connect_timeout => open_timeout)
|
2165
3200
|
s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
|
2166
3201
|
s
|
2167
3202
|
rescue Errno::ETIMEDOUT
|
2168
3203
|
raise Net::OpenTimeout, "Timeout to open TCP connection to " +
|
2169
|
-
"#{host}:#{port} (exceeds #{
|
3204
|
+
"#{host}:#{port} (exceeds #{open_timeout} seconds)"
|
2170
3205
|
end
|
2171
3206
|
|
2172
3207
|
def receive_responses
|
@@ -2205,11 +3240,7 @@ module Net
|
|
2205
3240
|
@continuation_request_arrival.signal
|
2206
3241
|
end
|
2207
3242
|
when UntaggedResponse
|
2208
|
-
|
2209
|
-
if resp.data.instance_of?(ResponseText) &&
|
2210
|
-
(code = resp.data.code)
|
2211
|
-
record_response(code.name, code.data)
|
2212
|
-
end
|
3243
|
+
record_untagged_response(resp)
|
2213
3244
|
if resp.name == "BYE" && @logout_command_tag.nil?
|
2214
3245
|
@sock.close
|
2215
3246
|
@exception = ByeResponseError.new(resp)
|
@@ -2263,24 +3294,67 @@ module Net
|
|
2263
3294
|
when /\A(?:BAD)\z/ni
|
2264
3295
|
raise BadResponseError, resp
|
2265
3296
|
else
|
2266
|
-
|
3297
|
+
disconnect
|
3298
|
+
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw.chomp]
|
2267
3299
|
end
|
2268
3300
|
end
|
2269
3301
|
|
2270
3302
|
def get_response
|
2271
|
-
buff =
|
3303
|
+
buff = String.new
|
3304
|
+
while true
|
3305
|
+
s = @sock.gets(CRLF)
|
3306
|
+
break unless s
|
3307
|
+
buff.concat(s)
|
3308
|
+
if /\{(\d+)\}\r\n/n =~ s
|
3309
|
+
s = @sock.read($1.to_i)
|
3310
|
+
buff.concat(s)
|
3311
|
+
else
|
3312
|
+
break
|
3313
|
+
end
|
3314
|
+
end
|
2272
3315
|
return nil if buff.length == 0
|
2273
|
-
|
2274
|
-
|
3316
|
+
if config.debug?
|
3317
|
+
$stderr.print(buff.gsub(/^/n, "S: "))
|
3318
|
+
end
|
3319
|
+
return @parser.parse(buff)
|
3320
|
+
end
|
3321
|
+
|
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
|
2275
3344
|
end
|
2276
3345
|
|
2277
3346
|
#############################
|
2278
3347
|
|
2279
|
-
|
2280
|
-
|
2281
|
-
|
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
|
2282
3357
|
end
|
2283
|
-
@responses[name].push(data)
|
2284
3358
|
end
|
2285
3359
|
|
2286
3360
|
def send_command(cmd, *args, &block)
|
@@ -2318,12 +3392,12 @@ module Net
|
|
2318
3392
|
|
2319
3393
|
def put_string(str)
|
2320
3394
|
@sock.print(str)
|
2321
|
-
if
|
3395
|
+
if config.debug?
|
2322
3396
|
if @debug_output_bol
|
2323
3397
|
$stderr.print("C: ")
|
2324
3398
|
end
|
2325
|
-
$stderr.print(str.gsub(/\n
|
2326
|
-
if /\
|
3399
|
+
$stderr.print(str.gsub(/\n/n) { $'.empty? ? $& : "\nC: " })
|
3400
|
+
if /\n\z/n.match(str)
|
2327
3401
|
@debug_output_bol = true
|
2328
3402
|
else
|
2329
3403
|
@debug_output_bol = false
|
@@ -2331,23 +3405,119 @@ module Net
|
|
2331
3405
|
end
|
2332
3406
|
end
|
2333
3407
|
|
2334
|
-
def
|
2335
|
-
if
|
2336
|
-
|
3408
|
+
def enforce_logindisabled?
|
3409
|
+
if config.enforce_logindisabled == :when_capabilities_cached
|
3410
|
+
capabilities_cached?
|
2337
3411
|
else
|
2338
|
-
|
3412
|
+
config.enforce_logindisabled
|
2339
3413
|
end
|
3414
|
+
end
|
3415
|
+
|
3416
|
+
def expunge_internal(...)
|
3417
|
+
synchronize do
|
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
|
3425
|
+
else
|
3426
|
+
merged_uids = SequenceSet[*vanished_array.map(&:uids)]
|
3427
|
+
VanishedData[uids: merged_uids, earlier: false]
|
3428
|
+
end
|
3429
|
+
end
|
3430
|
+
end
|
3431
|
+
|
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(...)
|
2340
3487
|
synchronize do
|
2341
|
-
|
2342
|
-
|
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 ")]
|
2343
3504
|
else
|
2344
|
-
|
3505
|
+
# warn NO_SEARCH_RESPONSE
|
3506
|
+
SearchResult[]
|
2345
3507
|
end
|
2346
|
-
return @responses.delete("SEARCH")[-1]
|
2347
3508
|
end
|
2348
3509
|
end
|
2349
3510
|
|
2350
|
-
def fetch_internal(cmd, set, attr, mod = nil)
|
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
|
2351
3521
|
case attr
|
2352
3522
|
when String then
|
2353
3523
|
attr = RawData.new(attr)
|
@@ -2357,119 +3527,134 @@ module Net
|
|
2357
3527
|
}
|
2358
3528
|
end
|
2359
3529
|
|
2360
|
-
|
2361
|
-
|
2362
|
-
|
2363
|
-
send_command(cmd, MessageSet.new(set), attr, mod)
|
2364
|
-
else
|
2365
|
-
send_command(cmd, MessageSet.new(set), attr)
|
2366
|
-
end
|
2367
|
-
return @responses.delete("FETCH")
|
2368
|
-
end
|
3530
|
+
args = [cmd, set, attr]
|
3531
|
+
args << mod if mod
|
3532
|
+
send_command_returning_fetch_results(*args)
|
2369
3533
|
end
|
2370
3534
|
|
2371
|
-
def store_internal(cmd, set, attr, flags)
|
2372
|
-
if attr.instance_of?(String)
|
2373
|
-
|
2374
|
-
|
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(...)
|
2375
3544
|
synchronize do
|
2376
|
-
|
2377
|
-
|
2378
|
-
|
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
|
2379
3551
|
end
|
2380
3552
|
end
|
2381
3553
|
|
2382
3554
|
def copy_internal(cmd, set, mailbox)
|
2383
|
-
send_command(cmd,
|
3555
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
2384
3556
|
end
|
2385
3557
|
|
2386
3558
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
2387
|
-
|
2388
|
-
search_keys = [RawData.new(search_keys)]
|
2389
|
-
else
|
2390
|
-
normalize_searching_criteria(search_keys)
|
2391
|
-
end
|
2392
|
-
normalize_searching_criteria(search_keys)
|
3559
|
+
search_keys = normalize_searching_criteria(search_keys)
|
2393
3560
|
synchronize do
|
2394
3561
|
send_command(cmd, sort_keys, charset, *search_keys)
|
2395
|
-
|
3562
|
+
clear_responses("SORT").last || []
|
2396
3563
|
end
|
2397
3564
|
end
|
2398
3565
|
|
2399
3566
|
def thread_internal(cmd, algorithm, search_keys, charset)
|
2400
|
-
|
2401
|
-
|
2402
|
-
|
2403
|
-
|
3567
|
+
search_keys = normalize_searching_criteria(search_keys)
|
3568
|
+
synchronize do
|
3569
|
+
send_command(cmd, algorithm, charset, *search_keys)
|
3570
|
+
clear_responses("THREAD").last || []
|
2404
3571
|
end
|
2405
|
-
normalize_searching_criteria(search_keys)
|
2406
|
-
send_command(cmd, algorithm, charset, *search_keys)
|
2407
|
-
return @responses.delete("THREAD")[-1]
|
2408
3572
|
end
|
2409
3573
|
|
2410
|
-
def normalize_searching_criteria(
|
2411
|
-
|
2412
|
-
|
2413
|
-
|
2414
|
-
|
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]
|
2415
3579
|
else
|
2416
3580
|
i
|
2417
3581
|
end
|
2418
|
-
|
3582
|
+
}
|
2419
3583
|
end
|
2420
3584
|
|
2421
|
-
def
|
2422
|
-
|
2423
|
-
|
2424
|
-
|
2425
|
-
|
2426
|
-
|
2427
|
-
params[:ca_path] = certs
|
2428
|
-
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)
|
2429
3591
|
end
|
2430
|
-
|
2431
|
-
|
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)
|
2432
3598
|
else
|
2433
|
-
|
3599
|
+
coerce_search_arg_to_seqset?(obj)
|
2434
3600
|
end
|
2435
|
-
return params
|
2436
3601
|
end
|
2437
3602
|
|
2438
|
-
def
|
2439
|
-
|
2440
|
-
|
2441
|
-
|
2442
|
-
|
2443
|
-
|
2444
|
-
|
2445
|
-
|
2446
|
-
|
2447
|
-
|
2448
|
-
|
2449
|
-
|
2450
|
-
context = SSLContext.new
|
2451
|
-
context.set_params(params)
|
2452
|
-
if defined?(VerifyCallbackProc)
|
2453
|
-
context.verify_callback = VerifyCallbackProc
|
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
|
2454
3615
|
end
|
2455
|
-
|
2456
|
-
|
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)
|
2457
3623
|
@sock.sync_close = true
|
2458
3624
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
2459
|
-
ssl_socket_connect(@sock,
|
2460
|
-
if
|
3625
|
+
ssl_socket_connect(@sock, open_timeout)
|
3626
|
+
if ssl_ctx.verify_mode != VERIFY_NONE
|
2461
3627
|
@sock.post_connection_check(@host)
|
3628
|
+
@tls_verified = true
|
2462
3629
|
end
|
2463
3630
|
end
|
2464
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
|
+
|
2465
3646
|
end
|
2466
3647
|
end
|
2467
3648
|
|
2468
3649
|
require_relative "imap/errors"
|
3650
|
+
require_relative "imap/config"
|
2469
3651
|
require_relative "imap/command_data"
|
2470
3652
|
require_relative "imap/data_encoding"
|
3653
|
+
require_relative "imap/data_lite"
|
2471
3654
|
require_relative "imap/flags"
|
2472
3655
|
require_relative "imap/response_data"
|
2473
3656
|
require_relative "imap/response_parser"
|
2474
3657
|
require_relative "imap/authenticators"
|
2475
|
-
|
3658
|
+
|
3659
|
+
require_relative "imap/deprecated_client_options"
|
3660
|
+
Net::IMAP.prepend Net::IMAP::DeprecatedClientOptions
|