Skip to content

Commit

Permalink
✨ Add condstore kwarg to #select, #examine
Browse files Browse the repository at this point in the history
Fixes #122.
  • Loading branch information
nevans committed Dec 12, 2023
1 parent 207fe5f commit 647a745
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 8 deletions.
25 changes: 21 additions & 4 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,8 @@ module Net
# ==== RFC7162: +CONDSTORE+
#
# - Updates #status with the +HIGHESTMODSEQ+ status attribute.
# - Updates #select and #examine with the +condstore+ modifier, and adds
# either a +HIGHESTMODSEQ+ or +NOMODSEQ+ ResponseCode to the responses.
# - Updates #search, #uid_search, #sort, and #uid_sort with the +MODSEQ+
# search criterion, and adds SearchResult#modseq to the search response.
# - Updates #thread and #uid_thread with the +MODSEQ+ search criterion
Expand Down Expand Up @@ -1353,6 +1355,12 @@ def login(user, password)
# or when existing messages are expunged; see #add_response_handler for a
# way to detect these events.
#
# When the +condstore+ keyword argument is true, the server is told to
# enable the extension. If +mailbox+ supports persistence of mod-sequences,
# the +HIGHESTMODSEQ+ ResponseCode will be sent as an untagged response to
# #select and all `FETCH` responses will include FetchData#modseq.
# Otherwise, the +NOMODSEQ+ ResponseCode will be sent.
#
# A Net::IMAP::NoResponseError is raised if the mailbox does not
# exist or is for some reason non-selectable.
#
Expand All @@ -1365,10 +1373,17 @@ def login(user, password)
# response code indicating that the mailstore does not support persistent
# UIDs:
# imap.responses("NO", &:last)&.code&.name == "UIDNOTSTICKY"
def select(mailbox)
#
# If [CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html]] is supported,
# the +condstore+ keyword parameter may be used.
# imap.select("mbox", condstore: true)
# modseq = imap.responses("HIGHESTMODSEQ", &:last)
def select(mailbox, condstore: false)
args = ["SELECT", mailbox]
args << ["CONDSTORE"] if condstore
synchronize do
@responses.clear
send_command("SELECT", mailbox)
send_command(*args)
end
end

Expand All @@ -1381,10 +1396,12 @@ def select(mailbox)
# exist or is for some reason non-examinable.
#
# Related: #select
def examine(mailbox)
def examine(mailbox, condstore: false)
args = ["EXAMINE", mailbox]
args << ["CONDSTORE"] if condstore
synchronize do
@responses.clear
send_command("EXAMINE", mailbox)
send_command(*args)
end
end

Expand Down
2 changes: 1 addition & 1 deletion test/net/imap/fake_server/command_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def parse(buf)
/\A([^ ]+) ((?:UID )?\w+)(?: (.+))?\r\n\z/min =~ buf or
raise "bad request: %p" [buf]
case $2.upcase
when "LOGIN", "SELECT", "ENABLE", "AUTHENTICATE"
when "LOGIN", "SELECT", "EXAMINE", "ENABLE", "AUTHENTICATE"
Command.new $1, $2, scan_astrings($3), buf
else
Command.new $1, $2, $3, buf # TODO...
Expand Down
10 changes: 7 additions & 3 deletions test/net/imap/fake_server/command_router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ def handler_for(command)
permanentflags: %i[Deleted Seen *].freeze,
}.freeze

on "SELECT" do |resp|
def select_handler(command, resp)
state.user or return resp.fail_bad_state(state)
name, args = resp.args
name or return resp.fail_bad_args
name = name.upcase if name.to_s.casecmp? "inbox"
mbox = config.mailboxes[name]
mbox or return resp.fail_no "invalid mailbox"
mbox or return resp.fail_no "invalid mailbox %p" % [name]
state.select mbox: mbox, args: args
attrs = RFC3501_6_3_1_SELECT_EXAMPLE_DATA.merge mbox.to_h
resp.untagged "%{exists} EXISTS" % attrs
Expand All @@ -153,9 +153,13 @@ def handler_for(command)
resp.untagged "OK [PERMANENTFLAGS (%s)] Limited" % [
flags(attrs[:permanentflags])
]
resp.done_ok code: "READ-WRITE"
code = command == "SELECT" ? "READ-WRITE" : "READ-ONLY"
resp.done_ok code: code
end

on "SELECT" do |resp| select_handler "SELECT", resp end
on "EXAMINE" do |resp| select_handler "EXAMINE", resp end

on "CLOSE", "UNSELECT" do |resp|
resp.args.nil? or return resp.fail_bad_args
state.unselect
Expand Down
16 changes: 16 additions & 0 deletions test/net/imap/test_imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,22 @@ def test_clear_responses
end
end

test "#select with condstore" do
with_fake_server do |server, imap|
imap.select "inbox", condstore: true
assert_equal("RUBY0001 SELECT inbox (CONDSTORE)",
server.commands.pop.raw.strip)
end
end

test "#examine with condstore" do
with_fake_server do |server, imap|
imap.examine "inbox", condstore: true
assert_equal("RUBY0001 EXAMINE inbox (CONDSTORE)",
server.commands.pop.raw.strip)
end
end

def test_close
with_fake_server(select: "inbox") do |server, imap|
resp = imap.close
Expand Down

0 comments on commit 647a745

Please sign in to comment.