net-imap 0.4.10 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/BSDL +22 -0
- data/COPYING +56 -0
- data/Gemfile +8 -1
- data/LICENSE.txt +3 -22
- data/README.md +10 -4
- data/docs/styles.css +75 -14
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +61 -48
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +62 -0
- data/lib/net/imap/config.rb +552 -0
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +4 -4
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +9 -6
- data/lib/net/imap/errors.rb +40 -1
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +126 -47
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +126 -239
- data/lib/net/imap/response_parser/parser_utils.rb +11 -6
- data/lib/net/imap/response_parser.rb +188 -34
- data/lib/net/imap/response_reader.rb +73 -0
- data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
- data/lib/net/imap/sasl/external_authenticator.rb +3 -3
- data/lib/net/imap/sasl/gs2_header.rb +7 -7
- data/lib/net/imap/sasl/login_authenticator.rb +4 -3
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
- data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
- data/lib/net/imap/sasl.rb +8 -5
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/search_result.rb +2 -2
- data/lib/net/imap/sequence_set.rb +471 -176
- data/lib/net/imap/stringprep/nameprep.rb +1 -1
- data/lib/net/imap/stringprep/trace.rb +4 -4
- data/lib/net/imap/uidplus_data.rb +244 -0
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +1225 -364
- data/net-imap.gemspec +4 -4
- data/rakelib/benchmarks.rake +1 -1
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/string_prep_tables_generator.rb +2 -0
- metadata +17 -12
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/pages.yml +0 -46
- data/.github/workflows/test.yml +0 -31
- data/.gitignore +0 -12
data/lib/net/imap.rb
CHANGED
@@ -25,8 +25,8 @@ module Net
|
|
25
25
|
|
26
26
|
# Net::IMAP implements Internet Message Access Protocol (\IMAP) client
|
27
27
|
# functionality. The protocol is described
|
28
|
-
# in {IMAP4rev1 [RFC3501]}[https://
|
29
|
-
# and {IMAP4rev2 [RFC9051]}[https://
|
28
|
+
# in {IMAP4rev1 [RFC3501]}[https://www.rfc-editor.org/rfc/rfc3501]
|
29
|
+
# and {IMAP4rev2 [RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051].
|
30
30
|
#
|
31
31
|
# == \IMAP Overview
|
32
32
|
#
|
@@ -43,10 +43,18 @@ module Net
|
|
43
43
|
# To work on the messages within a mailbox, the client must
|
44
44
|
# first select that mailbox, using either #select or #examine
|
45
45
|
# (for read-only access). Once the client has successfully
|
46
|
-
# selected a mailbox, they enter the
|
46
|
+
# selected a mailbox, they enter the +selected+ state, and that
|
47
47
|
# mailbox becomes the _current_ mailbox, on which mail-item
|
48
48
|
# related commands implicitly operate.
|
49
49
|
#
|
50
|
+
# === Connection state
|
51
|
+
#
|
52
|
+
# Once an IMAP connection is established, the connection is in one of four
|
53
|
+
# states: <tt>not authenticated</tt>, +authenticated+, +selected+, and
|
54
|
+
# +logout+. Most commands are valid only in certain states.
|
55
|
+
#
|
56
|
+
# See #connection_state.
|
57
|
+
#
|
50
58
|
# === Sequence numbers and UIDs
|
51
59
|
#
|
52
60
|
# Messages have two sorts of identifiers: message sequence
|
@@ -199,6 +207,42 @@ module Net
|
|
199
207
|
#
|
200
208
|
# This script invokes the FETCH command and the SEARCH command concurrently.
|
201
209
|
#
|
210
|
+
# When running multiple commands, care must be taken to avoid ambiguity. For
|
211
|
+
# example, SEARCH responses are ambiguous about which command they are
|
212
|
+
# responding to, so search commands should not run simultaneously, unless the
|
213
|
+
# server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
|
214
|
+
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
|
215
|
+
# §5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
|
216
|
+
# other examples of command sequences which should not be pipelined.
|
217
|
+
#
|
218
|
+
# == Unbounded memory use
|
219
|
+
#
|
220
|
+
# Net::IMAP reads server responses in a separate receiver thread per client.
|
221
|
+
# Unhandled response data is saved to #responses, and response_handlers run
|
222
|
+
# inside the receiver thread. See the list of methods for {handling server
|
223
|
+
# responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
|
224
|
+
#
|
225
|
+
# Because the receiver thread continuously reads and saves new responses, some
|
226
|
+
# scenarios must be careful to avoid unbounded memory use:
|
227
|
+
#
|
228
|
+
# * Commands such as #list or #fetch can have an enormous number of responses.
|
229
|
+
# * Commands such as #fetch can result in an enormous size per response.
|
230
|
+
# * Long-lived connections will gradually accumulate unsolicited server
|
231
|
+
# responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
|
232
|
+
# * A buggy or untrusted server could send inappropriate responses, which
|
233
|
+
# could be very numerous, very large, and very rapid.
|
234
|
+
#
|
235
|
+
# Use paginated or limited versions of commands whenever possible.
|
236
|
+
#
|
237
|
+
# Use Config#max_response_size to impose a limit on incoming server responses
|
238
|
+
# as they are being read. <em>This is especially important for untrusted
|
239
|
+
# servers.</em>
|
240
|
+
#
|
241
|
+
# Use #add_response_handler to handle responses after each one is received.
|
242
|
+
# Use the +response_handlers+ argument to ::new to assign response handlers
|
243
|
+
# before the receiver thread is started. Use #extract_responses,
|
244
|
+
# #clear_responses, or #responses (with a block) to prune responses.
|
245
|
+
#
|
202
246
|
# == Errors
|
203
247
|
#
|
204
248
|
# An \IMAP server can send three different types of responses to indicate
|
@@ -260,8 +304,9 @@ module Net
|
|
260
304
|
#
|
261
305
|
# - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
|
262
306
|
# waits for a successful server greeting before the method returns.
|
307
|
+
# - #connection_state: Returns the connection state.
|
263
308
|
# - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
|
264
|
-
# - #logout: Tells the server to end the session.
|
309
|
+
# - #logout: Tells the server to end the session. Enters the +logout+ state.
|
265
310
|
# - #disconnect: Disconnects the connection (without sending #logout first).
|
266
311
|
# - #disconnected?: True if the connection has been closed.
|
267
312
|
#
|
@@ -288,6 +333,8 @@ module Net
|
|
288
333
|
# pre-authenticated connection.
|
289
334
|
# - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
|
290
335
|
# ResponseCode#data.
|
336
|
+
# - #extract_responses: Removes and returns the responses for which the block
|
337
|
+
# returns a true value.
|
291
338
|
# - #clear_responses: Deletes unhandled data from #responses and returns it.
|
292
339
|
# - #add_response_handler: Add a block to be called inside the receiver thread
|
293
340
|
# with every server response.
|
@@ -297,15 +344,15 @@ module Net
|
|
297
344
|
# === Core \IMAP commands
|
298
345
|
#
|
299
346
|
# The following commands are defined either by
|
300
|
-
# the [IMAP4rev1[https://
|
347
|
+
# the [IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501]] base specification, or
|
301
348
|
# by one of the following extensions:
|
302
|
-
# [IDLE[https://
|
303
|
-
# [NAMESPACE[https://
|
304
|
-
# [UNSELECT[https://
|
305
|
-
# [ENABLE[https://
|
306
|
-
# [MOVE[https://
|
349
|
+
# [IDLE[https://www.rfc-editor.org/rfc/rfc2177]],
|
350
|
+
# [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]],
|
351
|
+
# [UNSELECT[https://www.rfc-editor.org/rfc/rfc3691]],
|
352
|
+
# [ENABLE[https://www.rfc-editor.org/rfc/rfc5161]],
|
353
|
+
# [MOVE[https://www.rfc-editor.org/rfc/rfc6851]].
|
307
354
|
# These extensions are widely supported by modern IMAP4rev1 servers and have
|
308
|
-
# all been integrated into [IMAP4rev2[https://
|
355
|
+
# all been integrated into [IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]].
|
309
356
|
# <em>*NOTE:* Net::IMAP doesn't support IMAP4rev2 yet.</em>
|
310
357
|
#
|
311
358
|
# ==== Any state
|
@@ -315,37 +362,36 @@ module Net
|
|
315
362
|
# <em>In general, #capable? should be used rather than explicitly sending a
|
316
363
|
# +CAPABILITY+ command to the server.</em>
|
317
364
|
# - #noop: Allows the server to send unsolicited untagged #responses.
|
318
|
-
# - #logout: Tells the server to end the session. Enters the
|
365
|
+
# - #logout: Tells the server to end the session. Enters the +logout+ state.
|
319
366
|
#
|
320
367
|
# ==== Not Authenticated state
|
321
368
|
#
|
322
369
|
# In addition to the commands for any state, the following commands are valid
|
323
|
-
# in the
|
370
|
+
# in the +not_authenticated+ state:
|
324
371
|
#
|
325
372
|
# - #starttls: Upgrades a clear-text connection to use TLS.
|
326
373
|
#
|
327
374
|
# <em>Requires the +STARTTLS+ capability.</em>
|
328
375
|
# - #authenticate: Identifies the client to the server using the given
|
329
376
|
# {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
330
|
-
# and credentials. Enters the
|
377
|
+
# and credentials. Enters the +authenticated+ state.
|
331
378
|
#
|
332
379
|
# <em>The server should list <tt>"AUTH=#{mechanism}"</tt> capabilities for
|
333
380
|
# supported mechanisms.</em>
|
334
381
|
# - #login: Identifies the client to the server using a plain text password.
|
335
|
-
# Using #authenticate is
|
336
|
-
# state.
|
382
|
+
# Using #authenticate is preferred. Enters the +authenticated+ state.
|
337
383
|
#
|
338
384
|
# <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
|
339
385
|
#
|
340
386
|
# ==== Authenticated state
|
341
387
|
#
|
342
388
|
# In addition to the commands for any state, the following commands are valid
|
343
|
-
# in the
|
389
|
+
# in the +authenticated+ state:
|
344
390
|
#
|
345
391
|
# - #enable: Enables backwards incompatible server extensions.
|
346
392
|
# <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
|
347
|
-
# - #select: Open a mailbox and enter the
|
348
|
-
# - #examine: Open a mailbox read-only, and enter the
|
393
|
+
# - #select: Open a mailbox and enter the +selected+ state.
|
394
|
+
# - #examine: Open a mailbox read-only, and enter the +selected+ state.
|
349
395
|
# - #create: Creates a new mailbox.
|
350
396
|
# - #delete: Permanently remove a mailbox.
|
351
397
|
# - #rename: Change the name of a mailbox.
|
@@ -367,12 +413,12 @@ module Net
|
|
367
413
|
#
|
368
414
|
# ==== Selected state
|
369
415
|
#
|
370
|
-
# In addition to the commands for any state and the
|
371
|
-
# commands, the following commands are valid in the
|
416
|
+
# In addition to the commands for any state and the +authenticated+
|
417
|
+
# commands, the following commands are valid in the +selected+ state:
|
372
418
|
#
|
373
|
-
# - #close: Closes the mailbox and returns to the
|
419
|
+
# - #close: Closes the mailbox and returns to the +authenticated+ state,
|
374
420
|
# expunging deleted messages, unless the mailbox was opened as read-only.
|
375
|
-
# - #unselect: Closes the mailbox and returns to the
|
421
|
+
# - #unselect: Closes the mailbox and returns to the +authenticated+ state,
|
376
422
|
# without expunging any messages.
|
377
423
|
# <em>Requires the +UNSELECT+ or +IMAP4rev2+ capability.</em>
|
378
424
|
# - #expunge: Permanently removes messages which have the Deleted flag set.
|
@@ -393,7 +439,7 @@ module Net
|
|
393
439
|
#
|
394
440
|
# ==== Logout state
|
395
441
|
#
|
396
|
-
# No \IMAP commands are valid in the
|
442
|
+
# No \IMAP commands are valid in the +logout+ state. If the socket is still
|
397
443
|
# open, Net::IMAP will close it after receiving server confirmation.
|
398
444
|
# Exceptions will be raised by \IMAP commands that have already started and
|
399
445
|
# are waiting for a response, as well as any that are called after logout.
|
@@ -402,7 +448,7 @@ module Net
|
|
402
448
|
#
|
403
449
|
# ==== RFC9051: +IMAP4rev2+
|
404
450
|
#
|
405
|
-
# Although IMAP4rev2[https://
|
451
|
+
# Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not supported
|
406
452
|
# yet, Net::IMAP supports several extensions that have been folded into it:
|
407
453
|
# +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+,
|
408
454
|
# <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
|
@@ -412,7 +458,7 @@ module Net
|
|
412
458
|
# >>>
|
413
459
|
# <em>The following are folded into +IMAP4rev2+ but are currently
|
414
460
|
# unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
|
415
|
-
# extensions, +
|
461
|
+
# extensions, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
|
416
462
|
# +LITERAL-+, and +SPECIAL-USE+.</em>
|
417
463
|
#
|
418
464
|
# ==== RFC2087: +QUOTA+
|
@@ -422,13 +468,13 @@ module Net
|
|
422
468
|
# - #setquota: sets the resource limits for a given quota root.
|
423
469
|
#
|
424
470
|
# ==== RFC2177: +IDLE+
|
425
|
-
# Folded into IMAP4rev2[https://
|
471
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
426
472
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
427
473
|
# - #idle: Allows the server to send updates to the client, without the client
|
428
474
|
# needing to poll using #noop.
|
429
475
|
#
|
430
476
|
# ==== RFC2342: +NAMESPACE+
|
431
|
-
# Folded into IMAP4rev2[https://
|
477
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
432
478
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
433
479
|
# - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
|
434
480
|
#
|
@@ -437,7 +483,7 @@ module Net
|
|
437
483
|
#
|
438
484
|
# ==== RFC3516: +BINARY+
|
439
485
|
# The fetch side of +BINARY+ has been folded into
|
440
|
-
# IMAP4rev2[https://
|
486
|
+
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
|
441
487
|
# - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
|
442
488
|
# +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
|
443
489
|
#
|
@@ -445,9 +491,9 @@ module Net
|
|
445
491
|
# *NOTE:* The binary extension the #append command is _not_ supported yet.
|
446
492
|
#
|
447
493
|
# ==== RFC3691: +UNSELECT+
|
448
|
-
# Folded into IMAP4rev2[https://
|
494
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
449
495
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
450
|
-
# - #unselect: Closes the mailbox and returns to the
|
496
|
+
# - #unselect: Closes the mailbox and returns to the +authenticated+ state,
|
451
497
|
# without expunging any messages.
|
452
498
|
#
|
453
499
|
# ==== RFC4314: +ACL+
|
@@ -457,19 +503,23 @@ module Net
|
|
457
503
|
# *NOTE:* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
|
458
504
|
#
|
459
505
|
# ==== RFC4315: +UIDPLUS+
|
460
|
-
# Folded into IMAP4rev2[https://
|
506
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
461
507
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
462
508
|
# - #uid_expunge: Restricts #expunge to only remove the specified UIDs.
|
463
509
|
# - Updates #select, #examine with the +UIDNOTSTICKY+ ResponseCode
|
464
510
|
# - Updates #append with the +APPENDUID+ ResponseCode
|
465
511
|
# - Updates #copy, #move with the +COPYUID+ ResponseCode
|
466
512
|
#
|
513
|
+
# ==== RFC4731: +ESEARCH+
|
514
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
|
515
|
+
# - Updates #search, #uid_search with +return+ options and ESearchResult.
|
516
|
+
#
|
467
517
|
# ==== RFC4959: +SASL-IR+
|
468
|
-
# Folded into IMAP4rev2[https://
|
518
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
|
469
519
|
# - Updates #authenticate with the option to send an initial response.
|
470
520
|
#
|
471
521
|
# ==== RFC5161: +ENABLE+
|
472
|
-
# Folded into IMAP4rev2[https://
|
522
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
473
523
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
474
524
|
# - #enable: Enables backwards incompatible server extensions.
|
475
525
|
#
|
@@ -493,7 +543,7 @@ module Net
|
|
493
543
|
# +X-GM-THRID+, but Gmail does not support it (as of 2023-11-10).
|
494
544
|
#
|
495
545
|
# ==== RFC6851: +MOVE+
|
496
|
-
# Folded into IMAP4rev2[https://
|
546
|
+
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
497
547
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
498
548
|
# - #move, #uid_move: Moves the specified messages to the end of the
|
499
549
|
# specified destination mailbox, expunging them from the current mailbox.
|
@@ -528,6 +578,17 @@ module Net
|
|
528
578
|
# See FetchData#emailid and FetchData#emailid.
|
529
579
|
# - Updates #status with support for the +MAILBOXID+ status attribute.
|
530
580
|
#
|
581
|
+
# ==== RFC9394: +PARTIAL+
|
582
|
+
# - Updates #search, #uid_search with the +PARTIAL+ return option which adds
|
583
|
+
# ESearchResult#partial return data.
|
584
|
+
# - Updates #uid_fetch with the +partial+ modifier.
|
585
|
+
#
|
586
|
+
# ==== RFC9586: +UIDONLY+
|
587
|
+
# - Updates #enable with +UIDONLY+ parameter.
|
588
|
+
# - Updates #uid_fetch and #uid_store to return +UIDFETCH+ response.
|
589
|
+
# - Updates #expunge and #uid_expunge to return +VANISHED+ response.
|
590
|
+
# - Prohibits use of message sequence numbers in responses or requests.
|
591
|
+
#
|
531
592
|
# == References
|
532
593
|
#
|
533
594
|
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
|
@@ -558,57 +619,57 @@ module Net
|
|
558
619
|
# Gahrns, M., "IMAP4 Multi-Accessed Mailbox Practice", RFC 2180, DOI
|
559
620
|
# 10.17487/RFC2180, July 1997, <https://www.rfc-editor.org/info/rfc2180>.
|
560
621
|
#
|
561
|
-
# [UTF7[https://
|
622
|
+
# [UTF7[https://www.rfc-editor.org/rfc/rfc2152]]::
|
562
623
|
# Goldsmith, D. and M. Davis, "UTF-7 A Mail-Safe Transformation Format of
|
563
624
|
# Unicode", RFC 2152, DOI 10.17487/RFC2152, May 1997,
|
564
625
|
# <https://www.rfc-editor.org/info/rfc2152>.
|
565
626
|
#
|
566
627
|
# === Message envelope and body structure
|
567
628
|
#
|
568
|
-
# [RFC5322[https://
|
629
|
+
# [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]::
|
569
630
|
# Resnick, P., Ed., "Internet Message Format",
|
570
631
|
# RFC 5322, DOI 10.17487/RFC5322, October 2008,
|
571
632
|
# <https://www.rfc-editor.org/info/rfc5322>.
|
572
633
|
#
|
573
634
|
# <em>Note: obsoletes</em>
|
574
|
-
# RFC-2822[https://
|
575
|
-
# RFC-822[https://
|
635
|
+
# RFC-2822[https://www.rfc-editor.org/rfc/rfc2822]<em> (April 2001) and</em>
|
636
|
+
# RFC-822[https://www.rfc-editor.org/rfc/rfc822]<em> (August 1982).</em>
|
576
637
|
#
|
577
|
-
# [CHARSET[https://
|
638
|
+
# [CHARSET[https://www.rfc-editor.org/rfc/rfc2978]]::
|
578
639
|
# Freed, N. and J. Postel, "IANA Charset Registration Procedures", BCP 19,
|
579
640
|
# RFC 2978, DOI 10.17487/RFC2978, October 2000,
|
580
641
|
# <https://www.rfc-editor.org/info/rfc2978>.
|
581
642
|
#
|
582
|
-
# [DISPOSITION[https://
|
643
|
+
# [DISPOSITION[https://www.rfc-editor.org/rfc/rfc2183]]::
|
583
644
|
# Troost, R., Dorner, S., and K. Moore, Ed., "Communicating Presentation
|
584
645
|
# Information in Internet Messages: The Content-Disposition Header
|
585
646
|
# Field", RFC 2183, DOI 10.17487/RFC2183, August 1997,
|
586
647
|
# <https://www.rfc-editor.org/info/rfc2183>.
|
587
648
|
#
|
588
|
-
# [MIME-IMB[https://
|
649
|
+
# [MIME-IMB[https://www.rfc-editor.org/rfc/rfc2045]]::
|
589
650
|
# Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
|
590
651
|
# (MIME) Part One: Format of Internet Message Bodies",
|
591
652
|
# RFC 2045, DOI 10.17487/RFC2045, November 1996,
|
592
653
|
# <https://www.rfc-editor.org/info/rfc2045>.
|
593
654
|
#
|
594
|
-
# [MIME-IMT[https://
|
655
|
+
# [MIME-IMT[https://www.rfc-editor.org/rfc/rfc2046]]::
|
595
656
|
# Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
|
596
657
|
# (MIME) Part Two: Media Types", RFC 2046, DOI 10.17487/RFC2046,
|
597
658
|
# November 1996, <https://www.rfc-editor.org/info/rfc2046>.
|
598
659
|
#
|
599
|
-
# [MIME-HDRS[https://
|
660
|
+
# [MIME-HDRS[https://www.rfc-editor.org/rfc/rfc2047]]::
|
600
661
|
# Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part Three:
|
601
662
|
# Message Header Extensions for Non-ASCII Text",
|
602
663
|
# RFC 2047, DOI 10.17487/RFC2047, November 1996,
|
603
664
|
# <https://www.rfc-editor.org/info/rfc2047>.
|
604
665
|
#
|
605
|
-
# [RFC2231[https://
|
666
|
+
# [RFC2231[https://www.rfc-editor.org/rfc/rfc2231]]::
|
606
667
|
# Freed, N. and K. Moore, "MIME Parameter Value and Encoded Word
|
607
668
|
# Extensions: Character Sets, Languages, and Continuations",
|
608
669
|
# RFC 2231, DOI 10.17487/RFC2231, November 1997,
|
609
670
|
# <https://www.rfc-editor.org/info/rfc2231>.
|
610
671
|
#
|
611
|
-
# [I18n-HDRS[https://
|
672
|
+
# [I18n-HDRS[https://www.rfc-editor.org/rfc/rfc6532]]::
|
612
673
|
# Yang, A., Steele, S., and N. Freed, "Internationalized Email Headers",
|
613
674
|
# RFC 6532, DOI 10.17487/RFC6532, February 2012,
|
614
675
|
# <https://www.rfc-editor.org/info/rfc6532>.
|
@@ -624,12 +685,12 @@ module Net
|
|
624
685
|
# RFC 2557, DOI 10.17487/RFC2557, March 1999,
|
625
686
|
# <https://www.rfc-editor.org/info/rfc2557>.
|
626
687
|
#
|
627
|
-
# [MD5[https://
|
688
|
+
# [MD5[https://www.rfc-editor.org/rfc/rfc1864]]::
|
628
689
|
# Myers, J. and M. Rose, "The Content-MD5 Header Field",
|
629
690
|
# RFC 1864, DOI 10.17487/RFC1864, October 1995,
|
630
691
|
# <https://www.rfc-editor.org/info/rfc1864>.
|
631
692
|
#
|
632
|
-
# [RFC3503[https://
|
693
|
+
# [RFC3503[https://www.rfc-editor.org/rfc/rfc3503]]::
|
633
694
|
# Melnikov, A., "Message Disposition Notification (MDN)
|
634
695
|
# profile for Internet Message Access Protocol (IMAP)",
|
635
696
|
# RFC 3503, DOI 10.17487/RFC3503, March 2003,
|
@@ -637,27 +698,27 @@ module Net
|
|
637
698
|
#
|
638
699
|
# === \IMAP Extensions
|
639
700
|
#
|
640
|
-
# [QUOTA[https://
|
701
|
+
# [QUOTA[https://www.rfc-editor.org/rfc/rfc9208]]::
|
641
702
|
# Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
|
642
703
|
# March 2022, <https://www.rfc-editor.org/info/rfc9208>.
|
643
704
|
#
|
644
705
|
# <em>Note: obsoletes</em>
|
645
|
-
# RFC-2087[https://
|
706
|
+
# RFC-2087[https://www.rfc-editor.org/rfc/rfc2087]<em> (January 1997)</em>.
|
646
707
|
# <em>Net::IMAP does not fully support the RFC9208 updates yet.</em>
|
647
|
-
# [IDLE[https://
|
708
|
+
# [IDLE[https://www.rfc-editor.org/rfc/rfc2177]]::
|
648
709
|
# Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
|
649
710
|
# June 1997, <https://www.rfc-editor.org/info/rfc2177>.
|
650
|
-
# [NAMESPACE[https://
|
711
|
+
# [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]]::
|
651
712
|
# Gahrns, M. and C. Newman, "IMAP4 Namespace", RFC 2342,
|
652
713
|
# DOI 10.17487/RFC2342, May 1998, <https://www.rfc-editor.org/info/rfc2342>.
|
653
|
-
# [ID[https://
|
714
|
+
# [ID[https://www.rfc-editor.org/rfc/rfc2971]]::
|
654
715
|
# Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
|
655
716
|
# October 2000, <https://www.rfc-editor.org/info/rfc2971>.
|
656
|
-
# [BINARY[https://
|
717
|
+
# [BINARY[https://www.rfc-editor.org/rfc/rfc3516]]::
|
657
718
|
# Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
|
658
719
|
# DOI 10.17487/RFC3516, April 2003,
|
659
720
|
# <https://www.rfc-editor.org/info/rfc3516>.
|
660
|
-
# [ACL[https://
|
721
|
+
# [ACL[https://www.rfc-editor.org/rfc/rfc4314]]::
|
661
722
|
# Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
|
662
723
|
# DOI 10.17487/RFC4314, December 2005,
|
663
724
|
# <https://www.rfc-editor.org/info/rfc4314>.
|
@@ -665,36 +726,46 @@ module Net
|
|
665
726
|
# Crispin, M., "Internet Message Access Protocol (\IMAP) - UIDPLUS
|
666
727
|
# extension", RFC 4315, DOI 10.17487/RFC4315, December 2005,
|
667
728
|
# <https://www.rfc-editor.org/info/rfc4315>.
|
668
|
-
# [SORT[https://
|
729
|
+
# [SORT[https://www.rfc-editor.org/rfc/rfc5256]]::
|
669
730
|
# Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
|
670
731
|
# THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
|
671
732
|
# <https://www.rfc-editor.org/info/rfc5256>.
|
672
|
-
# [THREAD[https://
|
733
|
+
# [THREAD[https://www.rfc-editor.org/rfc/rfc5256]]::
|
673
734
|
# Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
|
674
735
|
# THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
|
675
736
|
# <https://www.rfc-editor.org/info/rfc5256>.
|
676
737
|
# [RFC5530[https://www.rfc-editor.org/rfc/rfc5530.html]]::
|
677
738
|
# Gulbrandsen, A., "IMAP Response Codes", RFC 5530, DOI 10.17487/RFC5530,
|
678
739
|
# May 2009, <https://www.rfc-editor.org/info/rfc5530>.
|
679
|
-
# [MOVE[https://
|
740
|
+
# [MOVE[https://www.rfc-editor.org/rfc/rfc6851]]::
|
680
741
|
# Gulbrandsen, A. and N. Freed, Ed., "Internet Message Access Protocol
|
681
742
|
# (\IMAP) - MOVE Extension", RFC 6851, DOI 10.17487/RFC6851, January 2013,
|
682
743
|
# <https://www.rfc-editor.org/info/rfc6851>.
|
683
|
-
# [UTF8=ACCEPT[https://
|
684
|
-
# [UTF8=ONLY[https://
|
744
|
+
# [UTF8=ACCEPT[https://www.rfc-editor.org/rfc/rfc6855]]::
|
745
|
+
# [UTF8=ONLY[https://www.rfc-editor.org/rfc/rfc6855]]::
|
685
746
|
# Resnick, P., Ed., Newman, C., Ed., and S. Shen, Ed.,
|
686
747
|
# "IMAP Support for UTF-8", RFC 6855, DOI 10.17487/RFC6855, March 2013,
|
687
748
|
# <https://www.rfc-editor.org/info/rfc6855>.
|
688
|
-
# [CONDSTORE[https://
|
689
|
-
# [QRESYNC[https://
|
749
|
+
# [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162]]::
|
750
|
+
# [QRESYNC[https://www.rfc-editor.org/rfc/rfc7162]]::
|
690
751
|
# Melnikov, A. and D. Cridland, "IMAP Extensions: Quick Flag Changes
|
691
752
|
# Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization
|
692
753
|
# (QRESYNC)", RFC 7162, DOI 10.17487/RFC7162, May 2014,
|
693
754
|
# <https://www.rfc-editor.org/info/rfc7162>.
|
694
|
-
# [OBJECTID[https://
|
755
|
+
# [OBJECTID[https://www.rfc-editor.org/rfc/rfc8474]]::
|
695
756
|
# Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
|
696
757
|
# RFC 8474, DOI 10.17487/RFC8474, September 2018,
|
697
758
|
# <https://www.rfc-editor.org/info/rfc8474>.
|
759
|
+
# [PARTIAL[https://www.rfc-editor.org/info/rfc9394]]::
|
760
|
+
# Melnikov, A., Achuthan, A., Nagulakonda, V., and L. Alves,
|
761
|
+
# "IMAP PARTIAL Extension for Paged SEARCH and FETCH", RFC 9394,
|
762
|
+
# DOI 10.17487/RFC9394, June 2023,
|
763
|
+
# <https://www.rfc-editor.org/info/rfc9394>.
|
764
|
+
# [UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.pdf]]::
|
765
|
+
# Melnikov, A., Achuthan, A., Nagulakonda, V., Singh, A., and L. Alves,
|
766
|
+
# "\IMAP Extension for Using and Returning Unique Identifiers (UIDs) Only",
|
767
|
+
# RFC 9586, DOI 10.17487/RFC9586, May 2024,
|
768
|
+
# <https://www.rfc-editor.org/info/rfc9586>.
|
698
769
|
#
|
699
770
|
# === IANA registries
|
700
771
|
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
|
@@ -708,7 +779,7 @@ module Net
|
|
708
779
|
# * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
|
709
780
|
# +imap+
|
710
781
|
# * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
711
|
-
#
|
782
|
+
# ==== For currently unsupported features:
|
712
783
|
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
713
784
|
# * {LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
|
714
785
|
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
|
@@ -717,7 +788,7 @@ module Net
|
|
717
788
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
718
789
|
#
|
719
790
|
class IMAP < Protocol
|
720
|
-
VERSION = "0.
|
791
|
+
VERSION = "0.5.8"
|
721
792
|
|
722
793
|
# Aliases for supported capabilities, to be used with the #enable command.
|
723
794
|
ENABLE_ALIASES = {
|
@@ -725,9 +796,12 @@ module Net
|
|
725
796
|
"UTF8=ONLY" => "UTF8=ACCEPT",
|
726
797
|
}.freeze
|
727
798
|
|
728
|
-
|
729
|
-
autoload :
|
730
|
-
autoload :
|
799
|
+
dir = File.expand_path("imap", __dir__)
|
800
|
+
autoload :ConnectionState, "#{dir}/connection_state"
|
801
|
+
autoload :ResponseReader, "#{dir}/response_reader"
|
802
|
+
autoload :SASL, "#{dir}/sasl"
|
803
|
+
autoload :SASLAdapter, "#{dir}/sasl_adapter"
|
804
|
+
autoload :StringPrep, "#{dir}/stringprep"
|
731
805
|
|
732
806
|
include MonitorMixin
|
733
807
|
if defined?(OpenSSL::SSL)
|
@@ -735,14 +809,17 @@ module Net
|
|
735
809
|
include SSL
|
736
810
|
end
|
737
811
|
|
738
|
-
# Returns the
|
739
|
-
def self.
|
740
|
-
|
741
|
-
|
812
|
+
# Returns the global Config object
|
813
|
+
def self.config; Config.global end
|
814
|
+
|
815
|
+
# Returns the global debug mode.
|
816
|
+
# Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
|
817
|
+
def self.debug; config.debug end
|
742
818
|
|
743
|
-
# Sets the debug mode.
|
819
|
+
# Sets the global debug mode.
|
820
|
+
# Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
|
744
821
|
def self.debug=(val)
|
745
|
-
|
822
|
+
config.debug = val
|
746
823
|
end
|
747
824
|
|
748
825
|
# The default port for IMAP connections, port 143
|
@@ -761,16 +838,37 @@ module Net
|
|
761
838
|
alias default_ssl_port default_tls_port
|
762
839
|
end
|
763
840
|
|
764
|
-
# Returns the initial greeting the server, an UntaggedResponse.
|
841
|
+
# Returns the initial greeting sent by the server, an UntaggedResponse.
|
765
842
|
attr_reader :greeting
|
766
843
|
|
767
|
-
#
|
768
|
-
#
|
769
|
-
#
|
770
|
-
|
844
|
+
# The client configuration. See Net::IMAP::Config.
|
845
|
+
#
|
846
|
+
# By default, the client's local configuration inherits from the global
|
847
|
+
# Net::IMAP.config.
|
848
|
+
attr_reader :config
|
849
|
+
|
850
|
+
##
|
851
|
+
# :attr_reader: open_timeout
|
852
|
+
# Seconds to wait until a connection is opened. Also used by #starttls.
|
853
|
+
# Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout].
|
771
854
|
|
855
|
+
##
|
856
|
+
# :attr_reader: idle_response_timeout
|
772
857
|
# Seconds to wait until an IDLE response is received.
|
773
|
-
|
858
|
+
# Delegates to {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout].
|
859
|
+
|
860
|
+
##
|
861
|
+
# :attr_accessor: max_response_size
|
862
|
+
#
|
863
|
+
# The maximum allowed server response size, in bytes.
|
864
|
+
# Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
|
865
|
+
|
866
|
+
# :stopdoc:
|
867
|
+
def open_timeout; config.open_timeout end
|
868
|
+
def idle_response_timeout; config.idle_response_timeout end
|
869
|
+
def max_response_size; config.max_response_size end
|
870
|
+
def max_response_size=(val) config.max_response_size = val end
|
871
|
+
# :startdoc:
|
774
872
|
|
775
873
|
# The hostname this client connected to
|
776
874
|
attr_reader :host
|
@@ -793,6 +891,67 @@ module Net
|
|
793
891
|
# Returns +false+ for a plaintext connection.
|
794
892
|
attr_reader :ssl_ctx_params
|
795
893
|
|
894
|
+
# Returns the current connection state.
|
895
|
+
#
|
896
|
+
# Once an IMAP connection is established, the connection is in one of four
|
897
|
+
# states: +not_authenticated+, +authenticated+, +selected+, and +logout+.
|
898
|
+
# Most commands are valid only in certain states.
|
899
|
+
#
|
900
|
+
# The connection state object responds to +to_sym+ and +name+ with the name
|
901
|
+
# of the current connection state, as a Symbol or String. Future versions
|
902
|
+
# of +net-imap+ may store additional information on the state object.
|
903
|
+
#
|
904
|
+
# From {RFC9051}[https://www.rfc-editor.org/rfc/rfc9051#section-3]:
|
905
|
+
# +----------------------+
|
906
|
+
# |connection established|
|
907
|
+
# +----------------------+
|
908
|
+
# ||
|
909
|
+
# \/
|
910
|
+
# +--------------------------------------+
|
911
|
+
# | server greeting |
|
912
|
+
# +--------------------------------------+
|
913
|
+
# || (1) || (2) || (3)
|
914
|
+
# \/ || ||
|
915
|
+
# +-----------------+ || ||
|
916
|
+
# |Not Authenticated| || ||
|
917
|
+
# +-----------------+ || ||
|
918
|
+
# || (7) || (4) || ||
|
919
|
+
# || \/ \/ ||
|
920
|
+
# || +----------------+ ||
|
921
|
+
# || | Authenticated |<=++ ||
|
922
|
+
# || +----------------+ || ||
|
923
|
+
# || || (7) || (5) || (6) ||
|
924
|
+
# || || \/ || ||
|
925
|
+
# || || +--------+ || ||
|
926
|
+
# || || |Selected|==++ ||
|
927
|
+
# || || +--------+ ||
|
928
|
+
# || || || (7) ||
|
929
|
+
# \/ \/ \/ \/
|
930
|
+
# +--------------------------------------+
|
931
|
+
# | Logout |
|
932
|
+
# +--------------------------------------+
|
933
|
+
# ||
|
934
|
+
# \/
|
935
|
+
# +-------------------------------+
|
936
|
+
# |both sides close the connection|
|
937
|
+
# +-------------------------------+
|
938
|
+
#
|
939
|
+
# >>>
|
940
|
+
# Legend for the above diagram:
|
941
|
+
#
|
942
|
+
# 1. connection without pre-authentication (+OK+ #greeting)
|
943
|
+
# 2. pre-authenticated connection (+PREAUTH+ #greeting)
|
944
|
+
# 3. rejected connection (+BYE+ #greeting)
|
945
|
+
# 4. successful #login or #authenticate command
|
946
|
+
# 5. successful #select or #examine command
|
947
|
+
# 6. #close or #unselect command, unsolicited +CLOSED+ response code, or
|
948
|
+
# failed #select or #examine command
|
949
|
+
# 7. #logout command, server shutdown, or connection closed
|
950
|
+
#
|
951
|
+
# Before the server greeting, the state is +not_authenticated+.
|
952
|
+
# After the connection closes, the state remains +logout+.
|
953
|
+
attr_reader :connection_state
|
954
|
+
|
796
955
|
# Creates a new Net::IMAP object and connects it to the specified
|
797
956
|
# +host+.
|
798
957
|
#
|
@@ -809,18 +968,50 @@ module Net
|
|
809
968
|
# If +ssl+ is a hash, it's passed to
|
810
969
|
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
|
811
970
|
# the keys are names of attribute assignment methods on
|
812
|
-
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
|
971
|
+
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]. For example:
|
972
|
+
#
|
973
|
+
# [{ca_file}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_file]]
|
974
|
+
# The path to a file containing a PEM-format CA certificate.
|
975
|
+
# [{ca_path}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_path]]
|
976
|
+
# The path to a directory containing CA certificates in PEM format.
|
977
|
+
# [{min_version}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D]]
|
978
|
+
# Sets the lower bound on the supported SSL/TLS protocol version. Set to
|
979
|
+
# an +OpenSSL+ constant such as +OpenSSL::SSL::TLS1_2_VERSION+,
|
980
|
+
# [{verify_mode}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-verify_mode]]
|
981
|
+
# SSL session verification mode. Valid modes include
|
982
|
+
# +OpenSSL::SSL::VERIFY_PEER+ and +OpenSSL::SSL::VERIFY_NONE+.
|
983
|
+
#
|
984
|
+
# See {OpenSSL::SSL::SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html] for other valid SSL context params.
|
985
|
+
#
|
986
|
+
# See DeprecatedClientOptions.new for deprecated SSL arguments.
|
987
|
+
#
|
988
|
+
# [response_handlers]
|
989
|
+
# A list of response handlers to be added before the receiver thread is
|
990
|
+
# started. This ensures every server response is handled, including the
|
991
|
+
# #greeting. Note that the greeting is handled in the current thread, but
|
992
|
+
# all other responses are handled in the receiver thread.
|
993
|
+
#
|
994
|
+
# [config]
|
995
|
+
# A Net::IMAP::Config object to use as the basis for #config. By default,
|
996
|
+
# the global Net::IMAP.config is used.
|
997
|
+
#
|
998
|
+
# >>>
|
999
|
+
# *NOTE:* +config+ does not set #config directly---it sets the _parent_
|
1000
|
+
# config for inheritance. Every client creates its own unique #config.
|
813
1001
|
#
|
814
|
-
#
|
815
|
-
#
|
816
|
-
# [idle_response_timeout]
|
817
|
-
# Seconds to wait until an IDLE response is received
|
1002
|
+
# All other keyword arguments are forwarded to Net::IMAP::Config.new, to
|
1003
|
+
# initialize the client's #config. For example:
|
818
1004
|
#
|
819
|
-
#
|
1005
|
+
# [{open_timeout}[rdoc-ref:Config#open_timeout]]
|
1006
|
+
# Seconds to wait until a connection is opened
|
1007
|
+
# [{idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]]
|
1008
|
+
# Seconds to wait until an IDLE response is received
|
1009
|
+
#
|
1010
|
+
# See Net::IMAP::Config for other valid options.
|
820
1011
|
#
|
821
1012
|
# ==== Examples
|
822
1013
|
#
|
823
|
-
# Connect to cleartext port 143 at mail.example.com and
|
1014
|
+
# Connect to cleartext port 143 at mail.example.com and receive the server greeting:
|
824
1015
|
# imap = Net::IMAP.new('mail.example.com', ssl: false) # => #<Net::IMAP:0x00007f79b0872bd0>
|
825
1016
|
# imap.port => 143
|
826
1017
|
# imap.tls_verified? => false
|
@@ -871,14 +1062,13 @@ module Net
|
|
871
1062
|
# [Net::IMAP::ByeResponseError]
|
872
1063
|
# Connected to the host successfully, but it immediately said goodbye.
|
873
1064
|
#
|
874
|
-
def initialize(host, port: nil, ssl:
|
875
|
-
|
1065
|
+
def initialize(host, port: nil, ssl: nil, response_handlers: nil,
|
1066
|
+
config: Config.global, **config_options)
|
876
1067
|
super()
|
877
1068
|
# Config options
|
878
1069
|
@host = host
|
1070
|
+
@config = Config.new(config, **config_options)
|
879
1071
|
@port = port || (ssl ? SSL_PORT : PORT)
|
880
|
-
@open_timeout = Integer(open_timeout)
|
881
|
-
@idle_response_timeout = Integer(idle_response_timeout)
|
882
1072
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
|
883
1073
|
|
884
1074
|
# Basic Client State
|
@@ -887,14 +1077,17 @@ module Net
|
|
887
1077
|
@exception = nil
|
888
1078
|
@greeting = nil
|
889
1079
|
@capabilities = nil
|
1080
|
+
@tls_verified = false
|
1081
|
+
@connection_state = ConnectionState::NotAuthenticated.new
|
890
1082
|
|
891
|
-
# Client Protocol
|
892
|
-
@parser = ResponseParser.new
|
1083
|
+
# Client Protocol Receiver
|
1084
|
+
@parser = ResponseParser.new(config: @config)
|
893
1085
|
@responses = Hash.new {|h, k| h[k] = [] }
|
894
1086
|
@response_handlers = []
|
895
1087
|
@receiver_thread = nil
|
896
1088
|
@receiver_thread_exception = nil
|
897
1089
|
@receiver_thread_terminating = false
|
1090
|
+
response_handlers&.each do add_response_handler(_1) end
|
898
1091
|
|
899
1092
|
# Client Protocol Sender (including state for currently running commands)
|
900
1093
|
@tag_prefix = "RUBY"
|
@@ -908,13 +1101,10 @@ module Net
|
|
908
1101
|
@logout_command_tag = nil
|
909
1102
|
|
910
1103
|
# Connection
|
911
|
-
@tls_verified = false
|
912
1104
|
@sock = tcp_socket(@host, @port)
|
1105
|
+
@reader = ResponseReader.new(self, @sock)
|
913
1106
|
start_tls_session if ssl_ctx
|
914
1107
|
start_imap_connection
|
915
|
-
|
916
|
-
# DEPRECATED: to remove in next version
|
917
|
-
@client_thread = Thread.current
|
918
1108
|
end
|
919
1109
|
|
920
1110
|
# Returns true after the TLS negotiation has completed and the remote
|
@@ -922,16 +1112,12 @@ module Net
|
|
922
1112
|
# but peer verification was disabled.
|
923
1113
|
def tls_verified?; @tls_verified end
|
924
1114
|
|
925
|
-
def client_thread # :nodoc:
|
926
|
-
warn "Net::IMAP#client_thread is deprecated and will be removed soon."
|
927
|
-
@client_thread
|
928
|
-
end
|
929
|
-
|
930
1115
|
# Disconnects from the server.
|
931
1116
|
#
|
932
1117
|
# Related: #logout, #logout!
|
933
1118
|
def disconnect
|
934
1119
|
return if disconnected?
|
1120
|
+
state_logout!
|
935
1121
|
begin
|
936
1122
|
begin
|
937
1123
|
# try to call SSL::SSLSocket#io.
|
@@ -1095,12 +1281,12 @@ module Net
|
|
1095
1281
|
# )
|
1096
1282
|
# end
|
1097
1283
|
#
|
1098
|
-
# See [ID[https://
|
1284
|
+
# See [ID[https://www.rfc-editor.org/rfc/rfc2971]] for field definitions.
|
1099
1285
|
#
|
1100
|
-
#
|
1286
|
+
# ==== Capabilities
|
1101
1287
|
#
|
1102
1288
|
# The server's capabilities must include +ID+
|
1103
|
-
# [RFC2971[https://
|
1289
|
+
# [RFC2971[https://www.rfc-editor.org/rfc/rfc2971]].
|
1104
1290
|
def id(client_id=nil)
|
1105
1291
|
synchronize do
|
1106
1292
|
send_command("ID", ClientID.new(client_id))
|
@@ -1170,6 +1356,10 @@ module Net
|
|
1170
1356
|
# both successful. Any error indicates that the connection has not been
|
1171
1357
|
# secured.
|
1172
1358
|
#
|
1359
|
+
# After the server agrees to start a TLS connection, this method waits up to
|
1360
|
+
# {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
|
1361
|
+
# +Net::OpenTimeout+.
|
1362
|
+
#
|
1173
1363
|
# *Note:*
|
1174
1364
|
# >>>
|
1175
1365
|
# Any #response_handlers added before STARTTLS should be aware that the
|
@@ -1179,7 +1369,7 @@ module Net
|
|
1179
1369
|
#
|
1180
1370
|
# Related: Net::IMAP.new, #login, #authenticate
|
1181
1371
|
#
|
1182
|
-
#
|
1372
|
+
# ==== Capability
|
1183
1373
|
# Clients should not call #starttls unless the server advertises the
|
1184
1374
|
# +STARTTLS+ capability.
|
1185
1375
|
#
|
@@ -1188,17 +1378,25 @@ module Net
|
|
1188
1378
|
#
|
1189
1379
|
def starttls(**options)
|
1190
1380
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
1191
|
-
|
1381
|
+
error = nil
|
1382
|
+
ok = send_command("STARTTLS") do |resp|
|
1192
1383
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
1193
1384
|
clear_cached_capabilities
|
1194
1385
|
clear_responses
|
1195
1386
|
start_tls_session
|
1196
1387
|
end
|
1388
|
+
rescue Exception => error
|
1389
|
+
raise # note that the error backtrace is in the receiver_thread
|
1197
1390
|
end
|
1391
|
+
if error
|
1392
|
+
disconnect
|
1393
|
+
raise error
|
1394
|
+
end
|
1395
|
+
ok
|
1198
1396
|
end
|
1199
1397
|
|
1200
1398
|
# :call-seq:
|
1201
|
-
# authenticate(mechanism, *, sasl_ir:
|
1399
|
+
# authenticate(mechanism, *, sasl_ir: config.sasl_ir, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
|
1202
1400
|
#
|
1203
1401
|
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
|
1204
1402
|
# to authenticate the client. If successful, the connection enters the
|
@@ -1207,7 +1405,11 @@ module Net
|
|
1207
1405
|
# +mechanism+ is the name of the \SASL authentication mechanism to be used.
|
1208
1406
|
#
|
1209
1407
|
# +sasl_ir+ allows or disallows sending an "initial response" (see the
|
1210
|
-
# +SASL-IR+ capability, below).
|
1408
|
+
# +SASL-IR+ capability, below). Defaults to the #config value for
|
1409
|
+
# {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
|
1410
|
+
#
|
1411
|
+
# The +registry+ kwarg can be used to select the mechanism implementation
|
1412
|
+
# from a custom registry. See SASL.authenticator and SASL::Authenticators.
|
1211
1413
|
#
|
1212
1414
|
# All other arguments are forwarded to the registered SASL authenticator for
|
1213
1415
|
# the requested mechanism. <em>The documentation for each individual
|
@@ -1303,27 +1505,9 @@ module Net
|
|
1303
1505
|
# Previously cached #capabilities will be cleared when this method
|
1304
1506
|
# completes. If the TaggedResponse to #authenticate includes updated
|
1305
1507
|
# capabilities, they will be cached.
|
1306
|
-
def authenticate(
|
1307
|
-
|
1308
|
-
|
1309
|
-
cmdargs = ["AUTHENTICATE", mechanism]
|
1310
|
-
if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
|
1311
|
-
authenticator.respond_to?(:initial_response?) &&
|
1312
|
-
authenticator.initial_response?
|
1313
|
-
response = authenticator.process(nil)
|
1314
|
-
cmdargs << (response.empty? ? "=" : [response].pack("m0"))
|
1315
|
-
end
|
1316
|
-
result = send_command_with_continuations(*cmdargs) {|data|
|
1317
|
-
challenge = data.unpack1("m")
|
1318
|
-
response = authenticator.process challenge
|
1319
|
-
[response].pack("m0")
|
1320
|
-
}
|
1321
|
-
if authenticator.respond_to?(:done?) && !authenticator.done?
|
1322
|
-
logout!
|
1323
|
-
raise SASL::AuthenticationIncomplete, result
|
1324
|
-
end
|
1325
|
-
@capabilities = capabilities_from_resp_code result
|
1326
|
-
result
|
1508
|
+
def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
|
1509
|
+
sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
|
1510
|
+
.tap do state_authenticated! _1 end
|
1327
1511
|
end
|
1328
1512
|
|
1329
1513
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1340,16 +1524,12 @@ module Net
|
|
1340
1524
|
#
|
1341
1525
|
# Related: #authenticate, #starttls
|
1342
1526
|
#
|
1343
|
-
#
|
1527
|
+
# ==== Capabilities
|
1344
1528
|
#
|
1345
1529
|
# An IMAP client MUST NOT call #login when the server advertises the
|
1346
|
-
# +LOGINDISABLED+ capability.
|
1347
|
-
#
|
1348
|
-
#
|
1349
|
-
# raise "Remote server has disabled the login command"
|
1350
|
-
# else
|
1351
|
-
# imap.login username, password
|
1352
|
-
# end
|
1530
|
+
# +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
|
1531
|
+
# LoginDisabledError when that capability is present. See
|
1532
|
+
# Config#enforce_logindisabled.
|
1353
1533
|
#
|
1354
1534
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1355
1535
|
# Cached capabilities _must_ be invalidated after this method completes.
|
@@ -1357,8 +1537,11 @@ module Net
|
|
1357
1537
|
# ResponseCode.
|
1358
1538
|
#
|
1359
1539
|
def login(user, password)
|
1540
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
1541
|
+
raise LoginDisabledError
|
1542
|
+
end
|
1360
1543
|
send_command("LOGIN", user, password)
|
1361
|
-
.tap
|
1544
|
+
.tap do state_authenticated! _1 end
|
1362
1545
|
end
|
1363
1546
|
|
1364
1547
|
# Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
|
@@ -1382,7 +1565,7 @@ module Net
|
|
1382
1565
|
#
|
1383
1566
|
# Related: #examine
|
1384
1567
|
#
|
1385
|
-
#
|
1568
|
+
# ==== Capabilities
|
1386
1569
|
#
|
1387
1570
|
# If [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] is supported,
|
1388
1571
|
# the server may return an untagged "NO" response with a "UIDNOTSTICKY"
|
@@ -1398,8 +1581,10 @@ module Net
|
|
1398
1581
|
args = ["SELECT", mailbox]
|
1399
1582
|
args << ["CONDSTORE"] if condstore
|
1400
1583
|
synchronize do
|
1584
|
+
state_unselected! # implicitly closes current mailbox
|
1401
1585
|
@responses.clear
|
1402
1586
|
send_command(*args)
|
1587
|
+
.tap do state_selected! end
|
1403
1588
|
end
|
1404
1589
|
end
|
1405
1590
|
|
@@ -1416,8 +1601,10 @@ module Net
|
|
1416
1601
|
args = ["EXAMINE", mailbox]
|
1417
1602
|
args << ["CONDSTORE"] if condstore
|
1418
1603
|
synchronize do
|
1604
|
+
state_unselected! # implicitly closes current mailbox
|
1419
1605
|
@responses.clear
|
1420
1606
|
send_command(*args)
|
1607
|
+
.tap do state_selected! end
|
1421
1608
|
end
|
1422
1609
|
end
|
1423
1610
|
|
@@ -1500,7 +1687,7 @@ module Net
|
|
1500
1687
|
#
|
1501
1688
|
# Related: #lsub, MailboxList
|
1502
1689
|
#
|
1503
|
-
#
|
1690
|
+
# ==== For example:
|
1504
1691
|
#
|
1505
1692
|
# imap.create("foo/bar")
|
1506
1693
|
# imap.create("foo/baz")
|
@@ -1538,7 +1725,7 @@ module Net
|
|
1538
1725
|
# servers, then folder creation (and listing, moving, etc) can lead to
|
1539
1726
|
# errors.
|
1540
1727
|
#
|
1541
|
-
# From RFC2342[https://
|
1728
|
+
# From RFC2342[https://www.rfc-editor.org/rfc/rfc2342]:
|
1542
1729
|
# >>>
|
1543
1730
|
# <em>Although typically a server will support only a single Personal
|
1544
1731
|
# Namespace, and a single Other User's Namespace, circumstances exist
|
@@ -1551,7 +1738,7 @@ module Net
|
|
1551
1738
|
#
|
1552
1739
|
# Related: #list, Namespaces, Namespace
|
1553
1740
|
#
|
1554
|
-
#
|
1741
|
+
# ==== For example:
|
1555
1742
|
#
|
1556
1743
|
# if capable?("NAMESPACE")
|
1557
1744
|
# namespaces = imap.namespace
|
@@ -1565,10 +1752,10 @@ module Net
|
|
1565
1752
|
# end
|
1566
1753
|
# end
|
1567
1754
|
#
|
1568
|
-
#
|
1755
|
+
# ==== Capabilities
|
1569
1756
|
#
|
1570
|
-
# The server's capabilities must include +NAMESPACE+
|
1571
|
-
# [RFC2342[https://
|
1757
|
+
# The server's capabilities must include either +IMAP4rev2+ or +NAMESPACE+
|
1758
|
+
# [RFC2342[https://www.rfc-editor.org/rfc/rfc2342]].
|
1572
1759
|
def namespace
|
1573
1760
|
synchronize do
|
1574
1761
|
send_command("NAMESPACE")
|
@@ -1604,7 +1791,7 @@ module Net
|
|
1604
1791
|
#
|
1605
1792
|
# Related: #list, MailboxList
|
1606
1793
|
#
|
1607
|
-
#
|
1794
|
+
# ==== Capabilities
|
1608
1795
|
#
|
1609
1796
|
# The server's capabilities must include +XLIST+,
|
1610
1797
|
# a deprecated Gmail extension (replaced by +SPECIAL-USE+).
|
@@ -1627,10 +1814,10 @@ module Net
|
|
1627
1814
|
#
|
1628
1815
|
# Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
|
1629
1816
|
#
|
1630
|
-
#
|
1817
|
+
# ==== Capabilities
|
1631
1818
|
#
|
1632
1819
|
# The server's capabilities must include +QUOTA+
|
1633
|
-
# [RFC2087[https://
|
1820
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1634
1821
|
def getquotaroot(mailbox)
|
1635
1822
|
synchronize do
|
1636
1823
|
send_command("GETQUOTAROOT", mailbox)
|
@@ -1648,10 +1835,10 @@ module Net
|
|
1648
1835
|
#
|
1649
1836
|
# Related: #getquotaroot, #setquota, MailboxQuota
|
1650
1837
|
#
|
1651
|
-
#
|
1838
|
+
# ==== Capabilities
|
1652
1839
|
#
|
1653
1840
|
# The server's capabilities must include +QUOTA+
|
1654
|
-
# [RFC2087[https://
|
1841
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1655
1842
|
def getquota(mailbox)
|
1656
1843
|
synchronize do
|
1657
1844
|
send_command("GETQUOTA", mailbox)
|
@@ -1666,10 +1853,10 @@ module Net
|
|
1666
1853
|
#
|
1667
1854
|
# Related: #getquota, #getquotaroot
|
1668
1855
|
#
|
1669
|
-
#
|
1856
|
+
# ==== Capabilities
|
1670
1857
|
#
|
1671
1858
|
# The server's capabilities must include +QUOTA+
|
1672
|
-
# [RFC2087[https://
|
1859
|
+
# [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
|
1673
1860
|
def setquota(mailbox, quota)
|
1674
1861
|
if quota.nil?
|
1675
1862
|
data = '()'
|
@@ -1686,10 +1873,10 @@ module Net
|
|
1686
1873
|
#
|
1687
1874
|
# Related: #getacl
|
1688
1875
|
#
|
1689
|
-
#
|
1876
|
+
# ==== Capabilities
|
1690
1877
|
#
|
1691
1878
|
# The server's capabilities must include +ACL+
|
1692
|
-
# [RFC4314[https://
|
1879
|
+
# [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
|
1693
1880
|
def setacl(mailbox, user, rights)
|
1694
1881
|
if rights.nil?
|
1695
1882
|
send_command("SETACL", mailbox, user, "")
|
@@ -1704,10 +1891,10 @@ module Net
|
|
1704
1891
|
#
|
1705
1892
|
# Related: #setacl, MailboxACLItem
|
1706
1893
|
#
|
1707
|
-
#
|
1894
|
+
# ==== Capabilities
|
1708
1895
|
#
|
1709
1896
|
# The server's capabilities must include +ACL+
|
1710
|
-
# [RFC4314[https://
|
1897
|
+
# [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
|
1711
1898
|
def getacl(mailbox)
|
1712
1899
|
synchronize do
|
1713
1900
|
send_command("GETACL", mailbox)
|
@@ -1741,7 +1928,7 @@ module Net
|
|
1741
1928
|
# for +mailbox+ cannot be returned; for instance, because it
|
1742
1929
|
# does not exist.
|
1743
1930
|
#
|
1744
|
-
#
|
1931
|
+
# ==== Supported attributes
|
1745
1932
|
#
|
1746
1933
|
# +MESSAGES+:: The number of messages in the mailbox.
|
1747
1934
|
#
|
@@ -1772,12 +1959,12 @@ module Net
|
|
1772
1959
|
# Unsupported attributes may be requested. The attribute value will be
|
1773
1960
|
# either an Integer or an ExtensionData object.
|
1774
1961
|
#
|
1775
|
-
#
|
1962
|
+
# ==== For example:
|
1776
1963
|
#
|
1777
1964
|
# p imap.status("inbox", ["MESSAGES", "RECENT"])
|
1778
1965
|
# #=> {"RECENT"=>0, "MESSAGES"=>44}
|
1779
1966
|
#
|
1780
|
-
#
|
1967
|
+
# ==== Capabilities
|
1781
1968
|
#
|
1782
1969
|
# +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
|
1783
1970
|
# <tt>STATUS=SIZE</tt>
|
@@ -1817,7 +2004,7 @@ module Net
|
|
1817
2004
|
# not exist (it is not created automatically), or if the flags,
|
1818
2005
|
# date_time, or message arguments contain errors.
|
1819
2006
|
#
|
1820
|
-
#
|
2007
|
+
# ==== Capabilities
|
1821
2008
|
#
|
1822
2009
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
1823
2010
|
# supported and the destination supports persistent UIDs, the server's
|
@@ -1856,6 +2043,7 @@ module Net
|
|
1856
2043
|
# Related: #unselect
|
1857
2044
|
def close
|
1858
2045
|
send_command("CLOSE")
|
2046
|
+
.tap do state_authenticated! end
|
1859
2047
|
end
|
1860
2048
|
|
1861
2049
|
# Sends an {UNSELECT command [RFC3691 §2]}[https://www.rfc-editor.org/rfc/rfc3691#section-3]
|
@@ -1866,129 +2054,493 @@ module Net
|
|
1866
2054
|
#
|
1867
2055
|
# Related: #close
|
1868
2056
|
#
|
1869
|
-
#
|
2057
|
+
# ==== Capabilities
|
1870
2058
|
#
|
1871
|
-
# The server's capabilities must include +UNSELECT+
|
1872
|
-
# [RFC3691[https://
|
2059
|
+
# The server's capabilities must include either +IMAP4rev2+ or +UNSELECT+
|
2060
|
+
# [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
|
1873
2061
|
def unselect
|
1874
2062
|
send_command("UNSELECT")
|
2063
|
+
.tap do state_authenticated! end
|
1875
2064
|
end
|
1876
2065
|
|
2066
|
+
# call-seq:
|
2067
|
+
# expunge -> array of message sequence numbers
|
2068
|
+
# expunge -> VanishedData of UIDs
|
2069
|
+
#
|
1877
2070
|
# Sends an {EXPUNGE command [IMAP4rev1 §6.4.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.3]
|
1878
|
-
#
|
1879
|
-
# selected mailbox
|
2071
|
+
# to permanently remove all messages with the +\Deleted+ flag from the
|
2072
|
+
# currently selected mailbox.
|
2073
|
+
#
|
2074
|
+
# Returns either an array of expunged message <em>sequence numbers</em> or
|
2075
|
+
# (when the appropriate capability is enabled) VanishedData of expunged
|
2076
|
+
# UIDs. Previously unhandled +EXPUNGE+ or +VANISHED+ responses are merged
|
2077
|
+
# with the direct response to this command. <tt>VANISHED (EARLIER)</tt>
|
2078
|
+
# responses will _not_ be merged.
|
2079
|
+
#
|
2080
|
+
# When no messages have been expunged, an empty array is returned,
|
2081
|
+
# regardless of which extensions are enabled. In a future release, an empty
|
2082
|
+
# VanishedData may be returned, based on the currently enabled extensions.
|
1880
2083
|
#
|
1881
2084
|
# Related: #uid_expunge
|
2085
|
+
#
|
2086
|
+
# ==== Capabilities
|
2087
|
+
#
|
2088
|
+
# When either QRESYNC[https://www.rfc-editor.org/rfc/rfc7162] or
|
2089
|
+
# UIDONLY[https://www.rfc-editor.org/rfc/rfc9586] are enabled, #expunge
|
2090
|
+
# returns VanishedData, which contains UIDs---<em>not message sequence
|
2091
|
+
# numbers</em>.
|
1882
2092
|
def expunge
|
1883
|
-
|
1884
|
-
send_command("EXPUNGE")
|
1885
|
-
clear_responses("EXPUNGE")
|
1886
|
-
end
|
2093
|
+
expunge_internal("EXPUNGE")
|
1887
2094
|
end
|
1888
2095
|
|
2096
|
+
# call-seq:
|
2097
|
+
# uid_expunge{uid_set) -> array of message sequence numbers
|
2098
|
+
# uid_expunge{uid_set) -> VanishedData of UIDs
|
2099
|
+
#
|
1889
2100
|
# Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
|
1890
2101
|
# {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
|
1891
2102
|
# to permanently remove all messages that have both the <tt>\\Deleted</tt>
|
1892
2103
|
# flag set and a UID that is included in +uid_set+.
|
1893
2104
|
#
|
2105
|
+
# Returns the same result type as #expunge.
|
2106
|
+
#
|
1894
2107
|
# By using #uid_expunge instead of #expunge when resynchronizing with
|
1895
2108
|
# the server, the client can ensure that it does not inadvertantly
|
1896
2109
|
# remove any messages that have been marked as <tt>\\Deleted</tt> by other
|
1897
2110
|
# clients between the time that the client was last connected and
|
1898
2111
|
# the time the client resynchronizes.
|
1899
2112
|
#
|
1900
|
-
# *Note:*
|
1901
|
-
# >>>
|
1902
|
-
# Although the command takes a set of UIDs for its argument, the
|
1903
|
-
# server still returns regular EXPUNGE responses, which contain
|
1904
|
-
# a <em>sequence number</em>. These will be deleted from
|
1905
|
-
# #responses and this method returns them as an array of
|
1906
|
-
# <em>sequence number</em> integers.
|
1907
|
-
#
|
1908
2113
|
# Related: #expunge
|
1909
2114
|
#
|
1910
|
-
#
|
2115
|
+
# ==== Capabilities
|
1911
2116
|
#
|
1912
|
-
# The server's capabilities must include +UIDPLUS+
|
2117
|
+
# The server's capabilities must include either +IMAP4rev2+ or +UIDPLUS+
|
1913
2118
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
2119
|
+
#
|
2120
|
+
# Otherwise, #uid_expunge is updated by extensions in the same way as
|
2121
|
+
# #expunge.
|
1914
2122
|
def uid_expunge(uid_set)
|
1915
|
-
|
1916
|
-
send_command("UID EXPUNGE", MessageSet.new(uid_set))
|
1917
|
-
clear_responses("EXPUNGE")
|
1918
|
-
end
|
2123
|
+
expunge_internal("UID EXPUNGE", SequenceSet.new(uid_set))
|
1919
2124
|
end
|
1920
2125
|
|
1921
|
-
#
|
1922
|
-
#
|
1923
|
-
#
|
1924
|
-
# string holding the entire search string, or a single-dimension array of
|
1925
|
-
# search keywords and arguments.
|
2126
|
+
# :call-seq:
|
2127
|
+
# search(criteria, charset = nil) -> result
|
2128
|
+
# search(criteria, charset: nil, return: nil) -> result
|
1926
2129
|
#
|
1927
|
-
#
|
1928
|
-
#
|
1929
|
-
#
|
2130
|
+
# Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
|
2131
|
+
# to search the mailbox for messages that match the given search +criteria+,
|
2132
|
+
# and returns either a SearchResult or an ESearchResult. SearchResult
|
2133
|
+
# inherits from Array (for backward compatibility) but adds
|
2134
|
+
# SearchResult#modseq when the +CONDSTORE+ capability has been enabled.
|
2135
|
+
# ESearchResult also implements {#to_a}[rdoc-ref:ESearchResult#to_a], for
|
2136
|
+
# compatibility with SearchResult.
|
2137
|
+
#
|
2138
|
+
# +criteria+ is one or more search keys and their arguments, which may be
|
2139
|
+
# provided as an array or a string.
|
2140
|
+
# See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
|
2141
|
+
# and {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
|
2142
|
+
#
|
2143
|
+
# +return+ options control what kind of information is returned about
|
2144
|
+
# messages matching the search +criteria+. Specifying +return+ should force
|
2145
|
+
# the server to return an ESearchResult instead of a SearchResult, but some
|
2146
|
+
# servers disobey this requirement. <em>Requires an extended search
|
2147
|
+
# capability, such as +ESEARCH+ or +IMAP4rev2+.</em>
|
2148
|
+
# See {"Argument translation"}[rdoc-ref:#search@Argument+translation] and
|
2149
|
+
# {"Supported return options"}[rdoc-ref:#search@Supported+return+options],
|
2150
|
+
# below.
|
2151
|
+
#
|
2152
|
+
# +charset+ is the name of the {registered character
|
2153
|
+
# set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
2154
|
+
# used by strings in the search +criteria+. When +charset+ isn't specified,
|
2155
|
+
# either <tt>"US-ASCII"</tt> or <tt>"UTF-8"</tt> is assumed, depending on
|
2156
|
+
# the server's capabilities.
|
2157
|
+
#
|
2158
|
+
# _NOTE:_ Return options and charset may be sent as part of +criteria+. Do
|
2159
|
+
# not use the +return+ or +charset+ arguments when either return options or
|
2160
|
+
# charset are embedded in +criteria+.
|
1930
2161
|
#
|
1931
2162
|
# Related: #uid_search
|
1932
2163
|
#
|
1933
|
-
#
|
2164
|
+
# ==== For example:
|
1934
2165
|
#
|
1935
|
-
#
|
2166
|
+
# imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
|
2167
|
+
# #=> [1, 6, 7, 8]
|
2168
|
+
#
|
2169
|
+
# The following assumes the server supports +ESEARCH+ and +CONDSTORE+:
|
2170
|
+
#
|
2171
|
+
# result = imap.uid_search(["UID", 12345.., "MODSEQ", 620_162_338],
|
2172
|
+
# return: %w(all count min max))
|
2173
|
+
# # => #<data Net::IMAP::ESearchResult tag="RUBY0123", uid=true,
|
2174
|
+
# # data=[["ALL", Net::IMAP::SequenceSet["12346:12349,22222:22230"]],
|
2175
|
+
# # ["COUNT", 13], ["MIN", 12346], ["MAX", 22230],
|
2176
|
+
# # ["MODSEQ", 917162488]]>
|
2177
|
+
# result.to_a # => [12346, 12347, 12348, 12349, 22222, 22223, 22224,
|
2178
|
+
# # 22225, 22226, 22227, 22228, 22229, 22230]
|
2179
|
+
# result.uid? # => true
|
2180
|
+
# result.count # => 13
|
2181
|
+
# result.min # => 12346
|
2182
|
+
# result.max # => 22230
|
2183
|
+
# result.modseq # => 917162488
|
2184
|
+
#
|
2185
|
+
# Using +return+ options to limit the result to only min, max, and count:
|
2186
|
+
#
|
2187
|
+
# result = imap.uid_search(["UID", 12345..,], return: %w(count min max))
|
2188
|
+
# # => #<data Net::IMAP::ESearchResult tag="RUBY0124", uid=true,
|
2189
|
+
# # data=[["COUNT", 13], ["MIN", 12346], ["MAX", 22230]]>
|
2190
|
+
# result.to_a # => []
|
2191
|
+
# result.count # => 13
|
2192
|
+
# result.min # => 12346
|
2193
|
+
# result.max # => 22230
|
2194
|
+
#
|
2195
|
+
# Return options and charset may be sent as keyword args or embedded in the
|
2196
|
+
# +criteria+ arg, but they must be in the correct order: <tt>"RETURN (...)
|
2197
|
+
# CHARSET ... criteria..."</tt>. The following searches
|
2198
|
+
# send the exact same command to the server:
|
2199
|
+
#
|
2200
|
+
# # Return options and charset as keyword arguments (preferred)
|
2201
|
+
# imap.search(%w(OR UNSEEN FLAGGED), return: %w(MIN MAX), charset: "UTF-8")
|
2202
|
+
# # Embedding return and charset in the criteria array
|
2203
|
+
# imap.search(["RETURN", %w(MIN MAX), "CHARSET", "UTF-8", *%w(OR UNSEEN FLAGGED)])
|
2204
|
+
# # Embedding return and charset in the criteria string
|
2205
|
+
# imap.search("RETURN (MIN MAX) CHARSET UTF-8 OR UNSEEN FLAGGED")
|
2206
|
+
#
|
2207
|
+
# Sending charset as the second positional argument is supported for
|
2208
|
+
# backward compatibility. Future versions may print a deprecation warning:
|
2209
|
+
# imap.search(%w(OR UNSEEN FLAGGED), "UTF-8", return: %w(MIN MAX))
|
2210
|
+
#
|
2211
|
+
# ==== Argument translation
|
2212
|
+
#
|
2213
|
+
# [+return+ options]
|
2214
|
+
# Must be an Array. Return option names may be either strings or symbols.
|
2215
|
+
# +Range+ elements which begin and end with negative integers are encoded
|
2216
|
+
# for use with +PARTIAL+--any other ranges are converted to SequenceSet.
|
2217
|
+
# Unlike +criteria+, other return option arguments are not automatically
|
2218
|
+
# converted to SequenceSet.
|
2219
|
+
#
|
2220
|
+
# [When +criteria+ is an Array]
|
2221
|
+
# When the array begins with <tt>"RETURN"</tt> (case insensitive), the
|
2222
|
+
# second array element is translated like the +return+ parameter (as
|
2223
|
+
# described above).
|
2224
|
+
#
|
2225
|
+
# Every other member is a +SEARCH+ command argument:
|
2226
|
+
# [SequenceSet]
|
2227
|
+
# Encoded as an \IMAP +sequence-set+ with SequenceSet#valid_string.
|
2228
|
+
# [Set, Range, <tt>-1</tt>, +:*+, responds to +#to_sequence_set+]
|
2229
|
+
# Converted to SequenceSet for validation and encoding.
|
2230
|
+
# [nested sequence-set +Array+]
|
2231
|
+
# When every element in a nested array is one of the above types, a
|
2232
|
+
# positive +Integer+, a sequence-set formatted +String+, or a deeply
|
2233
|
+
# nested +Array+ of these same types, the array will be converted to
|
2234
|
+
# SequenceSet for validation and encoding.
|
2235
|
+
# [Any other nested +Array+]
|
2236
|
+
# Otherwise, a nested array is encoded as a parenthesized list, to
|
2237
|
+
# combine multiple search keys (e.g., for use with +OR+ and +NOT+).
|
2238
|
+
# [+String+]
|
2239
|
+
# Sent verbatim when it is a valid \IMAP +atom+, and encoded as an \IMAP
|
2240
|
+
# +quoted+ or +literal+ string otherwise. Every standard search key
|
2241
|
+
# name is a valid \IMAP +atom+ and every standard search key string
|
2242
|
+
# argument is an +astring+ which may be encoded as +atom+, +quoted+, or
|
2243
|
+
# +literal+.
|
2244
|
+
#
|
2245
|
+
# *Note:* <tt>*</tt> is not a valid \IMAP +atom+ character. Any string
|
2246
|
+
# containing <tt>*</tt> will be encoded as a +quoted+ string, _not_ a
|
2247
|
+
# +sequence-set+.
|
2248
|
+
# [+Integer+ (except for <tt>-1</tt>)]
|
2249
|
+
# Encoded using +#to_s+.
|
2250
|
+
# [+Date+]
|
2251
|
+
# Encoded as an \IMAP date (see ::encode_date).
|
2252
|
+
#
|
2253
|
+
# [When +criteria+ is a String]
|
2254
|
+
# +criteria+ will be sent directly to the server <em>without any
|
2255
|
+
# validation or encoding</em>.
|
2256
|
+
#
|
2257
|
+
# <em>*WARNING:* This is vulnerable to injection attacks when external
|
2258
|
+
# inputs are used.</em>
|
2259
|
+
#
|
2260
|
+
# ==== Supported return options
|
2261
|
+
#
|
2262
|
+
# For full definitions of the standard return options and return data, see
|
2263
|
+
# the relevant RFCs.
|
2264
|
+
#
|
2265
|
+
# [+ALL+]
|
2266
|
+
# Returns ESearchResult#all with a SequenceSet of all matching sequence
|
2267
|
+
# numbers or UIDs. This is the default, when return options are empty.
|
2268
|
+
#
|
2269
|
+
# For compatibility with SearchResult, ESearchResult#to_a returns an
|
2270
|
+
# Array of message sequence numbers or UIDs.
|
2271
|
+
#
|
2272
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2273
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2274
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2275
|
+
#
|
2276
|
+
# [+COUNT+]
|
2277
|
+
# Returns ESearchResult#count with the number of matching messages.
|
2278
|
+
#
|
2279
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2280
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2281
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2282
|
+
#
|
2283
|
+
# [+MAX+]
|
2284
|
+
# Returns ESearchResult#max with the highest matching sequence number or
|
2285
|
+
# UID.
|
2286
|
+
#
|
2287
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2288
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2289
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2290
|
+
#
|
2291
|
+
# [+MIN+]
|
2292
|
+
# Returns ESearchResult#min with the lowest matching sequence number or
|
2293
|
+
# UID.
|
2294
|
+
#
|
2295
|
+
# <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
|
2296
|
+
# {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
|
2297
|
+
# {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
|
2298
|
+
#
|
2299
|
+
# [+PARTIAL+ _range_]
|
2300
|
+
# Returns ESearchResult#partial with a SequenceSet of a subset of
|
2301
|
+
# matching sequence numbers or UIDs, as selected by _range_. As with
|
2302
|
+
# sequence numbers, the first result is +1+: <tt>1..500</tt> selects the
|
2303
|
+
# first 500 search results (in mailbox order), <tt>501..1000</tt> the
|
2304
|
+
# second 500, and so on. _range_ may also be negative: <tt>-500..-1</tt>
|
2305
|
+
# selects the last 500 search results.
|
2306
|
+
#
|
2307
|
+
# <em>Requires either the <tt>CONTEXT=SEARCH</tt> or +PARTIAL+ capabability.</em>
|
2308
|
+
# {[RFC5267]}[https://rfc-editor.org/rfc/rfc5267]
|
2309
|
+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
|
2310
|
+
#
|
2311
|
+
# ===== +MODSEQ+ return data
|
2312
|
+
#
|
2313
|
+
# ESearchResult#modseq return data does not have a corresponding return
|
2314
|
+
# option. Instead, it is returned if the +MODSEQ+ search key is used or
|
2315
|
+
# when the +CONDSTORE+ extension is enabled for the selected mailbox.
|
2316
|
+
# See [{RFC4731 §3.2}[https://www.rfc-editor.org/rfc/rfc4731#section-3.2]]
|
2317
|
+
# or [{RFC7162 §2.1.5}[https://www.rfc-editor.org/rfc/rfc7162#section-3.1.5]].
|
2318
|
+
#
|
2319
|
+
# ===== +RFC4466+ compatible extensions
|
2320
|
+
#
|
2321
|
+
# {RFC4466 §2.6}[https://www.rfc-editor.org/rfc/rfc4466.html#section-2.6]
|
2322
|
+
# defines standard syntax for search extensions. Net::IMAP allows sending
|
2323
|
+
# unsupported search return options and will parse unsupported search
|
2324
|
+
# extensions' return values into ExtensionData. Please note that this is an
|
2325
|
+
# intentionally _unstable_ API. Future releases may return different
|
2326
|
+
# (incompatible) objects, <em>without deprecation or warning</em>.
|
2327
|
+
#
|
2328
|
+
# ==== Search keys
|
2329
|
+
#
|
2330
|
+
# For full definitions of the standard search +criteria+,
|
1936
2331
|
# see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
|
1937
2332
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
1938
2333
|
# in addition to documentation for
|
1939
|
-
# any
|
1940
|
-
# reported by #capabilities which may define additional search filters, e.g:
|
2334
|
+
# any #capabilities which may define additional search filters, such as
|
1941
2335
|
# +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
|
1942
|
-
# +SAVEDATE+.
|
2336
|
+
# +SAVEDATE+.
|
1943
2337
|
#
|
1944
|
-
#
|
1945
|
-
#
|
1946
|
-
#
|
2338
|
+
# With the exception of <em>sequence-set</em> and <em>parenthesized
|
2339
|
+
# list</em>, all search keys are composed of prefix label with zero or more
|
2340
|
+
# arguments. The number and type of arguments is specific to each search
|
2341
|
+
# key.
|
1947
2342
|
#
|
1948
|
-
#
|
1949
|
-
# <b><date></b>. The date argument has a format similar
|
1950
|
-
# to <tt>8-Aug-2002</tt>, and can be formatted using
|
1951
|
-
# Net::IMAP.format_date.
|
2343
|
+
# ===== Search keys that match all messages
|
1952
2344
|
#
|
1953
|
-
#
|
2345
|
+
# [+ALL+]
|
2346
|
+
# The default initial key. Matches every message in the mailbox.
|
1954
2347
|
#
|
1955
|
-
#
|
2348
|
+
# [+SAVEDATESUPPORTED+]
|
2349
|
+
# Matches every message in the mailbox when the mailbox supports the save
|
2350
|
+
# date attribute. Otherwise, it matches no messages.
|
1956
2351
|
#
|
1957
|
-
#
|
2352
|
+
# <em>Requires +SAVEDATE+ capability</em>.
|
2353
|
+
# {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
|
1958
2354
|
#
|
1959
|
-
#
|
2355
|
+
# ===== Sequence set search keys
|
1960
2356
|
#
|
1961
|
-
#
|
2357
|
+
# [_sequence-set_]
|
2358
|
+
# Matches messages with message sequence numbers in _sequence-set_.
|
1962
2359
|
#
|
1963
|
-
#
|
2360
|
+
# _Note:_ this search key has no label.
|
1964
2361
|
#
|
1965
|
-
#
|
1966
|
-
#
|
2362
|
+
# <em>+UIDONLY+ must *not* be enabled.</em>
|
2363
|
+
# {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.html]
|
1967
2364
|
#
|
1968
|
-
#
|
2365
|
+
# [+UID+ _sequence-set_]
|
2366
|
+
# Matches messages with a UID in _sequence-set_.
|
1969
2367
|
#
|
1970
|
-
#
|
2368
|
+
# ===== Compound search keys
|
1971
2369
|
#
|
1972
|
-
#
|
2370
|
+
# [(_search-key_ _search-key_...)]
|
2371
|
+
# Combines one or more _search-key_ arguments to match
|
2372
|
+
# messages which match all contained search keys. Useful for +OR+, +NOT+,
|
2373
|
+
# and other search keys with _search-key_ arguments.
|
1973
2374
|
#
|
1974
|
-
#
|
2375
|
+
# _Note:_ this search key has no label.
|
1975
2376
|
#
|
1976
|
-
#
|
1977
|
-
#
|
2377
|
+
# [+OR+ _search-key_ _search-key_]
|
2378
|
+
# Matches messages which match either _search-key_ argument.
|
2379
|
+
#
|
2380
|
+
# [+NOT+ _search-key_]
|
2381
|
+
# Matches messages which do not match _search-key_.
|
2382
|
+
#
|
2383
|
+
# [+FUZZY+ _search-key_]
|
2384
|
+
# Uses fuzzy matching for the specified search key.
|
2385
|
+
#
|
2386
|
+
# <em>Requires <tt>SEARCH=FUZZY</tt> capability.</em>
|
2387
|
+
# {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
|
2388
|
+
#
|
2389
|
+
# ===== Flags search keys
|
2390
|
+
#
|
2391
|
+
# [+ANSWERED+, +UNANSWERED+]
|
2392
|
+
# Matches messages with or without the <tt>\\Answered</tt> flag.
|
2393
|
+
# [+DELETED+, +UNDELETED+]
|
2394
|
+
# Matches messages with or without the <tt>\\Deleted</tt> flag.
|
2395
|
+
# [+DRAFT+, +UNDRAFT+]
|
2396
|
+
# Matches messages with or without the <tt>\\Draft</tt> flag.
|
2397
|
+
# [+FLAGGED+, +UNFLAGGED+]
|
2398
|
+
# Matches messages with or without the <tt>\\Flagged</tt> flag.
|
2399
|
+
# [+SEEN+, +UNSEEN+]
|
2400
|
+
# Matches messages with or without the <tt>\\Seen</tt> flag.
|
2401
|
+
# [+KEYWORD+ _keyword_, +UNKEYWORD+ _keyword_]
|
2402
|
+
# Matches messages with or without the specified _keyword_.
|
2403
|
+
#
|
2404
|
+
# [+RECENT+, +UNRECENT+]
|
2405
|
+
# Matches messages with or without the <tt>\\Recent</tt> flag.
|
2406
|
+
#
|
2407
|
+
# *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
|
2408
|
+
# [+NEW+]
|
2409
|
+
# Equivalent to <tt>(RECENT UNSEEN)</tt>.
|
2410
|
+
#
|
2411
|
+
# *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
|
2412
|
+
#
|
2413
|
+
# ===== Header field substring search keys
|
2414
|
+
#
|
2415
|
+
# [+BCC+ _substring_]
|
2416
|
+
# Matches when _substring_ is in the envelope's +BCC+ field.
|
2417
|
+
# [+CC+ _substring_]
|
2418
|
+
# Matches when _substring_ is in the envelope's +CC+ field.
|
2419
|
+
# [+FROM+ _substring_]
|
2420
|
+
# Matches when _substring_ is in the envelope's +FROM+ field.
|
2421
|
+
# [+SUBJECT+ _substring_]
|
2422
|
+
# Matches when _substring_ is in the envelope's +SUBJECT+ field.
|
2423
|
+
# [+TO+ _substring_]
|
2424
|
+
# Matches when _substring_ is in the envelope's +TO+ field.
|
2425
|
+
#
|
2426
|
+
# [+HEADER+ _field_ _substring_]
|
2427
|
+
# Matches when _substring_ is in the specified header _field_.
|
2428
|
+
#
|
2429
|
+
# ===== Body text search keys
|
2430
|
+
# [+BODY+ _string_]
|
2431
|
+
# Matches when _string_ is in the body of the message.
|
2432
|
+
# Does not match on header fields.
|
2433
|
+
#
|
2434
|
+
# The server _may_ use flexible matching, rather than simple substring
|
2435
|
+
# matches. For example, this may use stemming or match only full words.
|
2436
|
+
#
|
2437
|
+
# [+TEXT+ _string_]
|
2438
|
+
# Matches when _string_ is in the header or body of the message.
|
2439
|
+
#
|
2440
|
+
# The server _may_ use flexible matching, rather than simple substring
|
2441
|
+
# matches. For example, this may use stemming or match only full words.
|
2442
|
+
#
|
2443
|
+
# ===== Date/Time search keys
|
2444
|
+
#
|
2445
|
+
# [+SENTBEFORE+ _date_]
|
2446
|
+
# [+SENTON+ _date_]
|
2447
|
+
# [+SENTSINCE+ _date_]
|
2448
|
+
# Matches when the +Date+ header is earlier than, on, or later than _date_.
|
2449
|
+
#
|
2450
|
+
# [+BEFORE+ _date_]
|
2451
|
+
# [+ON+ _date_]
|
2452
|
+
# [+SINCE+ _date_]
|
2453
|
+
# Matches when the +INTERNALDATE+ is earlier than, on, or later than
|
2454
|
+
# _date_.
|
2455
|
+
#
|
2456
|
+
# [+OLDER+ _interval_]
|
2457
|
+
# [+YOUNGER+ _interval_]
|
2458
|
+
# Matches when the +INTERNALDATE+ is more/less than _interval_ seconds ago.
|
2459
|
+
#
|
2460
|
+
# <em>Requires +WITHIN+ capability</em>.
|
2461
|
+
# {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
|
2462
|
+
#
|
2463
|
+
# [+SAVEDBEFORE+ _date_]
|
2464
|
+
# [+SAVEDON+ _date_]
|
2465
|
+
# [+SAVEDSINCE+ _date_]
|
2466
|
+
# Matches when the save date is earlier than, on, or later than _date_.
|
2467
|
+
#
|
2468
|
+
# <em>Requires +SAVEDATE+ capability.</em>
|
2469
|
+
# {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
|
2470
|
+
#
|
2471
|
+
# ===== Other message attribute search keys
|
2472
|
+
#
|
2473
|
+
# [+SMALLER+ _bytes_]
|
2474
|
+
# [+LARGER+ _bytes_]
|
2475
|
+
# Matches when +RFC822.SIZE+ is smaller or larger than _bytes_.
|
2476
|
+
#
|
2477
|
+
# [+ANNOTATION+ _entry_ _attr_ _value_]
|
2478
|
+
# Matches messages that have annotations with entries matching _entry_,
|
2479
|
+
# attributes matching _attr_, and _value_ in the attribute's values.
|
2480
|
+
#
|
2481
|
+
# <em>Requires +ANNOTATE-EXPERIMENT-1+ capability</em>.
|
2482
|
+
# {[RFC5257]}[https://www.rfc-editor.org/rfc/rfc5257.html].
|
2483
|
+
#
|
2484
|
+
# [+FILTER+ _filter_]
|
2485
|
+
# References a _filter_ that is stored on the server and matches all
|
2486
|
+
# messages which would be matched by that filter's search criteria.
|
2487
|
+
#
|
2488
|
+
# <em>Requires +FILTERS+ capability</em>.
|
2489
|
+
# {[RFC5466]}[https://www.rfc-editor.org/rfc/rfc5466.html#section-3.1]
|
2490
|
+
#
|
2491
|
+
# [+MODSEQ+ _modseq_]
|
2492
|
+
# Matches when +MODSEQ+ is greater than or equal to _modseq_.
|
2493
|
+
#
|
2494
|
+
# <em>Requires +CONDSTORE+ capability</em>.
|
2495
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
|
2496
|
+
#
|
2497
|
+
# [+MODSEQ+ _entry_ _entry-type_ _modseq_]
|
2498
|
+
# Matches when a specific metadata _entry_ has been updated since
|
2499
|
+
# _modseq_.
|
2500
|
+
#
|
2501
|
+
# For flags, the corresponding _entry_ name is
|
2502
|
+
# <tt>"/flags/#{flag_name}"</tt>, where _flag_name_ includes the
|
2503
|
+
# <tt>\\</tt> prefix. _entry-type_ can be one of <tt>"shared"</tt>,
|
2504
|
+
# <tt>"priv"</tt> (private), or <tt>"all"</tt>.
|
2505
|
+
#
|
2506
|
+
# <em>Requires +CONDSTORE+ capability</em>.
|
2507
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
|
2508
|
+
#
|
2509
|
+
# [+EMAILID+ _objectid_]
|
2510
|
+
# [+THREADID+ _objectid_]
|
2511
|
+
# Matches when +EMAILID+/+THREADID+ is equal to _objectid_
|
2512
|
+
# (substring matches are not supported).
|
2513
|
+
#
|
2514
|
+
# <em>Requires +OBJECTID+ capability</em>.
|
2515
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-6]
|
2516
|
+
#
|
2517
|
+
# ==== Capabilities
|
2518
|
+
#
|
2519
|
+
# Return options should only be specified when the server supports
|
2520
|
+
# +IMAP4rev2+ or an extension that allows them, such as +ESEARCH+
|
2521
|
+
# [RFC4731[https://rfc-editor.org/rfc/rfc4731#section-3.1]].
|
1978
2522
|
#
|
1979
|
-
#
|
2523
|
+
# When +IMAP4rev2+ is enabled, or when the server supports +IMAP4rev2+ but
|
2524
|
+
# not +IMAP4rev1+, ESearchResult is always returned instead of SearchResult.
|
1980
2525
|
#
|
1981
|
-
# If
|
2526
|
+
# If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
|
1982
2527
|
# and enabled for the selected mailbox, a non-empty SearchResult will
|
1983
2528
|
# include a +MODSEQ+ value.
|
1984
2529
|
# imap.select("mbox", condstore: true)
|
1985
|
-
# result = imap.search(["SUBJECT", "hi there", "not", "new")
|
2530
|
+
# result = imap.search(["SUBJECT", "hi there", "not", "new"])
|
1986
2531
|
# #=> Net::IMAP::SearchResult[1, 6, 7, 8, modseq: 5594]
|
1987
2532
|
# result.modseq # => 5594
|
1988
|
-
|
1989
|
-
|
2533
|
+
#
|
2534
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2535
|
+
# the +SEARCH+ command is prohibited. Use #uid_search instead.
|
2536
|
+
def search(...)
|
2537
|
+
search_internal("SEARCH", ...)
|
1990
2538
|
end
|
1991
2539
|
|
2540
|
+
# :call-seq:
|
2541
|
+
# uid_search(criteria, charset = nil) -> result
|
2542
|
+
# uid_search(criteria, charset: nil, return: nil) -> result
|
2543
|
+
#
|
1992
2544
|
# Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1993
2545
|
# to search the mailbox for messages that match the given searching
|
1994
2546
|
# criteria, and returns unique identifiers (<tt>UID</tt>s).
|
@@ -1997,9 +2549,19 @@ module Net
|
|
1997
2549
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
1998
2550
|
# capability has been enabled.
|
1999
2551
|
#
|
2000
|
-
# See #search for documentation of
|
2001
|
-
|
2002
|
-
|
2552
|
+
# See #search for documentation of parameters.
|
2553
|
+
#
|
2554
|
+
# ==== Capabilities
|
2555
|
+
#
|
2556
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2557
|
+
# #uid_search must be used instead of #search, and the <tt><message
|
2558
|
+
# set></tt> search criterion is prohibited. Use +ALL+ or <tt>UID
|
2559
|
+
# sequence-set</tt> instead.
|
2560
|
+
#
|
2561
|
+
# Otherwise, #uid_search is updated by extensions in the same way as
|
2562
|
+
# #search.
|
2563
|
+
def uid_search(...)
|
2564
|
+
search_internal("UID SEARCH", ...)
|
2003
2565
|
end
|
2004
2566
|
|
2005
2567
|
# :call-seq:
|
@@ -2008,26 +2570,21 @@ module Net
|
|
2008
2570
|
# Sends a {FETCH command [IMAP4rev1 §6.4.5]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.5]
|
2009
2571
|
# to retrieve data associated with a message in the mailbox.
|
2010
2572
|
#
|
2011
|
-
#
|
2012
|
-
#
|
2013
|
-
#
|
2014
|
-
# being interpreted as '100:*'. Beware that the +exclude_end?+
|
2015
|
-
# property of a Range object is ignored, and the contents of a
|
2016
|
-
# range are independent of the order of the range endpoints as per
|
2017
|
-
# the protocol specification, so 1...5, 5..1 and 5...1 are all
|
2018
|
-
# equivalent to 1..5.
|
2573
|
+
# +set+ is the message sequence numbers to fetch, and may be any valid input
|
2574
|
+
# to {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
|
2575
|
+
# (For UIDs, use #uid_fetch instead.)
|
2019
2576
|
#
|
2020
|
-
# +attr+ is a list of attributes to fetch; see
|
2021
|
-
#
|
2577
|
+
# +attr+ is a list of attributes to fetch; see FetchStruct documentation for
|
2578
|
+
# a list of supported attributes.
|
2022
2579
|
#
|
2023
2580
|
# +changedsince+ is an optional integer mod-sequence. It limits results to
|
2024
2581
|
# messages with a mod-sequence greater than +changedsince+.
|
2025
2582
|
#
|
2026
2583
|
# The return value is an array of FetchData.
|
2027
2584
|
#
|
2028
|
-
# Related: #
|
2585
|
+
# Related: #uid_fetch, FetchData
|
2029
2586
|
#
|
2030
|
-
#
|
2587
|
+
# ==== For example:
|
2031
2588
|
#
|
2032
2589
|
# p imap.fetch(6..8, "UID")
|
2033
2590
|
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
|
@@ -2045,39 +2602,82 @@ module Net
|
|
2045
2602
|
# p data.attr["UID"]
|
2046
2603
|
# #=> 98
|
2047
2604
|
#
|
2048
|
-
#
|
2605
|
+
# ==== Capabilities
|
2049
2606
|
#
|
2050
|
-
# Many extensions define new message +attr+ names. See
|
2051
|
-
# of supported extension fields.
|
2607
|
+
# Many extensions define new message +attr+ names. See FetchStruct for a
|
2608
|
+
# list of supported extension fields.
|
2052
2609
|
#
|
2053
2610
|
# The server's capabilities must include +CONDSTORE+
|
2054
|
-
# {[RFC7162]}[https://
|
2611
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
|
2055
2612
|
# +changedsince+ argument. Using +changedsince+ implicitly enables the
|
2056
2613
|
# +CONDSTORE+ extension.
|
2057
|
-
|
2058
|
-
|
2614
|
+
#
|
2615
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2616
|
+
# +FETCH+ command is prohibited. Use #uid_fetch instead.
|
2617
|
+
def fetch(...)
|
2618
|
+
fetch_internal("FETCH", ...)
|
2059
2619
|
end
|
2060
2620
|
|
2061
2621
|
# :call-seq:
|
2062
|
-
# uid_fetch(set, attr, changedsince: nil) -> array of FetchData
|
2622
|
+
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData (or UIDFetchData)
|
2063
2623
|
#
|
2064
2624
|
# Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
2065
2625
|
# to retrieve data associated with a message in the mailbox.
|
2066
2626
|
#
|
2067
|
-
#
|
2068
|
-
#
|
2627
|
+
# +set+ is the message UIDs to fetch, and may be any valid input to
|
2628
|
+
# {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
|
2629
|
+
# (For message sequence numbers, use #fetch instead.)
|
2069
2630
|
#
|
2631
|
+
# +attr+ behaves the same as with #fetch.
|
2070
2632
|
# >>>
|
2071
2633
|
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
|
2072
2634
|
# part of any +FETCH+ response caused by a +UID+ command, regardless of
|
2073
2635
|
# whether a +UID+ was specified as a message data item to the +FETCH+.
|
2074
2636
|
#
|
2637
|
+
# +changedsince+ (optional) behaves the same as with #fetch.
|
2638
|
+
#
|
2639
|
+
# +partial+ is an optional range to limit the number of results returned.
|
2640
|
+
# It's useful when +set+ contains an unknown number of messages.
|
2641
|
+
# <tt>1..500</tt> returns the first 500 messages in +set+ (in mailbox
|
2642
|
+
# order), <tt>501..1000</tt> the second 500, and so on. +partial+ may also
|
2643
|
+
# be negative: <tt>-500..-1</tt> selects the last 500 messages in +set+.
|
2644
|
+
# <em>Requires the +PARTIAL+ capabability.</em>
|
2645
|
+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
|
2646
|
+
#
|
2647
|
+
# For example:
|
2648
|
+
#
|
2649
|
+
# # Without partial, the size of the results may be unknown beforehand:
|
2650
|
+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
|
2651
|
+
# # ... maybe wait for a long time ... and allocate a lot of memory ...
|
2652
|
+
# results.size # => 0..2**32-1
|
2653
|
+
# process results # may also take a long time and use a lot of memory...
|
2654
|
+
#
|
2655
|
+
# # Using partial, the results may be paginated:
|
2656
|
+
# loop do
|
2657
|
+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
|
2658
|
+
# partial: 1..500)
|
2659
|
+
# # fetch should return quickly and allocate little memory
|
2660
|
+
# results.size # => 0..500
|
2661
|
+
# break if results.empty?
|
2662
|
+
# next_uid_to_fetch = results.last.uid + 1
|
2663
|
+
# process results
|
2664
|
+
# end
|
2665
|
+
#
|
2075
2666
|
# Related: #fetch, FetchData
|
2076
2667
|
#
|
2077
|
-
#
|
2078
|
-
#
|
2079
|
-
|
2080
|
-
|
2668
|
+
# ==== Capabilities
|
2669
|
+
#
|
2670
|
+
# The server's capabilities must include +PARTIAL+
|
2671
|
+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394] in order to use the
|
2672
|
+
# +partial+ argument.
|
2673
|
+
#
|
2674
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2675
|
+
# #uid_fetch must be used instead of #fetch, and UIDFetchData will be
|
2676
|
+
# returned instead of FetchData.
|
2677
|
+
#
|
2678
|
+
# Otherwise, #uid_fetch is updated by extensions in the same way as #fetch.
|
2679
|
+
def uid_fetch(...)
|
2680
|
+
fetch_internal("UID FETCH", ...)
|
2081
2681
|
end
|
2082
2682
|
|
2083
2683
|
# :call-seq:
|
@@ -2108,27 +2708,30 @@ module Net
|
|
2108
2708
|
#
|
2109
2709
|
# Related: #uid_store
|
2110
2710
|
#
|
2111
|
-
#
|
2711
|
+
# ==== For example:
|
2112
2712
|
#
|
2113
2713
|
# p imap.store(6..8, "+FLAGS", [:Deleted])
|
2114
2714
|
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
2115
2715
|
# #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
|
2116
2716
|
# #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
|
2117
2717
|
#
|
2118
|
-
#
|
2718
|
+
# ==== Capabilities
|
2119
2719
|
#
|
2120
2720
|
# Extensions may define new data items to be used with #store.
|
2121
2721
|
#
|
2122
2722
|
# The server's capabilities must include +CONDSTORE+
|
2123
|
-
# {[RFC7162]}[https://
|
2723
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
|
2124
2724
|
# +unchangedsince+ argument. Using +unchangedsince+ implicitly enables the
|
2125
2725
|
# +CONDSTORE+ extension.
|
2726
|
+
#
|
2727
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2728
|
+
# +STORE+ command is prohibited. Use #uid_store instead.
|
2126
2729
|
def store(set, attr, flags, unchangedsince: nil)
|
2127
2730
|
store_internal("STORE", set, attr, flags, unchangedsince: unchangedsince)
|
2128
2731
|
end
|
2129
2732
|
|
2130
2733
|
# :call-seq:
|
2131
|
-
# uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData
|
2734
|
+
# uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData (or UIDFetchData)
|
2132
2735
|
#
|
2133
2736
|
# Sends a {UID STORE command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
2134
2737
|
# to alter data associated with messages in the mailbox, in particular their
|
@@ -2139,8 +2742,13 @@ module Net
|
|
2139
2742
|
#
|
2140
2743
|
# Related: #store
|
2141
2744
|
#
|
2142
|
-
#
|
2143
|
-
#
|
2745
|
+
# ==== Capabilities
|
2746
|
+
#
|
2747
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2748
|
+
# #uid_store must be used instead of #store, and UIDFetchData will be
|
2749
|
+
# returned instead of FetchData.
|
2750
|
+
#
|
2751
|
+
# Otherwise, #uid_store is updated by extensions in the same way as #store.
|
2144
2752
|
def uid_store(set, attr, flags, unchangedsince: nil)
|
2145
2753
|
store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
|
2146
2754
|
end
|
@@ -2152,13 +2760,16 @@ module Net
|
|
2152
2760
|
#
|
2153
2761
|
# Related: #uid_copy
|
2154
2762
|
#
|
2155
|
-
#
|
2763
|
+
# ==== Capabilities
|
2156
2764
|
#
|
2157
2765
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
2158
2766
|
# supported, the server's response should include a +COPYUID+ response code
|
2159
2767
|
# with UIDPlusData. This will report the UIDVALIDITY of the destination
|
2160
2768
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
2161
2769
|
# the moved messages.
|
2770
|
+
#
|
2771
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2772
|
+
# +COPY+ command is prohibited. Use #uid_copy instead.
|
2162
2773
|
def copy(set, mailbox)
|
2163
2774
|
copy_internal("COPY", set, mailbox)
|
2164
2775
|
end
|
@@ -2169,9 +2780,12 @@ module Net
|
|
2169
2780
|
#
|
2170
2781
|
# Similar to #copy, but +set+ contains unique identifiers.
|
2171
2782
|
#
|
2172
|
-
#
|
2783
|
+
# ==== Capabilities
|
2784
|
+
#
|
2785
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] in enabled,
|
2786
|
+
# #uid_copy must be used instead of #copy.
|
2173
2787
|
#
|
2174
|
-
#
|
2788
|
+
# Otherwise, #uid_copy is updated by extensions in the same way as #copy.
|
2175
2789
|
def uid_copy(set, mailbox)
|
2176
2790
|
copy_internal("UID COPY", set, mailbox)
|
2177
2791
|
end
|
@@ -2184,10 +2798,10 @@ module Net
|
|
2184
2798
|
#
|
2185
2799
|
# Related: #uid_move
|
2186
2800
|
#
|
2187
|
-
#
|
2801
|
+
# ==== Capabilities
|
2188
2802
|
#
|
2189
|
-
# The server's capabilities must include +MOVE+
|
2190
|
-
# [RFC6851[https://
|
2803
|
+
# The server's capabilities must include either +IMAP4rev2+ or +MOVE+
|
2804
|
+
# [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
|
2191
2805
|
#
|
2192
2806
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
2193
2807
|
# supported, the server's response should include a +COPYUID+ response code
|
@@ -2195,6 +2809,8 @@ module Net
|
|
2195
2809
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
2196
2810
|
# the moved messages.
|
2197
2811
|
#
|
2812
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
|
2813
|
+
# +MOVE+ command is prohibited. Use #uid_move instead.
|
2198
2814
|
def move(set, mailbox)
|
2199
2815
|
copy_internal("MOVE", set, mailbox)
|
2200
2816
|
end
|
@@ -2208,11 +2824,15 @@ module Net
|
|
2208
2824
|
#
|
2209
2825
|
# Related: #move
|
2210
2826
|
#
|
2211
|
-
#
|
2827
|
+
# ==== Capabilities
|
2828
|
+
#
|
2829
|
+
# The server's capabilities must include either +IMAP4rev2+ or +MOVE+
|
2830
|
+
# [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
|
2831
|
+
#
|
2832
|
+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
|
2833
|
+
# #uid_move must be used instead of #move.
|
2212
2834
|
#
|
2213
|
-
#
|
2214
|
-
# [RFC6851[https://tools.ietf.org/html/rfc6851]]. +UIDPLUS+ also affects
|
2215
|
-
# #uid_move the same way it affects #move.
|
2835
|
+
# Otherwise, #uid_move is updated by extensions in the same way as #move.
|
2216
2836
|
def uid_move(set, mailbox)
|
2217
2837
|
copy_internal("UID MOVE", set, mailbox)
|
2218
2838
|
end
|
@@ -2228,17 +2848,17 @@ module Net
|
|
2228
2848
|
#
|
2229
2849
|
# Related: #uid_sort, #search, #uid_search, #thread, #uid_thread
|
2230
2850
|
#
|
2231
|
-
#
|
2851
|
+
# ==== For example:
|
2232
2852
|
#
|
2233
2853
|
# p imap.sort(["FROM"], ["ALL"], "US-ASCII")
|
2234
2854
|
# #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
|
2235
2855
|
# p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
|
2236
2856
|
# #=> [6, 7, 8, 1]
|
2237
2857
|
#
|
2238
|
-
#
|
2858
|
+
# ==== Capabilities
|
2239
2859
|
#
|
2240
2860
|
# The server's capabilities must include +SORT+
|
2241
|
-
# [RFC5256[https://
|
2861
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
2242
2862
|
def sort(sort_keys, search_keys, charset)
|
2243
2863
|
return sort_internal("SORT", sort_keys, search_keys, charset)
|
2244
2864
|
end
|
@@ -2250,10 +2870,10 @@ module Net
|
|
2250
2870
|
#
|
2251
2871
|
# Related: #sort, #search, #uid_search, #thread, #uid_thread
|
2252
2872
|
#
|
2253
|
-
#
|
2873
|
+
# ==== Capabilities
|
2254
2874
|
#
|
2255
2875
|
# The server's capabilities must include +SORT+
|
2256
|
-
# [RFC5256[https://
|
2876
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
2257
2877
|
def uid_sort(sort_keys, search_keys, charset)
|
2258
2878
|
return sort_internal("UID SORT", sort_keys, search_keys, charset)
|
2259
2879
|
end
|
@@ -2275,10 +2895,10 @@ module Net
|
|
2275
2895
|
#
|
2276
2896
|
# Related: #uid_thread, #search, #uid_search, #sort, #uid_sort
|
2277
2897
|
#
|
2278
|
-
#
|
2898
|
+
# ==== Capabilities
|
2279
2899
|
#
|
2280
2900
|
# The server's capabilities must include +THREAD+
|
2281
|
-
# [RFC5256[https://
|
2901
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
2282
2902
|
def thread(algorithm, search_keys, charset)
|
2283
2903
|
return thread_internal("THREAD", algorithm, search_keys, charset)
|
2284
2904
|
end
|
@@ -2289,10 +2909,10 @@ module Net
|
|
2289
2909
|
#
|
2290
2910
|
# Related: #thread, #search, #uid_search, #sort, #uid_sort
|
2291
2911
|
#
|
2292
|
-
#
|
2912
|
+
# ==== Capabilities
|
2293
2913
|
#
|
2294
2914
|
# The server's capabilities must include +THREAD+
|
2295
|
-
# [RFC5256[https://
|
2915
|
+
# [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
|
2296
2916
|
def uid_thread(algorithm, search_keys, charset)
|
2297
2917
|
return thread_internal("UID THREAD", algorithm, search_keys, charset)
|
2298
2918
|
end
|
@@ -2308,11 +2928,11 @@ module Net
|
|
2308
2928
|
#
|
2309
2929
|
# Related: #capable?, #capabilities, #capability
|
2310
2930
|
#
|
2311
|
-
#
|
2931
|
+
# ==== Capabilities
|
2312
2932
|
#
|
2313
2933
|
# The server's capabilities must include
|
2314
|
-
# +ENABLE+ [RFC5161[https://
|
2315
|
-
# or +IMAP4REV2+ [RFC9051[https://
|
2934
|
+
# +ENABLE+ [RFC5161[https://www.rfc-editor.org/rfc/rfc5161]]
|
2935
|
+
# or +IMAP4REV2+ [RFC9051[https://www.rfc-editor.org/rfc/rfc9051]].
|
2316
2936
|
#
|
2317
2937
|
# Additionally, the server capabilities must include a capability matching
|
2318
2938
|
# each enabled extension (usually the same name as the enabled extension).
|
@@ -2331,7 +2951,7 @@ module Net
|
|
2331
2951
|
# <tt>"UTF8=ACCEPT"</tt> or <tt>"IMAP4rev2"</tt>, depending on server
|
2332
2952
|
# capabilities.
|
2333
2953
|
#
|
2334
|
-
# [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://
|
2954
|
+
# [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
|
2335
2955
|
#
|
2336
2956
|
# The server's capabilities must include <tt>UTF8=ACCEPT</tt> _or_
|
2337
2957
|
# <tt>UTF8=ONLY</tt>.
|
@@ -2350,13 +2970,23 @@ module Net
|
|
2350
2970
|
# encoding, even if they generally contain UTF-8 data, if they are
|
2351
2971
|
# text at all.
|
2352
2972
|
#
|
2353
|
-
# [<tt>"UTF8=ONLY"</tt> [RFC6855[https://
|
2973
|
+
# [<tt>"UTF8=ONLY"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
|
2354
2974
|
#
|
2355
2975
|
# A server that reports the <tt>UTF8=ONLY</tt> capability _requires_ that
|
2356
2976
|
# the client <tt>enable("UTF8=ACCEPT")</tt> before any mailboxes may be
|
2357
2977
|
# selected. For convenience, <tt>enable("UTF8=ONLY")</tt> is aliased to
|
2358
2978
|
# <tt>enable("UTF8=ACCEPT")</tt>.
|
2359
2979
|
#
|
2980
|
+
# [+UIDONLY+ {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.pdf]]
|
2981
|
+
#
|
2982
|
+
# When UIDONLY is enabled, the #fetch, #store, #search, #copy, and #move
|
2983
|
+
# commands are prohibited and result in a tagged BAD response. Clients
|
2984
|
+
# should instead use uid_fetch, uid_store, uid_search, uid_copy, or
|
2985
|
+
# uid_move, respectively. All +FETCH+ responses that would be returned are
|
2986
|
+
# replaced by +UIDFETCH+ responses. All +EXPUNGED+ responses that would be
|
2987
|
+
# returned are replaced by +VANISHED+ responses. The "<sequence set>"
|
2988
|
+
# uid_search criterion is prohibited.
|
2989
|
+
#
|
2360
2990
|
# ===== Unsupported capabilities
|
2361
2991
|
#
|
2362
2992
|
# *Note:* Some extensions that use ENABLE permit the server to send syntax
|
@@ -2397,17 +3027,23 @@ module Net
|
|
2397
3027
|
# checks the connection for each 60 seconds.
|
2398
3028
|
#
|
2399
3029
|
# loop do
|
2400
|
-
# imap.idle(60) do |
|
2401
|
-
#
|
3030
|
+
# imap.idle(60) do |response|
|
3031
|
+
# do_something_with(response)
|
3032
|
+
# imap.idle_done if some_condition?(response)
|
2402
3033
|
# end
|
2403
3034
|
# end
|
2404
3035
|
#
|
3036
|
+
# Returns the server's response to indicate the IDLE state has ended.
|
3037
|
+
# Returns +nil+ if the server does not respond to #idle_done within
|
3038
|
+
# {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
|
3039
|
+
# seconds.
|
3040
|
+
#
|
2405
3041
|
# Related: #idle_done, #noop, #check
|
2406
3042
|
#
|
2407
|
-
#
|
3043
|
+
# ==== Capabilities
|
2408
3044
|
#
|
2409
|
-
# The server's capabilities must include +IDLE+
|
2410
|
-
# [RFC2177[https://
|
3045
|
+
# The server's capabilities must include either +IMAP4rev2+ or +IDLE+
|
3046
|
+
# [RFC2177[https://www.rfc-editor.org/rfc/rfc2177]].
|
2411
3047
|
def idle(timeout = nil, &response_handler)
|
2412
3048
|
raise LocalJumpError, "no block given" unless response_handler
|
2413
3049
|
|
@@ -2429,7 +3065,7 @@ module Net
|
|
2429
3065
|
unless @receiver_thread_terminating
|
2430
3066
|
remove_response_handler(response_handler)
|
2431
3067
|
put_string("DONE#{CRLF}")
|
2432
|
-
response = get_tagged_response(tag, "IDLE",
|
3068
|
+
response = get_tagged_response(tag, "IDLE", idle_response_timeout)
|
2433
3069
|
end
|
2434
3070
|
end
|
2435
3071
|
end
|
@@ -2437,7 +3073,11 @@ module Net
|
|
2437
3073
|
return response
|
2438
3074
|
end
|
2439
3075
|
|
2440
|
-
# Leaves IDLE.
|
3076
|
+
# Leaves IDLE, allowing #idle to return.
|
3077
|
+
#
|
3078
|
+
# If the server does not respond within
|
3079
|
+
# {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
|
3080
|
+
# seconds, #idle will return +nil+.
|
2441
3081
|
#
|
2442
3082
|
# Related: #idle
|
2443
3083
|
def idle_done
|
@@ -2449,40 +3089,98 @@ module Net
|
|
2449
3089
|
end
|
2450
3090
|
end
|
2451
3091
|
|
3092
|
+
RESPONSES_DEPRECATION_MSG =
|
3093
|
+
"Pass a type or block to #responses, " \
|
3094
|
+
"set config.responses_without_block to :frozen_dup " \
|
3095
|
+
"or :silence_deprecation_warning, " \
|
3096
|
+
"or use #extract_responses or #clear_responses."
|
3097
|
+
private_constant :RESPONSES_DEPRECATION_MSG
|
3098
|
+
|
2452
3099
|
# :call-seq:
|
3100
|
+
# responses -> hash of {String => Array} (see config.responses_without_block)
|
3101
|
+
# responses(type) -> frozen array
|
2453
3102
|
# responses {|hash| ...} -> block result
|
2454
3103
|
# responses(type) {|array| ...} -> block result
|
2455
3104
|
#
|
2456
|
-
# Yields
|
3105
|
+
# Yields or returns unhandled server responses. Unhandled responses are
|
3106
|
+
# stored in a hash, with arrays of UntaggedResponse#data keyed by
|
3107
|
+
# UntaggedResponse#name and <em>non-+nil+</em> untagged ResponseCode#data
|
3108
|
+
# keyed by ResponseCode#name.
|
3109
|
+
#
|
3110
|
+
# When a block is given, yields unhandled responses and returns the block's
|
3111
|
+
# result. Without a block, returns the unhandled responses.
|
2457
3112
|
#
|
2458
|
-
#
|
2459
|
-
#
|
2460
|
-
#
|
2461
|
-
#
|
2462
|
-
#
|
3113
|
+
# [With +type+]
|
3114
|
+
# Yield or return only the array of responses for that +type+.
|
3115
|
+
# When no block is given, the returned array is a frozen copy.
|
3116
|
+
# [Without +type+]
|
3117
|
+
# Yield or return the entire responses hash.
|
3118
|
+
#
|
3119
|
+
# When no block is given, the behavior is determined by
|
3120
|
+
# Config#responses_without_block:
|
3121
|
+
# >>>
|
3122
|
+
# [+:silence_deprecation_warning+ <em>(original behavior)</em>]
|
3123
|
+
# Returns the mutable responses hash (without any warnings).
|
3124
|
+
# <em>This is not thread-safe.</em>
|
3125
|
+
#
|
3126
|
+
# [+:warn+ <em>(default since +v0.5+)</em>]
|
3127
|
+
# Prints a warning and returns the mutable responses hash.
|
3128
|
+
# <em>This is not thread-safe.</em>
|
3129
|
+
#
|
3130
|
+
# [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
|
3131
|
+
# Returns a frozen copy of the unhandled responses hash, with frozen
|
3132
|
+
# array values.
|
3133
|
+
#
|
3134
|
+
# [+:raise+]
|
3135
|
+
# Raise an +ArgumentError+ with the deprecation warning.
|
2463
3136
|
#
|
2464
3137
|
# For example:
|
2465
3138
|
#
|
2466
3139
|
# imap.select("inbox")
|
2467
|
-
# p imap.responses("EXISTS"
|
3140
|
+
# p imap.responses("EXISTS").last
|
2468
3141
|
# #=> 2
|
3142
|
+
# p imap.responses("UIDNEXT", &:last)
|
3143
|
+
# #=> 123456
|
2469
3144
|
# p imap.responses("UIDVALIDITY", &:last)
|
2470
3145
|
# #=> 968263756
|
3146
|
+
# p imap.responses {|responses|
|
3147
|
+
# {
|
3148
|
+
# exists: responses.delete("EXISTS").last,
|
3149
|
+
# uidnext: responses.delete("UIDNEXT").last,
|
3150
|
+
# uidvalidity: responses.delete("UIDVALIDITY").last,
|
3151
|
+
# }
|
3152
|
+
# }
|
3153
|
+
# #=> {:exists=>2, :uidnext=>123456, :uidvalidity=>968263756}
|
3154
|
+
# # "EXISTS", "UIDNEXT", and "UIDVALIDITY" have been removed:
|
3155
|
+
# p imap.responses(&:keys)
|
3156
|
+
# #=> ["FLAGS", "OK", "PERMANENTFLAGS", "RECENT", "HIGHESTMODSEQ"]
|
3157
|
+
#
|
3158
|
+
# Related: #extract_responses, #clear_responses, #response_handlers, #greeting
|
2471
3159
|
#
|
3160
|
+
# ==== Thread safety
|
2472
3161
|
# >>>
|
2473
3162
|
# *Note:* Access to the responses hash is synchronized for thread-safety.
|
2474
3163
|
# The receiver thread and response_handlers cannot process new responses
|
2475
3164
|
# until the block completes. Accessing either the response hash or its
|
2476
|
-
# response type arrays outside of the block is unsafe.
|
3165
|
+
# response type arrays outside of the block is unsafe. They can be safely
|
3166
|
+
# updated inside the block. Consider using #clear_responses or
|
3167
|
+
# #extract_responses instead.
|
2477
3168
|
#
|
2478
|
-
#
|
2479
|
-
#
|
3169
|
+
# Net::IMAP will add and remove responses from the responses hash and its
|
3170
|
+
# array values, in the calling threads for commands and in the receiver
|
3171
|
+
# thread, but will not modify any responses after adding them to the
|
3172
|
+
# responses hash.
|
3173
|
+
#
|
3174
|
+
# ==== Clearing responses
|
2480
3175
|
#
|
2481
3176
|
# Previously unhandled responses are automatically cleared before entering a
|
2482
3177
|
# mailbox with #select or #examine. Long-lived connections can receive many
|
2483
3178
|
# unhandled server responses, which must be pruned or they will continually
|
2484
3179
|
# consume more memory. Update or clear the responses hash or arrays inside
|
2485
|
-
# the block, or
|
3180
|
+
# the block, or remove responses with #extract_responses, #clear_responses,
|
3181
|
+
# or #add_response_handler.
|
3182
|
+
#
|
3183
|
+
# ==== Missing responses
|
2486
3184
|
#
|
2487
3185
|
# Only non-+nil+ data is stored. Many important response codes have no data
|
2488
3186
|
# of their own, but are used as "tags" on the ResponseText object they are
|
@@ -2493,15 +3191,25 @@ module Net
|
|
2493
3191
|
# ResponseCode#data on tagged responses. Although some command methods do
|
2494
3192
|
# return the TaggedResponse directly, #add_response_handler must be used to
|
2495
3193
|
# handle all response codes.
|
2496
|
-
#
|
2497
|
-
# Related: #clear_responses, #response_handlers, #greeting
|
2498
3194
|
def responses(type = nil)
|
2499
3195
|
if block_given?
|
2500
3196
|
synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
|
2501
3197
|
elsif type
|
2502
|
-
|
3198
|
+
synchronize { @responses[type.to_s.upcase].dup.freeze }
|
2503
3199
|
else
|
2504
|
-
|
3200
|
+
case config.responses_without_block
|
3201
|
+
when :raise
|
3202
|
+
raise ArgumentError, RESPONSES_DEPRECATION_MSG
|
3203
|
+
when :warn
|
3204
|
+
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
3205
|
+
when :frozen_dup
|
3206
|
+
synchronize {
|
3207
|
+
responses = @responses.transform_values(&:freeze)
|
3208
|
+
responses.default_proc = nil
|
3209
|
+
responses.default = [].freeze
|
3210
|
+
return responses.freeze
|
3211
|
+
}
|
3212
|
+
end
|
2505
3213
|
@responses
|
2506
3214
|
end
|
2507
3215
|
end
|
@@ -2516,7 +3224,7 @@ module Net
|
|
2516
3224
|
# Clearing responses is synchronized with other threads. The lock is
|
2517
3225
|
# released before returning.
|
2518
3226
|
#
|
2519
|
-
# Related: #responses, #response_handlers
|
3227
|
+
# Related: #extract_responses, #responses, #response_handlers
|
2520
3228
|
def clear_responses(type = nil)
|
2521
3229
|
synchronize {
|
2522
3230
|
if type
|
@@ -2530,6 +3238,30 @@ module Net
|
|
2530
3238
|
.freeze
|
2531
3239
|
end
|
2532
3240
|
|
3241
|
+
# :call-seq:
|
3242
|
+
# extract_responses(type) {|response| ... } -> array
|
3243
|
+
#
|
3244
|
+
# Yields all of the unhandled #responses for a single response +type+.
|
3245
|
+
# Removes and returns the responses for which the block returns a true
|
3246
|
+
# value.
|
3247
|
+
#
|
3248
|
+
# Extracting responses is synchronized with other threads. The lock is
|
3249
|
+
# released before returning.
|
3250
|
+
#
|
3251
|
+
# Related: #responses, #clear_responses
|
3252
|
+
def extract_responses(type)
|
3253
|
+
type = String.try_convert(type) or
|
3254
|
+
raise ArgumentError, "type must be a string"
|
3255
|
+
raise ArgumentError, "must provide a block" unless block_given?
|
3256
|
+
extracted = []
|
3257
|
+
responses(type) do |all|
|
3258
|
+
all.reject! do |response|
|
3259
|
+
extracted << response if yield response
|
3260
|
+
end
|
3261
|
+
end
|
3262
|
+
extracted
|
3263
|
+
end
|
3264
|
+
|
2533
3265
|
# Returns all response handlers, including those that are added internally
|
2534
3266
|
# by commands. Each response handler will be called with every new
|
2535
3267
|
# UntaggedResponse, TaggedResponse, and ContinuationRequest.
|
@@ -2559,6 +3291,10 @@ module Net
|
|
2559
3291
|
# end
|
2560
3292
|
# }
|
2561
3293
|
#
|
3294
|
+
# Response handlers can also be added when the client is created before the
|
3295
|
+
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
3296
|
+
# This ensures every server response is handled, including the #greeting.
|
3297
|
+
#
|
2562
3298
|
# Related: #remove_response_handler, #response_handlers
|
2563
3299
|
def add_response_handler(handler = nil, &block)
|
2564
3300
|
raise ArgumentError, "two Procs are passed" if handler && block
|
@@ -2582,13 +3318,13 @@ module Net
|
|
2582
3318
|
PORT = 143 # :nodoc:
|
2583
3319
|
SSL_PORT = 993 # :nodoc:
|
2584
3320
|
|
2585
|
-
@@debug = false
|
2586
|
-
|
2587
3321
|
def start_imap_connection
|
2588
3322
|
@greeting = get_server_greeting
|
2589
3323
|
@capabilities = capabilities_from_resp_code @greeting
|
3324
|
+
@response_handlers.each do |handler| handler.call(@greeting) end
|
2590
3325
|
@receiver_thread = start_receiver_thread
|
2591
3326
|
rescue Exception
|
3327
|
+
state_logout!
|
2592
3328
|
@sock.close
|
2593
3329
|
raise
|
2594
3330
|
end
|
@@ -2597,7 +3333,10 @@ module Net
|
|
2597
3333
|
greeting = get_response
|
2598
3334
|
raise Error, "No server greeting - connection closed" unless greeting
|
2599
3335
|
record_untagged_response_code greeting
|
2600
|
-
|
3336
|
+
case greeting.name
|
3337
|
+
when "PREAUTH" then state_authenticated!
|
3338
|
+
when "BYE" then state_logout!; raise ByeResponseError, greeting
|
3339
|
+
end
|
2601
3340
|
greeting
|
2602
3341
|
end
|
2603
3342
|
|
@@ -2607,16 +3346,18 @@ module Net
|
|
2607
3346
|
rescue Exception => ex
|
2608
3347
|
@receiver_thread_exception = ex
|
2609
3348
|
# don't exit the thread with an exception
|
3349
|
+
ensure
|
3350
|
+
state_logout!
|
2610
3351
|
end
|
2611
3352
|
end
|
2612
3353
|
|
2613
3354
|
def tcp_socket(host, port)
|
2614
|
-
s = Socket.tcp(host, port, :connect_timeout =>
|
3355
|
+
s = Socket.tcp(host, port, :connect_timeout => open_timeout)
|
2615
3356
|
s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
|
2616
3357
|
s
|
2617
3358
|
rescue Errno::ETIMEDOUT
|
2618
3359
|
raise Net::OpenTimeout, "Timeout to open TCP connection to " +
|
2619
|
-
"#{host}:#{port} (exceeds #{
|
3360
|
+
"#{host}:#{port} (exceeds #{open_timeout} seconds)"
|
2620
3361
|
end
|
2621
3362
|
|
2622
3363
|
def receive_responses
|
@@ -2629,6 +3370,7 @@ module Net
|
|
2629
3370
|
resp = get_response
|
2630
3371
|
rescue Exception => e
|
2631
3372
|
synchronize do
|
3373
|
+
state_logout!
|
2632
3374
|
@sock.close
|
2633
3375
|
@exception = e
|
2634
3376
|
end
|
@@ -2648,6 +3390,7 @@ module Net
|
|
2648
3390
|
@tagged_response_arrival.broadcast
|
2649
3391
|
case resp.tag
|
2650
3392
|
when @logout_command_tag
|
3393
|
+
state_logout!
|
2651
3394
|
return
|
2652
3395
|
when @continued_command_tag
|
2653
3396
|
@continuation_request_exception =
|
@@ -2657,6 +3400,7 @@ module Net
|
|
2657
3400
|
when UntaggedResponse
|
2658
3401
|
record_untagged_response(resp)
|
2659
3402
|
if resp.name == "BYE" && @logout_command_tag.nil?
|
3403
|
+
state_logout!
|
2660
3404
|
@sock.close
|
2661
3405
|
@exception = ByeResponseError.new(resp)
|
2662
3406
|
connection_closed = true
|
@@ -2664,6 +3408,7 @@ module Net
|
|
2664
3408
|
when ContinuationRequest
|
2665
3409
|
@continuation_request_arrival.signal
|
2666
3410
|
end
|
3411
|
+
state_unselected! if resp in {data: {code: {name: "CLOSED"}}}
|
2667
3412
|
@response_handlers.each do |handler|
|
2668
3413
|
handler.call(resp)
|
2669
3414
|
end
|
@@ -2715,23 +3460,10 @@ module Net
|
|
2715
3460
|
end
|
2716
3461
|
|
2717
3462
|
def get_response
|
2718
|
-
buff =
|
2719
|
-
while true
|
2720
|
-
s = @sock.gets(CRLF)
|
2721
|
-
break unless s
|
2722
|
-
buff.concat(s)
|
2723
|
-
if /\{(\d+)\}\r\n/n =~ s
|
2724
|
-
s = @sock.read($1.to_i)
|
2725
|
-
buff.concat(s)
|
2726
|
-
else
|
2727
|
-
break
|
2728
|
-
end
|
2729
|
-
end
|
3463
|
+
buff = @reader.read_response_buffer
|
2730
3464
|
return nil if buff.length == 0
|
2731
|
-
if
|
2732
|
-
|
2733
|
-
end
|
2734
|
-
return @parser.parse(buff)
|
3465
|
+
$stderr.print(buff.gsub(/^/n, "S: ")) if config.debug?
|
3466
|
+
@parser.parse(buff)
|
2735
3467
|
end
|
2736
3468
|
|
2737
3469
|
#############################
|
@@ -2807,7 +3539,7 @@ module Net
|
|
2807
3539
|
|
2808
3540
|
def put_string(str)
|
2809
3541
|
@sock.print(str)
|
2810
|
-
if
|
3542
|
+
if config.debug?
|
2811
3543
|
if @debug_output_bol
|
2812
3544
|
$stderr.print("C: ")
|
2813
3545
|
end
|
@@ -2820,23 +3552,115 @@ module Net
|
|
2820
3552
|
end
|
2821
3553
|
end
|
2822
3554
|
|
2823
|
-
def
|
2824
|
-
if
|
2825
|
-
|
3555
|
+
def enforce_logindisabled?
|
3556
|
+
if config.enforce_logindisabled == :when_capabilities_cached
|
3557
|
+
capabilities_cached?
|
2826
3558
|
else
|
2827
|
-
|
3559
|
+
config.enforce_logindisabled
|
3560
|
+
end
|
3561
|
+
end
|
3562
|
+
|
3563
|
+
def expunge_internal(...)
|
3564
|
+
synchronize do
|
3565
|
+
send_command(...)
|
3566
|
+
expunged_array = clear_responses("EXPUNGE")
|
3567
|
+
vanished_array = extract_responses("VANISHED") { !_1.earlier? }
|
3568
|
+
if vanished_array.empty?
|
3569
|
+
expunged_array
|
3570
|
+
elsif vanished_array.length == 1
|
3571
|
+
vanished_array.first
|
3572
|
+
else
|
3573
|
+
merged_uids = SequenceSet[*vanished_array.map(&:uids)]
|
3574
|
+
VanishedData[uids: merged_uids, earlier: false]
|
3575
|
+
end
|
3576
|
+
end
|
3577
|
+
end
|
3578
|
+
|
3579
|
+
RETURN_WHOLE = /\ARETURN\z/i
|
3580
|
+
RETURN_START = /\ARETURN\b/i
|
3581
|
+
private_constant :RETURN_WHOLE, :RETURN_START
|
3582
|
+
|
3583
|
+
def search_args(keys, charset_arg = nil, return: nil, charset: nil)
|
3584
|
+
{return:} => {return: return_kw}
|
3585
|
+
case [return_kw, keys]
|
3586
|
+
in [nil, Array[RETURN_WHOLE, return_opts, *keys]]
|
3587
|
+
return_opts = convert_return_opts(return_opts)
|
3588
|
+
esearch = true
|
3589
|
+
in [nil => return_opts, RETURN_START]
|
3590
|
+
esearch = true
|
3591
|
+
in [nil => return_opts, keys]
|
3592
|
+
esearch = false
|
3593
|
+
in [_, Array[RETURN_WHOLE, _, *] | RETURN_START]
|
3594
|
+
raise ArgumentError, "conflicting return options"
|
3595
|
+
in [_, Array[RETURN_WHOLE, _, *]] # workaround for https://bugs.ruby-lang.org/issues/20956
|
3596
|
+
raise ArgumentError, "conflicting return options"
|
3597
|
+
in [_, RETURN_START] # workaround for https://bugs.ruby-lang.org/issues/20956
|
3598
|
+
raise ArgumentError, "conflicting return options"
|
3599
|
+
in [return_opts, keys]
|
3600
|
+
return_opts = convert_return_opts(return_opts)
|
3601
|
+
esearch = true
|
3602
|
+
end
|
3603
|
+
if charset && charset_arg
|
3604
|
+
raise ArgumentError, "multiple charset arguments"
|
2828
3605
|
end
|
3606
|
+
charset ||= charset_arg
|
3607
|
+
# NOTE: not handling combined RETURN and CHARSET for raw strings
|
3608
|
+
if charset && keys in /\ACHARSET\b/i | Array[/\ACHARSET\z/i, *]
|
3609
|
+
raise ArgumentError, "multiple charset arguments"
|
3610
|
+
end
|
3611
|
+
args = normalize_searching_criteria(keys)
|
3612
|
+
args.prepend("CHARSET", charset) if charset
|
3613
|
+
args.prepend("RETURN", return_opts) if return_opts
|
3614
|
+
return args, esearch
|
3615
|
+
end
|
3616
|
+
|
3617
|
+
def convert_return_opts(unconverted)
|
3618
|
+
return_opts = Array.try_convert(unconverted) or
|
3619
|
+
raise TypeError, "expected return options to be Array, got %s" % [
|
3620
|
+
unconverted.class
|
3621
|
+
]
|
3622
|
+
return_opts.map {|opt|
|
3623
|
+
case opt
|
3624
|
+
when Symbol then opt.to_s
|
3625
|
+
when PartialRange::Negative then PartialRange[opt]
|
3626
|
+
when Range then SequenceSet[opt]
|
3627
|
+
else opt
|
3628
|
+
end
|
3629
|
+
}
|
3630
|
+
end
|
3631
|
+
|
3632
|
+
def search_internal(cmd, ...)
|
3633
|
+
args, esearch = search_args(...)
|
2829
3634
|
synchronize do
|
2830
|
-
|
2831
|
-
|
3635
|
+
tagged = send_command(cmd, *args)
|
3636
|
+
tag = tagged.tag
|
3637
|
+
# Only the last ESEARCH or SEARCH is used. Excess results are ignored.
|
3638
|
+
esearch_result = extract_responses("ESEARCH") {|response|
|
3639
|
+
response in ESearchResult(tag: ^tag)
|
3640
|
+
}.last
|
3641
|
+
search_result = clear_responses("SEARCH").last
|
3642
|
+
if esearch_result
|
3643
|
+
# silently ignore SEARCH results, if any
|
3644
|
+
esearch_result
|
3645
|
+
elsif search_result
|
3646
|
+
# warn EXPECTED_ESEARCH_RESULT if esearch
|
3647
|
+
search_result
|
3648
|
+
elsif esearch
|
3649
|
+
# warn NO_SEARCH_RESPONSE
|
3650
|
+
ESearchResult[tag:, uid: cmd.start_with?("UID ")]
|
2832
3651
|
else
|
2833
|
-
|
3652
|
+
# warn NO_SEARCH_RESPONSE
|
3653
|
+
SearchResult[]
|
2834
3654
|
end
|
2835
|
-
clear_responses("SEARCH").last || []
|
2836
3655
|
end
|
2837
3656
|
end
|
2838
3657
|
|
2839
|
-
def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
|
3658
|
+
def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
|
3659
|
+
set = SequenceSet[set]
|
3660
|
+
if partial
|
3661
|
+
mod ||= []
|
3662
|
+
mod << "PARTIAL" << PartialRange[partial]
|
3663
|
+
end
|
2840
3664
|
if changedsince
|
2841
3665
|
mod ||= []
|
2842
3666
|
mod << "CHANGEDSINCE" << Integer(changedsince)
|
@@ -2850,39 +3674,36 @@ module Net
|
|
2850
3674
|
}
|
2851
3675
|
end
|
2852
3676
|
|
2853
|
-
|
2854
|
-
|
2855
|
-
|
2856
|
-
send_command(cmd, MessageSet.new(set), attr, mod)
|
2857
|
-
else
|
2858
|
-
send_command(cmd, MessageSet.new(set), attr)
|
2859
|
-
end
|
2860
|
-
clear_responses("FETCH")
|
2861
|
-
end
|
3677
|
+
args = [cmd, set, attr]
|
3678
|
+
args << mod if mod
|
3679
|
+
send_command_returning_fetch_results(*args)
|
2862
3680
|
end
|
2863
3681
|
|
2864
3682
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
2865
3683
|
attr = RawData.new(attr) if attr.instance_of?(String)
|
2866
|
-
args = [
|
3684
|
+
args = [SequenceSet.new(set)]
|
2867
3685
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
2868
3686
|
args << attr << flags
|
3687
|
+
send_command_returning_fetch_results(cmd, *args)
|
3688
|
+
end
|
3689
|
+
|
3690
|
+
def send_command_returning_fetch_results(...)
|
2869
3691
|
synchronize do
|
2870
3692
|
clear_responses("FETCH")
|
2871
|
-
|
2872
|
-
|
3693
|
+
clear_responses("UIDFETCH")
|
3694
|
+
send_command(...)
|
3695
|
+
fetches = clear_responses("FETCH")
|
3696
|
+
uidfetches = clear_responses("UIDFETCH")
|
3697
|
+
uidfetches.any? ? uidfetches : fetches
|
2873
3698
|
end
|
2874
3699
|
end
|
2875
3700
|
|
2876
3701
|
def copy_internal(cmd, set, mailbox)
|
2877
|
-
send_command(cmd,
|
3702
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
2878
3703
|
end
|
2879
3704
|
|
2880
3705
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
2881
|
-
|
2882
|
-
search_keys = [RawData.new(search_keys)]
|
2883
|
-
else
|
2884
|
-
normalize_searching_criteria(search_keys)
|
2885
|
-
end
|
3706
|
+
search_keys = normalize_searching_criteria(search_keys)
|
2886
3707
|
synchronize do
|
2887
3708
|
send_command(cmd, sort_keys, charset, *search_keys)
|
2888
3709
|
clear_responses("SORT").last || []
|
@@ -2890,25 +3711,39 @@ module Net
|
|
2890
3711
|
end
|
2891
3712
|
|
2892
3713
|
def thread_internal(cmd, algorithm, search_keys, charset)
|
2893
|
-
|
2894
|
-
search_keys = [RawData.new(search_keys)]
|
2895
|
-
else
|
2896
|
-
normalize_searching_criteria(search_keys)
|
2897
|
-
end
|
3714
|
+
search_keys = normalize_searching_criteria(search_keys)
|
2898
3715
|
synchronize do
|
2899
3716
|
send_command(cmd, algorithm, charset, *search_keys)
|
2900
3717
|
clear_responses("THREAD").last || []
|
2901
3718
|
end
|
2902
3719
|
end
|
2903
3720
|
|
2904
|
-
def normalize_searching_criteria(
|
2905
|
-
|
2906
|
-
|
2907
|
-
|
2908
|
-
|
3721
|
+
def normalize_searching_criteria(criteria)
|
3722
|
+
return [RawData.new(criteria)] if criteria.is_a?(String)
|
3723
|
+
criteria.map {|i|
|
3724
|
+
if coerce_search_arg_to_seqset?(i)
|
3725
|
+
SequenceSet[i]
|
2909
3726
|
else
|
2910
3727
|
i
|
2911
3728
|
end
|
3729
|
+
}
|
3730
|
+
end
|
3731
|
+
|
3732
|
+
def coerce_search_arg_to_seqset?(obj)
|
3733
|
+
case obj
|
3734
|
+
when Set, -1, :* then true
|
3735
|
+
when Range then true
|
3736
|
+
when Array then obj.all? { coerce_search_array_arg_to_seqset? _1 }
|
3737
|
+
else obj.respond_to?(:to_sequence_set)
|
3738
|
+
end
|
3739
|
+
end
|
3740
|
+
|
3741
|
+
def coerce_search_array_arg_to_seqset?(obj)
|
3742
|
+
case obj
|
3743
|
+
when Integer then obj.positive? || obj == -1
|
3744
|
+
when String then ResponseParser::Patterns::SEQUENCE_SET_STR.match?(obj.b)
|
3745
|
+
else
|
3746
|
+
coerce_search_arg_to_seqset?(obj)
|
2912
3747
|
end
|
2913
3748
|
end
|
2914
3749
|
|
@@ -2932,15 +3767,39 @@ module Net
|
|
2932
3767
|
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
2933
3768
|
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
2934
3769
|
@sock = SSLSocket.new(@sock, ssl_ctx)
|
3770
|
+
@reader = ResponseReader.new(self, @sock)
|
2935
3771
|
@sock.sync_close = true
|
2936
3772
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
2937
|
-
ssl_socket_connect(@sock,
|
3773
|
+
ssl_socket_connect(@sock, open_timeout)
|
2938
3774
|
if ssl_ctx.verify_mode != VERIFY_NONE
|
2939
3775
|
@sock.post_connection_check(@host)
|
2940
3776
|
@tls_verified = true
|
2941
3777
|
end
|
2942
3778
|
end
|
2943
3779
|
|
3780
|
+
def state_authenticated!(resp = nil)
|
3781
|
+
synchronize do
|
3782
|
+
@capabilities = capabilities_from_resp_code resp if resp
|
3783
|
+
@connection_state = ConnectionState::Authenticated.new
|
3784
|
+
end
|
3785
|
+
end
|
3786
|
+
|
3787
|
+
def state_selected!
|
3788
|
+
synchronize do
|
3789
|
+
@connection_state = ConnectionState::Selected.new
|
3790
|
+
end
|
3791
|
+
end
|
3792
|
+
|
3793
|
+
def state_unselected!
|
3794
|
+
state_authenticated! if connection_state.to_sym == :selected
|
3795
|
+
end
|
3796
|
+
|
3797
|
+
def state_logout!
|
3798
|
+
synchronize do
|
3799
|
+
@connection_state = ConnectionState::Logout.new
|
3800
|
+
end
|
3801
|
+
end
|
3802
|
+
|
2944
3803
|
def sasl_adapter
|
2945
3804
|
SASLAdapter.new(self, &method(:send_command_with_continuations))
|
2946
3805
|
end
|
@@ -2959,8 +3818,10 @@ module Net
|
|
2959
3818
|
end
|
2960
3819
|
|
2961
3820
|
require_relative "imap/errors"
|
3821
|
+
require_relative "imap/config"
|
2962
3822
|
require_relative "imap/command_data"
|
2963
3823
|
require_relative "imap/data_encoding"
|
3824
|
+
require_relative "imap/data_lite"
|
2964
3825
|
require_relative "imap/flags"
|
2965
3826
|
require_relative "imap/response_data"
|
2966
3827
|
require_relative "imap/response_parser"
|