net-imap 0.4.9.1 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/COPYING +56 -0
  4. data/Gemfile +12 -1
  5. data/LICENSE.txt +3 -22
  6. data/README.md +10 -4
  7. data/docs/styles.css +75 -14
  8. data/lib/net/imap/authenticators.rb +2 -2
  9. data/lib/net/imap/command_data.rb +61 -48
  10. data/lib/net/imap/config/attr_accessors.rb +75 -0
  11. data/lib/net/imap/config/attr_inheritance.rb +90 -0
  12. data/lib/net/imap/config/attr_type_coercion.rb +61 -0
  13. data/lib/net/imap/config.rb +470 -0
  14. data/lib/net/imap/data_encoding.rb +4 -4
  15. data/lib/net/imap/data_lite.rb +226 -0
  16. data/lib/net/imap/deprecated_client_options.rb +9 -6
  17. data/lib/net/imap/errors.rb +7 -1
  18. data/lib/net/imap/esearch_result.rb +180 -0
  19. data/lib/net/imap/fetch_data.rb +126 -47
  20. data/lib/net/imap/flags.rb +1 -1
  21. data/lib/net/imap/response_data.rb +126 -239
  22. data/lib/net/imap/response_parser/parser_utils.rb +11 -6
  23. data/lib/net/imap/response_parser.rb +188 -34
  24. data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
  25. data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
  26. data/lib/net/imap/sasl/authenticators.rb +8 -4
  27. data/lib/net/imap/sasl/client_adapter.rb +77 -26
  28. data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
  29. data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
  30. data/lib/net/imap/sasl/external_authenticator.rb +3 -3
  31. data/lib/net/imap/sasl/gs2_header.rb +7 -7
  32. data/lib/net/imap/sasl/login_authenticator.rb +4 -3
  33. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
  34. data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
  35. data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
  36. data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
  37. data/lib/net/imap/sasl.rb +8 -5
  38. data/lib/net/imap/sasl_adapter.rb +0 -1
  39. data/lib/net/imap/search_result.rb +4 -8
  40. data/lib/net/imap/sequence_set.rb +239 -88
  41. data/lib/net/imap/stringprep/nameprep.rb +1 -1
  42. data/lib/net/imap/stringprep/trace.rb +4 -4
  43. data/lib/net/imap/uidplus_data.rb +244 -0
  44. data/lib/net/imap/vanished_data.rb +56 -0
  45. data/lib/net/imap.rb +1012 -322
  46. data/net-imap.gemspec +4 -7
  47. data/rakelib/benchmarks.rake +1 -1
  48. data/rakelib/rfcs.rake +2 -0
  49. data/rakelib/string_prep_tables_generator.rb +2 -0
  50. data/sample/net-imap.rb +167 -0
  51. metadata +16 -40
  52. data/.github/dependabot.yml +0 -6
  53. data/.github/workflows/pages.yml +0 -46
  54. data/.github/workflows/test.yml +0 -31
  55. data/.gitignore +0 -12
data/lib/net/imap.rb CHANGED
@@ -25,8 +25,8 @@ module Net
25
25
 
26
26
  # Net::IMAP implements Internet Message Access Protocol (\IMAP) client
27
27
  # functionality. The protocol is described
28
- # in {IMAP4rev1 [RFC3501]}[https://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
  #
@@ -288,6 +288,8 @@ module Net
288
288
  # pre-authenticated connection.
289
289
  # - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
290
290
  # ResponseCode#data.
291
+ # - #extract_responses: Removes and returns the responses for which the block
292
+ # returns a true value.
291
293
  # - #clear_responses: Deletes unhandled data from #responses and returns it.
292
294
  # - #add_response_handler: Add a block to be called inside the receiver thread
293
295
  # with every server response.
@@ -297,15 +299,15 @@ module Net
297
299
  # === Core \IMAP commands
298
300
  #
299
301
  # The following commands are defined either by
300
- # the [IMAP4rev1[https://tools.ietf.org/html/rfc3501]] base specification, or
302
+ # the [IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501]] base specification, or
301
303
  # by one of the following extensions:
302
- # [IDLE[https://tools.ietf.org/html/rfc2177]],
303
- # [NAMESPACE[https://tools.ietf.org/html/rfc2342]],
304
- # [UNSELECT[https://tools.ietf.org/html/rfc3691]],
305
- # [ENABLE[https://tools.ietf.org/html/rfc5161]],
306
- # [MOVE[https://tools.ietf.org/html/rfc6851]].
304
+ # [IDLE[https://www.rfc-editor.org/rfc/rfc2177]],
305
+ # [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]],
306
+ # [UNSELECT[https://www.rfc-editor.org/rfc/rfc3691]],
307
+ # [ENABLE[https://www.rfc-editor.org/rfc/rfc5161]],
308
+ # [MOVE[https://www.rfc-editor.org/rfc/rfc6851]].
307
309
  # These extensions are widely supported by modern IMAP4rev1 servers and have
308
- # all been integrated into [IMAP4rev2[https://tools.ietf.org/html/rfc9051]].
310
+ # all been integrated into [IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]].
309
311
  # <em>*NOTE:* Net::IMAP doesn't support IMAP4rev2 yet.</em>
310
312
  #
311
313
  # ==== Any state
@@ -402,7 +404,7 @@ module Net
402
404
  #
403
405
  # ==== RFC9051: +IMAP4rev2+
404
406
  #
405
- # Although IMAP4rev2[https://tools.ietf.org/html/rfc9051] is not supported
407
+ # Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not supported
406
408
  # yet, Net::IMAP supports several extensions that have been folded into it:
407
409
  # +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+,
408
410
  # <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
@@ -412,7 +414,7 @@ module Net
412
414
  # >>>
413
415
  # <em>The following are folded into +IMAP4rev2+ but are currently
414
416
  # unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
415
- # extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
417
+ # extensions, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
416
418
  # +LITERAL-+, and +SPECIAL-USE+.</em>
417
419
  #
418
420
  # ==== RFC2087: +QUOTA+
@@ -422,13 +424,13 @@ module Net
422
424
  # - #setquota: sets the resource limits for a given quota root.
423
425
  #
424
426
  # ==== RFC2177: +IDLE+
425
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
427
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
426
428
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
427
429
  # - #idle: Allows the server to send updates to the client, without the client
428
430
  # needing to poll using #noop.
429
431
  #
430
432
  # ==== RFC2342: +NAMESPACE+
431
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
433
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
432
434
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
433
435
  # - #namespace: Returns mailbox namespaces, with path prefixes and delimiters.
434
436
  #
@@ -437,7 +439,7 @@ module Net
437
439
  #
438
440
  # ==== RFC3516: +BINARY+
439
441
  # The fetch side of +BINARY+ has been folded into
440
- # IMAP4rev2[https://tools.ietf.org/html/rfc9051].
442
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
441
443
  # - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
442
444
  # +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
443
445
  #
@@ -445,7 +447,7 @@ module Net
445
447
  # *NOTE:* The binary extension the #append command is _not_ supported yet.
446
448
  #
447
449
  # ==== RFC3691: +UNSELECT+
448
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
450
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
449
451
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
450
452
  # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
451
453
  # without expunging any messages.
@@ -457,19 +459,23 @@ module Net
457
459
  # *NOTE:* +DELETEACL+, +LISTRIGHTS+, and +MYRIGHTS+ are not supported yet.
458
460
  #
459
461
  # ==== RFC4315: +UIDPLUS+
460
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
462
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
461
463
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
462
464
  # - #uid_expunge: Restricts #expunge to only remove the specified UIDs.
463
465
  # - Updates #select, #examine with the +UIDNOTSTICKY+ ResponseCode
464
466
  # - Updates #append with the +APPENDUID+ ResponseCode
465
467
  # - Updates #copy, #move with the +COPYUID+ ResponseCode
466
468
  #
469
+ # ==== RFC4731: +ESEARCH+
470
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
471
+ # - Updates #search, #uid_search with +return+ options and ESearchResult.
472
+ #
467
473
  # ==== RFC4959: +SASL-IR+
468
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
474
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
469
475
  # - Updates #authenticate with the option to send an initial response.
470
476
  #
471
477
  # ==== RFC5161: +ENABLE+
472
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
478
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
473
479
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
474
480
  # - #enable: Enables backwards incompatible server extensions.
475
481
  #
@@ -493,7 +499,7 @@ module Net
493
499
  # +X-GM-THRID+, but Gmail does not support it (as of 2023-11-10).
494
500
  #
495
501
  # ==== RFC6851: +MOVE+
496
- # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
502
+ # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
497
503
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
498
504
  # - #move, #uid_move: Moves the specified messages to the end of the
499
505
  # specified destination mailbox, expunging them from the current mailbox.
@@ -528,6 +534,17 @@ module Net
528
534
  # See FetchData#emailid and FetchData#emailid.
529
535
  # - Updates #status with support for the +MAILBOXID+ status attribute.
530
536
  #
537
+ # ==== RFC9394: +PARTIAL+
538
+ # - Updates #search, #uid_search with the +PARTIAL+ return option which adds
539
+ # ESearchResult#partial return data.
540
+ # - Updates #uid_fetch with the +partial+ modifier.
541
+ #
542
+ # ==== RFC9586: +UIDONLY+
543
+ # - Updates #enable with +UIDONLY+ parameter.
544
+ # - Updates #uid_fetch and #uid_store to return +UIDFETCH+ response.
545
+ # - Updates #expunge and #uid_expunge to return +VANISHED+ response.
546
+ # - Prohibits use of message sequence numbers in responses or requests.
547
+ #
531
548
  # == References
532
549
  #
533
550
  # [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
@@ -558,57 +575,57 @@ module Net
558
575
  # Gahrns, M., "IMAP4 Multi-Accessed Mailbox Practice", RFC 2180, DOI
559
576
  # 10.17487/RFC2180, July 1997, <https://www.rfc-editor.org/info/rfc2180>.
560
577
  #
561
- # [UTF7[https://tools.ietf.org/html/rfc2152]]::
578
+ # [UTF7[https://www.rfc-editor.org/rfc/rfc2152]]::
562
579
  # Goldsmith, D. and M. Davis, "UTF-7 A Mail-Safe Transformation Format of
563
580
  # Unicode", RFC 2152, DOI 10.17487/RFC2152, May 1997,
564
581
  # <https://www.rfc-editor.org/info/rfc2152>.
565
582
  #
566
583
  # === Message envelope and body structure
567
584
  #
568
- # [RFC5322[https://tools.ietf.org/html/rfc5322]]::
585
+ # [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]::
569
586
  # Resnick, P., Ed., "Internet Message Format",
570
587
  # RFC 5322, DOI 10.17487/RFC5322, October 2008,
571
588
  # <https://www.rfc-editor.org/info/rfc5322>.
572
589
  #
573
590
  # <em>Note: obsoletes</em>
574
- # RFC-2822[https://tools.ietf.org/html/rfc2822]<em> (April 2001) and</em>
575
- # RFC-822[https://tools.ietf.org/html/rfc822]<em> (August 1982).</em>
591
+ # RFC-2822[https://www.rfc-editor.org/rfc/rfc2822]<em> (April 2001) and</em>
592
+ # RFC-822[https://www.rfc-editor.org/rfc/rfc822]<em> (August 1982).</em>
576
593
  #
577
- # [CHARSET[https://tools.ietf.org/html/rfc2978]]::
594
+ # [CHARSET[https://www.rfc-editor.org/rfc/rfc2978]]::
578
595
  # Freed, N. and J. Postel, "IANA Charset Registration Procedures", BCP 19,
579
596
  # RFC 2978, DOI 10.17487/RFC2978, October 2000,
580
597
  # <https://www.rfc-editor.org/info/rfc2978>.
581
598
  #
582
- # [DISPOSITION[https://tools.ietf.org/html/rfc2183]]::
599
+ # [DISPOSITION[https://www.rfc-editor.org/rfc/rfc2183]]::
583
600
  # Troost, R., Dorner, S., and K. Moore, Ed., "Communicating Presentation
584
601
  # Information in Internet Messages: The Content-Disposition Header
585
602
  # Field", RFC 2183, DOI 10.17487/RFC2183, August 1997,
586
603
  # <https://www.rfc-editor.org/info/rfc2183>.
587
604
  #
588
- # [MIME-IMB[https://tools.ietf.org/html/rfc2045]]::
605
+ # [MIME-IMB[https://www.rfc-editor.org/rfc/rfc2045]]::
589
606
  # Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
590
607
  # (MIME) Part One: Format of Internet Message Bodies",
591
608
  # RFC 2045, DOI 10.17487/RFC2045, November 1996,
592
609
  # <https://www.rfc-editor.org/info/rfc2045>.
593
610
  #
594
- # [MIME-IMT[https://tools.ietf.org/html/rfc2046]]::
611
+ # [MIME-IMT[https://www.rfc-editor.org/rfc/rfc2046]]::
595
612
  # Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions
596
613
  # (MIME) Part Two: Media Types", RFC 2046, DOI 10.17487/RFC2046,
597
614
  # November 1996, <https://www.rfc-editor.org/info/rfc2046>.
598
615
  #
599
- # [MIME-HDRS[https://tools.ietf.org/html/rfc2047]]::
616
+ # [MIME-HDRS[https://www.rfc-editor.org/rfc/rfc2047]]::
600
617
  # Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part Three:
601
618
  # Message Header Extensions for Non-ASCII Text",
602
619
  # RFC 2047, DOI 10.17487/RFC2047, November 1996,
603
620
  # <https://www.rfc-editor.org/info/rfc2047>.
604
621
  #
605
- # [RFC2231[https://tools.ietf.org/html/rfc2231]]::
622
+ # [RFC2231[https://www.rfc-editor.org/rfc/rfc2231]]::
606
623
  # Freed, N. and K. Moore, "MIME Parameter Value and Encoded Word
607
624
  # Extensions: Character Sets, Languages, and Continuations",
608
625
  # RFC 2231, DOI 10.17487/RFC2231, November 1997,
609
626
  # <https://www.rfc-editor.org/info/rfc2231>.
610
627
  #
611
- # [I18n-HDRS[https://tools.ietf.org/html/rfc6532]]::
628
+ # [I18n-HDRS[https://www.rfc-editor.org/rfc/rfc6532]]::
612
629
  # Yang, A., Steele, S., and N. Freed, "Internationalized Email Headers",
613
630
  # RFC 6532, DOI 10.17487/RFC6532, February 2012,
614
631
  # <https://www.rfc-editor.org/info/rfc6532>.
@@ -624,12 +641,12 @@ module Net
624
641
  # RFC 2557, DOI 10.17487/RFC2557, March 1999,
625
642
  # <https://www.rfc-editor.org/info/rfc2557>.
626
643
  #
627
- # [MD5[https://tools.ietf.org/html/rfc1864]]::
644
+ # [MD5[https://www.rfc-editor.org/rfc/rfc1864]]::
628
645
  # Myers, J. and M. Rose, "The Content-MD5 Header Field",
629
646
  # RFC 1864, DOI 10.17487/RFC1864, October 1995,
630
647
  # <https://www.rfc-editor.org/info/rfc1864>.
631
648
  #
632
- # [RFC3503[https://tools.ietf.org/html/rfc3503]]::
649
+ # [RFC3503[https://www.rfc-editor.org/rfc/rfc3503]]::
633
650
  # Melnikov, A., "Message Disposition Notification (MDN)
634
651
  # profile for Internet Message Access Protocol (IMAP)",
635
652
  # RFC 3503, DOI 10.17487/RFC3503, March 2003,
@@ -637,27 +654,27 @@ module Net
637
654
  #
638
655
  # === \IMAP Extensions
639
656
  #
640
- # [QUOTA[https://tools.ietf.org/html/rfc9208]]::
657
+ # [QUOTA[https://www.rfc-editor.org/rfc/rfc9208]]::
641
658
  # Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
642
659
  # March 2022, <https://www.rfc-editor.org/info/rfc9208>.
643
660
  #
644
661
  # <em>Note: obsoletes</em>
645
- # RFC-2087[https://tools.ietf.org/html/rfc2087]<em> (January 1997)</em>.
662
+ # RFC-2087[https://www.rfc-editor.org/rfc/rfc2087]<em> (January 1997)</em>.
646
663
  # <em>Net::IMAP does not fully support the RFC9208 updates yet.</em>
647
- # [IDLE[https://tools.ietf.org/html/rfc2177]]::
664
+ # [IDLE[https://www.rfc-editor.org/rfc/rfc2177]]::
648
665
  # Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
649
666
  # June 1997, <https://www.rfc-editor.org/info/rfc2177>.
650
- # [NAMESPACE[https://tools.ietf.org/html/rfc2342]]::
667
+ # [NAMESPACE[https://www.rfc-editor.org/rfc/rfc2342]]::
651
668
  # Gahrns, M. and C. Newman, "IMAP4 Namespace", RFC 2342,
652
669
  # DOI 10.17487/RFC2342, May 1998, <https://www.rfc-editor.org/info/rfc2342>.
653
- # [ID[https://tools.ietf.org/html/rfc2971]]::
670
+ # [ID[https://www.rfc-editor.org/rfc/rfc2971]]::
654
671
  # Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
655
672
  # October 2000, <https://www.rfc-editor.org/info/rfc2971>.
656
- # [BINARY[https://tools.ietf.org/html/rfc3516]]::
673
+ # [BINARY[https://www.rfc-editor.org/rfc/rfc3516]]::
657
674
  # Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
658
675
  # DOI 10.17487/RFC3516, April 2003,
659
676
  # <https://www.rfc-editor.org/info/rfc3516>.
660
- # [ACL[https://tools.ietf.org/html/rfc4314]]::
677
+ # [ACL[https://www.rfc-editor.org/rfc/rfc4314]]::
661
678
  # Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
662
679
  # DOI 10.17487/RFC4314, December 2005,
663
680
  # <https://www.rfc-editor.org/info/rfc4314>.
@@ -665,36 +682,46 @@ module Net
665
682
  # Crispin, M., "Internet Message Access Protocol (\IMAP) - UIDPLUS
666
683
  # extension", RFC 4315, DOI 10.17487/RFC4315, December 2005,
667
684
  # <https://www.rfc-editor.org/info/rfc4315>.
668
- # [SORT[https://tools.ietf.org/html/rfc5256]]::
685
+ # [SORT[https://www.rfc-editor.org/rfc/rfc5256]]::
669
686
  # Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
670
687
  # THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
671
688
  # <https://www.rfc-editor.org/info/rfc5256>.
672
- # [THREAD[https://tools.ietf.org/html/rfc5256]]::
689
+ # [THREAD[https://www.rfc-editor.org/rfc/rfc5256]]::
673
690
  # Crispin, M. and K. Murchison, "Internet Message Access Protocol - SORT and
674
691
  # THREAD Extensions", RFC 5256, DOI 10.17487/RFC5256, June 2008,
675
692
  # <https://www.rfc-editor.org/info/rfc5256>.
676
693
  # [RFC5530[https://www.rfc-editor.org/rfc/rfc5530.html]]::
677
694
  # Gulbrandsen, A., "IMAP Response Codes", RFC 5530, DOI 10.17487/RFC5530,
678
695
  # May 2009, <https://www.rfc-editor.org/info/rfc5530>.
679
- # [MOVE[https://tools.ietf.org/html/rfc6851]]::
696
+ # [MOVE[https://www.rfc-editor.org/rfc/rfc6851]]::
680
697
  # Gulbrandsen, A. and N. Freed, Ed., "Internet Message Access Protocol
681
698
  # (\IMAP) - MOVE Extension", RFC 6851, DOI 10.17487/RFC6851, January 2013,
682
699
  # <https://www.rfc-editor.org/info/rfc6851>.
683
- # [UTF8=ACCEPT[https://tools.ietf.org/html/rfc6855]]::
684
- # [UTF8=ONLY[https://tools.ietf.org/html/rfc6855]]::
700
+ # [UTF8=ACCEPT[https://www.rfc-editor.org/rfc/rfc6855]]::
701
+ # [UTF8=ONLY[https://www.rfc-editor.org/rfc/rfc6855]]::
685
702
  # Resnick, P., Ed., Newman, C., Ed., and S. Shen, Ed.,
686
703
  # "IMAP Support for UTF-8", RFC 6855, DOI 10.17487/RFC6855, March 2013,
687
704
  # <https://www.rfc-editor.org/info/rfc6855>.
688
- # [CONDSTORE[https://tools.ietf.org/html/rfc7162]]::
689
- # [QRESYNC[https://tools.ietf.org/html/rfc7162]]::
705
+ # [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162]]::
706
+ # [QRESYNC[https://www.rfc-editor.org/rfc/rfc7162]]::
690
707
  # Melnikov, A. and D. Cridland, "IMAP Extensions: Quick Flag Changes
691
708
  # Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization
692
709
  # (QRESYNC)", RFC 7162, DOI 10.17487/RFC7162, May 2014,
693
710
  # <https://www.rfc-editor.org/info/rfc7162>.
694
- # [OBJECTID[https://tools.ietf.org/html/rfc8474]]::
711
+ # [OBJECTID[https://www.rfc-editor.org/rfc/rfc8474]]::
695
712
  # Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
696
713
  # RFC 8474, DOI 10.17487/RFC8474, September 2018,
697
714
  # <https://www.rfc-editor.org/info/rfc8474>.
715
+ # [PARTIAL[https://www.rfc-editor.org/info/rfc9394]]::
716
+ # Melnikov, A., Achuthan, A., Nagulakonda, V., and L. Alves,
717
+ # "IMAP PARTIAL Extension for Paged SEARCH and FETCH", RFC 9394,
718
+ # DOI 10.17487/RFC9394, June 2023,
719
+ # <https://www.rfc-editor.org/info/rfc9394>.
720
+ # [UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.pdf]]::
721
+ # Melnikov, A., Achuthan, A., Nagulakonda, V., Singh, A., and L. Alves,
722
+ # "\IMAP Extension for Using and Returning Unique Identifiers (UIDs) Only",
723
+ # RFC 9586, DOI 10.17487/RFC9586, May 2024,
724
+ # <https://www.rfc-editor.org/info/rfc9586>.
698
725
  #
699
726
  # === IANA registries
700
727
  # * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
@@ -708,7 +735,7 @@ module Net
708
735
  # * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
709
736
  # +imap+
710
737
  # * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
711
- # ===== For currently unsupported features:
738
+ # ==== For currently unsupported features:
712
739
  # * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
713
740
  # * {LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
714
741
  # * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
@@ -717,7 +744,7 @@ module Net
717
744
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
718
745
  #
719
746
  class IMAP < Protocol
720
- VERSION = "0.4.9.1"
747
+ VERSION = "0.5.6"
721
748
 
722
749
  # Aliases for supported capabilities, to be used with the #enable command.
723
750
  ENABLE_ALIASES = {
@@ -735,14 +762,15 @@ module Net
735
762
  include SSL
736
763
  end
737
764
 
738
- # Returns the debug mode.
739
- def self.debug
740
- return @@debug
741
- end
765
+ # Returns the global Config object
766
+ def self.config; Config.global end
742
767
 
743
- # Sets the debug mode.
768
+ # Returns the global debug mode.
769
+ def self.debug; config.debug end
770
+
771
+ # Sets the global debug mode.
744
772
  def self.debug=(val)
745
- return @@debug = val
773
+ config.debug = val
746
774
  end
747
775
 
748
776
  # The default port for IMAP connections, port 143
@@ -764,13 +792,19 @@ module Net
764
792
  # Returns the initial greeting the server, an UntaggedResponse.
765
793
  attr_reader :greeting
766
794
 
795
+ # The client configuration. See Net::IMAP::Config.
796
+ #
797
+ # By default, the client's local configuration inherits from the global
798
+ # Net::IMAP.config.
799
+ attr_reader :config
800
+
767
801
  # Seconds to wait until a connection is opened.
768
802
  # If the IMAP object cannot open a connection within this time,
769
803
  # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
770
- attr_reader :open_timeout
804
+ def open_timeout; config.open_timeout end
771
805
 
772
806
  # Seconds to wait until an IDLE response is received.
773
- attr_reader :idle_response_timeout
807
+ def idle_response_timeout; config.idle_response_timeout end
774
808
 
775
809
  # The hostname this client connected to
776
810
  attr_reader :host
@@ -809,18 +843,44 @@ module Net
809
843
  # If +ssl+ is a hash, it's passed to
810
844
  # {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
811
845
  # the keys are names of attribute assignment methods on
812
- # SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
846
+ # SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]. For example:
813
847
  #
814
- # [open_timeout]
815
- # Seconds to wait until a connection is opened
816
- # [idle_response_timeout]
817
- # Seconds to wait until an IDLE response is received
848
+ # [{ca_file}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_file]]
849
+ # The path to a file containing a PEM-format CA certificate.
850
+ # [{ca_path}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_path]]
851
+ # The path to a directory containing CA certificates in PEM format.
852
+ # [{min_version}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D]]
853
+ # Sets the lower bound on the supported SSL/TLS protocol version. Set to
854
+ # an +OpenSSL+ constant such as +OpenSSL::SSL::TLS1_2_VERSION+,
855
+ # [{verify_mode}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-verify_mode]]
856
+ # SSL session verification mode. Valid modes include
857
+ # +OpenSSL::SSL::VERIFY_PEER+ and +OpenSSL::SSL::VERIFY_NONE+.
818
858
  #
819
- # See DeprecatedClientOptions.new for deprecated arguments.
859
+ # See {OpenSSL::SSL::SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html] for other valid SSL context params.
860
+ #
861
+ # See DeprecatedClientOptions.new for deprecated SSL arguments.
862
+ #
863
+ # [config]
864
+ # A Net::IMAP::Config object to use as the basis for #config. By default,
865
+ # the global Net::IMAP.config is used.
866
+ #
867
+ # >>>
868
+ # *NOTE:* +config+ does not set #config directly---it sets the _parent_
869
+ # config for inheritance. Every client creates its own unique #config.
870
+ #
871
+ # All other keyword arguments are forwarded to Net::IMAP::Config.new, to
872
+ # initialize the client's #config. For example:
873
+ #
874
+ # [{open_timeout}[rdoc-ref:Config#open_timeout]]
875
+ # Seconds to wait until a connection is opened
876
+ # [{idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]]
877
+ # Seconds to wait until an IDLE response is received
878
+ #
879
+ # See Net::IMAP::Config for other valid options.
820
880
  #
821
881
  # ==== Examples
822
882
  #
823
- # Connect to cleartext port 143 at mail.example.com and recieve the server greeting:
883
+ # Connect to cleartext port 143 at mail.example.com and receive the server greeting:
824
884
  # imap = Net::IMAP.new('mail.example.com', ssl: false) # => #<Net::IMAP:0x00007f79b0872bd0>
825
885
  # imap.port => 143
826
886
  # imap.tls_verified? => false
@@ -872,13 +932,12 @@ module Net
872
932
  # Connected to the host successfully, but it immediately said goodbye.
873
933
  #
874
934
  def initialize(host, port: nil, ssl: nil,
875
- open_timeout: 30, idle_response_timeout: 5)
935
+ config: Config.global, **config_options)
876
936
  super()
877
937
  # Config options
878
938
  @host = host
939
+ @config = Config.new(config, **config_options)
879
940
  @port = port || (ssl ? SSL_PORT : PORT)
880
- @open_timeout = Integer(open_timeout)
881
- @idle_response_timeout = Integer(idle_response_timeout)
882
941
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
883
942
 
884
943
  # Basic Client State
@@ -888,8 +947,8 @@ module Net
888
947
  @greeting = nil
889
948
  @capabilities = nil
890
949
 
891
- # Client Protocol Reciever
892
- @parser = ResponseParser.new
950
+ # Client Protocol Receiver
951
+ @parser = ResponseParser.new(config: @config)
893
952
  @responses = Hash.new {|h, k| h[k] = [] }
894
953
  @response_handlers = []
895
954
  @receiver_thread = nil
@@ -912,9 +971,6 @@ module Net
912
971
  @sock = tcp_socket(@host, @port)
913
972
  start_tls_session if ssl_ctx
914
973
  start_imap_connection
915
-
916
- # DEPRECATED: to remove in next version
917
- @client_thread = Thread.current
918
974
  end
919
975
 
920
976
  # Returns true after the TLS negotiation has completed and the remote
@@ -922,11 +978,6 @@ module Net
922
978
  # but peer verification was disabled.
923
979
  def tls_verified?; @tls_verified end
924
980
 
925
- def client_thread # :nodoc:
926
- warn "Net::IMAP#client_thread is deprecated and will be removed soon."
927
- @client_thread
928
- end
929
-
930
981
  # Disconnects from the server.
931
982
  #
932
983
  # Related: #logout, #logout!
@@ -1095,12 +1146,12 @@ module Net
1095
1146
  # )
1096
1147
  # end
1097
1148
  #
1098
- # See [ID[https://tools.ietf.org/html/rfc2971]] for field definitions.
1149
+ # See [ID[https://www.rfc-editor.org/rfc/rfc2971]] for field definitions.
1099
1150
  #
1100
- # ===== Capabilities
1151
+ # ==== Capabilities
1101
1152
  #
1102
1153
  # The server's capabilities must include +ID+
1103
- # [RFC2971[https://tools.ietf.org/html/rfc2971]].
1154
+ # [RFC2971[https://www.rfc-editor.org/rfc/rfc2971]].
1104
1155
  def id(client_id=nil)
1105
1156
  synchronize do
1106
1157
  send_command("ID", ClientID.new(client_id))
@@ -1179,7 +1230,7 @@ module Net
1179
1230
  #
1180
1231
  # Related: Net::IMAP.new, #login, #authenticate
1181
1232
  #
1182
- # ===== Capability
1233
+ # ==== Capability
1183
1234
  # Clients should not call #starttls unless the server advertises the
1184
1235
  # +STARTTLS+ capability.
1185
1236
  #
@@ -1188,17 +1239,25 @@ module Net
1188
1239
  #
1189
1240
  def starttls(**options)
1190
1241
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
1191
- send_command("STARTTLS") do |resp|
1242
+ error = nil
1243
+ ok = send_command("STARTTLS") do |resp|
1192
1244
  if resp.kind_of?(TaggedResponse) && resp.name == "OK"
1193
1245
  clear_cached_capabilities
1194
1246
  clear_responses
1195
1247
  start_tls_session
1196
1248
  end
1249
+ rescue Exception => error
1250
+ raise # note that the error backtrace is in the receiver_thread
1197
1251
  end
1252
+ if error
1253
+ disconnect
1254
+ raise error
1255
+ end
1256
+ ok
1198
1257
  end
1199
1258
 
1200
1259
  # :call-seq:
1201
- # authenticate(mechanism, *, sasl_ir: true, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
1260
+ # authenticate(mechanism, *, sasl_ir: config.sasl_ir, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
1202
1261
  #
1203
1262
  # Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
1204
1263
  # to authenticate the client. If successful, the connection enters the
@@ -1207,7 +1266,11 @@ module Net
1207
1266
  # +mechanism+ is the name of the \SASL authentication mechanism to be used.
1208
1267
  #
1209
1268
  # +sasl_ir+ allows or disallows sending an "initial response" (see the
1210
- # +SASL-IR+ capability, below).
1269
+ # +SASL-IR+ capability, below). Defaults to the #config value for
1270
+ # {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
1271
+ #
1272
+ # The +registry+ kwarg can be used to select the mechanism implementation
1273
+ # from a custom registry. See SASL.authenticator and SASL::Authenticators.
1211
1274
  #
1212
1275
  # All other arguments are forwarded to the registered SASL authenticator for
1213
1276
  # the requested mechanism. <em>The documentation for each individual
@@ -1303,27 +1366,9 @@ module Net
1303
1366
  # Previously cached #capabilities will be cleared when this method
1304
1367
  # completes. If the TaggedResponse to #authenticate includes updated
1305
1368
  # capabilities, they will be cached.
1306
- def authenticate(mechanism, *creds, sasl_ir: true, **props, &callback)
1307
- mechanism = mechanism.to_s.tr("_", "-").upcase
1308
- authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
1309
- cmdargs = ["AUTHENTICATE", mechanism]
1310
- if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
1311
- authenticator.respond_to?(:initial_response?) &&
1312
- authenticator.initial_response?
1313
- response = authenticator.process(nil)
1314
- cmdargs << (response.empty? ? "=" : [response].pack("m0"))
1315
- end
1316
- result = send_command_with_continuations(*cmdargs) {|data|
1317
- challenge = data.unpack1("m")
1318
- response = authenticator.process challenge
1319
- [response].pack("m0")
1320
- }
1321
- if authenticator.respond_to?(:done?) && !authenticator.done?
1322
- logout!
1323
- raise SASL::AuthenticationIncomplete, result
1324
- end
1325
- @capabilities = capabilities_from_resp_code result
1326
- result
1369
+ def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
1370
+ sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
1371
+ .tap { @capabilities = capabilities_from_resp_code _1 }
1327
1372
  end
1328
1373
 
1329
1374
  # Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
@@ -1340,16 +1385,12 @@ module Net
1340
1385
  #
1341
1386
  # Related: #authenticate, #starttls
1342
1387
  #
1343
- # ===== Capabilities
1388
+ # ==== Capabilities
1344
1389
  #
1345
1390
  # An IMAP client MUST NOT call #login when the server advertises the
1346
- # +LOGINDISABLED+ capability.
1347
- #
1348
- # if imap.capability? "LOGINDISABLED"
1349
- # raise "Remote server has disabled the login command"
1350
- # else
1351
- # imap.login username, password
1352
- # end
1391
+ # +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
1392
+ # LoginDisabledError when that capability is present. See
1393
+ # Config#enforce_logindisabled.
1353
1394
  #
1354
1395
  # Server capabilities may change after #starttls, #login, and #authenticate.
1355
1396
  # Cached capabilities _must_ be invalidated after this method completes.
@@ -1357,6 +1398,9 @@ module Net
1357
1398
  # ResponseCode.
1358
1399
  #
1359
1400
  def login(user, password)
1401
+ if enforce_logindisabled? && capability?("LOGINDISABLED")
1402
+ raise LoginDisabledError
1403
+ end
1360
1404
  send_command("LOGIN", user, password)
1361
1405
  .tap { @capabilities = capabilities_from_resp_code _1 }
1362
1406
  end
@@ -1382,7 +1426,7 @@ module Net
1382
1426
  #
1383
1427
  # Related: #examine
1384
1428
  #
1385
- # ===== Capabilities
1429
+ # ==== Capabilities
1386
1430
  #
1387
1431
  # If [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] is supported,
1388
1432
  # the server may return an untagged "NO" response with a "UIDNOTSTICKY"
@@ -1500,7 +1544,7 @@ module Net
1500
1544
  #
1501
1545
  # Related: #lsub, MailboxList
1502
1546
  #
1503
- # ===== For example:
1547
+ # ==== For example:
1504
1548
  #
1505
1549
  # imap.create("foo/bar")
1506
1550
  # imap.create("foo/baz")
@@ -1538,7 +1582,7 @@ module Net
1538
1582
  # servers, then folder creation (and listing, moving, etc) can lead to
1539
1583
  # errors.
1540
1584
  #
1541
- # From RFC2342[https://tools.ietf.org/html/rfc2342]:
1585
+ # From RFC2342[https://www.rfc-editor.org/rfc/rfc2342]:
1542
1586
  # >>>
1543
1587
  # <em>Although typically a server will support only a single Personal
1544
1588
  # Namespace, and a single Other User's Namespace, circumstances exist
@@ -1551,7 +1595,7 @@ module Net
1551
1595
  #
1552
1596
  # Related: #list, Namespaces, Namespace
1553
1597
  #
1554
- # ===== For example:
1598
+ # ==== For example:
1555
1599
  #
1556
1600
  # if capable?("NAMESPACE")
1557
1601
  # namespaces = imap.namespace
@@ -1565,10 +1609,10 @@ module Net
1565
1609
  # end
1566
1610
  # end
1567
1611
  #
1568
- # ===== Capabilities
1612
+ # ==== Capabilities
1569
1613
  #
1570
- # The server's capabilities must include +NAMESPACE+
1571
- # [RFC2342[https://tools.ietf.org/html/rfc2342]].
1614
+ # The server's capabilities must include either +IMAP4rev2+ or +NAMESPACE+
1615
+ # [RFC2342[https://www.rfc-editor.org/rfc/rfc2342]].
1572
1616
  def namespace
1573
1617
  synchronize do
1574
1618
  send_command("NAMESPACE")
@@ -1604,7 +1648,7 @@ module Net
1604
1648
  #
1605
1649
  # Related: #list, MailboxList
1606
1650
  #
1607
- # ===== Capabilities
1651
+ # ==== Capabilities
1608
1652
  #
1609
1653
  # The server's capabilities must include +XLIST+,
1610
1654
  # a deprecated Gmail extension (replaced by +SPECIAL-USE+).
@@ -1627,10 +1671,10 @@ module Net
1627
1671
  #
1628
1672
  # Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
1629
1673
  #
1630
- # ===== Capabilities
1674
+ # ==== Capabilities
1631
1675
  #
1632
1676
  # The server's capabilities must include +QUOTA+
1633
- # [RFC2087[https://tools.ietf.org/html/rfc2087]].
1677
+ # [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
1634
1678
  def getquotaroot(mailbox)
1635
1679
  synchronize do
1636
1680
  send_command("GETQUOTAROOT", mailbox)
@@ -1648,10 +1692,10 @@ module Net
1648
1692
  #
1649
1693
  # Related: #getquotaroot, #setquota, MailboxQuota
1650
1694
  #
1651
- # ===== Capabilities
1695
+ # ==== Capabilities
1652
1696
  #
1653
1697
  # The server's capabilities must include +QUOTA+
1654
- # [RFC2087[https://tools.ietf.org/html/rfc2087]].
1698
+ # [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
1655
1699
  def getquota(mailbox)
1656
1700
  synchronize do
1657
1701
  send_command("GETQUOTA", mailbox)
@@ -1666,10 +1710,10 @@ module Net
1666
1710
  #
1667
1711
  # Related: #getquota, #getquotaroot
1668
1712
  #
1669
- # ===== Capabilities
1713
+ # ==== Capabilities
1670
1714
  #
1671
1715
  # The server's capabilities must include +QUOTA+
1672
- # [RFC2087[https://tools.ietf.org/html/rfc2087]].
1716
+ # [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]].
1673
1717
  def setquota(mailbox, quota)
1674
1718
  if quota.nil?
1675
1719
  data = '()'
@@ -1686,10 +1730,10 @@ module Net
1686
1730
  #
1687
1731
  # Related: #getacl
1688
1732
  #
1689
- # ===== Capabilities
1733
+ # ==== Capabilities
1690
1734
  #
1691
1735
  # The server's capabilities must include +ACL+
1692
- # [RFC4314[https://tools.ietf.org/html/rfc4314]].
1736
+ # [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
1693
1737
  def setacl(mailbox, user, rights)
1694
1738
  if rights.nil?
1695
1739
  send_command("SETACL", mailbox, user, "")
@@ -1704,10 +1748,10 @@ module Net
1704
1748
  #
1705
1749
  # Related: #setacl, MailboxACLItem
1706
1750
  #
1707
- # ===== Capabilities
1751
+ # ==== Capabilities
1708
1752
  #
1709
1753
  # The server's capabilities must include +ACL+
1710
- # [RFC4314[https://tools.ietf.org/html/rfc4314]].
1754
+ # [RFC4314[https://www.rfc-editor.org/rfc/rfc4314]].
1711
1755
  def getacl(mailbox)
1712
1756
  synchronize do
1713
1757
  send_command("GETACL", mailbox)
@@ -1741,7 +1785,7 @@ module Net
1741
1785
  # for +mailbox+ cannot be returned; for instance, because it
1742
1786
  # does not exist.
1743
1787
  #
1744
- # ===== Supported attributes
1788
+ # ==== Supported attributes
1745
1789
  #
1746
1790
  # +MESSAGES+:: The number of messages in the mailbox.
1747
1791
  #
@@ -1772,12 +1816,12 @@ module Net
1772
1816
  # Unsupported attributes may be requested. The attribute value will be
1773
1817
  # either an Integer or an ExtensionData object.
1774
1818
  #
1775
- # ===== For example:
1819
+ # ==== For example:
1776
1820
  #
1777
1821
  # p imap.status("inbox", ["MESSAGES", "RECENT"])
1778
1822
  # #=> {"RECENT"=>0, "MESSAGES"=>44}
1779
1823
  #
1780
- # ===== Capabilities
1824
+ # ==== Capabilities
1781
1825
  #
1782
1826
  # +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
1783
1827
  # <tt>STATUS=SIZE</tt>
@@ -1817,7 +1861,7 @@ module Net
1817
1861
  # not exist (it is not created automatically), or if the flags,
1818
1862
  # date_time, or message arguments contain errors.
1819
1863
  #
1820
- # ===== Capabilities
1864
+ # ==== Capabilities
1821
1865
  #
1822
1866
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
1823
1867
  # supported and the destination supports persistent UIDs, the server's
@@ -1866,129 +1910,492 @@ module Net
1866
1910
  #
1867
1911
  # Related: #close
1868
1912
  #
1869
- # ===== Capabilities
1913
+ # ==== Capabilities
1870
1914
  #
1871
- # The server's capabilities must include +UNSELECT+
1872
- # [RFC3691[https://tools.ietf.org/html/rfc3691]].
1915
+ # The server's capabilities must include either +IMAP4rev2+ or +UNSELECT+
1916
+ # [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
1873
1917
  def unselect
1874
1918
  send_command("UNSELECT")
1875
1919
  end
1876
1920
 
1921
+ # call-seq:
1922
+ # expunge -> array of message sequence numbers
1923
+ # expunge -> VanishedData of UIDs
1924
+ #
1877
1925
  # Sends an {EXPUNGE command [IMAP4rev1 §6.4.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.3]
1878
- # Sends a EXPUNGE command to permanently remove from the currently
1879
- # selected mailbox all messages that have the \Deleted flag set.
1926
+ # to permanently remove all messages with the +\Deleted+ flag from the
1927
+ # currently selected mailbox.
1928
+ #
1929
+ # Returns either an array of expunged message <em>sequence numbers</em> or
1930
+ # (when the appropriate capability is enabled) VanishedData of expunged
1931
+ # UIDs. Previously unhandled +EXPUNGE+ or +VANISHED+ responses are merged
1932
+ # with the direct response to this command. <tt>VANISHED (EARLIER)</tt>
1933
+ # responses will _not_ be merged.
1934
+ #
1935
+ # When no messages have been expunged, an empty array is returned,
1936
+ # regardless of which extensions are enabled. In a future release, an empty
1937
+ # VanishedData may be returned, based on the currently enabled extensions.
1880
1938
  #
1881
1939
  # Related: #uid_expunge
1940
+ #
1941
+ # ==== Capabilities
1942
+ #
1943
+ # When either QRESYNC[https://www.rfc-editor.org/rfc/rfc7162] or
1944
+ # UIDONLY[https://www.rfc-editor.org/rfc/rfc9586] are enabled, #expunge
1945
+ # returns VanishedData, which contains UIDs---<em>not message sequence
1946
+ # numbers</em>.
1882
1947
  def expunge
1883
- synchronize do
1884
- send_command("EXPUNGE")
1885
- clear_responses("EXPUNGE")
1886
- end
1948
+ expunge_internal("EXPUNGE")
1887
1949
  end
1888
1950
 
1951
+ # call-seq:
1952
+ # uid_expunge{uid_set) -> array of message sequence numbers
1953
+ # uid_expunge{uid_set) -> VanishedData of UIDs
1954
+ #
1889
1955
  # Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
1890
1956
  # {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
1891
1957
  # to permanently remove all messages that have both the <tt>\\Deleted</tt>
1892
1958
  # flag set and a UID that is included in +uid_set+.
1893
1959
  #
1960
+ # Returns the same result type as #expunge.
1961
+ #
1894
1962
  # By using #uid_expunge instead of #expunge when resynchronizing with
1895
1963
  # the server, the client can ensure that it does not inadvertantly
1896
1964
  # remove any messages that have been marked as <tt>\\Deleted</tt> by other
1897
1965
  # clients between the time that the client was last connected and
1898
1966
  # the time the client resynchronizes.
1899
1967
  #
1900
- # *Note:*
1901
- # >>>
1902
- # Although the command takes a set of UIDs for its argument, the
1903
- # server still returns regular EXPUNGE responses, which contain
1904
- # a <em>sequence number</em>. These will be deleted from
1905
- # #responses and this method returns them as an array of
1906
- # <em>sequence number</em> integers.
1907
- #
1908
1968
  # Related: #expunge
1909
1969
  #
1910
- # ===== Capabilities
1970
+ # ==== Capabilities
1911
1971
  #
1912
- # The server's capabilities must include +UIDPLUS+
1972
+ # The server's capabilities must include either +IMAP4rev2+ or +UIDPLUS+
1913
1973
  # [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
1974
+ #
1975
+ # Otherwise, #uid_expunge is updated by extensions in the same way as
1976
+ # #expunge.
1914
1977
  def uid_expunge(uid_set)
1915
- synchronize do
1916
- send_command("UID EXPUNGE", MessageSet.new(uid_set))
1917
- clear_responses("EXPUNGE")
1918
- end
1978
+ expunge_internal("UID EXPUNGE", SequenceSet.new(uid_set))
1919
1979
  end
1920
1980
 
1921
- # Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
1922
- # to search the mailbox for messages that match the given searching
1923
- # criteria, and returns message sequence numbers. +keys+ can either be a
1924
- # string holding the entire search string, or a single-dimension array of
1925
- # search keywords and arguments.
1981
+ # :call-seq:
1982
+ # search(criteria, charset = nil) -> result
1983
+ # search(criteria, charset: nil, return: nil) -> result
1926
1984
  #
1927
- # Returns a SearchResult object. SearchResult inherits from Array (for
1928
- # backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
1929
- # capability has been enabled.
1985
+ # Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
1986
+ # to search the mailbox for messages that match the given search +criteria+,
1987
+ # and returns either a SearchResult or an ESearchResult. SearchResult
1988
+ # inherits from Array (for backward compatibility) but adds
1989
+ # SearchResult#modseq when the +CONDSTORE+ capability has been enabled.
1990
+ # ESearchResult also implements {#to_a}[rdoc-ref:ESearchResult#to_a], for
1991
+ # compatibility with SearchResult.
1992
+ #
1993
+ # +criteria+ is one or more search keys and their arguments, which may be
1994
+ # provided as an array or a string.
1995
+ # See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
1996
+ # and {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
1997
+ #
1998
+ # +return+ options control what kind of information is returned about
1999
+ # messages matching the search +criteria+. Specifying +return+ should force
2000
+ # the server to return an ESearchResult instead of a SearchResult, but some
2001
+ # servers disobey this requirement. <em>Requires an extended search
2002
+ # capability, such as +ESEARCH+ or +IMAP4rev2+.</em>
2003
+ # See {"Argument translation"}[rdoc-ref:#search@Argument+translation] and
2004
+ # {"Supported return options"}[rdoc-ref:#search@Supported+return+options],
2005
+ # below.
2006
+ #
2007
+ # +charset+ is the name of the {registered character
2008
+ # set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
2009
+ # used by strings in the search +criteria+. When +charset+ isn't specified,
2010
+ # either <tt>"US-ASCII"</tt> or <tt>"UTF-8"</tt> is assumed, depending on
2011
+ # the server's capabilities.
2012
+ #
2013
+ # _NOTE:_ Return options and charset may be sent as part of +criteria+. Do
2014
+ # not use the +return+ or +charset+ arguments when either return options or
2015
+ # charset are embedded in +criteria+.
1930
2016
  #
1931
2017
  # Related: #uid_search
1932
2018
  #
1933
- # ===== Search criteria
2019
+ # ==== For example:
2020
+ #
2021
+ # imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
2022
+ # #=> [1, 6, 7, 8]
1934
2023
  #
1935
- # For a full list of search criteria,
2024
+ # The following assumes the server supports +ESEARCH+ and +CONDSTORE+:
2025
+ #
2026
+ # result = imap.uid_search(["UID", 12345.., "MODSEQ", 620_162_338],
2027
+ # return: %w(all count min max))
2028
+ # # => #<data Net::IMAP::ESearchResult tag="RUBY0123", uid=true,
2029
+ # # data=[["ALL", Net::IMAP::SequenceSet["12346:12349,22222:22230"]],
2030
+ # # ["COUNT", 13], ["MIN", 12346], ["MAX", 22230],
2031
+ # # ["MODSEQ", 917162488]]>
2032
+ # result.to_a # => [12346, 12347, 12348, 12349, 22222, 22223, 22224,
2033
+ # # 22225, 22226, 22227, 22228, 22229, 22230]
2034
+ # result.uid? # => true
2035
+ # result.count # => 13
2036
+ # result.min # => 12346
2037
+ # result.max # => 22230
2038
+ # result.modseq # => 917162488
2039
+ #
2040
+ # Using +return+ options to limit the result to only min, max, and count:
2041
+ #
2042
+ # result = imap.uid_search(["UID", 12345..,], return: %w(count min max))
2043
+ # # => #<data Net::IMAP::ESearchResult tag="RUBY0124", uid=true,
2044
+ # # data=[["COUNT", 13], ["MIN", 12346], ["MAX", 22230]]>
2045
+ # result.to_a # => []
2046
+ # result.count # => 13
2047
+ # result.min # => 12346
2048
+ # result.max # => 22230
2049
+ #
2050
+ # Return options and charset may be sent as keyword args or embedded in the
2051
+ # +criteria+ arg, but they must be in the correct order: <tt>"RETURN (...)
2052
+ # CHARSET ... criteria..."</tt>. The following searches
2053
+ # send the exact same command to the server:
2054
+ #
2055
+ # # Return options and charset as keyword arguments (preferred)
2056
+ # imap.search(%w(OR UNSEEN FLAGGED), return: %w(MIN MAX), charset: "UTF-8")
2057
+ # # Embedding return and charset in the criteria array
2058
+ # imap.search(["RETURN", %w(MIN MAX), "CHARSET", "UTF-8", *%w(OR UNSEEN FLAGGED)])
2059
+ # # Embedding return and charset in the criteria string
2060
+ # imap.search("RETURN (MIN MAX) CHARSET UTF-8 OR UNSEEN FLAGGED")
2061
+ #
2062
+ # Sending charset as the second positional argument is supported for
2063
+ # backward compatibility. Future versions may print a deprecation warning:
2064
+ # imap.search(%w(OR UNSEEN FLAGGED), "UTF-8", return: %w(MIN MAX))
2065
+ #
2066
+ # ==== Argument translation
2067
+ #
2068
+ # [+return+ options]
2069
+ # Must be an Array. Return option names may be either strings or symbols.
2070
+ # +Range+ elements which begin and end with negative integers are encoded
2071
+ # for use with +PARTIAL+--any other ranges are converted to SequenceSet.
2072
+ # Unlike +criteria+, other return option arguments are not automatically
2073
+ # converted to SequenceSet.
2074
+ #
2075
+ # [When +criteria+ is an Array]
2076
+ # When the array begins with <tt>"RETURN"</tt> (case insensitive), the
2077
+ # second array element is translated like the +return+ parameter (as
2078
+ # described above).
2079
+ #
2080
+ # Every other member is a +SEARCH+ command argument:
2081
+ # [SequenceSet]
2082
+ # Encoded as an \IMAP +sequence-set+ with SequenceSet#valid_string.
2083
+ # [Set, Range, <tt>-1</tt>, +:*+, responds to +#to_sequence_set+]
2084
+ # Converted to SequenceSet for validation and encoding.
2085
+ # [nested sequence-set +Array+]
2086
+ # When every element in a nested array is one of the above types, a
2087
+ # positive +Integer+, a sequence-set formatted +String+, or a deeply
2088
+ # nested +Array+ of these same types, the array will be converted to
2089
+ # SequenceSet for validation and encoding.
2090
+ # [Any other nested +Array+]
2091
+ # Otherwise, a nested array is encoded as a parenthesized list, to
2092
+ # combine multiple search keys (e.g., for use with +OR+ and +NOT+).
2093
+ # [+String+]
2094
+ # Sent verbatim when it is a valid \IMAP +atom+, and encoded as an \IMAP
2095
+ # +quoted+ or +literal+ string otherwise. Every standard search key
2096
+ # name is a valid \IMAP +atom+ and every standard search key string
2097
+ # argument is an +astring+ which may be encoded as +atom+, +quoted+, or
2098
+ # +literal+.
2099
+ #
2100
+ # *Note:* <tt>*</tt> is not a valid \IMAP +atom+ character. Any string
2101
+ # containing <tt>*</tt> will be encoded as a +quoted+ string, _not_ a
2102
+ # +sequence-set+.
2103
+ # [+Integer+ (except for <tt>-1</tt>)]
2104
+ # Encoded using +#to_s+.
2105
+ # [+Date+]
2106
+ # Encoded as an \IMAP date (see ::encode_date).
2107
+ #
2108
+ # [When +criteria+ is a String]
2109
+ # +criteria+ will be sent directly to the server <em>without any
2110
+ # validation or encoding</em>.
2111
+ #
2112
+ # <em>*WARNING:* This is vulnerable to injection attacks when external
2113
+ # inputs are used.</em>
2114
+ #
2115
+ # ==== Supported return options
2116
+ #
2117
+ # For full definitions of the standard return options and return data, see
2118
+ # the relevant RFCs.
2119
+ #
2120
+ # [+ALL+]
2121
+ # Returns ESearchResult#all with a SequenceSet of all matching sequence
2122
+ # numbers or UIDs. This is the default, when return options are empty.
2123
+ #
2124
+ # For compatibility with SearchResult, ESearchResult#to_a returns an
2125
+ # Array of message sequence numbers or UIDs.
2126
+ #
2127
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2128
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2129
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2130
+ #
2131
+ # [+COUNT+]
2132
+ # Returns ESearchResult#count with the number of matching messages.
2133
+ #
2134
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2135
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2136
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2137
+ #
2138
+ # [+MAX+]
2139
+ # Returns ESearchResult#max with the highest matching sequence number or
2140
+ # UID.
2141
+ #
2142
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2143
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2144
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2145
+ #
2146
+ # [+MIN+]
2147
+ # Returns ESearchResult#min with the lowest matching sequence number or
2148
+ # UID.
2149
+ #
2150
+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2151
+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2152
+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2153
+ #
2154
+ # [+PARTIAL+ _range_]
2155
+ # Returns ESearchResult#partial with a SequenceSet of a subset of
2156
+ # matching sequence numbers or UIDs, as selected by _range_. As with
2157
+ # sequence numbers, the first result is +1+: <tt>1..500</tt> selects the
2158
+ # first 500 search results (in mailbox order), <tt>501..1000</tt> the
2159
+ # second 500, and so on. _range_ may also be negative: <tt>-500..-1</tt>
2160
+ # selects the last 500 search results.
2161
+ #
2162
+ # <em>Requires either the <tt>CONTEXT=SEARCH</tt> or +PARTIAL+ capabability.</em>
2163
+ # {[RFC5267]}[https://rfc-editor.org/rfc/rfc5267]
2164
+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2165
+ #
2166
+ # ===== +MODSEQ+ return data
2167
+ #
2168
+ # ESearchResult#modseq return data does not have a corresponding return
2169
+ # option. Instead, it is returned if the +MODSEQ+ search key is used or
2170
+ # when the +CONDSTORE+ extension is enabled for the selected mailbox.
2171
+ # See [{RFC4731 §3.2}[https://www.rfc-editor.org/rfc/rfc4731#section-3.2]]
2172
+ # or [{RFC7162 §2.1.5}[https://www.rfc-editor.org/rfc/rfc7162#section-3.1.5]].
2173
+ #
2174
+ # ===== +RFC4466+ compatible extensions
2175
+ #
2176
+ # {RFC4466 §2.6}[https://www.rfc-editor.org/rfc/rfc4466.html#section-2.6]
2177
+ # defines standard syntax for search extensions. Net::IMAP allows sending
2178
+ # unsupported search return options and will parse unsupported search
2179
+ # extensions' return values into ExtensionData. Please note that this is an
2180
+ # intentionally _unstable_ API. Future releases may return different
2181
+ # (incompatible) objects, <em>without deprecation or warning</em>.
2182
+ #
2183
+ # ==== Search keys
2184
+ #
2185
+ # For full definitions of the standard search +criteria+,
1936
2186
  # see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
1937
2187
  # or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
1938
2188
  # in addition to documentation for
1939
- # any [CAPABILITIES[https://www.iana.org/assignments/imap-capabilities/imap-capabilities.xhtml]]
1940
- # reported by #capabilities which may define additional search filters, e.g:
2189
+ # any #capabilities which may define additional search filters, such as
1941
2190
  # +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
1942
- # +SAVEDATE+. The following are some common search criteria:
2191
+ # +SAVEDATE+.
1943
2192
  #
1944
- # <message set>:: a set of message sequence numbers. "<tt>,</tt>" indicates
1945
- # an interval, "+:+" indicates a range. For instance,
1946
- # "<tt>2,10:12,15</tt>" means "<tt>2,10,11,12,15</tt>".
2193
+ # With the exception of <em>sequence-set</em> and <em>parenthesized
2194
+ # list</em>, all search keys are composed of prefix label with zero or more
2195
+ # arguments. The number and type of arguments is specific to each search
2196
+ # key.
1947
2197
  #
1948
- # BEFORE <date>:: messages with an internal date strictly before
1949
- # <b><date></b>. The date argument has a format similar
1950
- # to <tt>8-Aug-2002</tt>, and can be formatted using
1951
- # Net::IMAP.format_date.
2198
+ # ===== Search keys that match all messages
1952
2199
  #
1953
- # BODY <string>:: messages that contain <string> within their body.
2200
+ # [+ALL+]
2201
+ # The default initial key. Matches every message in the mailbox.
1954
2202
  #
1955
- # CC <string>:: messages containing <string> in their CC field.
2203
+ # [+SAVEDATESUPPORTED+]
2204
+ # Matches every message in the mailbox when the mailbox supports the save
2205
+ # date attribute. Otherwise, it matches no messages.
1956
2206
  #
1957
- # FROM <string>:: messages that contain <string> in their FROM field.
2207
+ # <em>Requires +SAVEDATE+ capability</em>.
2208
+ # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
1958
2209
  #
1959
- # NEW:: messages with the \Recent, but not the \Seen, flag set.
2210
+ # ===== Sequence set search keys
1960
2211
  #
1961
- # NOT <search-key>:: negate the following search key.
2212
+ # [_sequence-set_]
2213
+ # Matches messages with message sequence numbers in _sequence-set_.
1962
2214
  #
1963
- # OR <search-key> <search-key>:: "or" two search keys together.
2215
+ # _Note:_ this search key has no label.
1964
2216
  #
1965
- # ON <date>:: messages with an internal date exactly equal to <date>,
1966
- # which has a format similar to 8-Aug-2002.
2217
+ # <em>+UIDONLY+ must *not* be enabled.</em>
2218
+ # {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.html]
1967
2219
  #
1968
- # SINCE <date>:: messages with an internal date on or after <date>.
2220
+ # [+UID+ _sequence-set_]
2221
+ # Matches messages with a UID in _sequence-set_.
1969
2222
  #
1970
- # SUBJECT <string>:: messages with <string> in their subject.
2223
+ # ===== Compound search keys
1971
2224
  #
1972
- # TO <string>:: messages with <string> in their TO field.
2225
+ # [(_search-key_ _search-key_...)]
2226
+ # Combines one or more _search-key_ arguments to match
2227
+ # messages which match all contained search keys. Useful for +OR+, +NOT+,
2228
+ # and other search keys with _search-key_ arguments.
1973
2229
  #
1974
- # ===== For example:
2230
+ # _Note:_ this search key has no label.
1975
2231
  #
1976
- # p imap.search(["SUBJECT", "hello", "NOT", "NEW"])
1977
- # #=> [1, 6, 7, 8]
2232
+ # [+OR+ _search-key_ _search-key_]
2233
+ # Matches messages which match either _search-key_ argument.
2234
+ #
2235
+ # [+NOT+ _search-key_]
2236
+ # Matches messages which do not match _search-key_.
2237
+ #
2238
+ # [+FUZZY+ _search-key_]
2239
+ # Uses fuzzy matching for the specified search key.
2240
+ #
2241
+ # <em>Requires <tt>SEARCH=FUZZY</tt> capability.</em>
2242
+ # {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
2243
+ #
2244
+ # ===== Flags search keys
2245
+ #
2246
+ # [+ANSWERED+, +UNANSWERED+]
2247
+ # Matches messages with or without the <tt>\\Answered</tt> flag.
2248
+ # [+DELETED+, +UNDELETED+]
2249
+ # Matches messages with or without the <tt>\\Deleted</tt> flag.
2250
+ # [+DRAFT+, +UNDRAFT+]
2251
+ # Matches messages with or without the <tt>\\Draft</tt> flag.
2252
+ # [+FLAGGED+, +UNFLAGGED+]
2253
+ # Matches messages with or without the <tt>\\Flagged</tt> flag.
2254
+ # [+SEEN+, +UNSEEN+]
2255
+ # Matches messages with or without the <tt>\\Seen</tt> flag.
2256
+ # [+KEYWORD+ _keyword_, +UNKEYWORD+ _keyword_]
2257
+ # Matches messages with or without the specified _keyword_.
2258
+ #
2259
+ # [+RECENT+, +UNRECENT+]
2260
+ # Matches messages with or without the <tt>\\Recent</tt> flag.
2261
+ #
2262
+ # *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
2263
+ # [+NEW+]
2264
+ # Equivalent to <tt>(RECENT UNSEEN)</tt>.
2265
+ #
2266
+ # *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
2267
+ #
2268
+ # ===== Header field substring search keys
2269
+ #
2270
+ # [+BCC+ _substring_]
2271
+ # Matches when _substring_ is in the envelope's +BCC+ field.
2272
+ # [+CC+ _substring_]
2273
+ # Matches when _substring_ is in the envelope's +CC+ field.
2274
+ # [+FROM+ _substring_]
2275
+ # Matches when _substring_ is in the envelope's +FROM+ field.
2276
+ # [+SUBJECT+ _substring_]
2277
+ # Matches when _substring_ is in the envelope's +SUBJECT+ field.
2278
+ # [+TO+ _substring_]
2279
+ # Matches when _substring_ is in the envelope's +TO+ field.
2280
+ #
2281
+ # [+HEADER+ _field_ _substring_]
2282
+ # Matches when _substring_ is in the specified header _field_.
2283
+ #
2284
+ # ===== Body text search keys
2285
+ # [+BODY+ _string_]
2286
+ # Matches when _string_ is in the body of the message.
2287
+ # Does not match on header fields.
2288
+ #
2289
+ # The server _may_ use flexible matching, rather than simple substring
2290
+ # matches. For example, this may use stemming or match only full words.
2291
+ #
2292
+ # [+TEXT+ _string_]
2293
+ # Matches when _string_ is in the header or body of the message.
2294
+ #
2295
+ # The server _may_ use flexible matching, rather than simple substring
2296
+ # matches. For example, this may use stemming or match only full words.
2297
+ #
2298
+ # ===== Date/Time search keys
2299
+ #
2300
+ # [+SENTBEFORE+ _date_]
2301
+ # [+SENTON+ _date_]
2302
+ # [+SENTSINCE+ _date_]
2303
+ # Matches when the +Date+ header is earlier than, on, or later than _date_.
2304
+ #
2305
+ # [+BEFORE+ _date_]
2306
+ # [+ON+ _date_]
2307
+ # [+SINCE+ _date_]
2308
+ # Matches when the +INTERNALDATE+ is earlier than, on, or later than
2309
+ # _date_.
2310
+ #
2311
+ # [+OLDER+ _interval_]
2312
+ # [+YOUNGER+ _interval_]
2313
+ # Matches when the +INTERNALDATE+ is more/less than _interval_ seconds ago.
2314
+ #
2315
+ # <em>Requires +WITHIN+ capability</em>.
2316
+ # {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
2317
+ #
2318
+ # [+SAVEDBEFORE+ _date_]
2319
+ # [+SAVEDON+ _date_]
2320
+ # [+SAVEDSINCE+ _date_]
2321
+ # Matches when the save date is earlier than, on, or later than _date_.
2322
+ #
2323
+ # <em>Requires +SAVEDATE+ capability.</em>
2324
+ # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
2325
+ #
2326
+ # ===== Other message attribute search keys
2327
+ #
2328
+ # [+SMALLER+ _bytes_]
2329
+ # [+LARGER+ _bytes_]
2330
+ # Matches when +RFC822.SIZE+ is smaller or larger than _bytes_.
2331
+ #
2332
+ # [+ANNOTATION+ _entry_ _attr_ _value_]
2333
+ # Matches messages that have annotations with entries matching _entry_,
2334
+ # attributes matching _attr_, and _value_ in the attribute's values.
2335
+ #
2336
+ # <em>Requires +ANNOTATE-EXPERIMENT-1+ capability</em>.
2337
+ # {[RFC5257]}[https://www.rfc-editor.org/rfc/rfc5257.html].
2338
+ #
2339
+ # [+FILTER+ _filter_]
2340
+ # References a _filter_ that is stored on the server and matches all
2341
+ # messages which would be matched by that filter's search criteria.
2342
+ #
2343
+ # <em>Requires +FILTERS+ capability</em>.
2344
+ # {[RFC5466]}[https://www.rfc-editor.org/rfc/rfc5466.html#section-3.1]
2345
+ #
2346
+ # [+MODSEQ+ _modseq_]
2347
+ # Matches when +MODSEQ+ is greater than or equal to _modseq_.
2348
+ #
2349
+ # <em>Requires +CONDSTORE+ capability</em>.
2350
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
2351
+ #
2352
+ # [+MODSEQ+ _entry_ _entry-type_ _modseq_]
2353
+ # Matches when a specific metadata _entry_ has been updated since
2354
+ # _modseq_.
2355
+ #
2356
+ # For flags, the corresponding _entry_ name is
2357
+ # <tt>"/flags/#{flag_name}"</tt>, where _flag_name_ includes the
2358
+ # <tt>\\</tt> prefix. _entry-type_ can be one of <tt>"shared"</tt>,
2359
+ # <tt>"priv"</tt> (private), or <tt>"all"</tt>.
2360
+ #
2361
+ # <em>Requires +CONDSTORE+ capability</em>.
2362
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
2363
+ #
2364
+ # [+EMAILID+ _objectid_]
2365
+ # [+THREADID+ _objectid_]
2366
+ # Matches when +EMAILID+/+THREADID+ is equal to _objectid_
2367
+ # (substring matches are not supported).
2368
+ #
2369
+ # <em>Requires +OBJECTID+ capability</em>.
2370
+ # {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-6]
2371
+ #
2372
+ # ==== Capabilities
1978
2373
  #
1979
- # ===== Capabilities
2374
+ # Return options should only be specified when the server supports
2375
+ # +IMAP4rev2+ or an extension that allows them, such as +ESEARCH+
2376
+ # [RFC4731[https://rfc-editor.org/rfc/rfc4731#section-3.1]].
1980
2377
  #
1981
- # If [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html]] is supported
2378
+ # When +IMAP4rev2+ is enabled, or when the server supports +IMAP4rev2+ but
2379
+ # not +IMAP4rev1+, ESearchResult is always returned instead of SearchResult.
2380
+ #
2381
+ # If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
1982
2382
  # and enabled for the selected mailbox, a non-empty SearchResult will
1983
2383
  # include a +MODSEQ+ value.
1984
2384
  # imap.select("mbox", condstore: true)
1985
- # result = imap.search(["SUBJECT", "hi there", "not", "new")
2385
+ # result = imap.search(["SUBJECT", "hi there", "not", "new"])
1986
2386
  # #=> Net::IMAP::SearchResult[1, 6, 7, 8, modseq: 5594]
1987
2387
  # result.modseq # => 5594
1988
- def search(keys, charset = nil)
1989
- return search_internal("SEARCH", keys, charset)
2388
+ #
2389
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2390
+ # the +SEARCH+ command is prohibited. Use #uid_search instead.
2391
+ def search(...)
2392
+ search_internal("SEARCH", ...)
1990
2393
  end
1991
2394
 
2395
+ # :call-seq:
2396
+ # uid_search(criteria, charset = nil) -> result
2397
+ # uid_search(criteria, charset: nil, return: nil) -> result
2398
+ #
1992
2399
  # Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
1993
2400
  # to search the mailbox for messages that match the given searching
1994
2401
  # criteria, and returns unique identifiers (<tt>UID</tt>s).
@@ -1997,9 +2404,19 @@ module Net
1997
2404
  # backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
1998
2405
  # capability has been enabled.
1999
2406
  #
2000
- # See #search for documentation of search criteria.
2001
- def uid_search(keys, charset = nil)
2002
- return search_internal("UID SEARCH", keys, charset)
2407
+ # See #search for documentation of parameters.
2408
+ #
2409
+ # ==== Capabilities
2410
+ #
2411
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2412
+ # #uid_search must be used instead of #search, and the <tt><message
2413
+ # set></tt> search criterion is prohibited. Use +ALL+ or <tt>UID
2414
+ # sequence-set</tt> instead.
2415
+ #
2416
+ # Otherwise, #uid_search is updated by extensions in the same way as
2417
+ # #search.
2418
+ def uid_search(...)
2419
+ search_internal("UID SEARCH", ...)
2003
2420
  end
2004
2421
 
2005
2422
  # :call-seq:
@@ -2008,26 +2425,21 @@ module Net
2008
2425
  # Sends a {FETCH command [IMAP4rev1 §6.4.5]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.5]
2009
2426
  # to retrieve data associated with a message in the mailbox.
2010
2427
  #
2011
- # The +set+ parameter is a number or a range between two numbers,
2012
- # or an array of those. The number is a message sequence number,
2013
- # where -1 represents a '*' for use in range notation like 100..-1
2014
- # being interpreted as '100:*'. Beware that the +exclude_end?+
2015
- # property of a Range object is ignored, and the contents of a
2016
- # range are independent of the order of the range endpoints as per
2017
- # the protocol specification, so 1...5, 5..1 and 5...1 are all
2018
- # equivalent to 1..5.
2428
+ # +set+ is the message sequence numbers to fetch, and may be any valid input
2429
+ # to {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
2430
+ # (For UIDs, use #uid_fetch instead.)
2019
2431
  #
2020
- # +attr+ is a list of attributes to fetch; see the documentation
2021
- # for FetchData for a list of valid attributes.
2432
+ # +attr+ is a list of attributes to fetch; see FetchStruct documentation for
2433
+ # a list of supported attributes.
2022
2434
  #
2023
2435
  # +changedsince+ is an optional integer mod-sequence. It limits results to
2024
2436
  # messages with a mod-sequence greater than +changedsince+.
2025
2437
  #
2026
2438
  # The return value is an array of FetchData.
2027
2439
  #
2028
- # Related: #uid_search, FetchData
2440
+ # Related: #uid_fetch, FetchData
2029
2441
  #
2030
- # ===== For example:
2442
+ # ==== For example:
2031
2443
  #
2032
2444
  # p imap.fetch(6..8, "UID")
2033
2445
  # #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
@@ -2045,39 +2457,82 @@ module Net
2045
2457
  # p data.attr["UID"]
2046
2458
  # #=> 98
2047
2459
  #
2048
- # ===== Capabilities
2460
+ # ==== Capabilities
2049
2461
  #
2050
- # Many extensions define new message +attr+ names. See FetchData for a list
2051
- # of supported extension fields.
2462
+ # Many extensions define new message +attr+ names. See FetchStruct for a
2463
+ # list of supported extension fields.
2052
2464
  #
2053
2465
  # The server's capabilities must include +CONDSTORE+
2054
- # {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
2466
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
2055
2467
  # +changedsince+ argument. Using +changedsince+ implicitly enables the
2056
2468
  # +CONDSTORE+ extension.
2057
- def fetch(set, attr, mod = nil, changedsince: nil)
2058
- fetch_internal("FETCH", set, attr, mod, changedsince: changedsince)
2469
+ #
2470
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2471
+ # +FETCH+ command is prohibited. Use #uid_fetch instead.
2472
+ def fetch(...)
2473
+ fetch_internal("FETCH", ...)
2059
2474
  end
2060
2475
 
2061
2476
  # :call-seq:
2062
- # uid_fetch(set, attr, changedsince: nil) -> array of FetchData
2477
+ # uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData (or UIDFetchData)
2063
2478
  #
2064
2479
  # Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
2065
2480
  # to retrieve data associated with a message in the mailbox.
2066
2481
  #
2067
- # Similar to #fetch, but the +set+ parameter contains unique identifiers
2068
- # instead of message sequence numbers.
2482
+ # +set+ is the message UIDs to fetch, and may be any valid input to
2483
+ # {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
2484
+ # (For message sequence numbers, use #fetch instead.)
2069
2485
  #
2486
+ # +attr+ behaves the same as with #fetch.
2070
2487
  # >>>
2071
2488
  # *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
2072
2489
  # part of any +FETCH+ response caused by a +UID+ command, regardless of
2073
2490
  # whether a +UID+ was specified as a message data item to the +FETCH+.
2074
2491
  #
2492
+ # +changedsince+ (optional) behaves the same as with #fetch.
2493
+ #
2494
+ # +partial+ is an optional range to limit the number of results returned.
2495
+ # It's useful when +set+ contains an unknown number of messages.
2496
+ # <tt>1..500</tt> returns the first 500 messages in +set+ (in mailbox
2497
+ # order), <tt>501..1000</tt> the second 500, and so on. +partial+ may also
2498
+ # be negative: <tt>-500..-1</tt> selects the last 500 messages in +set+.
2499
+ # <em>Requires the +PARTIAL+ capabability.</em>
2500
+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2501
+ #
2502
+ # For example:
2503
+ #
2504
+ # # Without partial, the size of the results may be unknown beforehand:
2505
+ # results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
2506
+ # # ... maybe wait for a long time ... and allocate a lot of memory ...
2507
+ # results.size # => 0..2**32-1
2508
+ # process results # may also take a long time and use a lot of memory...
2509
+ #
2510
+ # # Using partial, the results may be paginated:
2511
+ # loop do
2512
+ # results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
2513
+ # partial: 1..500)
2514
+ # # fetch should return quickly and allocate little memory
2515
+ # results.size # => 0..500
2516
+ # break if results.empty?
2517
+ # next_uid_to_fetch = results.last.uid + 1
2518
+ # process results
2519
+ # end
2520
+ #
2075
2521
  # Related: #fetch, FetchData
2076
2522
  #
2077
- # ===== Capabilities
2078
- # Same as #fetch.
2079
- def uid_fetch(set, attr, mod = nil, changedsince: nil)
2080
- fetch_internal("UID FETCH", set, attr, mod, changedsince: changedsince)
2523
+ # ==== Capabilities
2524
+ #
2525
+ # The server's capabilities must include +PARTIAL+
2526
+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394] in order to use the
2527
+ # +partial+ argument.
2528
+ #
2529
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2530
+ # #uid_fetch must be used instead of #fetch, and UIDFetchData will be
2531
+ # returned instead of FetchData.
2532
+ #
2533
+ # Otherwise, #uid_fetch is updated by extensions in the same way as #fetch.
2534
+ def uid_fetch(...)
2535
+ fetch_internal("UID FETCH", ...)
2081
2536
  end
2082
2537
 
2083
2538
  # :call-seq:
@@ -2108,27 +2563,30 @@ module Net
2108
2563
  #
2109
2564
  # Related: #uid_store
2110
2565
  #
2111
- # ===== For example:
2566
+ # ==== For example:
2112
2567
  #
2113
2568
  # p imap.store(6..8, "+FLAGS", [:Deleted])
2114
2569
  # #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
2115
2570
  # #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
2116
2571
  # #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
2117
2572
  #
2118
- # ===== Capabilities
2573
+ # ==== Capabilities
2119
2574
  #
2120
2575
  # Extensions may define new data items to be used with #store.
2121
2576
  #
2122
2577
  # The server's capabilities must include +CONDSTORE+
2123
- # {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
2578
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162] in order to use the
2124
2579
  # +unchangedsince+ argument. Using +unchangedsince+ implicitly enables the
2125
2580
  # +CONDSTORE+ extension.
2581
+ #
2582
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2583
+ # +STORE+ command is prohibited. Use #uid_store instead.
2126
2584
  def store(set, attr, flags, unchangedsince: nil)
2127
2585
  store_internal("STORE", set, attr, flags, unchangedsince: unchangedsince)
2128
2586
  end
2129
2587
 
2130
2588
  # :call-seq:
2131
- # uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData
2589
+ # uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData (or UIDFetchData)
2132
2590
  #
2133
2591
  # Sends a {UID STORE command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
2134
2592
  # to alter data associated with messages in the mailbox, in particular their
@@ -2139,8 +2597,13 @@ module Net
2139
2597
  #
2140
2598
  # Related: #store
2141
2599
  #
2142
- # ===== Capabilities
2143
- # Same as #store.
2600
+ # ==== Capabilities
2601
+ #
2602
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2603
+ # #uid_store must be used instead of #store, and UIDFetchData will be
2604
+ # returned instead of FetchData.
2605
+ #
2606
+ # Otherwise, #uid_store is updated by extensions in the same way as #store.
2144
2607
  def uid_store(set, attr, flags, unchangedsince: nil)
2145
2608
  store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
2146
2609
  end
@@ -2152,13 +2615,16 @@ module Net
2152
2615
  #
2153
2616
  # Related: #uid_copy
2154
2617
  #
2155
- # ===== Capabilities
2618
+ # ==== Capabilities
2156
2619
  #
2157
2620
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
2158
2621
  # supported, the server's response should include a +COPYUID+ response code
2159
2622
  # with UIDPlusData. This will report the UIDVALIDITY of the destination
2160
2623
  # mailbox, the UID set of the source messages, and the assigned UID set of
2161
2624
  # the moved messages.
2625
+ #
2626
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2627
+ # +COPY+ command is prohibited. Use #uid_copy instead.
2162
2628
  def copy(set, mailbox)
2163
2629
  copy_internal("COPY", set, mailbox)
2164
2630
  end
@@ -2169,9 +2635,12 @@ module Net
2169
2635
  #
2170
2636
  # Similar to #copy, but +set+ contains unique identifiers.
2171
2637
  #
2172
- # ===== Capabilities
2638
+ # ==== Capabilities
2173
2639
  #
2174
- # +UIDPLUS+ affects #uid_copy the same way it affects #copy.
2640
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] in enabled,
2641
+ # #uid_copy must be used instead of #copy.
2642
+ #
2643
+ # Otherwise, #uid_copy is updated by extensions in the same way as #copy.
2175
2644
  def uid_copy(set, mailbox)
2176
2645
  copy_internal("UID COPY", set, mailbox)
2177
2646
  end
@@ -2184,10 +2653,10 @@ module Net
2184
2653
  #
2185
2654
  # Related: #uid_move
2186
2655
  #
2187
- # ===== Capabilities
2656
+ # ==== Capabilities
2188
2657
  #
2189
- # The server's capabilities must include +MOVE+
2190
- # [RFC6851[https://tools.ietf.org/html/rfc6851]].
2658
+ # The server's capabilities must include either +IMAP4rev2+ or +MOVE+
2659
+ # [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
2191
2660
  #
2192
2661
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
2193
2662
  # supported, the server's response should include a +COPYUID+ response code
@@ -2195,6 +2664,8 @@ module Net
2195
2664
  # mailbox, the UID set of the source messages, and the assigned UID set of
2196
2665
  # the moved messages.
2197
2666
  #
2667
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2668
+ # +MOVE+ command is prohibited. Use #uid_move instead.
2198
2669
  def move(set, mailbox)
2199
2670
  copy_internal("MOVE", set, mailbox)
2200
2671
  end
@@ -2208,11 +2679,15 @@ module Net
2208
2679
  #
2209
2680
  # Related: #move
2210
2681
  #
2211
- # ===== Capabilities
2682
+ # ==== Capabilities
2212
2683
  #
2213
- # Same as #move: The server's capabilities must include +MOVE+
2214
- # [RFC6851[https://tools.ietf.org/html/rfc6851]]. +UIDPLUS+ also affects
2215
- # #uid_move the same way it affects #move.
2684
+ # The server's capabilities must include either +IMAP4rev2+ or +MOVE+
2685
+ # [RFC6851[https://www.rfc-editor.org/rfc/rfc6851]].
2686
+ #
2687
+ # When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2688
+ # #uid_move must be used instead of #move.
2689
+ #
2690
+ # Otherwise, #uid_move is updated by extensions in the same way as #move.
2216
2691
  def uid_move(set, mailbox)
2217
2692
  copy_internal("UID MOVE", set, mailbox)
2218
2693
  end
@@ -2228,17 +2703,17 @@ module Net
2228
2703
  #
2229
2704
  # Related: #uid_sort, #search, #uid_search, #thread, #uid_thread
2230
2705
  #
2231
- # ===== For example:
2706
+ # ==== For example:
2232
2707
  #
2233
2708
  # p imap.sort(["FROM"], ["ALL"], "US-ASCII")
2234
2709
  # #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
2235
2710
  # p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
2236
2711
  # #=> [6, 7, 8, 1]
2237
2712
  #
2238
- # ===== Capabilities
2713
+ # ==== Capabilities
2239
2714
  #
2240
2715
  # The server's capabilities must include +SORT+
2241
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2716
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2242
2717
  def sort(sort_keys, search_keys, charset)
2243
2718
  return sort_internal("SORT", sort_keys, search_keys, charset)
2244
2719
  end
@@ -2250,10 +2725,10 @@ module Net
2250
2725
  #
2251
2726
  # Related: #sort, #search, #uid_search, #thread, #uid_thread
2252
2727
  #
2253
- # ===== Capabilities
2728
+ # ==== Capabilities
2254
2729
  #
2255
2730
  # The server's capabilities must include +SORT+
2256
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2731
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2257
2732
  def uid_sort(sort_keys, search_keys, charset)
2258
2733
  return sort_internal("UID SORT", sort_keys, search_keys, charset)
2259
2734
  end
@@ -2275,10 +2750,10 @@ module Net
2275
2750
  #
2276
2751
  # Related: #uid_thread, #search, #uid_search, #sort, #uid_sort
2277
2752
  #
2278
- # ===== Capabilities
2753
+ # ==== Capabilities
2279
2754
  #
2280
2755
  # The server's capabilities must include +THREAD+
2281
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2756
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2282
2757
  def thread(algorithm, search_keys, charset)
2283
2758
  return thread_internal("THREAD", algorithm, search_keys, charset)
2284
2759
  end
@@ -2289,10 +2764,10 @@ module Net
2289
2764
  #
2290
2765
  # Related: #thread, #search, #uid_search, #sort, #uid_sort
2291
2766
  #
2292
- # ===== Capabilities
2767
+ # ==== Capabilities
2293
2768
  #
2294
2769
  # The server's capabilities must include +THREAD+
2295
- # [RFC5256[https://tools.ietf.org/html/rfc5256]].
2770
+ # [RFC5256[https://www.rfc-editor.org/rfc/rfc5256]].
2296
2771
  def uid_thread(algorithm, search_keys, charset)
2297
2772
  return thread_internal("UID THREAD", algorithm, search_keys, charset)
2298
2773
  end
@@ -2308,11 +2783,11 @@ module Net
2308
2783
  #
2309
2784
  # Related: #capable?, #capabilities, #capability
2310
2785
  #
2311
- # ===== Capabilities
2786
+ # ==== Capabilities
2312
2787
  #
2313
2788
  # The server's capabilities must include
2314
- # +ENABLE+ [RFC5161[https://tools.ietf.org/html/rfc5161]]
2315
- # or +IMAP4REV2+ [RFC9051[https://tools.ietf.org/html/rfc9051]].
2789
+ # +ENABLE+ [RFC5161[https://www.rfc-editor.org/rfc/rfc5161]]
2790
+ # or +IMAP4REV2+ [RFC9051[https://www.rfc-editor.org/rfc/rfc9051]].
2316
2791
  #
2317
2792
  # Additionally, the server capabilities must include a capability matching
2318
2793
  # each enabled extension (usually the same name as the enabled extension).
@@ -2331,7 +2806,7 @@ module Net
2331
2806
  # <tt>"UTF8=ACCEPT"</tt> or <tt>"IMAP4rev2"</tt>, depending on server
2332
2807
  # capabilities.
2333
2808
  #
2334
- # [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://tools.ietf.org/html/rfc6855]]]
2809
+ # [<tt>"UTF8=ACCEPT"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
2335
2810
  #
2336
2811
  # The server's capabilities must include <tt>UTF8=ACCEPT</tt> _or_
2337
2812
  # <tt>UTF8=ONLY</tt>.
@@ -2350,13 +2825,23 @@ module Net
2350
2825
  # encoding, even if they generally contain UTF-8 data, if they are
2351
2826
  # text at all.
2352
2827
  #
2353
- # [<tt>"UTF8=ONLY"</tt> [RFC6855[https://tools.ietf.org/html/rfc6855]]]
2828
+ # [<tt>"UTF8=ONLY"</tt> [RFC6855[https://www.rfc-editor.org/rfc/rfc6855]]]
2354
2829
  #
2355
2830
  # A server that reports the <tt>UTF8=ONLY</tt> capability _requires_ that
2356
2831
  # the client <tt>enable("UTF8=ACCEPT")</tt> before any mailboxes may be
2357
2832
  # selected. For convenience, <tt>enable("UTF8=ONLY")</tt> is aliased to
2358
2833
  # <tt>enable("UTF8=ACCEPT")</tt>.
2359
2834
  #
2835
+ # [+UIDONLY+ {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.pdf]]
2836
+ #
2837
+ # When UIDONLY is enabled, the #fetch, #store, #search, #copy, and #move
2838
+ # commands are prohibited and result in a tagged BAD response. Clients
2839
+ # should instead use uid_fetch, uid_store, uid_search, uid_copy, or
2840
+ # uid_move, respectively. All +FETCH+ responses that would be returned are
2841
+ # replaced by +UIDFETCH+ responses. All +EXPUNGED+ responses that would be
2842
+ # returned are replaced by +VANISHED+ responses. The "<sequence set>"
2843
+ # uid_search criterion is prohibited.
2844
+ #
2360
2845
  # ===== Unsupported capabilities
2361
2846
  #
2362
2847
  # *Note:* Some extensions that use ENABLE permit the server to send syntax
@@ -2397,17 +2882,23 @@ module Net
2397
2882
  # checks the connection for each 60 seconds.
2398
2883
  #
2399
2884
  # loop do
2400
- # imap.idle(60) do |res|
2401
- # ...
2885
+ # imap.idle(60) do |response|
2886
+ # do_something_with(response)
2887
+ # imap.idle_done if some_condition?(response)
2402
2888
  # end
2403
2889
  # end
2404
2890
  #
2891
+ # Returns the server's response to indicate the IDLE state has ended.
2892
+ # Returns +nil+ if the server does not respond to #idle_done within
2893
+ # {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
2894
+ # seconds.
2895
+ #
2405
2896
  # Related: #idle_done, #noop, #check
2406
2897
  #
2407
- # ===== Capabilities
2898
+ # ==== Capabilities
2408
2899
  #
2409
- # The server's capabilities must include +IDLE+
2410
- # [RFC2177[https://tools.ietf.org/html/rfc2177]].
2900
+ # The server's capabilities must include either +IMAP4rev2+ or +IDLE+
2901
+ # [RFC2177[https://www.rfc-editor.org/rfc/rfc2177]].
2411
2902
  def idle(timeout = nil, &response_handler)
2412
2903
  raise LocalJumpError, "no block given" unless response_handler
2413
2904
 
@@ -2429,7 +2920,7 @@ module Net
2429
2920
  unless @receiver_thread_terminating
2430
2921
  remove_response_handler(response_handler)
2431
2922
  put_string("DONE#{CRLF}")
2432
- response = get_tagged_response(tag, "IDLE", @idle_response_timeout)
2923
+ response = get_tagged_response(tag, "IDLE", idle_response_timeout)
2433
2924
  end
2434
2925
  end
2435
2926
  end
@@ -2437,7 +2928,11 @@ module Net
2437
2928
  return response
2438
2929
  end
2439
2930
 
2440
- # Leaves IDLE.
2931
+ # Leaves IDLE, allowing #idle to return.
2932
+ #
2933
+ # If the server does not respond within
2934
+ # {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
2935
+ # seconds, #idle will return +nil+.
2441
2936
  #
2442
2937
  # Related: #idle
2443
2938
  def idle_done
@@ -2449,40 +2944,98 @@ module Net
2449
2944
  end
2450
2945
  end
2451
2946
 
2947
+ RESPONSES_DEPRECATION_MSG =
2948
+ "Pass a type or block to #responses, " \
2949
+ "set config.responses_without_block to :frozen_dup " \
2950
+ "or :silence_deprecation_warning, " \
2951
+ "or use #extract_responses or #clear_responses."
2952
+ private_constant :RESPONSES_DEPRECATION_MSG
2953
+
2452
2954
  # :call-seq:
2955
+ # responses -> hash of {String => Array} (see config.responses_without_block)
2956
+ # responses(type) -> frozen array
2453
2957
  # responses {|hash| ...} -> block result
2454
2958
  # responses(type) {|array| ...} -> block result
2455
2959
  #
2456
- # Yields unhandled responses and returns the result of the block.
2960
+ # Yields or returns unhandled server responses. Unhandled responses are
2961
+ # stored in a hash, with arrays of UntaggedResponse#data keyed by
2962
+ # UntaggedResponse#name and <em>non-+nil+</em> untagged ResponseCode#data
2963
+ # keyed by ResponseCode#name.
2457
2964
  #
2458
- # Unhandled responses are stored in a hash, with arrays of
2459
- # <em>non-+nil+</em> UntaggedResponse#data keyed by UntaggedResponse#name
2460
- # and ResponseCode#data keyed by ResponseCode#name. Call without +type+ to
2461
- # yield the entire responses hash. Call with +type+ to yield only the array
2462
- # of responses for that type.
2965
+ # When a block is given, yields unhandled responses and returns the block's
2966
+ # result. Without a block, returns the unhandled responses.
2967
+ #
2968
+ # [With +type+]
2969
+ # Yield or return only the array of responses for that +type+.
2970
+ # When no block is given, the returned array is a frozen copy.
2971
+ # [Without +type+]
2972
+ # Yield or return the entire responses hash.
2973
+ #
2974
+ # When no block is given, the behavior is determined by
2975
+ # Config#responses_without_block:
2976
+ # >>>
2977
+ # [+:silence_deprecation_warning+ <em>(original behavior)</em>]
2978
+ # Returns the mutable responses hash (without any warnings).
2979
+ # <em>This is not thread-safe.</em>
2980
+ #
2981
+ # [+:warn+ <em>(default since +v0.5+)</em>]
2982
+ # Prints a warning and returns the mutable responses hash.
2983
+ # <em>This is not thread-safe.</em>
2984
+ #
2985
+ # [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
2986
+ # Returns a frozen copy of the unhandled responses hash, with frozen
2987
+ # array values.
2988
+ #
2989
+ # [+:raise+]
2990
+ # Raise an +ArgumentError+ with the deprecation warning.
2463
2991
  #
2464
2992
  # For example:
2465
2993
  #
2466
2994
  # imap.select("inbox")
2467
- # p imap.responses("EXISTS", &:last)
2995
+ # p imap.responses("EXISTS").last
2468
2996
  # #=> 2
2997
+ # p imap.responses("UIDNEXT", &:last)
2998
+ # #=> 123456
2469
2999
  # p imap.responses("UIDVALIDITY", &:last)
2470
3000
  # #=> 968263756
3001
+ # p imap.responses {|responses|
3002
+ # {
3003
+ # exists: responses.delete("EXISTS").last,
3004
+ # uidnext: responses.delete("UIDNEXT").last,
3005
+ # uidvalidity: responses.delete("UIDVALIDITY").last,
3006
+ # }
3007
+ # }
3008
+ # #=> {:exists=>2, :uidnext=>123456, :uidvalidity=>968263756}
3009
+ # # "EXISTS", "UIDNEXT", and "UIDVALIDITY" have been removed:
3010
+ # p imap.responses(&:keys)
3011
+ # #=> ["FLAGS", "OK", "PERMANENTFLAGS", "RECENT", "HIGHESTMODSEQ"]
2471
3012
  #
3013
+ # Related: #extract_responses, #clear_responses, #response_handlers, #greeting
3014
+ #
3015
+ # ==== Thread safety
2472
3016
  # >>>
2473
3017
  # *Note:* Access to the responses hash is synchronized for thread-safety.
2474
3018
  # The receiver thread and response_handlers cannot process new responses
2475
3019
  # until the block completes. Accessing either the response hash or its
2476
- # response type arrays outside of the block is unsafe.
3020
+ # response type arrays outside of the block is unsafe. They can be safely
3021
+ # updated inside the block. Consider using #clear_responses or
3022
+ # #extract_responses instead.
3023
+ #
3024
+ # Net::IMAP will add and remove responses from the responses hash and its
3025
+ # array values, in the calling threads for commands and in the receiver
3026
+ # thread, but will not modify any responses after adding them to the
3027
+ # responses hash.
2477
3028
  #
2478
- # Calling without a block is unsafe and deprecated. Future releases will
2479
- # raise ArgumentError unless a block is given.
3029
+ # ==== Clearing responses
2480
3030
  #
2481
3031
  # Previously unhandled responses are automatically cleared before entering a
2482
3032
  # mailbox with #select or #examine. Long-lived connections can receive many
2483
3033
  # unhandled server responses, which must be pruned or they will continually
2484
3034
  # consume more memory. Update or clear the responses hash or arrays inside
2485
- # the block, or use #clear_responses.
3035
+ # the block, or remove responses with #extract_responses, #clear_responses,
3036
+ # or #add_response_handler.
3037
+ #
3038
+ # ==== Missing responses
2486
3039
  #
2487
3040
  # Only non-+nil+ data is stored. Many important response codes have no data
2488
3041
  # of their own, but are used as "tags" on the ResponseText object they are
@@ -2493,15 +3046,25 @@ module Net
2493
3046
  # ResponseCode#data on tagged responses. Although some command methods do
2494
3047
  # return the TaggedResponse directly, #add_response_handler must be used to
2495
3048
  # handle all response codes.
2496
- #
2497
- # Related: #clear_responses, #response_handlers, #greeting
2498
3049
  def responses(type = nil)
2499
3050
  if block_given?
2500
3051
  synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
2501
3052
  elsif type
2502
- raise ArgumentError, "Pass a block or use #clear_responses"
3053
+ synchronize { @responses[type.to_s.upcase].dup.freeze }
2503
3054
  else
2504
- # warn("DEPRECATED: pass a block or use #clear_responses", uplevel: 1)
3055
+ case config.responses_without_block
3056
+ when :raise
3057
+ raise ArgumentError, RESPONSES_DEPRECATION_MSG
3058
+ when :warn
3059
+ warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
3060
+ when :frozen_dup
3061
+ synchronize {
3062
+ responses = @responses.transform_values(&:freeze)
3063
+ responses.default_proc = nil
3064
+ responses.default = [].freeze
3065
+ return responses.freeze
3066
+ }
3067
+ end
2505
3068
  @responses
2506
3069
  end
2507
3070
  end
@@ -2516,7 +3079,7 @@ module Net
2516
3079
  # Clearing responses is synchronized with other threads. The lock is
2517
3080
  # released before returning.
2518
3081
  #
2519
- # Related: #responses, #response_handlers
3082
+ # Related: #extract_responses, #responses, #response_handlers
2520
3083
  def clear_responses(type = nil)
2521
3084
  synchronize {
2522
3085
  if type
@@ -2530,6 +3093,30 @@ module Net
2530
3093
  .freeze
2531
3094
  end
2532
3095
 
3096
+ # :call-seq:
3097
+ # extract_responses(type) {|response| ... } -> array
3098
+ #
3099
+ # Yields all of the unhandled #responses for a single response +type+.
3100
+ # Removes and returns the responses for which the block returns a true
3101
+ # value.
3102
+ #
3103
+ # Extracting responses is synchronized with other threads. The lock is
3104
+ # released before returning.
3105
+ #
3106
+ # Related: #responses, #clear_responses
3107
+ def extract_responses(type)
3108
+ type = String.try_convert(type) or
3109
+ raise ArgumentError, "type must be a string"
3110
+ raise ArgumentError, "must provide a block" unless block_given?
3111
+ extracted = []
3112
+ responses(type) do |all|
3113
+ all.reject! do |response|
3114
+ extracted << response if yield response
3115
+ end
3116
+ end
3117
+ extracted
3118
+ end
3119
+
2533
3120
  # Returns all response handlers, including those that are added internally
2534
3121
  # by commands. Each response handler will be called with every new
2535
3122
  # UntaggedResponse, TaggedResponse, and ContinuationRequest.
@@ -2582,8 +3169,6 @@ module Net
2582
3169
  PORT = 143 # :nodoc:
2583
3170
  SSL_PORT = 993 # :nodoc:
2584
3171
 
2585
- @@debug = false
2586
-
2587
3172
  def start_imap_connection
2588
3173
  @greeting = get_server_greeting
2589
3174
  @capabilities = capabilities_from_resp_code @greeting
@@ -2611,12 +3196,12 @@ module Net
2611
3196
  end
2612
3197
 
2613
3198
  def tcp_socket(host, port)
2614
- s = Socket.tcp(host, port, :connect_timeout => @open_timeout)
3199
+ s = Socket.tcp(host, port, :connect_timeout => open_timeout)
2615
3200
  s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
2616
3201
  s
2617
3202
  rescue Errno::ETIMEDOUT
2618
3203
  raise Net::OpenTimeout, "Timeout to open TCP connection to " +
2619
- "#{host}:#{port} (exceeds #{@open_timeout} seconds)"
3204
+ "#{host}:#{port} (exceeds #{open_timeout} seconds)"
2620
3205
  end
2621
3206
 
2622
3207
  def receive_responses
@@ -2728,7 +3313,7 @@ module Net
2728
3313
  end
2729
3314
  end
2730
3315
  return nil if buff.length == 0
2731
- if @@debug
3316
+ if config.debug?
2732
3317
  $stderr.print(buff.gsub(/^/n, "S: "))
2733
3318
  end
2734
3319
  return @parser.parse(buff)
@@ -2807,7 +3392,7 @@ module Net
2807
3392
 
2808
3393
  def put_string(str)
2809
3394
  @sock.print(str)
2810
- if @@debug
3395
+ if config.debug?
2811
3396
  if @debug_output_bol
2812
3397
  $stderr.print("C: ")
2813
3398
  end
@@ -2820,23 +3405,115 @@ module Net
2820
3405
  end
2821
3406
  end
2822
3407
 
2823
- def search_internal(cmd, keys, charset)
2824
- if keys.instance_of?(String)
2825
- keys = [RawData.new(keys)]
3408
+ def enforce_logindisabled?
3409
+ if config.enforce_logindisabled == :when_capabilities_cached
3410
+ capabilities_cached?
2826
3411
  else
2827
- normalize_searching_criteria(keys)
3412
+ config.enforce_logindisabled
3413
+ end
3414
+ end
3415
+
3416
+ def expunge_internal(...)
3417
+ synchronize do
3418
+ send_command(...)
3419
+ expunged_array = clear_responses("EXPUNGE")
3420
+ vanished_array = extract_responses("VANISHED") { !_1.earlier? }
3421
+ if vanished_array.empty?
3422
+ expunged_array
3423
+ elsif vanished_array.length == 1
3424
+ vanished_array.first
3425
+ else
3426
+ merged_uids = SequenceSet[*vanished_array.map(&:uids)]
3427
+ VanishedData[uids: merged_uids, earlier: false]
3428
+ end
3429
+ end
3430
+ end
3431
+
3432
+ RETURN_WHOLE = /\ARETURN\z/i
3433
+ RETURN_START = /\ARETURN\b/i
3434
+ private_constant :RETURN_WHOLE, :RETURN_START
3435
+
3436
+ def search_args(keys, charset_arg = nil, return: nil, charset: nil)
3437
+ {return:} => {return: return_kw}
3438
+ case [return_kw, keys]
3439
+ in [nil, Array[RETURN_WHOLE, return_opts, *keys]]
3440
+ return_opts = convert_return_opts(return_opts)
3441
+ esearch = true
3442
+ in [nil => return_opts, RETURN_START]
3443
+ esearch = true
3444
+ in [nil => return_opts, keys]
3445
+ esearch = false
3446
+ in [_, Array[RETURN_WHOLE, _, *] | RETURN_START]
3447
+ raise ArgumentError, "conflicting return options"
3448
+ in [_, Array[RETURN_WHOLE, _, *]] # workaround for https://bugs.ruby-lang.org/issues/20956
3449
+ raise ArgumentError, "conflicting return options"
3450
+ in [_, RETURN_START] # workaround for https://bugs.ruby-lang.org/issues/20956
3451
+ raise ArgumentError, "conflicting return options"
3452
+ in [return_opts, keys]
3453
+ return_opts = convert_return_opts(return_opts)
3454
+ esearch = true
3455
+ end
3456
+ if charset && charset_arg
3457
+ raise ArgumentError, "multiple charset arguments"
3458
+ end
3459
+ charset ||= charset_arg
3460
+ # NOTE: not handling combined RETURN and CHARSET for raw strings
3461
+ if charset && keys in /\ACHARSET\b/i | Array[/\ACHARSET\z/i, *]
3462
+ raise ArgumentError, "multiple charset arguments"
2828
3463
  end
3464
+ args = normalize_searching_criteria(keys)
3465
+ args.prepend("CHARSET", charset) if charset
3466
+ args.prepend("RETURN", return_opts) if return_opts
3467
+ return args, esearch
3468
+ end
3469
+
3470
+ def convert_return_opts(unconverted)
3471
+ return_opts = Array.try_convert(unconverted) or
3472
+ raise TypeError, "expected return options to be Array, got %s" % [
3473
+ unconverted.class
3474
+ ]
3475
+ return_opts.map {|opt|
3476
+ case opt
3477
+ when Symbol then opt.to_s
3478
+ when PartialRange::Negative then PartialRange[opt]
3479
+ when Range then SequenceSet[opt]
3480
+ else opt
3481
+ end
3482
+ }
3483
+ end
3484
+
3485
+ def search_internal(cmd, ...)
3486
+ args, esearch = search_args(...)
2829
3487
  synchronize do
2830
- if charset
2831
- send_command(cmd, "CHARSET", charset, *keys)
3488
+ tagged = send_command(cmd, *args)
3489
+ tag = tagged.tag
3490
+ # Only the last ESEARCH or SEARCH is used. Excess results are ignored.
3491
+ esearch_result = extract_responses("ESEARCH") {|response|
3492
+ response in ESearchResult(tag: ^tag)
3493
+ }.last
3494
+ search_result = clear_responses("SEARCH").last
3495
+ if esearch_result
3496
+ # silently ignore SEARCH results, if any
3497
+ esearch_result
3498
+ elsif search_result
3499
+ # warn EXPECTED_ESEARCH_RESULT if esearch
3500
+ search_result
3501
+ elsif esearch
3502
+ # warn NO_SEARCH_RESPONSE
3503
+ ESearchResult[tag:, uid: cmd.start_with?("UID ")]
2832
3504
  else
2833
- send_command(cmd, *keys)
3505
+ # warn NO_SEARCH_RESPONSE
3506
+ SearchResult[]
2834
3507
  end
2835
- clear_responses("SEARCH").last || []
2836
3508
  end
2837
3509
  end
2838
3510
 
2839
- def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
3511
+ def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
3512
+ set = SequenceSet[set]
3513
+ if partial
3514
+ mod ||= []
3515
+ mod << "PARTIAL" << PartialRange[partial]
3516
+ end
2840
3517
  if changedsince
2841
3518
  mod ||= []
2842
3519
  mod << "CHANGEDSINCE" << Integer(changedsince)
@@ -2850,39 +3527,36 @@ module Net
2850
3527
  }
2851
3528
  end
2852
3529
 
2853
- synchronize do
2854
- clear_responses("FETCH")
2855
- if mod
2856
- send_command(cmd, MessageSet.new(set), attr, mod)
2857
- else
2858
- send_command(cmd, MessageSet.new(set), attr)
2859
- end
2860
- clear_responses("FETCH")
2861
- end
3530
+ args = [cmd, set, attr]
3531
+ args << mod if mod
3532
+ send_command_returning_fetch_results(*args)
2862
3533
  end
2863
3534
 
2864
3535
  def store_internal(cmd, set, attr, flags, unchangedsince: nil)
2865
3536
  attr = RawData.new(attr) if attr.instance_of?(String)
2866
- args = [MessageSet.new(set)]
3537
+ args = [SequenceSet.new(set)]
2867
3538
  args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
2868
3539
  args << attr << flags
3540
+ send_command_returning_fetch_results(cmd, *args)
3541
+ end
3542
+
3543
+ def send_command_returning_fetch_results(...)
2869
3544
  synchronize do
2870
3545
  clear_responses("FETCH")
2871
- send_command(cmd, *args)
2872
- clear_responses("FETCH")
3546
+ clear_responses("UIDFETCH")
3547
+ send_command(...)
3548
+ fetches = clear_responses("FETCH")
3549
+ uidfetches = clear_responses("UIDFETCH")
3550
+ uidfetches.any? ? uidfetches : fetches
2873
3551
  end
2874
3552
  end
2875
3553
 
2876
3554
  def copy_internal(cmd, set, mailbox)
2877
- send_command(cmd, MessageSet.new(set), mailbox)
3555
+ send_command(cmd, SequenceSet.new(set), mailbox)
2878
3556
  end
2879
3557
 
2880
3558
  def sort_internal(cmd, sort_keys, search_keys, charset)
2881
- if search_keys.instance_of?(String)
2882
- search_keys = [RawData.new(search_keys)]
2883
- else
2884
- normalize_searching_criteria(search_keys)
2885
- end
3559
+ search_keys = normalize_searching_criteria(search_keys)
2886
3560
  synchronize do
2887
3561
  send_command(cmd, sort_keys, charset, *search_keys)
2888
3562
  clear_responses("SORT").last || []
@@ -2890,25 +3564,39 @@ module Net
2890
3564
  end
2891
3565
 
2892
3566
  def thread_internal(cmd, algorithm, search_keys, charset)
2893
- if search_keys.instance_of?(String)
2894
- search_keys = [RawData.new(search_keys)]
2895
- else
2896
- normalize_searching_criteria(search_keys)
2897
- end
3567
+ search_keys = normalize_searching_criteria(search_keys)
2898
3568
  synchronize do
2899
3569
  send_command(cmd, algorithm, charset, *search_keys)
2900
3570
  clear_responses("THREAD").last || []
2901
3571
  end
2902
3572
  end
2903
3573
 
2904
- def normalize_searching_criteria(keys)
2905
- keys.collect! do |i|
2906
- case i
2907
- when -1, Range, Array
2908
- MessageSet.new(i)
3574
+ def normalize_searching_criteria(criteria)
3575
+ return [RawData.new(criteria)] if criteria.is_a?(String)
3576
+ criteria.map {|i|
3577
+ if coerce_search_arg_to_seqset?(i)
3578
+ SequenceSet[i]
2909
3579
  else
2910
3580
  i
2911
3581
  end
3582
+ }
3583
+ end
3584
+
3585
+ def coerce_search_arg_to_seqset?(obj)
3586
+ case obj
3587
+ when Set, -1, :* then true
3588
+ when Range then true
3589
+ when Array then obj.all? { coerce_search_array_arg_to_seqset? _1 }
3590
+ else obj.respond_to?(:to_sequence_set)
3591
+ end
3592
+ end
3593
+
3594
+ def coerce_search_array_arg_to_seqset?(obj)
3595
+ case obj
3596
+ when Integer then obj.positive? || obj == -1
3597
+ when String then ResponseParser::Patterns::SEQUENCE_SET_STR.match?(obj.b)
3598
+ else
3599
+ coerce_search_arg_to_seqset?(obj)
2912
3600
  end
2913
3601
  end
2914
3602
 
@@ -2934,7 +3622,7 @@ module Net
2934
3622
  @sock = SSLSocket.new(@sock, ssl_ctx)
2935
3623
  @sock.sync_close = true
2936
3624
  @sock.hostname = @host if @sock.respond_to? :hostname=
2937
- ssl_socket_connect(@sock, @open_timeout)
3625
+ ssl_socket_connect(@sock, open_timeout)
2938
3626
  if ssl_ctx.verify_mode != VERIFY_NONE
2939
3627
  @sock.post_connection_check(@host)
2940
3628
  @tls_verified = true
@@ -2959,8 +3647,10 @@ module Net
2959
3647
  end
2960
3648
 
2961
3649
  require_relative "imap/errors"
3650
+ require_relative "imap/config"
2962
3651
  require_relative "imap/command_data"
2963
3652
  require_relative "imap/data_encoding"
3653
+ require_relative "imap/data_lite"
2964
3654
  require_relative "imap/flags"
2965
3655
  require_relative "imap/response_data"
2966
3656
  require_relative "imap/response_parser"