net-imap 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of net-imap might be problematic. Click here for more details.

data/lib/net/imap.rb CHANGED
@@ -414,7 +414,7 @@ module Net
414
414
  # >>>
415
415
  # <em>The following are folded into +IMAP4rev2+ but are currently
416
416
  # unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
417
- # extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
417
+ # extensions, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
418
418
  # +LITERAL-+, and +SPECIAL-USE+.</em>
419
419
  #
420
420
  # ==== RFC2087: +QUOTA+
@@ -466,6 +466,10 @@ module Net
466
466
  # - Updates #append with the +APPENDUID+ ResponseCode
467
467
  # - Updates #copy, #move with the +COPYUID+ ResponseCode
468
468
  #
469
+ # ==== RFC4731: +ESEARCH+
470
+ # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
471
+ # - Updates #search, #uid_search with +return+ options and ESearchResult.
472
+ #
469
473
  # ==== RFC4959: +SASL-IR+
470
474
  # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
471
475
  # - Updates #authenticate with the option to send an initial response.
@@ -719,7 +723,7 @@ module Net
719
723
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
720
724
  #
721
725
  class IMAP < Protocol
722
- VERSION = "0.5.1"
726
+ VERSION = "0.5.2"
723
727
 
724
728
  # Aliases for supported capabilities, to be used with the #enable command.
725
729
  ENABLE_ALIASES = {
@@ -1123,7 +1127,7 @@ module Net
1123
1127
  #
1124
1128
  # See [ID[https://tools.ietf.org/html/rfc2971]] for field definitions.
1125
1129
  #
1126
- # ===== Capabilities
1130
+ # ==== Capabilities
1127
1131
  #
1128
1132
  # The server's capabilities must include +ID+
1129
1133
  # [RFC2971[https://tools.ietf.org/html/rfc2971]].
@@ -1205,7 +1209,7 @@ module Net
1205
1209
  #
1206
1210
  # Related: Net::IMAP.new, #login, #authenticate
1207
1211
  #
1208
- # ===== Capability
1212
+ # ==== Capability
1209
1213
  # Clients should not call #starttls unless the server advertises the
1210
1214
  # +STARTTLS+ capability.
1211
1215
  #
@@ -1352,7 +1356,7 @@ module Net
1352
1356
  #
1353
1357
  # Related: #authenticate, #starttls
1354
1358
  #
1355
- # ===== Capabilities
1359
+ # ==== Capabilities
1356
1360
  #
1357
1361
  # An IMAP client MUST NOT call #login when the server advertises the
1358
1362
  # +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
@@ -1393,7 +1397,7 @@ module Net
1393
1397
  #
1394
1398
  # Related: #examine
1395
1399
  #
1396
- # ===== Capabilities
1400
+ # ==== Capabilities
1397
1401
  #
1398
1402
  # If [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] is supported,
1399
1403
  # the server may return an untagged "NO" response with a "UIDNOTSTICKY"
@@ -1511,7 +1515,7 @@ module Net
1511
1515
  #
1512
1516
  # Related: #lsub, MailboxList
1513
1517
  #
1514
- # ===== For example:
1518
+ # ==== For example:
1515
1519
  #
1516
1520
  # imap.create("foo/bar")
1517
1521
  # imap.create("foo/baz")
@@ -1562,7 +1566,7 @@ module Net
1562
1566
  #
1563
1567
  # Related: #list, Namespaces, Namespace
1564
1568
  #
1565
- # ===== For example:
1569
+ # ==== For example:
1566
1570
  #
1567
1571
  # if capable?("NAMESPACE")
1568
1572
  # namespaces = imap.namespace
@@ -1576,7 +1580,7 @@ module Net
1576
1580
  # end
1577
1581
  # end
1578
1582
  #
1579
- # ===== Capabilities
1583
+ # ==== Capabilities
1580
1584
  #
1581
1585
  # The server's capabilities must include +NAMESPACE+
1582
1586
  # [RFC2342[https://tools.ietf.org/html/rfc2342]].
@@ -1615,7 +1619,7 @@ module Net
1615
1619
  #
1616
1620
  # Related: #list, MailboxList
1617
1621
  #
1618
- # ===== Capabilities
1622
+ # ==== Capabilities
1619
1623
  #
1620
1624
  # The server's capabilities must include +XLIST+,
1621
1625
  # a deprecated Gmail extension (replaced by +SPECIAL-USE+).
@@ -1638,7 +1642,7 @@ module Net
1638
1642
  #
1639
1643
  # Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
1640
1644
  #
1641
- # ===== Capabilities
1645
+ # ==== Capabilities
1642
1646
  #
1643
1647
  # The server's capabilities must include +QUOTA+
1644
1648
  # [RFC2087[https://tools.ietf.org/html/rfc2087]].
@@ -1659,7 +1663,7 @@ module Net
1659
1663
  #
1660
1664
  # Related: #getquotaroot, #setquota, MailboxQuota
1661
1665
  #
1662
- # ===== Capabilities
1666
+ # ==== Capabilities
1663
1667
  #
1664
1668
  # The server's capabilities must include +QUOTA+
1665
1669
  # [RFC2087[https://tools.ietf.org/html/rfc2087]].
@@ -1677,7 +1681,7 @@ module Net
1677
1681
  #
1678
1682
  # Related: #getquota, #getquotaroot
1679
1683
  #
1680
- # ===== Capabilities
1684
+ # ==== Capabilities
1681
1685
  #
1682
1686
  # The server's capabilities must include +QUOTA+
1683
1687
  # [RFC2087[https://tools.ietf.org/html/rfc2087]].
@@ -1697,7 +1701,7 @@ module Net
1697
1701
  #
1698
1702
  # Related: #getacl
1699
1703
  #
1700
- # ===== Capabilities
1704
+ # ==== Capabilities
1701
1705
  #
1702
1706
  # The server's capabilities must include +ACL+
1703
1707
  # [RFC4314[https://tools.ietf.org/html/rfc4314]].
@@ -1715,7 +1719,7 @@ module Net
1715
1719
  #
1716
1720
  # Related: #setacl, MailboxACLItem
1717
1721
  #
1718
- # ===== Capabilities
1722
+ # ==== Capabilities
1719
1723
  #
1720
1724
  # The server's capabilities must include +ACL+
1721
1725
  # [RFC4314[https://tools.ietf.org/html/rfc4314]].
@@ -1752,7 +1756,7 @@ module Net
1752
1756
  # for +mailbox+ cannot be returned; for instance, because it
1753
1757
  # does not exist.
1754
1758
  #
1755
- # ===== Supported attributes
1759
+ # ==== Supported attributes
1756
1760
  #
1757
1761
  # +MESSAGES+:: The number of messages in the mailbox.
1758
1762
  #
@@ -1783,12 +1787,12 @@ module Net
1783
1787
  # Unsupported attributes may be requested. The attribute value will be
1784
1788
  # either an Integer or an ExtensionData object.
1785
1789
  #
1786
- # ===== For example:
1790
+ # ==== For example:
1787
1791
  #
1788
1792
  # p imap.status("inbox", ["MESSAGES", "RECENT"])
1789
1793
  # #=> {"RECENT"=>0, "MESSAGES"=>44}
1790
1794
  #
1791
- # ===== Capabilities
1795
+ # ==== Capabilities
1792
1796
  #
1793
1797
  # +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
1794
1798
  # <tt>STATUS=SIZE</tt>
@@ -1828,7 +1832,7 @@ module Net
1828
1832
  # not exist (it is not created automatically), or if the flags,
1829
1833
  # date_time, or message arguments contain errors.
1830
1834
  #
1831
- # ===== Capabilities
1835
+ # ==== Capabilities
1832
1836
  #
1833
1837
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
1834
1838
  # supported and the destination supports persistent UIDs, the server's
@@ -1877,7 +1881,7 @@ module Net
1877
1881
  #
1878
1882
  # Related: #close
1879
1883
  #
1880
- # ===== Capabilities
1884
+ # ==== Capabilities
1881
1885
  #
1882
1886
  # The server's capabilities must include +UNSELECT+
1883
1887
  # [RFC3691[https://tools.ietf.org/html/rfc3691]].
@@ -1918,7 +1922,7 @@ module Net
1918
1922
  #
1919
1923
  # Related: #expunge
1920
1924
  #
1921
- # ===== Capabilities
1925
+ # ==== Capabilities
1922
1926
  #
1923
1927
  # The server's capabilities must include +UIDPLUS+
1924
1928
  # [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
@@ -1931,64 +1935,181 @@ module Net
1931
1935
 
1932
1936
  # :call-seq:
1933
1937
  # search(criteria, charset = nil) -> result
1938
+ # search(criteria, charset: nil, return: nil) -> result
1934
1939
  #
1935
1940
  # Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
1936
1941
  # to search the mailbox for messages that match the given search +criteria+,
1937
- # and returns a SearchResult. SearchResult inherits from Array (for
1938
- # backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
1939
- # capability has been enabled.
1942
+ # and returns either a SearchResult or an ESearchResult. SearchResult
1943
+ # inherits from Array (for backward compatibility) but adds
1944
+ # SearchResult#modseq when the +CONDSTORE+ capability has been enabled.
1945
+ # ESearchResult also implements to_a{rdoc-ref:ESearchResult#to_a}, for
1946
+ # compatibility with SearchResult.
1940
1947
  #
1941
1948
  # +criteria+ is one or more search keys and their arguments, which may be
1942
1949
  # provided as an array or a string.
1943
- # See {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
1944
- #
1945
- # * When +criteria+ is an array, each member is a +SEARCH+ command argument:
1946
- # * Any SequenceSet sends SequenceSet#valid_string.
1947
- # These types are converted to SequenceSet for validation and encoding:
1948
- # * +Set+
1949
- # * +Range+
1950
- # * <tt>-1</tt> and +:*+ -- both translate to <tt>*</tt>
1951
- # * responds to +#to_sequence_set+
1952
- # * +Array+, when each element is one of the above types, a positive
1953
- # +Integer+, a sequence-set formatted +String+, or a deeply nested
1954
- # +Array+ of these same types.
1955
- # * Any +String+ is sent verbatim when it is a valid \IMAP atom,
1956
- # and encoded as an \IMAP quoted or literal string otherwise.
1957
- # * Any other nested +Array+ is encoded as a parenthesized list, to group
1958
- # multiple search keys (e.g., for use with +OR+ and +NOT+).
1959
- # * Any other +Integer+ (besides <tt>-1</tt>) will be sent as +#to_s+.
1960
- # * +Date+ objects will be encoded as an \IMAP date (see ::encode_date).
1961
- #
1962
- # * When +criteria+ is a string, it will be sent directly to the server
1963
- # <em>without any validation or encoding</em>. *WARNING:* This is
1964
- # vulnerable to injection attacks when external inputs are used.
1950
+ # See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
1951
+ # and {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
1952
+ #
1953
+ # +return+ options control what kind of information is returned about
1954
+ # messages matching the search +criteria+. Specifying +return+ should force
1955
+ # the server to return an ESearchResult instead of a SearchResult, but some
1956
+ # servers disobey this requirement. <em>Requires an extended search
1957
+ # capability, such as +ESEARCH+ or +IMAP4rev2+.</em>
1958
+ # See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
1959
+ # and {"Return options"}[rdoc-ref:#search@Return+options], below.
1965
1960
  #
1966
1961
  # +charset+ is the name of the {registered character
1967
1962
  # set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
1968
1963
  # used by strings in the search +criteria+. When +charset+ isn't specified,
1969
1964
  # either <tt>"US-ASCII"</tt> or <tt>"UTF-8"</tt> is assumed, depending on
1970
- # the server's capabilities. +charset+ may be sent inside +criteria+
1971
- # instead of as a separate argument.
1965
+ # the server's capabilities.
1966
+ #
1967
+ # _NOTE:_ Return options and charset may be sent as part of +criteria+. Do
1968
+ # not use the +return+ or +charset+ arguments when either return options or
1969
+ # charset are embedded in +criteria+.
1972
1970
  #
1973
1971
  # Related: #uid_search
1974
1972
  #
1975
- # ===== For example:
1973
+ # ==== For example:
1976
1974
  #
1977
- # p imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
1975
+ # imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
1978
1976
  # #=> [1, 6, 7, 8]
1979
1977
  #
1980
- # The following searches send the exact same command to the server:
1981
- #
1982
- # # criteria array, charset arg
1983
- # imap.search(["OR", "UNSEEN", %w(FLAGGED SUBJECT foo)], "UTF-8")
1984
- # # criteria string, charset arg
1985
- # imap.search("OR UNSEEN (FLAGGED SUBJECT foo)", "UTF-8")
1986
- # # criteria array contains charset arg
1987
- # imap.search([*%w[CHARSET UTF-8], "OR", "UNSEEN", %w(FLAGGED SUBJECT foo)])
1988
- # # criteria string contains charset arg
1989
- # imap.search("CHARSET UTF-8 OR UNSEEN (FLAGGED SUBJECT foo)")
1990
- #
1991
- # ===== Search keys
1978
+ # The following assumes the server supports +ESEARCH+ and +CONDSTORE+:
1979
+ #
1980
+ # result = imap.uid_search(["UID", 12345.., "MODSEQ", 620_162_338],
1981
+ # return: %w(all count min max))
1982
+ # # => #<data Net::IMAP::ESearchResult tag="RUBY0123", uid=true,
1983
+ # # data=[["ALL", Net::IMAP::SequenceSet["12346:12349,22222:22230"]],
1984
+ # # ["COUNT", 13], ["MIN", 12346], ["MAX", 22230],
1985
+ # # ["MODSEQ", 917162488]]>
1986
+ # result.to_a # => [12346, 12347, 12348, 12349, 22222, 22223, 22224,
1987
+ # # 22225, 22226, 22227, 22228, 22229, 22230]
1988
+ # result.uid? # => true
1989
+ # result.count # => 13
1990
+ # result.min # => 12346
1991
+ # result.max # => 22230
1992
+ # result.modseq # => 917162488
1993
+ #
1994
+ # Using +return+ options to limit the result to only min, max, and count:
1995
+ #
1996
+ # result = imap.uid_search(["UID", 12345..,], return: %w(count min max))
1997
+ # # => #<data Net::IMAP::ESearchResult tag="RUBY0124", uid=true,
1998
+ # # data=[["COUNT", 13], ["MIN", 12346], ["MAX", 22230]]>
1999
+ # result.to_a # => []
2000
+ # result.count # => 13
2001
+ # result.min # => 12346
2002
+ # result.max # => 22230
2003
+ #
2004
+ # Return options and charset may be sent as keyword args or embedded in the
2005
+ # +criteria+ arg, but they must be in the correct order: <tt>"RETURN (...)
2006
+ # CHARSET ... criteria..."</tt>. The following searches
2007
+ # send the exact same command to the server:
2008
+ #
2009
+ # # Return options and charset as keyword arguments (preferred)
2010
+ # imap.search(%w(OR UNSEEN FLAGGED), return: %w(MIN MAX), charset: "UTF-8")
2011
+ # # Embedding return and charset in the criteria array
2012
+ # imap.search(["RETURN", %w(MIN MAX), "CHARSET", "UTF-8", *%w(OR UNSEEN FLAGGED)])
2013
+ # # Embedding return and charset in the criteria string
2014
+ # imap.search("RETURN (MIN MAX) CHARSET UTF-8 OR UNSEEN FLAGGED")
2015
+ #
2016
+ # Sending charset as the second positional argument is supported for
2017
+ # backward compatibility. Future versions may print a deprecation warning:
2018
+ # imap.search(%w(OR UNSEEN FLAGGED), "UTF-8", return: %w(MIN MAX))
2019
+ #
2020
+ # ==== Argument translation
2021
+ #
2022
+ # [+return+ options]
2023
+ # Must be an Array. Return option names may be either strings or symbols.
2024
+ # +Range+ elements which begin and end with negative integers are encoded
2025
+ # for use with +PARTIAL+--any other ranges are converted to SequenceSet.
2026
+ # Unlike +criteria+, other return option arguments are not automatically
2027
+ # converted to SequenceSet.
2028
+ #
2029
+ # [When +criteria+ is an Array]
2030
+ # When the array begins with <tt>"RETURN"</tt> (case insensitive), the
2031
+ # second array element is translated like the +return+ parameter (as
2032
+ # described above).
2033
+ #
2034
+ # Every other member is a +SEARCH+ command argument:
2035
+ # [SequenceSet]
2036
+ # Encoded as an \IMAP +sequence-set+ with SequenceSet#valid_string.
2037
+ # [Set, Range, <tt>-1</tt>, +:*+, responds to +#to_sequence_set+]
2038
+ # Converted to SequenceSet for validation and encoding.
2039
+ # [nested sequence-set +Array+]
2040
+ # When every element in a nested array is one of the above types, a
2041
+ # positive +Integer+, a sequence-set formatted +String+, or a deeply
2042
+ # nested +Array+ of these same types, the array will be converted to
2043
+ # SequenceSet for validation and encoding.
2044
+ # [Any other nested +Array+]
2045
+ # Otherwise, a nested array is encoded as a parenthesized list, to
2046
+ # combine multiple search keys (e.g., for use with +OR+ and +NOT+).
2047
+ # [+String+]
2048
+ # Sent verbatim when it is a valid \IMAP +atom+, and encoded as an \IMAP
2049
+ # +quoted+ or +literal+ string otherwise. Every standard search key
2050
+ # name is a valid \IMAP +atom+ and every standard search key string
2051
+ # argument is an +astring+ which may be encoded as +atom+, +quoted+, or
2052
+ # +literal+.
2053
+ #
2054
+ # *Note:* <tt>*</tt> is not a valid \IMAP +atom+ character. Any string
2055
+ # containing <tt>*</tt> will be encoded as a +quoted+ string, _not_ a
2056
+ # +sequence-set+.
2057
+ # [+Integer+ (except for <tt>-1</tt>)]
2058
+ # Encoded using +#to_s+.
2059
+ # [+Date+]
2060
+ # Encoded as an \IMAP date (see ::encode_date).
2061
+ #
2062
+ # [When +criteria+ is a String]
2063
+ # +criteria+ will be sent directly to the server <em>without any
2064
+ # validation or encoding</em>.
2065
+ #
2066
+ # <em>*WARNING:* This is vulnerable to injection attacks when external
2067
+ # inputs are used.</em>
2068
+ #
2069
+ # ==== Return options
2070
+ #
2071
+ # For full definitions of the standard return options and return data, see
2072
+ # the relevant RFCs.
2073
+ #
2074
+ # ===== +ESEARCH+ or +IMAP4rev2+
2075
+ #
2076
+ # The following return options require either +ESEARCH+ or +IMAP4rev2+.
2077
+ # See [{RFC4731 §3.1}[https://rfc-editor.org/rfc/rfc4731#section-3.1]] or
2078
+ # [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]].
2079
+ #
2080
+ # [+ALL+]
2081
+ # Returns ESearchResult#all with a SequenceSet of all matching sequence
2082
+ # numbers or UIDs. This is the default, when return options are empty.
2083
+ #
2084
+ # For compatibility with SearchResult, ESearchResult#to_a returns an
2085
+ # Array of message sequence numbers or UIDs.
2086
+ # [+COUNT+]
2087
+ # Returns ESearchResult#count with the number of matching messages.
2088
+ # [+MAX+]
2089
+ # Returns ESearchResult#max with the highest matching sequence number or
2090
+ # UID.
2091
+ # [+MIN+]
2092
+ # Returns ESearchResult#min with the lowest matching sequence number or
2093
+ # UID.
2094
+ #
2095
+ # ===== +CONDSTORE+
2096
+ #
2097
+ # ESearchResult#modseq return data does not have a corresponding return
2098
+ # option. Instead, it is returned if the +MODSEQ+ search key is used or
2099
+ # when the +CONDSTORE+ extension is enabled for the selected mailbox.
2100
+ # See [{RFC4731 §3.2}[https://www.rfc-editor.org/rfc/rfc4731#section-3.2]]
2101
+ # or [{RFC7162 §2.1.5}[https://www.rfc-editor.org/rfc/rfc7162#section-3.1.5]].
2102
+ #
2103
+ # ===== +RFC4466+ compatible extensions
2104
+ #
2105
+ # {RFC4466 §2.6}[https://www.rfc-editor.org/rfc/rfc4466.html#section-2.6]
2106
+ # defines standard syntax for search extensions. Net::IMAP allows sending
2107
+ # unknown search return options and will parse unknown search extensions'
2108
+ # return values into ExtensionData. Please note that this is an
2109
+ # intentionally _unstable_ API. Future releases may return different
2110
+ # (incompatible) objects, <em>without deprecation or warning</em>.
2111
+ #
2112
+ # ==== Search keys
1992
2113
  #
1993
2114
  # For full definitions of the standard search +criteria+,
1994
2115
  # see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
@@ -2003,23 +2124,21 @@ module Net
2003
2124
  # arguments. The number and type of arguments is specific to each search
2004
2125
  # key.
2005
2126
  #
2006
- # +ALL+::
2007
- # Matches every message in the mailbox.
2127
+ # ===== Search keys that match all messages
2008
2128
  #
2009
- # (_search-key_ _search-key_...)::
2010
- # Combines one or more _search-key_ arguments to match
2011
- # messages which match all contained search keys. Useful for +OR+, +NOT+,
2012
- # and other search keys with _search-key_ arguments.
2129
+ # [+ALL+]
2130
+ # The default initial key. Matches every message in the mailbox.
2013
2131
  #
2014
- # _Note:_ this search key has no label.
2132
+ # [+SAVEDATESUPPORTED+]
2133
+ # Matches every message in the mailbox when the mailbox supports the save
2134
+ # date attribute. Otherwise, it matches no messages.
2015
2135
  #
2016
- # +OR+ _search-key_ _search-key_::
2017
- # Matches messages which match either _search-key_ argument.
2136
+ # <em>Requires +SAVEDATE+ capability</em>.
2137
+ # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
2018
2138
  #
2019
- # +NOT+ _search-key_::
2020
- # Matches messages which do not match _search-key_.
2139
+ # ===== Sequence set search keys
2021
2140
  #
2022
- # _sequence-set_::
2141
+ # [_sequence-set_]
2023
2142
  # Matches messages with message sequence numbers in _sequence-set_.
2024
2143
  #
2025
2144
  # _Note:_ this search key has no label.
@@ -2027,121 +2146,139 @@ module Net
2027
2146
  # <em>+UIDONLY+ must *not* be enabled.</em>
2028
2147
  # {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.html]
2029
2148
  #
2030
- # +UID+ _sequence-set_::
2149
+ # [+UID+ _sequence-set_]
2031
2150
  # Matches messages with a UID in _sequence-set_.
2032
2151
  #
2033
- # +ANSWERED+::
2034
- # +UNANSWERED+::
2152
+ # ===== Compound search keys
2153
+ #
2154
+ # [(_search-key_ _search-key_...)]
2155
+ # Combines one or more _search-key_ arguments to match
2156
+ # messages which match all contained search keys. Useful for +OR+, +NOT+,
2157
+ # and other search keys with _search-key_ arguments.
2158
+ #
2159
+ # _Note:_ this search key has no label.
2160
+ #
2161
+ # [+OR+ _search-key_ _search-key_]
2162
+ # Matches messages which match either _search-key_ argument.
2163
+ #
2164
+ # [+NOT+ _search-key_]
2165
+ # Matches messages which do not match _search-key_.
2166
+ #
2167
+ # [+FUZZY+ _search-key_]
2168
+ # Uses fuzzy matching for the specified search key.
2169
+ #
2170
+ # <em>Requires <tt>SEARCH=FUZZY</tt> capability.</em>
2171
+ # {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
2172
+ #
2173
+ # ===== Flags search keys
2174
+ #
2175
+ # [+ANSWERED+, +UNANSWERED+]
2035
2176
  # Matches messages with or without the <tt>\\Answered</tt> flag.
2036
- # +DELETED+::
2037
- # +UNDELETED+::
2177
+ # [+DELETED+, +UNDELETED+]
2038
2178
  # Matches messages with or without the <tt>\\Deleted</tt> flag.
2039
- # +DRAFT+::
2040
- # +UNDRAFT+::
2179
+ # [+DRAFT+, +UNDRAFT+]
2041
2180
  # Matches messages with or without the <tt>\\Draft</tt> flag.
2042
- # +FLAGGED+::
2043
- # +UNFLAGGED+::
2181
+ # [+FLAGGED+, +UNFLAGGED+]
2044
2182
  # Matches messages with or without the <tt>\\Flagged</tt> flag.
2045
- # +SEEN+::
2046
- # +UNSEEN+::
2183
+ # [+SEEN+, +UNSEEN+]
2047
2184
  # Matches messages with or without the <tt>\\Seen</tt> flag.
2048
- #
2049
- # +KEYWORD+ _keyword_::
2050
- # +UNKEYWORD+ _keyword_::
2185
+ # [+KEYWORD+ _keyword_, +UNKEYWORD+ _keyword_]
2051
2186
  # Matches messages with or without the specified _keyword_.
2052
2187
  #
2053
- # +BCC+ _substring_::
2054
- # Matches when _substring_ is in the envelope's BCC field.
2055
- # +CC+ _substring_::
2056
- # Matches when _substring_ is in the envelope's CC field.
2057
- # +FROM+ _substring_::
2058
- # Matches when _substring_ is in the envelope's FROM field.
2059
- # +SUBJECT+ _substring_::
2060
- # Matches when _substring_ is in the envelope's SUBJECT field.
2061
- # +TO+ _substring_::
2062
- # Matches when _substring_ is in the envelope's TO field.
2063
- #
2064
- # +HEADER+ _field_ _substring_::
2188
+ # [+RECENT+, +UNRECENT+]
2189
+ # Matches messages with or without the <tt>\\Recent</tt> flag.
2190
+ #
2191
+ # *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
2192
+ # [+NEW+]
2193
+ # Equivalent to <tt>(RECENT UNSEEN)</tt>.
2194
+ #
2195
+ # *NOTE:* The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+.
2196
+ #
2197
+ # ===== Header field substring search keys
2198
+ #
2199
+ # [+BCC+ _substring_]
2200
+ # Matches when _substring_ is in the envelope's +BCC+ field.
2201
+ # [+CC+ _substring_]
2202
+ # Matches when _substring_ is in the envelope's +CC+ field.
2203
+ # [+FROM+ _substring_]
2204
+ # Matches when _substring_ is in the envelope's +FROM+ field.
2205
+ # [+SUBJECT+ _substring_]
2206
+ # Matches when _substring_ is in the envelope's +SUBJECT+ field.
2207
+ # [+TO+ _substring_]
2208
+ # Matches when _substring_ is in the envelope's +TO+ field.
2209
+ #
2210
+ # [+HEADER+ _field_ _substring_]
2065
2211
  # Matches when _substring_ is in the specified header _field_.
2066
2212
  #
2067
- # +BODY+ _string_::
2213
+ # ===== Body text search keys
2214
+ # [+BODY+ _string_]
2068
2215
  # Matches when _string_ is in the body of the message.
2069
2216
  # Does not match on header fields.
2070
2217
  #
2071
2218
  # The server _may_ use flexible matching, rather than simple substring
2072
2219
  # matches. For example, this may use stemming or match only full words.
2073
2220
  #
2074
- # +TEXT+ _string_::
2221
+ # [+TEXT+ _string_]
2075
2222
  # Matches when _string_ is in the header or body of the message.
2076
2223
  #
2077
2224
  # The server _may_ use flexible matching, rather than simple substring
2078
2225
  # matches. For example, this may use stemming or match only full words.
2079
2226
  #
2080
- # +BEFORE+ _date_::
2081
- # +ON+ _date_::
2082
- # +SINCE+ _date_::
2083
- # Matches when the +INTERNALDATE+ is earlier than, on, or later than
2084
- # _date_.
2227
+ # ===== Date/Time search keys
2085
2228
  #
2086
- # +SENTBEFORE+ _date_::
2087
- # +SENTON+ _date_::
2088
- # +SENTSINCE+ _date_::
2229
+ # [+SENTBEFORE+ _date_]
2230
+ # [+SENTON+ _date_]
2231
+ # [+SENTSINCE+ _date_]
2089
2232
  # Matches when the +Date+ header is earlier than, on, or later than _date_.
2090
2233
  #
2091
- # +SMALLER+ _bytes_::
2092
- # +LARGER+ _bytes_::
2093
- # Matches when +RFC822.SIZE+ is smaller/larger than _bytes_.
2094
- #
2095
- # ====== Removed from +IMAP4rev2+
2096
- #
2097
- # The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+. So these
2098
- # search keys require the +IMAP4rev1+ capability.
2234
+ # [+BEFORE+ _date_]
2235
+ # [+ON+ _date_]
2236
+ # [+SINCE+ _date_]
2237
+ # Matches when the +INTERNALDATE+ is earlier than, on, or later than
2238
+ # _date_.
2099
2239
  #
2100
- # +RECENT+::
2101
- # +UNRECENT+::
2102
- # Matches messages with or without the <tt>\\Recent</tt> flag.
2240
+ # [+OLDER+ _interval_]
2241
+ # [+YOUNGER+ _interval_]
2242
+ # Matches when the +INTERNALDATE+ is more/less than _interval_ seconds ago.
2103
2243
  #
2104
- # +NEW+::
2105
- # Equivalent to <tt>(RECENT UNSEEN)</tt>.
2244
+ # <em>Requires +WITHIN+ capability</em>.
2245
+ # {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
2106
2246
  #
2107
- # ====== Extension search keys
2247
+ # [+SAVEDBEFORE+ _date_]
2248
+ # [+SAVEDON+ _date_]
2249
+ # [+SAVEDSINCE+ _date_]
2250
+ # Matches when the save date is earlier than, on, or later than _date_.
2108
2251
  #
2109
- # The search keys described below are defined by standard \IMAP extensions.
2252
+ # <em>Requires +SAVEDATE+ capability.</em>
2253
+ # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
2110
2254
  #
2111
- # +OLDER+ _interval_::
2112
- # +YOUNGER+ _interval_::
2113
- # Matches when +INTERNALDATE+ is more/less than _interval_ seconds ago.
2255
+ # ===== Other message attribute search keys
2114
2256
  #
2115
- # <em>Requires the +WITHIN+ capability</em>.
2116
- # {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
2257
+ # [+SMALLER+ _bytes_]
2258
+ # [+LARGER+ _bytes_]
2259
+ # Matches when +RFC822.SIZE+ is smaller or larger than _bytes_.
2117
2260
  #
2118
- # +ANNOTATION+ _entry_ _attr_ _value_::
2261
+ # [+ANNOTATION+ _entry_ _attr_ _value_]
2119
2262
  # Matches messages that have annotations with entries matching _entry_,
2120
2263
  # attributes matching _attr_, and _value_ in the attribute's values.
2121
2264
  #
2122
- # <em>Requires the +ANNOTATE-EXPERIMENT-1+ capability</em>.
2265
+ # <em>Requires +ANNOTATE-EXPERIMENT-1+ capability</em>.
2123
2266
  # {[RFC5257]}[https://www.rfc-editor.org/rfc/rfc5257.html].
2124
2267
  #
2125
- # +FILTER+ _filter_::
2268
+ # [+FILTER+ _filter_]
2126
2269
  # References a _filter_ that is stored on the server and matches all
2127
2270
  # messages which would be matched by that filter's search criteria.
2128
2271
  #
2129
- # <em>Requires the +FILTERS+ capability</em>.
2272
+ # <em>Requires +FILTERS+ capability</em>.
2130
2273
  # {[RFC5466]}[https://www.rfc-editor.org/rfc/rfc5466.html#section-3.1]
2131
2274
  #
2132
- # +FUZZY+ _search-key_::
2133
- # Uses fuzzy matching for the specified search key.
2134
- #
2135
- # <em>Requires the <tt>SEARCH=FUZZY</tt> capability.</em>
2136
- # {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
2137
- #
2138
- # +MODSEQ+ _modseq_::
2275
+ # [+MODSEQ+ _modseq_]
2139
2276
  # Matches when +MODSEQ+ is greater than or equal to _modseq_.
2140
2277
  #
2141
- # <em>Requires the +CONDSTORE+ capability</em>.
2278
+ # <em>Requires +CONDSTORE+ capability</em>.
2142
2279
  # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
2143
2280
  #
2144
- # +MODSEQ+ _entry_ _entry-type_ _modseq_::
2281
+ # [+MODSEQ+ _entry_ _entry-type_ _modseq_]
2145
2282
  # Matches when a specific metadata _entry_ has been updated since
2146
2283
  # _modseq_.
2147
2284
  #
@@ -2150,33 +2287,25 @@ module Net
2150
2287
  # <tt>\\</tt> prefix. _entry-type_ can be one of <tt>"shared"</tt>,
2151
2288
  # <tt>"priv"</tt> (private), or <tt>"all"</tt>.
2152
2289
  #
2153
- # <em>Requires the +CONDSTORE+ capability</em>.
2290
+ # <em>Requires +CONDSTORE+ capability</em>.
2154
2291
  # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
2155
2292
  #
2156
- # +EMAILID+ _objectid_::
2157
- # +THREADID+ _objectid_::
2293
+ # [+EMAILID+ _objectid_]
2294
+ # [+THREADID+ _objectid_]
2158
2295
  # Matches when +EMAILID+/+THREADID+ is equal to _objectid_
2159
2296
  # (substring matches are not supported).
2160
2297
  #
2161
- # <em>Requires the +OBJECTID+ capability</em>.
2298
+ # <em>Requires +OBJECTID+ capability</em>.
2162
2299
  # {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-6]
2163
2300
  #
2164
- # +SAVEDATESUPPORTED+::
2165
- # Matches every message in the mailbox when the mailbox supports the save
2166
- # date attribute. Otherwise, it matches no messages.
2167
- #
2168
- # <em>Requires the +SAVEDATE+ capability</em>.
2169
- # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
2170
- #
2171
- # +SAVEDBEFORE+ _date_::
2172
- # +SAVEDON+ _date_::
2173
- # +SAVEDSINCE+ _date_::
2174
- # Matches when the save date is earlier than, on, or later than _date_.
2301
+ # ==== Capabilities
2175
2302
  #
2176
- # <em>Requires the +SAVEDATE+ capability.</em>
2177
- # {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
2303
+ # Return options should only be specified when the server supports
2304
+ # +IMAP4rev2+ or an extension that allows them, such as +ESEARCH+
2305
+ # [RFC4731[https://rfc-editor.org/rfc/rfc4731#section-3.1]].
2178
2306
  #
2179
- # ===== Capabilities
2307
+ # When +IMAP4rev2+ is enabled, or when the server supports +IMAP4rev2+ but
2308
+ # not +IMAP4rev1+, ESearchResult is always returned instead of SearchResult.
2180
2309
  #
2181
2310
  # If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
2182
2311
  # and enabled for the selected mailbox, a non-empty SearchResult will
@@ -2191,6 +2320,7 @@ module Net
2191
2320
 
2192
2321
  # :call-seq:
2193
2322
  # uid_search(criteria, charset = nil) -> result
2323
+ # uid_search(criteria, charset: nil, return: nil) -> result
2194
2324
  #
2195
2325
  # Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
2196
2326
  # to search the mailbox for messages that match the given searching
@@ -2230,7 +2360,7 @@ module Net
2230
2360
  #
2231
2361
  # Related: #uid_search, FetchData
2232
2362
  #
2233
- # ===== For example:
2363
+ # ==== For example:
2234
2364
  #
2235
2365
  # p imap.fetch(6..8, "UID")
2236
2366
  # #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
@@ -2248,7 +2378,7 @@ module Net
2248
2378
  # p data.attr["UID"]
2249
2379
  # #=> 98
2250
2380
  #
2251
- # ===== Capabilities
2381
+ # ==== Capabilities
2252
2382
  #
2253
2383
  # Many extensions define new message +attr+ names. See FetchData for a list
2254
2384
  # of supported extension fields.
@@ -2277,7 +2407,7 @@ module Net
2277
2407
  #
2278
2408
  # Related: #fetch, FetchData
2279
2409
  #
2280
- # ===== Capabilities
2410
+ # ==== Capabilities
2281
2411
  # Same as #fetch.
2282
2412
  def uid_fetch(set, attr, mod = nil, changedsince: nil)
2283
2413
  fetch_internal("UID FETCH", set, attr, mod, changedsince: changedsince)
@@ -2311,14 +2441,14 @@ module Net
2311
2441
  #
2312
2442
  # Related: #uid_store
2313
2443
  #
2314
- # ===== For example:
2444
+ # ==== For example:
2315
2445
  #
2316
2446
  # p imap.store(6..8, "+FLAGS", [:Deleted])
2317
2447
  # #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
2318
2448
  # #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
2319
2449
  # #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
2320
2450
  #
2321
- # ===== Capabilities
2451
+ # ==== Capabilities
2322
2452
  #
2323
2453
  # Extensions may define new data items to be used with #store.
2324
2454
  #
@@ -2342,7 +2472,7 @@ module Net
2342
2472
  #
2343
2473
  # Related: #store
2344
2474
  #
2345
- # ===== Capabilities
2475
+ # ==== Capabilities
2346
2476
  # Same as #store.
2347
2477
  def uid_store(set, attr, flags, unchangedsince: nil)
2348
2478
  store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
@@ -2355,7 +2485,7 @@ module Net
2355
2485
  #
2356
2486
  # Related: #uid_copy
2357
2487
  #
2358
- # ===== Capabilities
2488
+ # ==== Capabilities
2359
2489
  #
2360
2490
  # If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
2361
2491
  # supported, the server's response should include a +COPYUID+ response code
@@ -2372,7 +2502,7 @@ module Net
2372
2502
  #
2373
2503
  # Similar to #copy, but +set+ contains unique identifiers.
2374
2504
  #
2375
- # ===== Capabilities
2505
+ # ==== Capabilities
2376
2506
  #
2377
2507
  # +UIDPLUS+ affects #uid_copy the same way it affects #copy.
2378
2508
  def uid_copy(set, mailbox)
@@ -2387,7 +2517,7 @@ module Net
2387
2517
  #
2388
2518
  # Related: #uid_move
2389
2519
  #
2390
- # ===== Capabilities
2520
+ # ==== Capabilities
2391
2521
  #
2392
2522
  # The server's capabilities must include +MOVE+
2393
2523
  # [RFC6851[https://tools.ietf.org/html/rfc6851]].
@@ -2411,7 +2541,7 @@ module Net
2411
2541
  #
2412
2542
  # Related: #move
2413
2543
  #
2414
- # ===== Capabilities
2544
+ # ==== Capabilities
2415
2545
  #
2416
2546
  # Same as #move: The server's capabilities must include +MOVE+
2417
2547
  # [RFC6851[https://tools.ietf.org/html/rfc6851]]. +UIDPLUS+ also affects
@@ -2431,14 +2561,14 @@ module Net
2431
2561
  #
2432
2562
  # Related: #uid_sort, #search, #uid_search, #thread, #uid_thread
2433
2563
  #
2434
- # ===== For example:
2564
+ # ==== For example:
2435
2565
  #
2436
2566
  # p imap.sort(["FROM"], ["ALL"], "US-ASCII")
2437
2567
  # #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
2438
2568
  # p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
2439
2569
  # #=> [6, 7, 8, 1]
2440
2570
  #
2441
- # ===== Capabilities
2571
+ # ==== Capabilities
2442
2572
  #
2443
2573
  # The server's capabilities must include +SORT+
2444
2574
  # [RFC5256[https://tools.ietf.org/html/rfc5256]].
@@ -2453,7 +2583,7 @@ module Net
2453
2583
  #
2454
2584
  # Related: #sort, #search, #uid_search, #thread, #uid_thread
2455
2585
  #
2456
- # ===== Capabilities
2586
+ # ==== Capabilities
2457
2587
  #
2458
2588
  # The server's capabilities must include +SORT+
2459
2589
  # [RFC5256[https://tools.ietf.org/html/rfc5256]].
@@ -2478,7 +2608,7 @@ module Net
2478
2608
  #
2479
2609
  # Related: #uid_thread, #search, #uid_search, #sort, #uid_sort
2480
2610
  #
2481
- # ===== Capabilities
2611
+ # ==== Capabilities
2482
2612
  #
2483
2613
  # The server's capabilities must include +THREAD+
2484
2614
  # [RFC5256[https://tools.ietf.org/html/rfc5256]].
@@ -2492,7 +2622,7 @@ module Net
2492
2622
  #
2493
2623
  # Related: #thread, #search, #uid_search, #sort, #uid_sort
2494
2624
  #
2495
- # ===== Capabilities
2625
+ # ==== Capabilities
2496
2626
  #
2497
2627
  # The server's capabilities must include +THREAD+
2498
2628
  # [RFC5256[https://tools.ietf.org/html/rfc5256]].
@@ -2511,7 +2641,7 @@ module Net
2511
2641
  #
2512
2642
  # Related: #capable?, #capabilities, #capability
2513
2643
  #
2514
- # ===== Capabilities
2644
+ # ==== Capabilities
2515
2645
  #
2516
2646
  # The server's capabilities must include
2517
2647
  # +ENABLE+ [RFC5161[https://tools.ietf.org/html/rfc5161]]
@@ -2613,7 +2743,7 @@ module Net
2613
2743
  #
2614
2744
  # Related: #idle_done, #noop, #check
2615
2745
  #
2616
- # ===== Capabilities
2746
+ # ==== Capabilities
2617
2747
  #
2618
2748
  # The server's capabilities must include +IDLE+
2619
2749
  # [RFC2177[https://tools.ietf.org/html/rfc2177]].
@@ -2730,7 +2860,7 @@ module Net
2730
2860
  #
2731
2861
  # Related: #extract_responses, #clear_responses, #response_handlers, #greeting
2732
2862
  #
2733
- # ===== Thread safety
2863
+ # ==== Thread safety
2734
2864
  # >>>
2735
2865
  # *Note:* Access to the responses hash is synchronized for thread-safety.
2736
2866
  # The receiver thread and response_handlers cannot process new responses
@@ -2744,7 +2874,7 @@ module Net
2744
2874
  # thread, but will not modify any responses after adding them to the
2745
2875
  # responses hash.
2746
2876
  #
2747
- # ===== Clearing responses
2877
+ # ==== Clearing responses
2748
2878
  #
2749
2879
  # Previously unhandled responses are automatically cleared before entering a
2750
2880
  # mailbox with #select or #examine. Long-lived connections can receive many
@@ -2753,7 +2883,7 @@ module Net
2753
2883
  # the block, or remove responses with #extract_responses, #clear_responses,
2754
2884
  # or #add_response_handler.
2755
2885
  #
2756
- # ===== Missing responses
2886
+ # ==== Missing responses
2757
2887
  #
2758
2888
  # Only non-+nil+ data is stored. Many important response codes have no data
2759
2889
  # of their own, but are used as "tags" on the ResponseText object they are
@@ -3131,12 +3261,92 @@ module Net
3131
3261
  end
3132
3262
  end
3133
3263
 
3134
- def search_internal(cmd, keys, charset = nil)
3135
- keys = normalize_searching_criteria(keys)
3136
- args = charset ? ["CHARSET", charset, *keys] : keys
3264
+ RETURN_WHOLE = /\ARETURN\z/i
3265
+ RETURN_START = /\ARETURN\b/i
3266
+ private_constant :RETURN_WHOLE, :RETURN_START
3267
+
3268
+ def search_args(keys, charset_arg = nil, return: nil, charset: nil)
3269
+ {return:} => {return: return_kw}
3270
+ case [return_kw, keys]
3271
+ in [nil, Array[RETURN_WHOLE, return_opts, *keys]]
3272
+ return_opts = convert_return_opts(return_opts)
3273
+ esearch = true
3274
+ in [nil => return_opts, RETURN_START]
3275
+ esearch = true
3276
+ in [nil => return_opts, keys]
3277
+ esearch = false
3278
+ in [_, Array[RETURN_WHOLE, _, *] | RETURN_START]
3279
+ raise ArgumentError, "conflicting return options"
3280
+ in [_, Array[RETURN_WHOLE, _, *]] # workaround for https://bugs.ruby-lang.org/issues/20956
3281
+ raise ArgumentError, "conflicting return options"
3282
+ in [_, RETURN_START] # workaround for https://bugs.ruby-lang.org/issues/20956
3283
+ raise ArgumentError, "conflicting return options"
3284
+ in [return_opts, keys]
3285
+ return_opts = convert_return_opts(return_opts)
3286
+ esearch = true
3287
+ end
3288
+ if charset && charset_arg
3289
+ raise ArgumentError, "multiple charset arguments"
3290
+ end
3291
+ charset ||= charset_arg
3292
+ # NOTE: not handling combined RETURN and CHARSET for raw strings
3293
+ if charset && keys in /\ACHARSET\b/i | Array[/\ACHARSET\z/i, *]
3294
+ raise ArgumentError, "multiple charset arguments"
3295
+ end
3296
+ args = normalize_searching_criteria(keys)
3297
+ args.prepend("CHARSET", charset) if charset
3298
+ args.prepend("RETURN", return_opts) if return_opts
3299
+ return args, esearch
3300
+ end
3301
+
3302
+ def convert_return_opts(unconverted)
3303
+ return_opts = Array.try_convert(unconverted) or
3304
+ raise TypeError, "expected return options to be Array, got %s" % [
3305
+ unconverted.class
3306
+ ]
3307
+ return_opts.map {|opt|
3308
+ case opt
3309
+ when Symbol then opt.to_s
3310
+ when Range then partial_range_last_or_seqset(opt)
3311
+ else opt
3312
+ end
3313
+ }
3314
+ end
3315
+
3316
+ def partial_range_last_or_seqset(range)
3317
+ case [range.begin, range.end]
3318
+ in [Integer => first, Integer => last] if first.negative? && last.negative?
3319
+ # partial-range-last [RFC9394]
3320
+ first <= last or raise DataFormatError, "empty range: %p" % [range]
3321
+ "#{first}:#{last}"
3322
+ else
3323
+ SequenceSet[range]
3324
+ end
3325
+ end
3326
+
3327
+ def search_internal(cmd, ...)
3328
+ args, esearch = search_args(...)
3137
3329
  synchronize do
3138
- send_command(cmd, *args)
3139
- clear_responses("SEARCH").last || []
3330
+ tagged = send_command(cmd, *args)
3331
+ tag = tagged.tag
3332
+ # Only the last ESEARCH or SEARCH is used. Excess results are ignored.
3333
+ esearch_result = extract_responses("ESEARCH") {|response|
3334
+ response in ESearchResult(tag: ^tag)
3335
+ }.last
3336
+ search_result = clear_responses("SEARCH").last
3337
+ if esearch_result
3338
+ # silently ignore SEARCH results, if any
3339
+ esearch_result
3340
+ elsif search_result
3341
+ # warn EXPECTED_ESEARCH_RESULT if esearch
3342
+ search_result
3343
+ elsif esearch
3344
+ # warn NO_SEARCH_RESPONSE
3345
+ ESearchResult[tag:, uid: cmd.start_with?("UID ")]
3346
+ else
3347
+ # warn NO_SEARCH_RESPONSE
3348
+ SearchResult[]
3349
+ end
3140
3350
  end
3141
3351
  end
3142
3352
 
@@ -3198,7 +3408,7 @@ module Net
3198
3408
  end
3199
3409
 
3200
3410
  def normalize_searching_criteria(criteria)
3201
- return RawData.new(criteria) if criteria.is_a?(String)
3411
+ return [RawData.new(criteria)] if criteria.is_a?(String)
3202
3412
  criteria.map {|i|
3203
3413
  if coerce_search_arg_to_seqset?(i)
3204
3414
  SequenceSet[i]
@@ -3276,6 +3486,7 @@ require_relative "imap/errors"
3276
3486
  require_relative "imap/config"
3277
3487
  require_relative "imap/command_data"
3278
3488
  require_relative "imap/data_encoding"
3489
+ require_relative "imap/data_lite"
3279
3490
  require_relative "imap/flags"
3280
3491
  require_relative "imap/response_data"
3281
3492
  require_relative "imap/response_parser"