net-imap 0.4.22 → 0.6.3

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -2
  3. data/README.md +10 -4
  4. data/docs/styles.css +75 -14
  5. data/lib/net/imap/authenticators.rb +2 -2
  6. data/lib/net/imap/command_data.rb +40 -95
  7. data/lib/net/imap/config/attr_accessors.rb +8 -9
  8. data/lib/net/imap/config/attr_inheritance.rb +64 -1
  9. data/lib/net/imap/config/attr_type_coercion.rb +22 -10
  10. data/lib/net/imap/config/attr_version_defaults.rb +90 -0
  11. data/lib/net/imap/config.rb +241 -125
  12. data/lib/net/imap/connection_state.rb +48 -0
  13. data/lib/net/imap/data_encoding.rb +80 -31
  14. data/lib/net/imap/deprecated_client_options.rb +6 -3
  15. data/lib/net/imap/errors.rb +158 -0
  16. data/lib/net/imap/esearch_result.rb +225 -0
  17. data/lib/net/imap/fetch_data.rb +126 -47
  18. data/lib/net/imap/flags.rb +1 -1
  19. data/lib/net/imap/response_data.rb +123 -187
  20. data/lib/net/imap/response_parser/parser_utils.rb +19 -23
  21. data/lib/net/imap/response_parser.rb +182 -38
  22. data/lib/net/imap/response_reader.rb +10 -12
  23. data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
  24. data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
  25. data/lib/net/imap/sasl/authenticators.rb +8 -4
  26. data/lib/net/imap/sasl/client_adapter.rb +77 -26
  27. data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
  28. data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
  29. data/lib/net/imap/sasl/external_authenticator.rb +2 -2
  30. data/lib/net/imap/sasl/gs2_header.rb +7 -7
  31. data/lib/net/imap/sasl/login_authenticator.rb +4 -3
  32. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
  33. data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
  34. data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
  35. data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
  36. data/lib/net/imap/sasl.rb +7 -4
  37. data/lib/net/imap/sasl_adapter.rb +0 -1
  38. data/lib/net/imap/search_result.rb +10 -5
  39. data/lib/net/imap/sequence_set.rb +1104 -421
  40. data/lib/net/imap/stringprep/nameprep.rb +1 -1
  41. data/lib/net/imap/stringprep/trace.rb +4 -4
  42. data/lib/net/imap/uidplus_data.rb +4 -147
  43. data/lib/net/imap/vanished_data.rb +65 -0
  44. data/lib/net/imap.rb +1002 -313
  45. data/net-imap.gemspec +1 -1
  46. data/rakelib/rfcs.rake +2 -0
  47. data/rakelib/string_prep_tables_generator.rb +6 -2
  48. metadata +7 -3
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://tools.ietf.org/html/rfc3501]
29
- # and {IMAP4rev2 [RFC9051]}[https://tools.ietf.org/html/rfc9051].
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
  #
@@ -53,6 +53,8 @@ module Net
53
53
  # states: <tt>not authenticated</tt>, +authenticated+, +selected+, and
54
54
  # +logout+. Most commands are valid only in certain states.
55
55
  #
56
+ # See #connection_state.
57
+ #
56
58
  # === Sequence numbers and UIDs
57
59
  #
58
60
  # Messages have two sorts of identifiers: message sequence
@@ -342,23 +344,23 @@ module Net
342
344
  # === Core \IMAP commands
343
345
  #
344
346
  # The following commands are defined either by
345
- # the [IMAP4rev1[https://tools.ietf.org/html/rfc3501]] base specification, or
347
+ # the [IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501]] base specification, or
346
348
  # by one of the following extensions:
347
- # [IDLE[https://tools.ietf.org/html/rfc2177]],
348
- # [NAMESPACE[https://tools.ietf.org/html/rfc2342]],
349
- # [UNSELECT[https://tools.ietf.org/html/rfc3691]],
350
- # [ENABLE[https://tools.ietf.org/html/rfc5161]],
351
- # [MOVE[https://tools.ietf.org/html/rfc6851]].
349
+ # [IDLE[https://www.rfc-editor.org/rfc/rfc2177]],
350
+ # [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]],
351
+ # [UNSELECT[https://www.rfc-editor.org/rfc/rfc3691]],
352
+ # [ENABLE[https://www.rfc-editor.org/rfc/rfc5161]],
353
+ # [MOVE[https://www.rfc-editor.org/rfc/rfc6851]].
352
354
  # These extensions are widely supported by modern IMAP4rev1 servers and have
353
- # all been integrated into [IMAP4rev2[https://tools.ietf.org/html/rfc9051]].
355
+ # all been integrated into [IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]].
354
356
  # <em>*NOTE:* Net::IMAP doesn't support IMAP4rev2 yet.</em>
355
357
  #
356
358
  # ==== Any state
357
359
  #
358
360
  # - #capability: Returns the server's capabilities as an array of strings.
359
361
  #
360
- # <em>In general, #capable? should be used rather than explicitly sending a
361
- # +CAPABILITY+ command to the server.</em>
362
+ # <em>In general,</em> #capable? <em>should be used rather than explicitly
363
+ # sending a +CAPABILITY+ command to the server.</em>
362
364
  # - #noop: Allows the server to send unsolicited untagged #responses.
363
365
  # - #logout: Tells the server to end the session. Enters the +logout+ state.
364
366
  #
@@ -446,7 +448,7 @@ module Net
446
448
  #
447
449
  # ==== RFC9051: +IMAP4rev2+
448
450
  #
449
- # Although IMAP4rev2[https://tools.ietf.org/html/rfc9051] is not supported
451
+ # Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not supported
450
452
  # yet, Net::IMAP supports several extensions that have been folded into it:
451
453
  # +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+,
452
454
  # <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
@@ -456,7 +458,7 @@ module Net
456
458
  # >>>
457
459
  # <em>The following are folded into +IMAP4rev2+ but are currently
458
460
  # unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
459
- # extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
461
+ # extensions, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
460
462
  # +LITERAL-+, and +SPECIAL-USE+.</em>
461
463
  #
462
464
  # ==== RFC2087: +QUOTA+
@@ -466,13 +468,13 @@ module Net
466
468
  # - #setquota: sets the resource limits for a given quota root.
467
469
  #
468
470
  # ==== RFC2177: +IDLE+
469
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
471
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
470
472
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
471
473
  # - #idle: Allows the server to send updates to the client, without the client
472
474
  # needing to poll using #noop.
473
475
  #
474
476
  # ==== RFC2342: +NAMESPACE+
475
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
477
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
476
478
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
477
479
  # - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
478
480
  #
@@ -481,7 +483,7 @@ module Net
481
483
  #
482
484
  # ==== RFC3516: +BINARY+
483
485
  # The fetch side of +BINARY+ has been folded into
484
- # IMAP4rev2[https://tools.ietf.org/html/rfc9051].
486
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
485
487
  # - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
486
488
  # +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
487
489
  #
@@ -489,7 +491,7 @@ module Net
489
491
  # *NOTE:* The binary extension the #append command is _not_ supported yet.
490
492
  #
491
493
  # ==== RFC3691: +UNSELECT+
492
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
494
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
493
495
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
494
496
  # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
495
497
  # without expunging any messages.
@@ -501,19 +503,23 @@ module Net
501
503
  # *NOTE:* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
502
504
  #
503
505
  # ==== RFC4315: +UIDPLUS+
504
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
506
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
505
507
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
506
508
  # - #uid_expunge: Restricts #expunge to only remove the specified UIDs.
507
509
  # - Updates #select, #examine with the +UIDNOTSTICKY+ ResponseCode
508
510
  # - Updates #append with the +APPENDUID+ ResponseCode
509
511
  # - Updates #copy, #move with the +COPYUID+ ResponseCode
510
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
+ #
511
517
  # ==== RFC4959: +SASL-IR+
512
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
518
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
513
519
  # - Updates #authenticate with the option to send an initial response.
514
520
  #
515
521
  # ==== RFC5161: +ENABLE+
516
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
522
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
517
523
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
518
524
  # - #enable: Enables backwards incompatible server extensions.
519
525
  #
@@ -537,7 +543,7 @@ module Net
537
543
  # +X-GM-THRID+, but Gmail does not support it (as of 2023-11-10).
538
544
  #
539
545
  # ==== RFC6851: +MOVE+
540
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
546
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
541
547
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
542
548
  # - #move, #uid_move: Moves the specified messages to the end of the
543
549
  # specified destination mailbox, expunging them from the current mailbox.
@@ -572,6 +578,17 @@ module Net
572
578
  # See FetchData#emailid and FetchData#emailid.
573
579
  # - Updates #status with support for the +MAILBOXID+ status attribute.
574
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
+ #
575
592
  # == References
576
593
  #
577
594
  # [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
@@ -602,57 +619,57 @@ module Net
602
619
  # Gahrns, M., "IMAP4 Multi-Accessed Mailbox Practice", RFC 2180, DOI
603
620
  # 10.17487/RFC2180, July 1997, <https://www.rfc-editor.org/info/rfc2180>.
604
621
  #
605
- # [UTF7[https://tools.ietf.org/html/rfc2152]]::
622
+ # [UTF7[https://www.rfc-editor.org/rfc/rfc2152]]::
606
623
  # Goldsmith, D. and M. Davis, "UTF-7 A Mail-Safe Transformation Format of
607
624
  # Unicode", RFC 2152, DOI 10.17487/RFC2152, May 1997,
608
625
  # <https://www.rfc-editor.org/info/rfc2152>.
609
626
  #
610
627
  # === Message envelope and body structure
611
628
  #
612
- # [RFC5322[https://tools.ietf.org/html/rfc5322]]::
629
+ # [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]::
613
630
  # Resnick, P., Ed., "Internet Message Format",
614
631
  # RFC 5322, DOI 10.17487/RFC5322, October 2008,
615
632
  # <https://www.rfc-editor.org/info/rfc5322>.
616
633
  #
617
634
  # <em>Note: obsoletes</em>
618
- # RFC-2822[https://tools.ietf.org/html/rfc2822]<em> (April 2001) and</em>
619
- # RFC-822[https://tools.ietf.org/html/rfc822]<em> (August 1982).</em>
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>
620
637
  #
621
- # [CHARSET[https://tools.ietf.org/html/rfc2978]]::
638
+ # [CHARSET[https://www.rfc-editor.org/rfc/rfc2978]]::
622
639
  # Freed, N. and J. Postel, "IANA Charset Registration Procedures", BCP 19,
623
640
  # RFC 2978, DOI 10.17487/RFC2978, October 2000,
624
641
  # <https://www.rfc-editor.org/info/rfc2978>.
625
642
  #
626
- # [DISPOSITION[https://tools.ietf.org/html/rfc2183]]::
643
+ # [DISPOSITION[https://www.rfc-editor.org/rfc/rfc2183]]::
627
644
  # Troost, R., Dorner, S., and K. Moore, Ed., "Communicating Presentation
628
645
  # Information in Internet Messages: The Content-Disposition Header
629
646
  # Field", RFC 2183, DOI 10.17487/RFC2183, August 1997,
630
647
  # <https://www.rfc-editor.org/info/rfc2183>.
631
648
  #
632
- # [MIME-IMB[https://tools.ietf.org/html/rfc2045]]::
649
+ # [MIME-IMB[https://www.rfc-editor.org/rfc/rfc2045]]::
633
650
  # Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
634
651
  # (MIME) Part One: Format of Internet Message Bodies",
635
652
  # RFC 2045, DOI 10.17487/RFC2045, November 1996,
636
653
  # <https://www.rfc-editor.org/info/rfc2045>.
637
654
  #
638
- # [MIME-IMT[https://tools.ietf.org/html/rfc2046]]::
655
+ # [MIME-IMT[https://www.rfc-editor.org/rfc/rfc2046]]::
639
656
  # Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
640
657
  # (MIME) Part Two: Media Types", RFC 2046, DOI 10.17487/RFC2046,
641
658
  # November 1996, <https://www.rfc-editor.org/info/rfc2046>.
642
659
  #
643
- # [MIME-HDRS[https://tools.ietf.org/html/rfc2047]]::
660
+ # [MIME-HDRS[https://www.rfc-editor.org/rfc/rfc2047]]::
644
661
  # Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part Three:
645
662
  # Message Header Extensions for Non-ASCII Text",
646
663
  # RFC 2047, DOI 10.17487/RFC2047, November 1996,
647
664
  # <https://www.rfc-editor.org/info/rfc2047>.
648
665
  #
649
- # [RFC2231[https://tools.ietf.org/html/rfc2231]]::
666
+ # [RFC2231[https://www.rfc-editor.org/rfc/rfc2231]]::
650
667
  # Freed, N. and K. Moore, "MIME Parameter Value and Encoded Word
651
668
  # Extensions: Character Sets, Languages, and Continuations",
652
669
  # RFC 2231, DOI 10.17487/RFC2231, November 1997,
653
670
  # <https://www.rfc-editor.org/info/rfc2231>.
654
671
  #
655
- # [I18n-HDRS[https://tools.ietf.org/html/rfc6532]]::
672
+ # [I18n-HDRS[https://www.rfc-editor.org/rfc/rfc6532]]::
656
673
  # Yang, A., Steele, S., and N. Freed, "Internationalized Email Headers",
657
674
  # RFC 6532, DOI 10.17487/RFC6532, February 2012,
658
675
  # <https://www.rfc-editor.org/info/rfc6532>.
@@ -668,12 +685,12 @@ module Net
668
685
  # RFC 2557, DOI 10.17487/RFC2557, March 1999,
669
686
  # <https://www.rfc-editor.org/info/rfc2557>.
670
687
  #
671
- # [MD5[https://tools.ietf.org/html/rfc1864]]::
688
+ # [MD5[https://www.rfc-editor.org/rfc/rfc1864]]::
672
689
  # Myers, J. and M. Rose, "The Content-MD5 Header Field",
673
690
  # RFC 1864, DOI 10.17487/RFC1864, October 1995,
674
691
  # <https://www.rfc-editor.org/info/rfc1864>.
675
692
  #
676
- # [RFC3503[https://tools.ietf.org/html/rfc3503]]::
693
+ # [RFC3503[https://www.rfc-editor.org/rfc/rfc3503]]::
677
694
  # Melnikov, A., "Message Disposition Notification (MDN)
678
695
  # profile for Internet Message Access Protocol (IMAP)",
679
696
  # RFC 3503, DOI 10.17487/RFC3503, March 2003,
@@ -681,27 +698,27 @@ module Net
681
698
  #
682
699
  # === \IMAP Extensions
683
700
  #
684
- # [QUOTA[https://tools.ietf.org/html/rfc9208]]::
701
+ # [QUOTA[https://www.rfc-editor.org/rfc/rfc9208]]::
685
702
  # Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
686
703
  # March 2022, <https://www.rfc-editor.org/info/rfc9208>.
687
704
  #
688
705
  # <em>Note: obsoletes</em>
689
- # RFC-2087[https://tools.ietf.org/html/rfc2087]<em> (January 1997)</em>.
706
+ # RFC-2087[https://www.rfc-editor.org/rfc/rfc2087]<em> (January 1997)</em>.
690
707
  # <em>Net::IMAP does not fully support the RFC9208 updates yet.</em>
691
- # [IDLE[https://tools.ietf.org/html/rfc2177]]::
708
+ # [IDLE[https://www.rfc-editor.org/rfc/rfc2177]]::
692
709
  # Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
693
710
  # June 1997, <https://www.rfc-editor.org/info/rfc2177>.
694
- # [NAMESPACE[https://tools.ietf.org/html/rfc2342]]::
711
+ # [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]]::
695
712
  # Gahrns, M. and C. Newman, "IMAP4 Namespace", RFC 2342,
696
713
  # DOI 10.17487/RFC2342, May 1998, <https://www.rfc-editor.org/info/rfc2342>.
697
- # [ID[https://tools.ietf.org/html/rfc2971]]::
714
+ # [ID[https://www.rfc-editor.org/rfc/rfc2971]]::
698
715
  # Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
699
716
  # October 2000, <https://www.rfc-editor.org/info/rfc2971>.
700
- # [BINARY[https://tools.ietf.org/html/rfc3516]]::
717
+ # [BINARY[https://www.rfc-editor.org/rfc/rfc3516]]::
701
718
  # Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
702
719
  # DOI 10.17487/RFC3516, April 2003,
703
720
  # <https://www.rfc-editor.org/info/rfc3516>.
704
- # [ACL[https://tools.ietf.org/html/rfc4314]]::
721
+ # [ACL[https://www.rfc-editor.org/rfc/rfc4314]]::
705
722
  # Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
706
723
  # DOI 10.17487/RFC4314, December 2005,
707
724
  # <https://www.rfc-editor.org/info/rfc4314>.
@@ -709,36 +726,46 @@ module Net
709
726
  # Crispin, M., "Internet Message Access Protocol (\IMAP) - UIDPLUS
710
727
  # extension", RFC 4315, DOI 10.17487/RFC4315, December 2005,
711
728
  # <https://www.rfc-editor.org/info/rfc4315>.
712
- # [SORT[https://tools.ietf.org/html/rfc5256]]::
729
+ # [SORT[https://www.rfc-editor.org/rfc/rfc5256]]::
713
730
  # Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
714
731
  # THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
715
732
  # <https://www.rfc-editor.org/info/rfc5256>.
716
- # [THREAD[https://tools.ietf.org/html/rfc5256]]::
733
+ # [THREAD[https://www.rfc-editor.org/rfc/rfc5256]]::
717
734
  # Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
718
735
  # THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
719
736
  # <https://www.rfc-editor.org/info/rfc5256>.
720
737
  # [RFC5530[https://www.rfc-editor.org/rfc/rfc5530.html]]::
721
738
  # Gulbrandsen, A., "IMAP Response Codes", RFC 5530, DOI 10.17487/RFC5530,
722
739
  # May 2009, <https://www.rfc-editor.org/info/rfc5530>.
723
- # [MOVE[https://tools.ietf.org/html/rfc6851]]::
740
+ # [MOVE[https://www.rfc-editor.org/rfc/rfc6851]]::
724
741
  # Gulbrandsen, A. and N. Freed, Ed., "Internet Message Access Protocol
725
742
  # (\IMAP) - MOVE Extension", RFC 6851, DOI 10.17487/RFC6851, January 2013,
726
743
  # <https://www.rfc-editor.org/info/rfc6851>.
727
- # [UTF8=ACCEPT[https://tools.ietf.org/html/rfc6855]]::
728
- # [UTF8=ONLY[https://tools.ietf.org/html/rfc6855]]::
744
+ # [UTF8=ACCEPT[https://www.rfc-editor.org/rfc/rfc6855]]::
745
+ # [UTF8=ONLY[https://www.rfc-editor.org/rfc/rfc6855]]::
729
746
  # Resnick, P., Ed., Newman, C., Ed., and S. Shen, Ed.,
730
747
  # "IMAP Support for UTF-8", RFC 6855, DOI 10.17487/RFC6855, March 2013,
731
748
  # <https://www.rfc-editor.org/info/rfc6855>.
732
- # [CONDSTORE[https://tools.ietf.org/html/rfc7162]]::
733
- # [QRESYNC[https://tools.ietf.org/html/rfc7162]]::
749
+ # [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162]]::
750
+ # [QRESYNC[https://www.rfc-editor.org/rfc/rfc7162]]::
734
751
  # Melnikov, A. and D. Cridland, "IMAP Extensions: Quick Flag Changes
735
752
  # Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization
736
753
  # (QRESYNC)", RFC 7162, DOI 10.17487/RFC7162, May 2014,
737
754
  # <https://www.rfc-editor.org/info/rfc7162>.
738
- # [OBJECTID[https://tools.ietf.org/html/rfc8474]]::
755
+ # [OBJECTID[https://www.rfc-editor.org/rfc/rfc8474]]::
739
756
  # Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
740
757
  # RFC 8474, DOI 10.17487/RFC8474, September 2018,
741
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>.
742
769
  #
743
770
  # === IANA registries
744
771
  # * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
@@ -752,7 +779,7 @@ module Net
752
779
  # * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
753
780
  # +imap+
754
781
  # * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
755
- # ===== For currently unsupported features:
782
+ # ==== For currently unsupported features:
756
783
  # * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
757
784
  # * {LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
758
785
  # * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
@@ -761,7 +788,7 @@ module Net
761
788
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
762
789
  #
763
790
  class IMAP < Protocol
764
- VERSION = "0.4.22"
791
+ VERSION = "0.6.3"
765
792
 
766
793
  # Aliases for supported capabilities, to be used with the #enable command.
767
794
  ENABLE_ALIASES = {
@@ -769,15 +796,30 @@ module Net
769
796
  "UTF8=ONLY" => "UTF8=ACCEPT",
770
797
  }.freeze
771
798
 
772
- autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__)
773
- autoload :SASL, File.expand_path("imap/sasl", __dir__)
774
- autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
775
- autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
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 :SequenceSet, "#{dir}/sequence_set"
805
+ autoload :StringPrep, "#{dir}/stringprep"
776
806
 
777
807
  include MonitorMixin
778
- if defined?(OpenSSL::SSL)
779
- include OpenSSL
780
- include SSL
808
+
809
+ # :call-seq:
810
+ # Net::IMAP::SequenceSet(set = nil) -> SequenceSet
811
+ #
812
+ # Coerces +set+ into a SequenceSet, using either SequenceSet.try_convert or
813
+ # SequenceSet.new.
814
+ #
815
+ # * When +set+ is a SequenceSet, that same set is returned.
816
+ # * When +set+ responds to +to_sequence_set+, +set.to_sequence_set+ is
817
+ # returned.
818
+ # * Otherwise, returns the result from calling SequenceSet.new with +set+.
819
+ #
820
+ # Related: SequenceSet.try_convert, SequenceSet.new, SequenceSet::[]
821
+ def self.SequenceSet(set = nil)
822
+ SequenceSet.try_convert(set) || SequenceSet.new(set)
781
823
  end
782
824
 
783
825
  # Returns the global Config object
@@ -862,6 +904,67 @@ module Net
862
904
  # Returns +false+ for a plaintext connection.
863
905
  attr_reader :ssl_ctx_params
864
906
 
907
+ # Returns the current connection state.
908
+ #
909
+ # Once an IMAP connection is established, the connection is in one of four
910
+ # states: +not_authenticated+, +authenticated+, +selected+, and +logout+.
911
+ # Most commands are valid only in certain states.
912
+ #
913
+ # The connection state object responds to +to_sym+ and +name+ with the name
914
+ # of the current connection state, as a Symbol or String. Future versions
915
+ # of +net-imap+ may store additional information on the state object.
916
+ #
917
+ # From {RFC9051}[https://www.rfc-editor.org/rfc/rfc9051#section-3]:
918
+ # +----------------------+
919
+ # |connection established|
920
+ # +----------------------+
921
+ # ||
922
+ # \/
923
+ # +--------------------------------------+
924
+ # | server greeting |
925
+ # +--------------------------------------+
926
+ # || (1) || (2) || (3)
927
+ # \/ || ||
928
+ # +-----------------+ || ||
929
+ # |Not Authenticated| || ||
930
+ # +-----------------+ || ||
931
+ # || (7) || (4) || ||
932
+ # || \/ \/ ||
933
+ # || +----------------+ ||
934
+ # || | Authenticated |<=++ ||
935
+ # || +----------------+ || ||
936
+ # || || (7) || (5) || (6) ||
937
+ # || || \/ || ||
938
+ # || || +--------+ || ||
939
+ # || || |Selected|==++ ||
940
+ # || || +--------+ ||
941
+ # || || || (7) ||
942
+ # \/ \/ \/ \/
943
+ # +--------------------------------------+
944
+ # | Logout |
945
+ # +--------------------------------------+
946
+ # ||
947
+ # \/
948
+ # +-------------------------------+
949
+ # |both sides close the connection|
950
+ # +-------------------------------+
951
+ #
952
+ # >>>
953
+ # Legend for the above diagram:
954
+ #
955
+ # 1. connection without pre-authentication (+OK+ #greeting)
956
+ # 2. pre-authenticated connection (+PREAUTH+ #greeting)
957
+ # 3. rejected connection (+BYE+ #greeting)
958
+ # 4. successful #login or #authenticate command
959
+ # 5. successful #select or #examine command
960
+ # 6. #close or #unselect command, unsolicited +CLOSED+ response code, or
961
+ # failed #select or #examine command
962
+ # 7. #logout command, server shutdown, or connection closed
963
+ #
964
+ # Before the server greeting, the state is +not_authenticated+.
965
+ # After the connection closes, the state remains +logout+.
966
+ attr_reader :connection_state
967
+
865
968
  # Creates a new Net::IMAP object and connects it to the specified
866
969
  # +host+.
867
970
  #
@@ -987,6 +1090,8 @@ module Net
987
1090
  @exception = nil
988
1091
  @greeting = nil
989
1092
  @capabilities = nil
1093
+ @tls_verified = false
1094
+ @connection_state = ConnectionState::NotAuthenticated.new
990
1095
 
991
1096
  # Client Protocol Receiver
992
1097
  @parser = ResponseParser.new(config: @config)
@@ -1009,14 +1114,10 @@ module Net
1009
1114
  @logout_command_tag = nil
1010
1115
 
1011
1116
  # Connection
1012
- @tls_verified = false
1013
1117
  @sock = tcp_socket(@host, @port)
1014
1118
  @reader = ResponseReader.new(self, @sock)
1015
1119
  start_tls_session if ssl_ctx
1016
1120
  start_imap_connection
1017
-
1018
- # DEPRECATED: to remove in next version
1019
- @client_thread = Thread.current
1020
1121
  end
1021
1122
 
1022
1123
  # Returns true after the TLS negotiation has completed and the remote
@@ -1024,34 +1125,29 @@ module Net
1024
1125
  # but peer verification was disabled.
1025
1126
  def tls_verified?; @tls_verified end
1026
1127
 
1027
- def client_thread # :nodoc:
1028
- warn "Net::IMAP#client_thread is deprecated and will be removed soon."
1029
- @client_thread
1030
- end
1031
-
1032
1128
  # Disconnects from the server.
1033
1129
  #
1130
+ # Waits for receiver thread to close before returning. Slow or stuck
1131
+ # response handlers can cause #disconnect to hang until they complete.
1132
+ #
1034
1133
  # Related: #logout, #logout!
1035
1134
  def disconnect
1135
+ in_logout_state = try_state_logout?
1036
1136
  return if disconnected?
1037
1137
  begin
1038
- begin
1039
- # try to call SSL::SSLSocket#io.
1040
- @sock.io.shutdown
1041
- rescue NoMethodError
1042
- # @sock is not an SSL::SSLSocket.
1043
- @sock.shutdown
1044
- end
1138
+ @sock.to_io.shutdown
1045
1139
  rescue Errno::ENOTCONN
1046
1140
  # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
1047
1141
  rescue Exception => e
1048
1142
  @receiver_thread.raise(e)
1049
1143
  end
1144
+ @sock.close
1050
1145
  @receiver_thread.join
1051
- synchronize do
1052
- @sock.close
1053
- end
1054
1146
  raise e if e
1147
+ ensure
1148
+ # Try again after shutting down the receiver thread. With no reciever
1149
+ # left to wait for, any remaining locks should be _very_ brief.
1150
+ state_logout! unless in_logout_state
1055
1151
  end
1056
1152
 
1057
1153
  # Returns true if disconnected from the server.
@@ -1197,12 +1293,12 @@ module Net
1197
1293
  # )
1198
1294
  # end
1199
1295
  #
1200
- # See [ID[https://tools.ietf.org/html/rfc2971]] for field definitions.
1296
+ # See [ID[https://www.rfc-editor.org/rfc/rfc2971]] for field definitions.
1201
1297
  #
1202
- # ===== Capabilities
1298
+ # ==== Capabilities
1203
1299
  #
1204
1300
  # The server's capabilities must include +ID+
1205
- # [RFC2971[https://tools.ietf.org/html/rfc2971]].
1301
+ # [RFC2971[https://www.rfc-editor.org/rfc/rfc2971]].
1206
1302
  def id(client_id=nil)
1207
1303
  synchronize do
1208
1304
  send_command("ID", ClientID.new(client_id))
@@ -1285,7 +1381,7 @@ module Net
1285
1381
  #
1286
1382
  # Related: Net::IMAP.new, #login, #authenticate
1287
1383
  #
1288
- # ===== Capability
1384
+ # ==== Capability
1289
1385
  # Clients should not call #starttls unless the server advertises the
1290
1386
  # +STARTTLS+ capability.
1291
1387
  #
@@ -1324,6 +1420,9 @@ module Net
1324
1420
  # +SASL-IR+ capability, below). Defaults to the #config value for
1325
1421
  # {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
1326
1422
  #
1423
+ # The +registry+ kwarg can be used to select the mechanism implementation
1424
+ # from a custom registry. See SASL.authenticator and SASL::Authenticators.
1425
+ #
1327
1426
  # All other arguments are forwarded to the registered SASL authenticator for
1328
1427
  # the requested mechanism. <em>The documentation for each individual
1329
1428
  # mechanism must be consulted for its specific parameters.</em>
@@ -1418,29 +1517,10 @@ module Net
1418
1517
  # Previously cached #capabilities will be cleared when this method
1419
1518
  # completes. If the TaggedResponse to #authenticate includes updated
1420
1519
  # capabilities, they will be cached.
1421
- def authenticate(mechanism, *creds,
1422
- sasl_ir: config.sasl_ir,
1423
- **props, &callback)
1424
- mechanism = mechanism.to_s.tr("_", "-").upcase
1425
- authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
1426
- cmdargs = ["AUTHENTICATE", mechanism]
1427
- if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
1428
- authenticator.respond_to?(:initial_response?) &&
1429
- authenticator.initial_response?
1430
- response = authenticator.process(nil)
1431
- cmdargs << (response.empty? ? "=" : [response].pack("m0"))
1432
- end
1433
- result = send_command_with_continuations(*cmdargs) {|data|
1434
- challenge = data.unpack1("m")
1435
- response = authenticator.process challenge
1436
- [response].pack("m0")
1437
- }
1438
- if authenticator.respond_to?(:done?) && !authenticator.done?
1439
- logout!
1440
- raise SASL::AuthenticationIncomplete, result
1441
- end
1442
- @capabilities = capabilities_from_resp_code result
1443
- result
1520
+ def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
1521
+ sasl_ir = may_depend_on_capabilities_cached?(sasl_ir)
1522
+ sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
1523
+ .tap do state_authenticated! _1 end
1444
1524
  end
1445
1525
 
1446
1526
  # Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
@@ -1457,16 +1537,12 @@ module Net
1457
1537
  #
1458
1538
  # Related: #authenticate, #starttls
1459
1539
  #
1460
- # ===== Capabilities
1540
+ # ==== Capabilities
1461
1541
  #
1462
1542
  # An IMAP client MUST NOT call #login when the server advertises the
1463
- # +LOGINDISABLED+ capability.
1464
- #
1465
- # if imap.capability? "LOGINDISABLED"
1466
- # raise "Remote server has disabled the login command"
1467
- # else
1468
- # imap.login username, password
1469
- # end
1543
+ # +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
1544
+ # LoginDisabledError when that capability is present. See
1545
+ # Config#enforce_logindisabled.
1470
1546
  #
1471
1547
  # Server capabilities may change after #starttls, #login, and #authenticate.
1472
1548
  # Cached capabilities _must_ be invalidated after this method completes.
@@ -1474,8 +1550,11 @@ module Net
1474
1550
  # ResponseCode.
1475
1551
  #
1476
1552
  def login(user, password)
1553
+ if enforce_logindisabled? && capability?("LOGINDISABLED")
1554
+ raise LoginDisabledError
1555
+ end
1477
1556
  send_command("LOGIN", user, password)
1478
- .tap { @capabilities = capabilities_from_resp_code _1 }
1557
+ .tap do state_authenticated! _1 end
1479
1558
  end
1480
1559
 
1481
1560
  # Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
@@ -1491,7 +1570,7 @@ module Net
1491
1570
  # When the +condstore+ keyword argument is true, the server is told to
1492
1571
  # enable the extension. If +mailbox+ supports persistence of mod-sequences,
1493
1572
  # the +HIGHESTMODSEQ+ ResponseCode will be sent as an untagged response to
1494
- # #select and all `FETCH` responses will include FetchData#modseq.
1573
+ # #select and all +FETCH+ responses will include FetchData#modseq.
1495
1574
  # Otherwise, the +NOMODSEQ+ ResponseCode will be sent.
1496
1575
  #
1497
1576
  # A Net::IMAP::NoResponseError is raised if the mailbox does not
@@ -1499,7 +1578,7 @@ module Net
1499
1578
  #
1500
1579
  # Related: #examine
1501
1580
  #
1502
- # ===== Capabilities
1581
+ # ==== Capabilities
1503
1582
  #
1504
1583
  # If [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] is supported,
1505
1584
  # the server may return an untagged "NO" response with a "UIDNOTSTICKY"
@@ -1515,8 +1594,10 @@ module Net
1515
1594
  args = ["SELECT", mailbox]
1516
1595
  args << ["CONDSTORE"] if condstore
1517
1596
  synchronize do
1597
+ state_unselected! # implicitly closes current mailbox
1518
1598
  @responses.clear
1519
1599
  send_command(*args)
1600
+ .tap do state_selected! end
1520
1601
  end
1521
1602
  end
1522
1603
 
@@ -1533,8 +1614,10 @@ module Net
1533
1614
  args = ["EXAMINE", mailbox]
1534
1615
  args << ["CONDSTORE"] if condstore
1535
1616
  synchronize do
1617
+ state_unselected! # implicitly closes current mailbox
1536
1618
  @responses.clear
1537
1619
  send_command(*args)
1620
+ .tap do state_selected! end
1538
1621
  end
1539
1622
  end
1540
1623
 
@@ -1617,7 +1700,7 @@ module Net
1617
1700
  #
1618
1701
  # Related: #lsub, MailboxList
1619
1702
  #
1620
- # ===== For example:
1703
+ # ==== For example:
1621
1704
  #
1622
1705
  # imap.create("foo/bar")
1623
1706
  # imap.create("foo/baz")
@@ -1655,7 +1738,7 @@ module Net
1655
1738
  # servers, then folder creation (and listing, moving, etc) can lead to
1656
1739
  # errors.
1657
1740
  #
1658
- # From RFC2342[https://tools.ietf.org/html/rfc2342]:
1741
+ # From RFC2342[https://www.rfc-editor.org/rfc/rfc2342]:
1659
1742
  # >>>
1660
1743
  # <em>Although typically a server will support only a single Personal
1661
1744
  # Namespace, and a single Other User's Namespace, circumstances exist
@@ -1668,7 +1751,7 @@ module Net
1668
1751
  #
1669
1752
  # Related: #list, Namespaces, Namespace
1670
1753
  #
1671
- # ===== For example:
1754
+ # ==== For example:
1672
1755
  #
1673
1756
  # if capable?("NAMESPACE")
1674
1757
  # namespaces = imap.namespace
@@ -1682,10 +1765,10 @@ module Net
1682
1765
  # end
1683
1766
  # end
1684
1767
  #
1685
- # ===== Capabilities
1768
+ # ==== Capabilities
1686
1769
  #
1687
- # The server's capabilities must include +NAMESPACE+
1688
- # [RFC2342[https://tools.ietf.org/html/rfc2342]].
1770
+ # The server's capabilities must include either +IMAP4rev2+ or +NAMESPACE+
1771
+ # [RFC2342[https://www.rfc-editor.org/rfc/rfc2342]].
1689
1772
  def namespace
1690
1773
  synchronize do
1691
1774
  send_command("NAMESPACE")
@@ -1721,7 +1804,7 @@ module Net
1721
1804
  #
1722
1805
  # Related: #list, MailboxList
1723
1806
  #
1724
- # ===== Capabilities
1807
+ # ==== Capabilities
1725
1808
  #
1726
1809
  # The server's capabilities must include +XLIST+,
1727
1810
  # a deprecated Gmail extension (replaced by +SPECIAL-USE+).
@@ -1744,10 +1827,10 @@ module Net
1744
1827
  #
1745
1828
  # Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
1746
1829
  #
1747
- # ===== Capabilities
1830
+ # ==== Capabilities
1748
1831
  #
1749
1832
  # The server's capabilities must include +QUOTA+
1750
- # [RFC2087[https://tools.ietf.org/html/rfc2087]].
1833
+ # [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
1751
1834
  def getquotaroot(mailbox)
1752
1835
  synchronize do
1753
1836
  send_command("GETQUOTAROOT", mailbox)
@@ -1765,10 +1848,10 @@ module Net
1765
1848
  #
1766
1849
  # Related: #getquotaroot, #setquota, MailboxQuota
1767
1850
  #
1768
- # ===== Capabilities
1851
+ # ==== Capabilities
1769
1852
  #
1770
1853
  # The server's capabilities must include +QUOTA+
1771
- # [RFC2087[https://tools.ietf.org/html/rfc2087]].
1854
+ # [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
1772
1855
  def getquota(mailbox)
1773
1856
  synchronize do
1774
1857
  send_command("GETQUOTA", mailbox)
@@ -1783,10 +1866,10 @@ module Net
1783
1866
  #
1784
1867
  # Related: #getquota, #getquotaroot
1785
1868
  #
1786
- # ===== Capabilities
1869
+ # ==== Capabilities
1787
1870
  #
1788
1871
  # The server's capabilities must include +QUOTA+
1789
- # [RFC2087[https://tools.ietf.org/html/rfc2087]].
1872
+ # [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
1790
1873
  def setquota(mailbox, quota)
1791
1874
  if quota.nil?
1792
1875
  data = '()'
@@ -1803,10 +1886,10 @@ module Net
1803
1886
  #
1804
1887
  # Related: #getacl
1805
1888
  #
1806
- # ===== Capabilities
1889
+ # ==== Capabilities
1807
1890
  #
1808
1891
  # The server's capabilities must include +ACL+
1809
- # [RFC4314[https://tools.ietf.org/html/rfc4314]].
1892
+ # [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
1810
1893
  def setacl(mailbox, user, rights)
1811
1894
  if rights.nil?
1812
1895
  send_command("SETACL", mailbox, user, "")
@@ -1821,10 +1904,10 @@ module Net
1821
1904
  #
1822
1905
  # Related: #setacl, MailboxACLItem
1823
1906
  #
1824
- # ===== Capabilities
1907
+ # ==== Capabilities
1825
1908
  #
1826
1909
  # The server's capabilities must include +ACL+
1827
- # [RFC4314[https://tools.ietf.org/html/rfc4314]].
1910
+ # [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
1828
1911
  def getacl(mailbox)
1829
1912
  synchronize do
1830
1913
  send_command("GETACL", mailbox)
@@ -1858,7 +1941,7 @@ module Net
1858
1941
  # for +mailbox+ cannot be returned; for instance, because it
1859
1942
  # does not exist.
1860
1943
  #
1861
- # ===== Supported attributes
1944
+ # ==== Supported attributes
1862
1945
  #
1863
1946
  # +MESSAGES+:: The number of messages in the mailbox.
1864
1947
  #
@@ -1889,12 +1972,12 @@ module Net
1889
1972
  # Unsupported attributes may be requested. The attribute value will be
1890
1973
  # either an Integer or an ExtensionData object.
1891
1974
  #
1892
- # ===== For example:
1975
+ # ==== For example:
1893
1976
  #
1894
1977
  # p imap.status("inbox", ["MESSAGES", "RECENT"])
1895
1978
  # #=> {"RECENT"=>0, "MESSAGES"=>44}
1896
1979
  #
1897
- # ===== Capabilities
1980
+ # ==== Capabilities
1898
1981
  #
1899
1982
  # +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
1900
1983
  # <tt>STATUS=SIZE</tt>
@@ -1934,11 +2017,11 @@ module Net
1934
2017
  # not exist (it is not created automatically), or if the flags,
1935
2018
  # date_time, or message arguments contain errors.
1936
2019
  #
1937
- # ===== Capabilities
2020
+ # ==== Capabilities
1938
2021
  #
1939
2022
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
1940
2023
  # supported and the destination supports persistent UIDs, the server's
1941
- # response should include an +APPENDUID+ response code with UIDPlusData.
2024
+ # response should include an +APPENDUID+ response code with AppendUIDData.
1942
2025
  # This will report the UIDVALIDITY of the destination mailbox and the
1943
2026
  # assigned UID of the appended message.
1944
2027
  #
@@ -1973,6 +2056,7 @@ module Net
1973
2056
  # Related: #unselect
1974
2057
  def close
1975
2058
  send_command("CLOSE")
2059
+ .tap do state_authenticated! end
1976
2060
  end
1977
2061
 
1978
2062
  # Sends an {UNSELECT command [RFC3691 §2]}[https://www.rfc-editor.org/rfc/rfc3691#section-3]
@@ -1983,129 +2067,493 @@ module Net
1983
2067
  #
1984
2068
  # Related: #close
1985
2069
  #
1986
- # ===== Capabilities
2070
+ # ==== Capabilities
1987
2071
  #
1988
- # The server's capabilities must include +UNSELECT+
1989
- # [RFC3691[https://tools.ietf.org/html/rfc3691]].
2072
+ # The server's capabilities must include either +IMAP4rev2+ or +UNSELECT+
2073
+ # [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
1990
2074
  def unselect
1991
2075
  send_command("UNSELECT")
2076
+ .tap do state_authenticated! end
1992
2077
  end
1993
2078
 
2079
+ # call-seq:
2080
+ # expunge -> array of message sequence numbers
2081
+ # expunge -> VanishedData of UIDs
2082
+ #
1994
2083
  # Sends an {EXPUNGE command [IMAP4rev1 §6.4.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.3]
1995
- # Sends a EXPUNGE command to permanently remove from the currently
1996
- # selected mailbox all messages that have the \Deleted flag set.
2084
+ # to permanently remove all messages with the +\Deleted+ flag from the
2085
+ # currently selected mailbox.
2086
+ #
2087
+ # Returns either an array of expunged message <em>sequence numbers</em> or
2088
+ # (when the appropriate capability is enabled) VanishedData of expunged
2089
+ # UIDs. Previously unhandled +EXPUNGE+ or +VANISHED+ responses are merged
2090
+ # with the direct response to this command. <tt>VANISHED (EARLIER)</tt>
2091
+ # responses will _not_ be merged.
2092
+ #
2093
+ # When no messages have been expunged, an empty array is returned,
2094
+ # regardless of which extensions are enabled. In a future release, an empty
2095
+ # VanishedData may be returned, based on the currently enabled extensions.
1997
2096
  #
1998
2097
  # Related: #uid_expunge
2098
+ #
2099
+ # ==== Capabilities
2100
+ #
2101
+ # When either QRESYNC[https://www.rfc-editor.org/rfc/rfc7162] or
2102
+ # UIDONLY[https://www.rfc-editor.org/rfc/rfc9586] are enabled, #expunge
2103
+ # returns VanishedData, which contains UIDs---<em>not message sequence
2104
+ # numbers</em>.
1999
2105
  def expunge
2000
- synchronize do
2001
- send_command("EXPUNGE")
2002
- clear_responses("EXPUNGE")
2003
- end
2106
+ expunge_internal("EXPUNGE")
2004
2107
  end
2005
2108
 
2109
+ # call-seq:
2110
+ # uid_expunge(uid_set) -> array of message sequence numbers
2111
+ # uid_expunge(uid_set) -> VanishedData of UIDs
2112
+ #
2006
2113
  # Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
2007
2114
  # {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
2008
2115
  # to permanently remove all messages that have both the <tt>\\Deleted</tt>
2009
2116
  # flag set and a UID that is included in +uid_set+.
2010
2117
  #
2118
+ # Returns the same result type as #expunge.
2119
+ #
2011
2120
  # By using #uid_expunge instead of #expunge when resynchronizing with
2012
2121
  # the server, the client can ensure that it does not inadvertantly
2013
2122
  # remove any messages that have been marked as <tt>\\Deleted</tt> by other
2014
2123
  # clients between the time that the client was last connected and
2015
2124
  # the time the client resynchronizes.
2016
2125
  #
2017
- # *Note:*
2018
- # >>>
2019
- # Although the command takes a set of UIDs for its argument, the
2020
- # server still returns regular EXPUNGE responses, which contain
2021
- # a <em>sequence number</em>. These will be deleted from
2022
- # #responses and this method returns them as an array of
2023
- # <em>sequence number</em> integers.
2024
- #
2025
2126
  # Related: #expunge
2026
2127
  #
2027
- # ===== Capabilities
2128
+ # ==== Capabilities
2028
2129
  #
2029
- # The server's capabilities must include +UIDPLUS+
2130
+ # The server's capabilities must include either +IMAP4rev2+ or +UIDPLUS+
2030
2131
  # [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
2132
+ #
2133
+ # Otherwise, #uid_expunge is updated by extensions in the same way as
2134
+ # #expunge.
2031
2135
  def uid_expunge(uid_set)
2032
- synchronize do
2033
- send_command("UID EXPUNGE", MessageSet.new(uid_set))
2034
- clear_responses("EXPUNGE")
2035
- end
2136
+ expunge_internal("UID EXPUNGE", SequenceSet.new(uid_set))
2036
2137
  end
2037
2138
 
2038
- # Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
2039
- # to search the mailbox for messages that match the given searching
2040
- # criteria, and returns message sequence numbers. +keys+ can either be a
2041
- # string holding the entire search string, or a single-dimension array of
2042
- # search keywords and arguments.
2139
+ # :call-seq:
2140
+ # search(criteria, charset = nil) -> result
2141
+ # search(criteria, charset: nil, return: nil) -> result
2043
2142
  #
2044
- # Returns a SearchResult object. SearchResult inherits from Array (for
2045
- # backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
2046
- # capability has been enabled.
2143
+ # Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
2144
+ # to search the mailbox for messages that match the given search +criteria+,
2145
+ # and returns either a SearchResult or an ESearchResult. SearchResult
2146
+ # inherits from Array (for backward compatibility) but adds
2147
+ # SearchResult#modseq when the +CONDSTORE+ capability has been enabled.
2148
+ # ESearchResult also implements {#to_a}[rdoc-ref:ESearchResult#to_a], for
2149
+ # compatibility with SearchResult.
2150
+ #
2151
+ # +criteria+ is one or more search keys and their arguments, which may be
2152
+ # provided as an array or a string.
2153
+ # See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
2154
+ # and {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
2155
+ #
2156
+ # +return+ options control what kind of information is returned about
2157
+ # messages matching the search +criteria+. Specifying +return+ should force
2158
+ # the server to return an ESearchResult instead of a SearchResult, but some
2159
+ # servers disobey this requirement. <em>Requires an extended search
2160
+ # capability, such as +ESEARCH+ or +IMAP4rev2+.</em>
2161
+ # See {"Argument translation"}[rdoc-ref:#search@Argument+translation] and
2162
+ # {"Supported return options"}[rdoc-ref:#search@Supported+return+options],
2163
+ # below.
2164
+ #
2165
+ # +charset+ is the name of the {registered character
2166
+ # set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
2167
+ # used by strings in the search +criteria+. When +charset+ isn't specified,
2168
+ # either <tt>"US-ASCII"</tt> or <tt>"UTF-8"</tt> is assumed, depending on
2169
+ # the server's capabilities.
2170
+ #
2171
+ # _NOTE:_ Return options and charset may be sent as part of +criteria+. Do
2172
+ # not use the +return+ or +charset+ arguments when either return options or
2173
+ # charset are embedded in +criteria+.
2047
2174
  #
2048
2175
  # Related: #uid_search
2049
2176
  #
2050
- # ===== Search criteria
2177
+ # ==== For example:
2051
2178
  #
2052
- # For a full list of search criteria,
2179
+ # imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
2180
+ # #=> [1, 6, 7, 8]
2181
+ #
2182
+ # The following assumes the server supports +ESEARCH+ and +CONDSTORE+:
2183
+ #
2184
+ # result = imap.uid_search(["UID", 12345.., "MODSEQ", 620_162_338],
2185
+ # return: %w(all count min max))
2186
+ # # => #<data Net::IMAP::ESearchResult tag="RUBY0123", uid=true,
2187
+ # # data=[["ALL", Net::IMAP::SequenceSet["12346:12349,22222:22230"]],
2188
+ # # ["COUNT", 13], ["MIN", 12346], ["MAX", 22230],
2189
+ # # ["MODSEQ", 917162488]]>
2190
+ # result.to_a # => [12346, 12347, 12348, 12349, 22222, 22223, 22224,
2191
+ # # 22225, 22226, 22227, 22228, 22229, 22230]
2192
+ # result.uid? # => true
2193
+ # result.count # => 13
2194
+ # result.min # => 12346
2195
+ # result.max # => 22230
2196
+ # result.modseq # => 917162488
2197
+ #
2198
+ # Using +return+ options to limit the result to only min, max, and count:
2199
+ #
2200
+ # result = imap.uid_search(["UID", 12345..,], return: %w(count min max))
2201
+ # # => #<data Net::IMAP::ESearchResult tag="RUBY0124", uid=true,
2202
+ # # data=[["COUNT", 13], ["MIN", 12346], ["MAX", 22230]]>
2203
+ # result.to_a # => []
2204
+ # result.count # => 13
2205
+ # result.min # => 12346
2206
+ # result.max # => 22230
2207
+ #
2208
+ # Return options and charset may be sent as keyword args or embedded in the
2209
+ # +criteria+ arg, but they must be in the correct order: <tt>"RETURN (...)
2210
+ # CHARSET ... criteria..."</tt>. The following searches
2211
+ # send the exact same command to the server:
2212
+ #
2213
+ # # Return options and charset as keyword arguments (preferred)
2214
+ # imap.search(%w(OR UNSEEN FLAGGED), return: %w(MIN MAX), charset: "UTF-8")
2215
+ # # Embedding return and charset in the criteria array
2216
+ # imap.search(["RETURN", %w(MIN MAX), "CHARSET", "UTF-8", *%w(OR UNSEEN FLAGGED)])
2217
+ # # Embedding return and charset in the criteria string
2218
+ # imap.search("RETURN (MIN MAX) CHARSET UTF-8 OR UNSEEN FLAGGED")
2219
+ #
2220
+ # Sending charset as the second positional argument is supported for
2221
+ # backward compatibility. Future versions may print a deprecation warning:
2222
+ # imap.search(%w(OR UNSEEN FLAGGED), "UTF-8", return: %w(MIN MAX))
2223
+ #
2224
+ # ==== Argument translation
2225
+ #
2226
+ # [+return+ options]
2227
+ # Must be an Array. Return option names may be either strings or symbols.
2228
+ # +Range+ elements which begin and end with negative integers are encoded
2229
+ # for use with +PARTIAL+--any other ranges are converted to SequenceSet.
2230
+ # Unlike +criteria+, other return option arguments are not automatically
2231
+ # converted to SequenceSet.
2232
+ #
2233
+ # [When +criteria+ is an Array]
2234
+ # When the array begins with <tt>"RETURN"</tt> (case insensitive), the
2235
+ # second array element is translated like the +return+ parameter (as
2236
+ # described above).
2237
+ #
2238
+ # Every other member is a +SEARCH+ command argument:
2239
+ # [SequenceSet]
2240
+ # Encoded as an \IMAP +sequence-set+ with SequenceSet#valid_string.
2241
+ # [Set, Range, <tt>-1</tt>, +:*+, responds to +#to_sequence_set+]
2242
+ # Converted to SequenceSet for validation and encoding.
2243
+ # [nested sequence-set +Array+]
2244
+ # When every element in a nested array is one of the above types, a
2245
+ # positive +Integer+, a sequence-set formatted +String+, or a deeply
2246
+ # nested +Array+ of these same types, the array will be converted to
2247
+ # SequenceSet for validation and encoding.
2248
+ # [Any other nested +Array+]
2249
+ # Otherwise, a nested array is encoded as a parenthesized list, to
2250
+ # combine multiple search keys (e.g., for use with +OR+ and +NOT+).
2251
+ # [+String+]
2252
+ # Sent verbatim when it is a valid \IMAP +atom+, and encoded as an \IMAP
2253
+ # +quoted+ or +literal+ string otherwise. Every standard search key
2254
+ # name is a valid \IMAP +atom+ and every standard search key string
2255
+ # argument is an +astring+ which may be encoded as +atom+, +quoted+, or
2256
+ # +literal+.
2257
+ #
2258
+ # *Note:* <tt>*</tt> is not a valid \IMAP +atom+ character. Any string
2259
+ # containing <tt>*</tt> will be encoded as a +quoted+ string, _not_ a
2260
+ # +sequence-set+.
2261
+ # [+Integer+ (except for <tt>-1</tt>)]
2262
+ # Encoded using +#to_s+.
2263
+ # [+Date+]
2264
+ # Encoded as an \IMAP date (see ::encode_date).
2265
+ #
2266
+ # [When +criteria+ is a String]
2267
+ # +criteria+ will be sent directly to the server <em>without any
2268
+ # validation or encoding</em>.
2269
+ #
2270
+ # <em>*WARNING:* This is vulnerable to injection attacks when external
2271
+ # inputs are used.</em>
2272
+ #
2273
+ # ==== Supported return options
2274
+ #
2275
+ # For full definitions of the standard return options and return data, see
2276
+ # the relevant RFCs.
2277
+ #
2278
+ # [+ALL+]
2279
+ # Returns ESearchResult#all with a SequenceSet of all matching sequence
2280
+ # numbers or UIDs. This is the default, when return options are empty.
2281
+ #
2282
+ # For compatibility with SearchResult, ESearchResult#to_a returns an
2283
+ # Array of message sequence numbers or UIDs.
2284
+ #
2285
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2286
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2287
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2288
+ #
2289
+ # [+COUNT+]
2290
+ # Returns ESearchResult#count with the number of matching messages.
2291
+ #
2292
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2293
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2294
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2295
+ #
2296
+ # [+MAX+]
2297
+ # Returns ESearchResult#max with the highest matching sequence number or
2298
+ # UID.
2299
+ #
2300
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2301
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2302
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2303
+ #
2304
+ # [+MIN+]
2305
+ # Returns ESearchResult#min with the lowest matching sequence number or
2306
+ # UID.
2307
+ #
2308
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2309
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2310
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2311
+ #
2312
+ # [+PARTIAL+ _range_]
2313
+ # Returns ESearchResult#partial with a SequenceSet of a subset of
2314
+ # matching sequence numbers or UIDs, as selected by _range_. As with
2315
+ # sequence numbers, the first result is +1+: <tt>1..500</tt> selects the
2316
+ # first 500 search results (in mailbox order), <tt>501..1000</tt> the
2317
+ # second 500, and so on. _range_ may also be negative: <tt>-500..-1</tt>
2318
+ # selects the last 500 search results.
2319
+ #
2320
+ # <em>Requires either the <tt>CONTEXT=SEARCH</tt> or +PARTIAL+ capabability.</em>
2321
+ # {[RFC5267]}[https://rfc-editor.org/rfc/rfc5267]
2322
+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2323
+ #
2324
+ # ===== +MODSEQ+ return data
2325
+ #
2326
+ # ESearchResult#modseq return data does not have a corresponding return
2327
+ # option. Instead, it is returned if the +MODSEQ+ search key is used or
2328
+ # when the +CONDSTORE+ extension is enabled for the selected mailbox.
2329
+ # See [{RFC4731 §3.2}[https://www.rfc-editor.org/rfc/rfc4731#section-3.2]]
2330
+ # or [{RFC7162 §2.1.5}[https://www.rfc-editor.org/rfc/rfc7162#section-3.1.5]].
2331
+ #
2332
+ # ===== +RFC4466+ compatible extensions
2333
+ #
2334
+ # {RFC4466 §2.6}[https://www.rfc-editor.org/rfc/rfc4466.html#section-2.6]
2335
+ # defines standard syntax for search extensions. Net::IMAP allows sending
2336
+ # unsupported search return options and will parse unsupported search
2337
+ # extensions' return values into ExtensionData. Please note that this is an
2338
+ # intentionally _unstable_ API. Future releases may return different
2339
+ # (incompatible) objects, <em>without deprecation or warning</em>.
2340
+ #
2341
+ # ==== Search keys
2342
+ #
2343
+ # For full definitions of the standard search +criteria+,
2053
2344
  # see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
2054
2345
  # or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
2055
2346
  # in addition to documentation for
2056
- # any [CAPABILITIES[https://www.iana.org/assignments/imap-capabilities/imap-capabilities.xhtml]]
2057
- # reported by #capabilities which may define additional search filters, e.g:
2347
+ # any #capabilities which may define additional search filters, such as
2058
2348
  # +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
2059
- # +SAVEDATE+. The following are some common search criteria:
2349
+ # +SAVEDATE+.
2060
2350
  #
2061
- # <message set>:: a set of message sequence numbers. "<tt>,</tt>" indicates
2062
- # an interval, "+:+" indicates a range. For instance,
2063
- # "<tt>2,10:12,15</tt>" means "<tt>2,10,11,12,15</tt>".
2351
+ # With the exception of <em>sequence-set</em> and <em>parenthesized
2352
+ # list</em>, all search keys are composed of prefix label with zero or more
2353
+ # arguments. The number and type of arguments is specific to each search
2354
+ # key.
2064
2355
  #
2065
- # BEFORE <date>:: messages with an internal date strictly before
2066
- # <b><date></b>. The date argument has a format similar
2067
- # to <tt>8-Aug-2002</tt>, and can be formatted using
2068
- # Net::IMAP.format_date.
2356
+ # ===== Search keys that match all messages
2069
2357
  #
2070
- # BODY <string>:: messages that contain <string> within their body.
2358
+ # [+ALL+]
2359
+ # The default initial key. Matches every message in the mailbox.
2071
2360
  #
2072
- # CC <string>:: messages containing <string> in their CC field.
2361
+ # [+SAVEDATESUPPORTED+]
2362
+ # Matches every message in the mailbox when the mailbox supports the save
2363
+ # date attribute. Otherwise, it matches no messages.
2073
2364
  #
2074
- # FROM <string>:: messages that contain <string> in their FROM field.
2365
+ # <em>Requires +SAVEDATE+ capability</em>.
2366
+ # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
2075
2367
  #
2076
- # NEW:: messages with the \Recent, but not the \Seen, flag set.
2368
+ # ===== Sequence set search keys
2077
2369
  #
2078
- # NOT <search-key>:: negate the following search key.
2370
+ # [_sequence-set_]
2371
+ # Matches messages with message sequence numbers in _sequence-set_.
2079
2372
  #
2080
- # OR <search-key> <search-key>:: "or" two search keys together.
2373
+ # _Note:_ this search key has no label.
2081
2374
  #
2082
- # ON <date>:: messages with an internal date exactly equal to <date>,
2083
- # which has a format similar to 8-Aug-2002.
2375
+ # <em>+UIDONLY+ must *not* be enabled.</em>
2376
+ # {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.html]
2084
2377
  #
2085
- # SINCE <date>:: messages with an internal date on or after <date>.
2378
+ # [+UID+ _sequence-set_]
2379
+ # Matches messages with a UID in _sequence-set_.
2086
2380
  #
2087
- # SUBJECT <string>:: messages with <string> in their subject.
2381
+ # ===== Compound search keys
2088
2382
  #
2089
- # TO <string>:: messages with <string> in their TO field.
2383
+ # [(_search-key_ _search-key_...)]
2384
+ # Combines one or more _search-key_ arguments to match
2385
+ # messages which match all contained search keys. Useful for +OR+, +NOT+,
2386
+ # and other search keys with _search-key_ arguments.
2090
2387
  #
2091
- # ===== For example:
2388
+ # _Note:_ this search key has no label.
2092
2389
  #
2093
- # p imap.search(["SUBJECT", "hello", "NOT", "NEW"])
2094
- # #=> [1, 6, 7, 8]
2390
+ # [+OR+ _search-key_ _search-key_]
2391
+ # Matches messages which match either _search-key_ argument.
2392
+ #
2393
+ # [+NOT+ _search-key_]
2394
+ # Matches messages which do not match _search-key_.
2395
+ #
2396
+ # [+FUZZY+ _search-key_]
2397
+ # Uses fuzzy matching for the specified search key.
2398
+ #
2399
+ # <em>Requires <tt>SEARCH=FUZZY</tt> capability.</em>
2400
+ # {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
2401
+ #
2402
+ # ===== Flags search keys
2403
+ #
2404
+ # [+ANSWERED+, +UNANSWERED+]
2405
+ # Matches messages with or without the <tt>\\Answered</tt> flag.
2406
+ # [+DELETED+, +UNDELETED+]
2407
+ # Matches messages with or without the <tt>\\Deleted</tt> flag.
2408
+ # [+DRAFT+, +UNDRAFT+]
2409
+ # Matches messages with or without the <tt>\\Draft</tt> flag.
2410
+ # [+FLAGGED+, +UNFLAGGED+]
2411
+ # Matches messages with or without the <tt>\\Flagged</tt> flag.
2412
+ # [+SEEN+, +UNSEEN+]
2413
+ # Matches messages with or without the <tt>\\Seen</tt> flag.
2414
+ # [+KEYWORD+ _keyword_, +UNKEYWORD+ _keyword_]
2415
+ # Matches messages with or without the specified _keyword_.
2416
+ #
2417
+ # [+RECENT+, +UNRECENT+]
2418
+ # Matches messages with or without the <tt>\\Recent</tt> flag.
2419
+ #
2420
+ # *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
2421
+ # [+NEW+]
2422
+ # Equivalent to <tt>(RECENT UNSEEN)</tt>.
2423
+ #
2424
+ # *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
2425
+ #
2426
+ # ===== Header field substring search keys
2427
+ #
2428
+ # [+BCC+ _substring_]
2429
+ # Matches when _substring_ is in the envelope's +BCC+ field.
2430
+ # [+CC+ _substring_]
2431
+ # Matches when _substring_ is in the envelope's +CC+ field.
2432
+ # [+FROM+ _substring_]
2433
+ # Matches when _substring_ is in the envelope's +FROM+ field.
2434
+ # [+SUBJECT+ _substring_]
2435
+ # Matches when _substring_ is in the envelope's +SUBJECT+ field.
2436
+ # [+TO+ _substring_]
2437
+ # Matches when _substring_ is in the envelope's +TO+ field.
2438
+ #
2439
+ # [+HEADER+ _field_ _substring_]
2440
+ # Matches when _substring_ is in the specified header _field_.
2441
+ #
2442
+ # ===== Body text search keys
2443
+ # [+BODY+ _string_]
2444
+ # Matches when _string_ is in the body of the message.
2445
+ # Does not match on header fields.
2446
+ #
2447
+ # The server _may_ use flexible matching, rather than simple substring
2448
+ # matches. For example, this may use stemming or match only full words.
2449
+ #
2450
+ # [+TEXT+ _string_]
2451
+ # Matches when _string_ is in the header or body of the message.
2452
+ #
2453
+ # The server _may_ use flexible matching, rather than simple substring
2454
+ # matches. For example, this may use stemming or match only full words.
2455
+ #
2456
+ # ===== Date/Time search keys
2457
+ #
2458
+ # [+SENTBEFORE+ _date_]
2459
+ # [+SENTON+ _date_]
2460
+ # [+SENTSINCE+ _date_]
2461
+ # Matches when the +Date+ header is earlier than, on, or later than _date_.
2462
+ #
2463
+ # [+BEFORE+ _date_]
2464
+ # [+ON+ _date_]
2465
+ # [+SINCE+ _date_]
2466
+ # Matches when the +INTERNALDATE+ is earlier than, on, or later than
2467
+ # _date_.
2468
+ #
2469
+ # [+OLDER+ _interval_]
2470
+ # [+YOUNGER+ _interval_]
2471
+ # Matches when the +INTERNALDATE+ is more/less than _interval_ seconds ago.
2472
+ #
2473
+ # <em>Requires +WITHIN+ capability</em>.
2474
+ # {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
2475
+ #
2476
+ # [+SAVEDBEFORE+ _date_]
2477
+ # [+SAVEDON+ _date_]
2478
+ # [+SAVEDSINCE+ _date_]
2479
+ # Matches when the save date is earlier than, on, or later than _date_.
2480
+ #
2481
+ # <em>Requires +SAVEDATE+ capability.</em>
2482
+ # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
2483
+ #
2484
+ # ===== Other message attribute search keys
2485
+ #
2486
+ # [+SMALLER+ _bytes_]
2487
+ # [+LARGER+ _bytes_]
2488
+ # Matches when +RFC822.SIZE+ is smaller or larger than _bytes_.
2489
+ #
2490
+ # [+ANNOTATION+ _entry_ _attr_ _value_]
2491
+ # Matches messages that have annotations with entries matching _entry_,
2492
+ # attributes matching _attr_, and _value_ in the attribute's values.
2493
+ #
2494
+ # <em>Requires +ANNOTATE-EXPERIMENT-1+ capability</em>.
2495
+ # {[RFC5257]}[https://www.rfc-editor.org/rfc/rfc5257.html].
2496
+ #
2497
+ # [+FILTER+ _filter_]
2498
+ # References a _filter_ that is stored on the server and matches all
2499
+ # messages which would be matched by that filter's search criteria.
2500
+ #
2501
+ # <em>Requires +FILTERS+ capability</em>.
2502
+ # {[RFC5466]}[https://www.rfc-editor.org/rfc/rfc5466.html#section-3.1]
2503
+ #
2504
+ # [+MODSEQ+ _modseq_]
2505
+ # Matches when +MODSEQ+ is greater than or equal to _modseq_.
2506
+ #
2507
+ # <em>Requires +CONDSTORE+ capability</em>.
2508
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
2509
+ #
2510
+ # [+MODSEQ+ _entry_ _entry-type_ _modseq_]
2511
+ # Matches when a specific metadata _entry_ has been updated since
2512
+ # _modseq_.
2513
+ #
2514
+ # For flags, the corresponding _entry_ name is
2515
+ # <tt>"/flags/#{flag_name}"</tt>, where _flag_name_ includes the
2516
+ # <tt>\\</tt> prefix. _entry-type_ can be one of <tt>"shared"</tt>,
2517
+ # <tt>"priv"</tt> (private), or <tt>"all"</tt>.
2518
+ #
2519
+ # <em>Requires +CONDSTORE+ capability</em>.
2520
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
2521
+ #
2522
+ # [+EMAILID+ _objectid_]
2523
+ # [+THREADID+ _objectid_]
2524
+ # Matches when +EMAILID+/+THREADID+ is equal to _objectid_
2525
+ # (substring matches are not supported).
2095
2526
  #
2096
- # ===== Capabilities
2527
+ # <em>Requires +OBJECTID+ capability</em>.
2528
+ # {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-6]
2097
2529
  #
2098
- # If [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html]] is supported
2530
+ # ==== Capabilities
2531
+ #
2532
+ # Return options should only be specified when the server supports
2533
+ # +IMAP4rev2+ or an extension that allows them, such as +ESEARCH+
2534
+ # [RFC4731[https://rfc-editor.org/rfc/rfc4731#section-3.1]].
2535
+ #
2536
+ # When +IMAP4rev2+ is enabled, or when the server supports +IMAP4rev2+ but
2537
+ # not +IMAP4rev1+, ESearchResult is always returned instead of SearchResult.
2538
+ #
2539
+ # If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
2099
2540
  # and enabled for the selected mailbox, a non-empty SearchResult will
2100
2541
  # include a +MODSEQ+ value.
2101
2542
  # imap.select("mbox", condstore: true)
2102
- # result = imap.search(["SUBJECT", "hi there", "not", "new")
2543
+ # result = imap.search(["SUBJECT", "hi there", "not", "new"])
2103
2544
  # #=> Net::IMAP::SearchResult[1, 6, 7, 8, modseq: 5594]
2104
2545
  # result.modseq # => 5594
2105
- def search(keys, charset = nil)
2106
- return search_internal("SEARCH", keys, charset)
2546
+ #
2547
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2548
+ # the +SEARCH+ command is prohibited. Use #uid_search instead.
2549
+ def search(...)
2550
+ search_internal("SEARCH", ...)
2107
2551
  end
2108
2552
 
2553
+ # :call-seq:
2554
+ # uid_search(criteria, charset = nil) -> result
2555
+ # uid_search(criteria, charset: nil, return: nil) -> result
2556
+ #
2109
2557
  # Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
2110
2558
  # to search the mailbox for messages that match the given searching
2111
2559
  # criteria, and returns unique identifiers (<tt>UID</tt>s).
@@ -2114,9 +2562,19 @@ module Net
2114
2562
  # backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
2115
2563
  # capability has been enabled.
2116
2564
  #
2117
- # See #search for documentation of search criteria.
2118
- def uid_search(keys, charset = nil)
2119
- return search_internal("UID SEARCH", keys, charset)
2565
+ # See #search for documentation of parameters.
2566
+ #
2567
+ # ==== Capabilities
2568
+ #
2569
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2570
+ # #uid_search must be used instead of #search, and the <tt><message
2571
+ # set></tt> search criterion is prohibited. Use +ALL+ or <tt>UID
2572
+ # sequence-set</tt> instead.
2573
+ #
2574
+ # Otherwise, #uid_search is updated by extensions in the same way as
2575
+ # #search.
2576
+ def uid_search(...)
2577
+ search_internal("UID SEARCH", ...)
2120
2578
  end
2121
2579
 
2122
2580
  # :call-seq:
@@ -2125,26 +2583,21 @@ module Net
2125
2583
  # Sends a {FETCH command [IMAP4rev1 §6.4.5]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.5]
2126
2584
  # to retrieve data associated with a message in the mailbox.
2127
2585
  #
2128
- # The +set+ parameter is a number or a range between two numbers,
2129
- # or an array of those. The number is a message sequence number,
2130
- # where -1 represents a '*' for use in range notation like 100..-1
2131
- # being interpreted as '100:*'. Beware that the +exclude_end?+
2132
- # property of a Range object is ignored, and the contents of a
2133
- # range are independent of the order of the range endpoints as per
2134
- # the protocol specification, so 1...5, 5..1 and 5...1 are all
2135
- # equivalent to 1..5.
2586
+ # +set+ is the message sequence numbers to fetch, and may be any valid input
2587
+ # to {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
2588
+ # (For UIDs, use #uid_fetch instead.)
2136
2589
  #
2137
- # +attr+ is a list of attributes to fetch; see the documentation
2138
- # for FetchData for a list of valid attributes.
2590
+ # +attr+ is a list of attributes to fetch; see FetchStruct documentation for
2591
+ # a list of supported attributes.
2139
2592
  #
2140
2593
  # +changedsince+ is an optional integer mod-sequence. It limits results to
2141
2594
  # messages with a mod-sequence greater than +changedsince+.
2142
2595
  #
2143
2596
  # The return value is an array of FetchData.
2144
2597
  #
2145
- # Related: #uid_search, FetchData
2598
+ # Related: #uid_fetch, FetchData
2146
2599
  #
2147
- # ===== For example:
2600
+ # ==== For example:
2148
2601
  #
2149
2602
  # p imap.fetch(6..8, "UID")
2150
2603
  # #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
@@ -2162,39 +2615,83 @@ module Net
2162
2615
  # p data.attr["UID"]
2163
2616
  # #=> 98
2164
2617
  #
2165
- # ===== Capabilities
2618
+ # ==== Capabilities
2166
2619
  #
2167
- # Many extensions define new message +attr+ names. See FetchData for a list
2168
- # of supported extension fields.
2620
+ # Many extensions define new message +attr+ names. See FetchStruct for a
2621
+ # list of supported extension fields.
2169
2622
  #
2170
2623
  # The server's capabilities must include +CONDSTORE+
2171
- # {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
2624
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
2172
2625
  # +changedsince+ argument. Using +changedsince+ implicitly enables the
2173
2626
  # +CONDSTORE+ extension.
2174
- def fetch(set, attr, mod = nil, changedsince: nil)
2175
- fetch_internal("FETCH", set, attr, mod, changedsince: changedsince)
2627
+ #
2628
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2629
+ # +FETCH+ command is prohibited. Use #uid_fetch instead.
2630
+ def fetch(...)
2631
+ fetch_internal("FETCH", ...)
2176
2632
  end
2177
2633
 
2178
2634
  # :call-seq:
2179
- # uid_fetch(set, attr, changedsince: nil) -> array of FetchData
2635
+ # uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData (or UIDFetchData)
2180
2636
  #
2181
2637
  # Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
2182
2638
  # to retrieve data associated with a message in the mailbox.
2183
2639
  #
2184
- # Similar to #fetch, but the +set+ parameter contains unique identifiers
2185
- # instead of message sequence numbers.
2640
+ # +set+ is the message UIDs to fetch, and may be any valid input to
2641
+ # {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
2642
+ # (For message sequence numbers, use #fetch instead.)
2186
2643
  #
2644
+ # +attr+ behaves the same as with #fetch.
2187
2645
  # >>>
2188
2646
  # *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
2189
2647
  # part of any +FETCH+ response caused by a +UID+ command, regardless of
2190
2648
  # whether a +UID+ was specified as a message data item to the +FETCH+.
2191
2649
  #
2650
+ # +changedsince+ (optional) behaves the same as with #fetch.
2651
+ #
2652
+ # +partial+ is an optional range to limit the number of results returned.
2653
+ # It's useful when +set+ contains an unknown number of messages.
2654
+ # <tt>1..500</tt> returns the first 500 messages in +set+ (in mailbox
2655
+ # order), <tt>501..1000</tt> the second 500, and so on. +partial+ may also
2656
+ # be negative: <tt>-500..-1</tt> selects the last 500 messages in +set+.
2657
+ # <em>Requires the +PARTIAL+ capabability.</em>
2658
+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2659
+ #
2660
+ # For example:
2661
+ #
2662
+ # # Without partial, the size of the results may be unknown beforehand:
2663
+ # results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
2664
+ # # ... maybe wait for a long time ... and allocate a lot of memory ...
2665
+ # results.size # => 0..2**32-1
2666
+ # process results # may also take a long time and use a lot of memory...
2667
+ #
2668
+ # # Using partial, the results may be paginated:
2669
+ # loop do
2670
+ # results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
2671
+ # partial: 1..500)
2672
+ # # fetch should return quickly and allocate little memory
2673
+ # results.size # => 0..500
2674
+ # break if results.empty?
2675
+ # results.sort_by!(&:uid) # server may return results out of order
2676
+ # next_uid_to_fetch = results.last.uid + 1
2677
+ # process results
2678
+ # end
2679
+ #
2192
2680
  # Related: #fetch, FetchData
2193
2681
  #
2194
- # ===== Capabilities
2195
- # Same as #fetch.
2196
- def uid_fetch(set, attr, mod = nil, changedsince: nil)
2197
- fetch_internal("UID FETCH", set, attr, mod, changedsince: changedsince)
2682
+ # ==== Capabilities
2683
+ #
2684
+ # The server's capabilities must include +PARTIAL+
2685
+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394] in order to use the
2686
+ # +partial+ argument.
2687
+ #
2688
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2689
+ # #uid_fetch must be used instead of #fetch, and UIDFetchData will be
2690
+ # returned instead of FetchData.
2691
+ #
2692
+ # Otherwise, #uid_fetch is updated by extensions in the same way as #fetch.
2693
+ def uid_fetch(...)
2694
+ fetch_internal("UID FETCH", ...)
2198
2695
  end
2199
2696
 
2200
2697
  # :call-seq:
@@ -2225,27 +2722,30 @@ module Net
2225
2722
  #
2226
2723
  # Related: #uid_store
2227
2724
  #
2228
- # ===== For example:
2725
+ # ==== For example:
2229
2726
  #
2230
2727
  # p imap.store(6..8, "+FLAGS", [:Deleted])
2231
2728
  # #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
2232
2729
  # #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
2233
2730
  # #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
2234
2731
  #
2235
- # ===== Capabilities
2732
+ # ==== Capabilities
2236
2733
  #
2237
2734
  # Extensions may define new data items to be used with #store.
2238
2735
  #
2239
2736
  # The server's capabilities must include +CONDSTORE+
2240
- # {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
2737
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
2241
2738
  # +unchangedsince+ argument. Using +unchangedsince+ implicitly enables the
2242
2739
  # +CONDSTORE+ extension.
2740
+ #
2741
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2742
+ # +STORE+ command is prohibited. Use #uid_store instead.
2243
2743
  def store(set, attr, flags, unchangedsince: nil)
2244
2744
  store_internal("STORE", set, attr, flags, unchangedsince: unchangedsince)
2245
2745
  end
2246
2746
 
2247
2747
  # :call-seq:
2248
- # uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData
2748
+ # uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData (or UIDFetchData)
2249
2749
  #
2250
2750
  # Sends a {UID STORE command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
2251
2751
  # to alter data associated with messages in the mailbox, in particular their
@@ -2256,8 +2756,13 @@ module Net
2256
2756
  #
2257
2757
  # Related: #store
2258
2758
  #
2259
- # ===== Capabilities
2260
- # Same as #store.
2759
+ # ==== Capabilities
2760
+ #
2761
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2762
+ # #uid_store must be used instead of #store, and UIDFetchData will be
2763
+ # returned instead of FetchData.
2764
+ #
2765
+ # Otherwise, #uid_store is updated by extensions in the same way as #store.
2261
2766
  def uid_store(set, attr, flags, unchangedsince: nil)
2262
2767
  store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
2263
2768
  end
@@ -2269,13 +2774,16 @@ module Net
2269
2774
  #
2270
2775
  # Related: #uid_copy
2271
2776
  #
2272
- # ===== Capabilities
2777
+ # ==== Capabilities
2273
2778
  #
2274
2779
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
2275
2780
  # supported, the server's response should include a +COPYUID+ response code
2276
- # with UIDPlusData. This will report the UIDVALIDITY of the destination
2781
+ # with CopyUIDData. This will report the UIDVALIDITY of the destination
2277
2782
  # mailbox, the UID set of the source messages, and the assigned UID set of
2278
2783
  # the moved messages.
2784
+ #
2785
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2786
+ # +COPY+ command is prohibited. Use #uid_copy instead.
2279
2787
  def copy(set, mailbox)
2280
2788
  copy_internal("COPY", set, mailbox)
2281
2789
  end
@@ -2286,9 +2794,12 @@ module Net
2286
2794
  #
2287
2795
  # Similar to #copy, but +set+ contains unique identifiers.
2288
2796
  #
2289
- # ===== Capabilities
2797
+ # ==== Capabilities
2798
+ #
2799
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] in enabled,
2800
+ # #uid_copy must be used instead of #copy.
2290
2801
  #
2291
- # +UIDPLUS+ affects #uid_copy the same way it affects #copy.
2802
+ # Otherwise, #uid_copy is updated by extensions in the same way as #copy.
2292
2803
  def uid_copy(set, mailbox)
2293
2804
  copy_internal("UID COPY", set, mailbox)
2294
2805
  end
@@ -2301,17 +2812,19 @@ module Net
2301
2812
  #
2302
2813
  # Related: #uid_move
2303
2814
  #
2304
- # ===== Capabilities
2815
+ # ==== Capabilities
2305
2816
  #
2306
- # The server's capabilities must include +MOVE+
2307
- # [RFC6851[https://tools.ietf.org/html/rfc6851]].
2817
+ # The server's capabilities must include either +IMAP4rev2+ or +MOVE+
2818
+ # [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
2308
2819
  #
2309
2820
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
2310
2821
  # supported, the server's response should include a +COPYUID+ response code
2311
- # with UIDPlusData. This will report the UIDVALIDITY of the destination
2822
+ # with CopyUIDData. This will report the UIDVALIDITY of the destination
2312
2823
  # mailbox, the UID set of the source messages, and the assigned UID set of
2313
2824
  # the moved messages.
2314
2825
  #
2826
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2827
+ # +MOVE+ command is prohibited. Use #uid_move instead.
2315
2828
  def move(set, mailbox)
2316
2829
  copy_internal("MOVE", set, mailbox)
2317
2830
  end
@@ -2325,11 +2838,15 @@ module Net
2325
2838
  #
2326
2839
  # Related: #move
2327
2840
  #
2328
- # ===== Capabilities
2841
+ # ==== Capabilities
2842
+ #
2843
+ # The server's capabilities must include either +IMAP4rev2+ or +MOVE+
2844
+ # [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
2329
2845
  #
2330
- # Same as #move: The server's capabilities must include +MOVE+
2331
- # [RFC6851[https://tools.ietf.org/html/rfc6851]]. +UIDPLUS+ also affects
2332
- # #uid_move the same way it affects #move.
2846
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2847
+ # #uid_move must be used instead of #move.
2848
+ #
2849
+ # Otherwise, #uid_move is updated by extensions in the same way as #move.
2333
2850
  def uid_move(set, mailbox)
2334
2851
  copy_internal("UID MOVE", set, mailbox)
2335
2852
  end
@@ -2345,17 +2862,17 @@ module Net
2345
2862
  #
2346
2863
  # Related: #uid_sort, #search, #uid_search, #thread, #uid_thread
2347
2864
  #
2348
- # ===== For example:
2865
+ # ==== For example:
2349
2866
  #
2350
2867
  # p imap.sort(["FROM"], ["ALL"], "US-ASCII")
2351
2868
  # #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
2352
2869
  # p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
2353
2870
  # #=> [6, 7, 8, 1]
2354
2871
  #
2355
- # ===== Capabilities
2872
+ # ==== Capabilities
2356
2873
  #
2357
2874
  # The server's capabilities must include +SORT+
2358
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2875
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2359
2876
  def sort(sort_keys, search_keys, charset)
2360
2877
  return sort_internal("SORT", sort_keys, search_keys, charset)
2361
2878
  end
@@ -2367,10 +2884,10 @@ module Net
2367
2884
  #
2368
2885
  # Related: #sort, #search, #uid_search, #thread, #uid_thread
2369
2886
  #
2370
- # ===== Capabilities
2887
+ # ==== Capabilities
2371
2888
  #
2372
2889
  # The server's capabilities must include +SORT+
2373
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2890
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2374
2891
  def uid_sort(sort_keys, search_keys, charset)
2375
2892
  return sort_internal("UID SORT", sort_keys, search_keys, charset)
2376
2893
  end
@@ -2392,10 +2909,10 @@ module Net
2392
2909
  #
2393
2910
  # Related: #uid_thread, #search, #uid_search, #sort, #uid_sort
2394
2911
  #
2395
- # ===== Capabilities
2912
+ # ==== Capabilities
2396
2913
  #
2397
2914
  # The server's capabilities must include +THREAD+
2398
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2915
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2399
2916
  def thread(algorithm, search_keys, charset)
2400
2917
  return thread_internal("THREAD", algorithm, search_keys, charset)
2401
2918
  end
@@ -2406,10 +2923,10 @@ module Net
2406
2923
  #
2407
2924
  # Related: #thread, #search, #uid_search, #sort, #uid_sort
2408
2925
  #
2409
- # ===== Capabilities
2926
+ # ==== Capabilities
2410
2927
  #
2411
2928
  # The server's capabilities must include +THREAD+
2412
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2929
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2413
2930
  def uid_thread(algorithm, search_keys, charset)
2414
2931
  return thread_internal("UID THREAD", algorithm, search_keys, charset)
2415
2932
  end
@@ -2425,11 +2942,11 @@ module Net
2425
2942
  #
2426
2943
  # Related: #capable?, #capabilities, #capability
2427
2944
  #
2428
- # ===== Capabilities
2945
+ # ==== Capabilities
2429
2946
  #
2430
2947
  # The server's capabilities must include
2431
- # +ENABLE+ [RFC5161[https://tools.ietf.org/html/rfc5161]]
2432
- # or +IMAP4REV2+ [RFC9051[https://tools.ietf.org/html/rfc9051]].
2948
+ # +ENABLE+ [RFC5161[https://www.rfc-editor.org/rfc/rfc5161]]
2949
+ # or +IMAP4REV2+ [RFC9051[https://www.rfc-editor.org/rfc/rfc9051]].
2433
2950
  #
2434
2951
  # Additionally, the server capabilities must include a capability matching
2435
2952
  # each enabled extension (usually the same name as the enabled extension).
@@ -2442,13 +2959,25 @@ module Net
2442
2959
  # command parameters defined by the extension will implicitly enable it.
2443
2960
  # See {[RFC7162 §3.1]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1].
2444
2961
  #
2962
+ # [+QRESYNC+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html]]
2963
+ # *NOTE:* Enabling QRESYNC will replace +EXPUNGE+ with +VANISHED+, but
2964
+ # the extension arguments to #select, #examine, and #uid_fetch are not
2965
+ # supported yet.
2966
+ #
2967
+ # Adds quick resynchronization options to #select, #examine, and
2968
+ # #uid_fetch. +QRESYNC+ _must_ be explicitly enabled before using any of
2969
+ # the extension's command parameters. All +EXPUNGE+ responses will be
2970
+ # replaced with +VANISHED+ responses. Enabling +QRESYNC+ implicitly
2971
+ # enables +CONDSTORE+ as well.
2972
+ # See {[RFC7162 §3.2]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.2].
2973
+ #
2445
2974
  # [+:utf8+ --- an alias for <tt>"UTF8=ACCEPT"</tt>]
2446
2975
  #
2447
2976
  # In a future release, <tt>enable(:utf8)</tt> will enable either
2448
2977
  # <tt>"UTF8=ACCEPT"</tt> or <tt>"IMAP4rev2"</tt>, depending on server
2449
2978
  # capabilities.
2450
2979
  #
2451
- # [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://tools.ietf.org/html/rfc6855]]]
2980
+ # [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
2452
2981
  #
2453
2982
  # The server's capabilities must include <tt>UTF8=ACCEPT</tt> _or_
2454
2983
  # <tt>UTF8=ONLY</tt>.
@@ -2467,13 +2996,23 @@ module Net
2467
2996
  # encoding, even if they generally contain UTF-8 data, if they are
2468
2997
  # text at all.
2469
2998
  #
2470
- # [<tt>"UTF8=ONLY"</tt> [RFC6855[https://tools.ietf.org/html/rfc6855]]]
2999
+ # [<tt>"UTF8=ONLY"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
2471
3000
  #
2472
3001
  # A server that reports the <tt>UTF8=ONLY</tt> capability _requires_ that
2473
3002
  # the client <tt>enable("UTF8=ACCEPT")</tt> before any mailboxes may be
2474
3003
  # selected. For convenience, <tt>enable("UTF8=ONLY")</tt> is aliased to
2475
3004
  # <tt>enable("UTF8=ACCEPT")</tt>.
2476
3005
  #
3006
+ # [+UIDONLY+ {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.pdf]]
3007
+ #
3008
+ # When UIDONLY is enabled, the #fetch, #store, #search, #copy, and #move
3009
+ # commands are prohibited and result in a tagged BAD response. Clients
3010
+ # should instead use uid_fetch, uid_store, uid_search, uid_copy, or
3011
+ # uid_move, respectively. All +FETCH+ responses that would be returned are
3012
+ # replaced by +UIDFETCH+ responses. All +EXPUNGED+ responses that would be
3013
+ # returned are replaced by +VANISHED+ responses. The "<sequence set>"
3014
+ # uid_search criterion is prohibited.
3015
+ #
2477
3016
  # ===== Unsupported capabilities
2478
3017
  #
2479
3018
  # *Note:* Some extensions that use ENABLE permit the server to send syntax
@@ -2527,10 +3066,10 @@ module Net
2527
3066
  #
2528
3067
  # Related: #idle_done, #noop, #check
2529
3068
  #
2530
- # ===== Capabilities
3069
+ # ==== Capabilities
2531
3070
  #
2532
- # The server's capabilities must include +IDLE+
2533
- # [RFC2177[https://tools.ietf.org/html/rfc2177]].
3071
+ # The server's capabilities must include either +IMAP4rev2+ or +IDLE+
3072
+ # [RFC2177[https://www.rfc-editor.org/rfc/rfc2177]].
2534
3073
  def idle(timeout = nil, &response_handler)
2535
3074
  raise LocalJumpError, "no block given" unless response_handler
2536
3075
 
@@ -2549,8 +3088,8 @@ module Net
2549
3088
  raise @exception || Net::IMAP::Error.new("connection closed")
2550
3089
  end
2551
3090
  ensure
3091
+ remove_response_handler(response_handler)
2552
3092
  unless @receiver_thread_terminating
2553
- remove_response_handler(response_handler)
2554
3093
  put_string("DONE#{CRLF}")
2555
3094
  response = get_tagged_response(tag, "IDLE", idle_response_timeout)
2556
3095
  end
@@ -2644,7 +3183,7 @@ module Net
2644
3183
  #
2645
3184
  # Related: #extract_responses, #clear_responses, #response_handlers, #greeting
2646
3185
  #
2647
- # ===== Thread safety
3186
+ # ==== Thread safety
2648
3187
  # >>>
2649
3188
  # *Note:* Access to the responses hash is synchronized for thread-safety.
2650
3189
  # The receiver thread and response_handlers cannot process new responses
@@ -2658,7 +3197,7 @@ module Net
2658
3197
  # thread, but will not modify any responses after adding them to the
2659
3198
  # responses hash.
2660
3199
  #
2661
- # ===== Clearing responses
3200
+ # ==== Clearing responses
2662
3201
  #
2663
3202
  # Previously unhandled responses are automatically cleared before entering a
2664
3203
  # mailbox with #select or #examine. Long-lived connections can receive many
@@ -2667,7 +3206,7 @@ module Net
2667
3206
  # the block, or remove responses with #extract_responses, #clear_responses,
2668
3207
  # or #add_response_handler.
2669
3208
  #
2670
- # ===== Missing responses
3209
+ # ==== Missing responses
2671
3210
  #
2672
3211
  # Only non-+nil+ data is stored. Many important response codes have no data
2673
3212
  # of their own, but are used as "tags" on the ResponseText object they are
@@ -2688,10 +3227,10 @@ module Net
2688
3227
  when :raise
2689
3228
  raise ArgumentError, RESPONSES_DEPRECATION_MSG
2690
3229
  when :warn
2691
- warn(RESPONSES_DEPRECATION_MSG, uplevel: 1)
3230
+ warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
2692
3231
  when :frozen_dup
2693
3232
  synchronize {
2694
- responses = @responses.transform_values(&:freeze)
3233
+ responses = @responses.transform_values { _1.dup.freeze }
2695
3234
  responses.default_proc = nil
2696
3235
  responses.default = [].freeze
2697
3236
  return responses.freeze
@@ -2811,6 +3350,7 @@ module Net
2811
3350
  @response_handlers.each do |handler| handler.call(@greeting) end
2812
3351
  @receiver_thread = start_receiver_thread
2813
3352
  rescue Exception
3353
+ state_logout!
2814
3354
  @sock.close
2815
3355
  raise
2816
3356
  end
@@ -2819,7 +3359,10 @@ module Net
2819
3359
  greeting = get_response
2820
3360
  raise Error, "No server greeting - connection closed" unless greeting
2821
3361
  record_untagged_response_code greeting
2822
- raise ByeResponseError, greeting if greeting.name == "BYE"
3362
+ case greeting.name
3363
+ when "PREAUTH" then state_authenticated!
3364
+ when "BYE" then state_logout!; raise ByeResponseError, greeting
3365
+ end
2823
3366
  greeting
2824
3367
  end
2825
3368
 
@@ -2851,6 +3394,7 @@ module Net
2851
3394
  resp = get_response
2852
3395
  rescue Exception => e
2853
3396
  synchronize do
3397
+ state_logout!
2854
3398
  @sock.close
2855
3399
  @exception = e
2856
3400
  end
@@ -2870,6 +3414,7 @@ module Net
2870
3414
  @tagged_response_arrival.broadcast
2871
3415
  case resp.tag
2872
3416
  when @logout_command_tag
3417
+ state_logout!
2873
3418
  return
2874
3419
  when @continued_command_tag
2875
3420
  @continuation_request_exception =
@@ -2879,6 +3424,7 @@ module Net
2879
3424
  when UntaggedResponse
2880
3425
  record_untagged_response(resp)
2881
3426
  if resp.name == "BYE" && @logout_command_tag.nil?
3427
+ state_logout!
2882
3428
  @sock.close
2883
3429
  @exception = ByeResponseError.new(resp)
2884
3430
  connection_closed = true
@@ -2886,6 +3432,7 @@ module Net
2886
3432
  when ContinuationRequest
2887
3433
  @continuation_request_arrival.signal
2888
3434
  end
3435
+ state_unselected! if resp in {data: {code: {name: "CLOSED"}}}
2889
3436
  @response_handlers.each do |handler|
2890
3437
  handler.call(resp)
2891
3438
  end
@@ -2906,6 +3453,8 @@ module Net
2906
3453
  @idle_done_cond.signal
2907
3454
  end
2908
3455
  end
3456
+ ensure
3457
+ state_logout!
2909
3458
  end
2910
3459
 
2911
3460
  def get_tagged_response(tag, cmd, timeout = nil)
@@ -3029,23 +3578,118 @@ module Net
3029
3578
  end
3030
3579
  end
3031
3580
 
3032
- def search_internal(cmd, keys, charset)
3033
- if keys.instance_of?(String)
3034
- keys = [RawData.new(keys)]
3035
- else
3036
- normalize_searching_criteria(keys)
3581
+ def enforce_logindisabled?
3582
+ may_depend_on_capabilities_cached?(config.enforce_logindisabled)
3583
+ end
3584
+
3585
+ def may_depend_on_capabilities_cached?(value)
3586
+ value == :when_capabilities_cached ? capabilities_cached? : value
3587
+ end
3588
+
3589
+ def expunge_internal(...)
3590
+ synchronize do
3591
+ send_command(...)
3592
+ expunged_array = clear_responses("EXPUNGE")
3593
+ vanished_array = extract_responses("VANISHED") { !_1.earlier? }
3594
+ if vanished_array.empty?
3595
+ expunged_array
3596
+ elsif vanished_array.length == 1
3597
+ vanished_array.first
3598
+ else
3599
+ merged_uids = SequenceSet[*vanished_array.map(&:uids)]
3600
+ VanishedData[uids: merged_uids, earlier: false]
3601
+ end
3602
+ end
3603
+ end
3604
+
3605
+ RETURN_WHOLE = /\ARETURN\z/i
3606
+ RETURN_START = /\ARETURN\b/i
3607
+ private_constant :RETURN_WHOLE, :RETURN_START
3608
+
3609
+ def search_args(keys, charset_arg = nil, return: nil, charset: nil)
3610
+ {return:} => {return: return_kw}
3611
+ case [return_kw, keys]
3612
+ in [nil, Array[RETURN_WHOLE, return_opts, *keys]]
3613
+ return_opts = convert_return_opts(return_opts)
3614
+ esearch = true
3615
+ in [nil => return_opts, RETURN_START]
3616
+ esearch = true
3617
+ in [nil => return_opts, keys]
3618
+ esearch = false
3619
+ in [_, Array[RETURN_WHOLE, _, *] | RETURN_START]
3620
+ raise ArgumentError, "conflicting return options"
3621
+ in [_, Array[RETURN_WHOLE, _, *]] # workaround for https://bugs.ruby-lang.org/issues/20956
3622
+ raise ArgumentError, "conflicting return options"
3623
+ in [_, RETURN_START] # workaround for https://bugs.ruby-lang.org/issues/20956
3624
+ raise ArgumentError, "conflicting return options"
3625
+ in [return_opts, keys]
3626
+ return_opts = convert_return_opts(return_opts)
3627
+ esearch = true
3628
+ end
3629
+ if charset && charset_arg
3630
+ raise ArgumentError, "multiple charset arguments"
3037
3631
  end
3632
+ charset ||= charset_arg
3633
+ # NOTE: not handling combined RETURN and CHARSET for raw strings
3634
+ if charset && keys in /\ACHARSET\b/i | Array[/\ACHARSET\z/i, *]
3635
+ raise ArgumentError, "multiple charset arguments"
3636
+ end
3637
+ args = normalize_searching_criteria(keys)
3638
+ args.prepend("CHARSET", charset) if charset
3639
+ args.prepend("RETURN", return_opts) if return_opts
3640
+ return args, esearch
3641
+ end
3642
+
3643
+ def convert_return_opts(unconverted)
3644
+ return_opts = Array.try_convert(unconverted) or
3645
+ raise TypeError, "expected return options to be Array, got %s" % [
3646
+ unconverted.class
3647
+ ]
3648
+ return_opts.map {|opt|
3649
+ case opt
3650
+ when Symbol then opt.to_s
3651
+ when PartialRange::Negative then PartialRange[opt]
3652
+ when Range then SequenceSet[opt]
3653
+ else opt
3654
+ end
3655
+ }
3656
+ end
3657
+
3658
+ def search_internal(cmd, ...)
3659
+ args, esearch = search_args(...)
3038
3660
  synchronize do
3039
- if charset
3040
- send_command(cmd, "CHARSET", charset, *keys)
3661
+ tagged = send_command(cmd, *args)
3662
+ tag = tagged.tag
3663
+ # Only the last ESEARCH or SEARCH is used. Excess results are ignored.
3664
+ esearch_result = extract_responses("ESEARCH") {|response|
3665
+ response in ESearchResult(tag: ^tag)
3666
+ }.last
3667
+ search_result = clear_responses("SEARCH").last
3668
+ if esearch_result
3669
+ # silently ignore SEARCH results, if any
3670
+ esearch_result
3671
+ elsif search_result
3672
+ # warn EXPECTED_ESEARCH_RESULT if esearch
3673
+ search_result
3674
+ elsif esearch
3675
+ # warn NO_SEARCH_RESPONSE
3676
+ ESearchResult[tag:, uid: cmd.start_with?("UID ")]
3041
3677
  else
3042
- send_command(cmd, *keys)
3678
+ # warn NO_SEARCH_RESPONSE
3679
+ SearchResult[]
3043
3680
  end
3044
- clear_responses("SEARCH").last || []
3045
3681
  end
3046
3682
  end
3047
3683
 
3048
- def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
3684
+ def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
3685
+ if partial && !cmd.start_with?("UID ")
3686
+ raise ArgumentError, "partial can only be used with uid_fetch"
3687
+ end
3688
+ set = SequenceSet[set]
3689
+ if partial
3690
+ mod ||= []
3691
+ mod << "PARTIAL" << PartialRange[partial]
3692
+ end
3049
3693
  if changedsince
3050
3694
  mod ||= []
3051
3695
  mod << "CHANGEDSINCE" << Integer(changedsince)
@@ -3059,39 +3703,36 @@ module Net
3059
3703
  }
3060
3704
  end
3061
3705
 
3062
- synchronize do
3063
- clear_responses("FETCH")
3064
- if mod
3065
- send_command(cmd, MessageSet.new(set), attr, mod)
3066
- else
3067
- send_command(cmd, MessageSet.new(set), attr)
3068
- end
3069
- clear_responses("FETCH")
3070
- end
3706
+ args = [cmd, set, attr]
3707
+ args << mod if mod
3708
+ send_command_returning_fetch_results(*args)
3071
3709
  end
3072
3710
 
3073
3711
  def store_internal(cmd, set, attr, flags, unchangedsince: nil)
3074
3712
  attr = RawData.new(attr) if attr.instance_of?(String)
3075
- args = [MessageSet.new(set)]
3713
+ args = [SequenceSet.new(set)]
3076
3714
  args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
3077
3715
  args << attr << flags
3716
+ send_command_returning_fetch_results(cmd, *args)
3717
+ end
3718
+
3719
+ def send_command_returning_fetch_results(...)
3078
3720
  synchronize do
3079
3721
  clear_responses("FETCH")
3080
- send_command(cmd, *args)
3081
- clear_responses("FETCH")
3722
+ clear_responses("UIDFETCH")
3723
+ send_command(...)
3724
+ fetches = clear_responses("FETCH")
3725
+ uidfetches = clear_responses("UIDFETCH")
3726
+ uidfetches.any? ? uidfetches : fetches
3082
3727
  end
3083
3728
  end
3084
3729
 
3085
3730
  def copy_internal(cmd, set, mailbox)
3086
- send_command(cmd, MessageSet.new(set), mailbox)
3731
+ send_command(cmd, SequenceSet.new(set), mailbox)
3087
3732
  end
3088
3733
 
3089
3734
  def sort_internal(cmd, sort_keys, search_keys, charset)
3090
- if search_keys.instance_of?(String)
3091
- search_keys = [RawData.new(search_keys)]
3092
- else
3093
- normalize_searching_criteria(search_keys)
3094
- end
3735
+ search_keys = normalize_searching_criteria(search_keys)
3095
3736
  synchronize do
3096
3737
  send_command(cmd, sort_keys, charset, *search_keys)
3097
3738
  clear_responses("SORT").last || []
@@ -3099,36 +3740,47 @@ module Net
3099
3740
  end
3100
3741
 
3101
3742
  def thread_internal(cmd, algorithm, search_keys, charset)
3102
- if search_keys.instance_of?(String)
3103
- search_keys = [RawData.new(search_keys)]
3104
- else
3105
- normalize_searching_criteria(search_keys)
3106
- end
3743
+ search_keys = normalize_searching_criteria(search_keys)
3107
3744
  synchronize do
3108
3745
  send_command(cmd, algorithm, charset, *search_keys)
3109
3746
  clear_responses("THREAD").last || []
3110
3747
  end
3111
3748
  end
3112
3749
 
3113
- def normalize_searching_criteria(keys)
3114
- keys.collect! do |i|
3115
- case i
3116
- when -1, Range, Array
3117
- MessageSet.new(i)
3750
+ def normalize_searching_criteria(criteria)
3751
+ return [RawData.new(criteria)] if criteria.is_a?(String)
3752
+ criteria.map {|i|
3753
+ if coerce_search_arg_to_seqset?(i)
3754
+ SequenceSet[i]
3118
3755
  else
3119
3756
  i
3120
3757
  end
3758
+ }
3759
+ end
3760
+
3761
+ def coerce_search_arg_to_seqset?(obj)
3762
+ case obj
3763
+ when Set, -1, :* then true
3764
+ when Range then true
3765
+ when Array then obj.all? { coerce_search_array_arg_to_seqset? _1 }
3766
+ else obj.respond_to?(:to_sequence_set)
3767
+ end
3768
+ end
3769
+
3770
+ def coerce_search_array_arg_to_seqset?(obj)
3771
+ case obj
3772
+ when Integer then obj.positive? || obj == -1
3773
+ when String then ResponseParser::Patterns::SEQUENCE_SET_STR.match?(obj.b)
3774
+ else
3775
+ coerce_search_arg_to_seqset?(obj)
3121
3776
  end
3122
3777
  end
3123
3778
 
3124
3779
  def build_ssl_ctx(ssl)
3125
3780
  if ssl
3126
3781
  params = (Hash.try_convert(ssl) || {}).freeze
3127
- context = SSLContext.new
3782
+ context = OpenSSL::SSL::SSLContext.new
3128
3783
  context.set_params(params)
3129
- if defined?(VerifyCallbackProc)
3130
- context.verify_callback = VerifyCallbackProc
3131
- end
3132
3784
  context.freeze
3133
3785
  [params, context]
3134
3786
  else
@@ -3140,17 +3792,54 @@ module Net
3140
3792
  raise "SSL extension not installed" unless defined?(OpenSSL::SSL)
3141
3793
  raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
3142
3794
  raise "cannot start TLS without SSLContext" unless ssl_ctx
3143
- @sock = SSLSocket.new(@sock, ssl_ctx)
3795
+ @sock = OpenSSL::SSL::SSLSocket.new(@sock, ssl_ctx)
3144
3796
  @reader = ResponseReader.new(self, @sock)
3145
3797
  @sock.sync_close = true
3146
3798
  @sock.hostname = @host if @sock.respond_to? :hostname=
3147
3799
  ssl_socket_connect(@sock, open_timeout)
3148
- if ssl_ctx.verify_mode != VERIFY_NONE
3800
+ if ssl_ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
3149
3801
  @sock.post_connection_check(@host)
3150
3802
  @tls_verified = true
3151
3803
  end
3152
3804
  end
3153
3805
 
3806
+ def state_authenticated!(resp = nil)
3807
+ synchronize do
3808
+ @capabilities = capabilities_from_resp_code resp if resp
3809
+ @connection_state = ConnectionState::Authenticated.new
3810
+ end
3811
+ end
3812
+
3813
+ def state_selected!
3814
+ synchronize do
3815
+ @connection_state = ConnectionState::Selected.new
3816
+ end
3817
+ end
3818
+
3819
+ def state_unselected!
3820
+ synchronize do
3821
+ state_authenticated! if connection_state.to_sym == :selected
3822
+ end
3823
+ end
3824
+
3825
+ def state_logout!
3826
+ return true if connection_state in [:logout, *]
3827
+ synchronize do
3828
+ return true if connection_state in [:logout, *]
3829
+ @connection_state = ConnectionState::Logout.new
3830
+ end
3831
+ end
3832
+
3833
+ # don't wait to aqcuire the lock
3834
+ def try_state_logout?
3835
+ return true if connection_state in [:logout, *]
3836
+ return false unless acquired_lock = mon_try_enter
3837
+ state_logout!
3838
+ true
3839
+ ensure
3840
+ mon_exit if acquired_lock
3841
+ end
3842
+
3154
3843
  def sasl_adapter
3155
3844
  SASLAdapter.new(self, &method(:send_command_with_continuations))
3156
3845
  end