Skip to content

Commit d86e849

Browse files
committed
✨ Gather ESEARCH response to #search/#uid_search
* Return empty ESearchResult when no result * Clear SEARCH responses before new search. Note that we don't need to do this for ESEARCH responses, since they are tagged.
1 parent cb7e87e commit d86e849

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

lib/net/imap.rb

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,7 +1930,7 @@ def uid_expunge(uid_set)
19301930
end
19311931

19321932
# :call-seq:
1933-
# search(criteria, charset = nil) -> result
1933+
# search(criteria, charset = nil, esearch: false) -> result
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+,
@@ -1973,6 +1973,11 @@ def uid_expunge(uid_set)
19731973
# Do not use the +charset+ argument when either return options or charset
19741974
# are embedded in +criteria+.
19751975
#
1976+
# +esearch+ controls the return type when the server does not return any
1977+
# search results. If +esearch+ is +true+ or +criteria+ begins with
1978+
# +RETURN+, an empty ESearchResult will be returned. When +esearch+ is
1979+
# +false+, an empty SearchResult will be returned.
1980+
#
19761981
# Related: #uid_search
19771982
#
19781983
# ===== For example:
@@ -3145,12 +3150,24 @@ def enforce_logindisabled?
31453150
end
31463151
end
31473152

3148-
def search_internal(cmd, keys, charset = nil)
3153+
def search_internal(cmd, keys, charset = nil, esearch: false)
31493154
keys = normalize_searching_criteria(keys)
31503155
args = charset ? ["CHARSET", charset, *keys] : keys
31513156
synchronize do
3152-
send_command(cmd, *args)
3153-
clear_responses("SEARCH").last || []
3157+
clear_responses("SEARCH")
3158+
result = nil
3159+
send_command(cmd, *args) do |response, tag|
3160+
if response in data: ESearchResult(tag: ^tag) => result
3161+
responses("ESEARCH") { _1.delete(result) }
3162+
end
3163+
end
3164+
if result
3165+
result
3166+
elsif esearch || keys in RawData[/\ARETURN /] | Array[/\ARETURN\z/i, *]
3167+
ESearchResult.new
3168+
else
3169+
clear_responses("SEARCH").last || []
3170+
end
31543171
end
31553172
end
31563173

test/net/imap/test_imap.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,45 @@ def seqset_coercible.to_sequence_set
12571257
end
12581258
end
12591259

1260+
test("#search/#uid_search with ESEARCH or IMAP4rev2") do
1261+
with_fake_server do |server, imap|
1262+
# Example from RFC9051, 6.4.4:
1263+
# C: A282 SEARCH RETURN (MIN COUNT) FLAGGED
1264+
# SINCE 1-Feb-1994 NOT FROM "Smith"
1265+
# S: * ESEARCH (TAG "A282") MIN 2 COUNT 3
1266+
# S: A282 OK SEARCH completed
1267+
server.on "SEARCH" do |cmd|
1268+
cmd.untagged "ESEARCH", "(TAG \"unrelated1\") MIN 1 COUNT 2"
1269+
cmd.untagged "ESEARCH", "(TAG %p) MIN 2 COUNT 3" % [cmd.tag]
1270+
cmd.untagged "ESEARCH", "(TAG \"unrelated2\") MIN 222 COUNT 333"
1271+
cmd.done_ok
1272+
end
1273+
result = imap.search(
1274+
'RETURN (MIN COUNT) FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith"'
1275+
)
1276+
cmd = server.commands.pop
1277+
assert_equal Net::IMAP::ESearchResult.new(
1278+
cmd.tag, false, [["MIN", 2], ["COUNT", 3]]
1279+
), result
1280+
esearch_responses = imap.clear_responses("ESEARCH")
1281+
assert_equal 2, esearch_responses.count
1282+
refute esearch_responses.include?(result)
1283+
end
1284+
end
1285+
1286+
test("missing server ESEARCH response") do
1287+
with_fake_server do |server, imap|
1288+
# Example from RFC9051, 6.4.4:
1289+
# C: A282 SEARCH RETURN (SAVE) FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith"
1290+
# S: A282 OK SEARCH completed, result saved
1291+
server.on "SEARCH" do |cmd| cmd.done_ok "result saved" end
1292+
result = imap.search(
1293+
'RETURN (SAVE) FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith"'
1294+
)
1295+
assert_equal Net::IMAP::ESearchResult.new, result
1296+
end
1297+
end
1298+
12601299
test("missing server SEARCH response") do
12611300
with_fake_server do |server, imap|
12621301
server.on "SEARCH", &:done_ok

0 commit comments

Comments
 (0)