@@ -1934,9 +1934,11 @@ def uid_expunge(uid_set)
19341934 #
19351935 # Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
19361936 # 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.
1937+ # and returns either a SearchResult or an ESearchResult. SearchResult
1938+ # inherits from Array (for backward compatibility) but adds
1939+ # SearchResult#modseq when the +CONDSTORE+ capability has been enabled.
1940+ # ESearchResult also implements to_a{rdoc-ref:ESearchResult#to_a}, for
1941+ # compatibility with SearchResult.
19401942 #
19411943 # +criteria+ is one or more search keys and their arguments, which may be
19421944 # provided as an array or a string.
@@ -1947,8 +1949,11 @@ def uid_expunge(uid_set)
19471949 # set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
19481950 # used by strings in the search +criteria+. When +charset+ isn't specified,
19491951 # either <tt>"US-ASCII"</tt> or <tt>"UTF-8"</tt> is assumed, depending on
1950- # the server's capabilities. +charset+ may be sent inside +criteria+
1951- # instead of as a separate argument.
1952+ # the server's capabilities.
1953+ #
1954+ # _NOTE:_ Return options and +charset+ may be sent as part of +criteria+.
1955+ # Do not use the +charset+ argument when either return options or charset
1956+ # are embedded in +criteria+.
19521957 #
19531958 # Related: #uid_search
19541959 #
@@ -1968,6 +1973,12 @@ def uid_expunge(uid_set)
19681973 # # criteria string contains charset arg
19691974 # imap.search("CHARSET UTF-8 OR UNSEEN (FLAGGED SUBJECT foo)")
19701975 #
1976+ # Sending return options and charset embedded in the +criteria+ arg:
1977+ # imap.search("RETURN (MIN MAX) CHARSET UTF-8 (OR UNSEEN FLAGGED)")
1978+ # imap.search(["RETURN", %w(MIN MAX),
1979+ # "CHARSET", "UTF-8",
1980+ # %w(OR UNSEEN FLAGGED)])
1981+ #
19711982 # ==== Argument translation
19721983 #
19731984 # [When +criteria+ is an Array]
@@ -2197,6 +2208,12 @@ def uid_expunge(uid_set)
21972208 #
21982209 # ==== Capabilities
21992210 #
2211+ # Return options should only be specified when the server supports
2212+ # +IMAP4rev2+ or an extension that allows them, such as +ESEARCH+.
2213+ #
2214+ # When +IMAP4rev2+ is enabled, or when the server supports +IMAP4rev2+ but
2215+ # not +IMAP4rev1+, ESearchResult is always returned instead of SearchResult.
2216+ #
22002217 # If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
22012218 # and enabled for the selected mailbox, a non-empty SearchResult will
22022219 # include a +MODSEQ+ value.
@@ -3150,14 +3167,31 @@ def enforce_logindisabled?
31503167 end
31513168 end
31523169
3170+ HasSearchReturnOpts = -> keys {
3171+ keys in RawData [ /\A RETURN / ] | Array [ /\A RETURN\z /i , *]
3172+ }
3173+ private_constant :HasSearchReturnOpts
3174+
31533175 def search_internal ( cmd , keys , charset = nil )
31543176 keys = normalize_searching_criteria ( keys )
31553177 args = charset ? [ "CHARSET" , charset , *keys ] : keys
31563178 synchronize do
3157- send_command ( cmd , *args )
3179+ tagged = send_command ( cmd , *args )
3180+ tag = tagged . tag
3181+ # Only the last ESEARCH or SEARCH is used. Excess results are ignored.
3182+ esearch_result = extract_responses ( "ESEARCH" ) { |response |
3183+ response in ESearchResult ( tag : ^tag )
3184+ } . last
31583185 search_result = clear_responses ( "SEARCH" ) . last
3159- if search_result
3186+ if esearch_result
3187+ # silently ignore SEARCH results, if any
3188+ esearch_result
3189+ elsif search_result
3190+ # warn EXPECTED_ESEARCH_RESULT if esearch
31603191 search_result
3192+ elsif keys in HasSearchReturnOpts # TODO: check if IMAP4rev2 enabled
3193+ # warn NO_SEARCH_RESPONSE
3194+ ESearchResult [ tag :, uid : cmd . start_with? ( "UID " ) ]
31613195 else
31623196 # warn NO_SEARCH_RESPONSE
31633197 SearchResult [ ]
0 commit comments