Skip to content

Commit

Permalink
✨ Add unchangedsince kwarg to #store, #uid_store
Browse files Browse the repository at this point in the history
Fixes #237.
  • Loading branch information
nevans committed Dec 12, 2023
1 parent 3d2493d commit f40e758
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 16 deletions.
66 changes: 50 additions & 16 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ module Net
# <em>(but thread responses are unchanged)</em>.
# - Updates #fetch and #uid_fetch with the +changedsince+ modifier and
# +MODSEQ+ FetchData attribute.
# - Updates #store and #uid_store with the +unchangedsince+ modifier and adds
# the +MODIFIED+ ResponseCode to the tagged response.
#
# ==== RFC8438: <tt>STATUS=SIZE</tt>
# - Updates #status with the +SIZE+ status attribute.
Expand Down Expand Up @@ -2066,13 +2068,29 @@ def uid_fetch(set, attr, mod = nil, changedsince: nil)
fetch_internal("UID FETCH", set, attr, mod, changedsince: changedsince)
end

# :call-seq:
# store(set, attr, value, unchangedsince: nil) -> array of FetchData
#
# Sends a {STORE command [IMAP4rev1 §6.4.6]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.6]
# to alter data associated with messages in the mailbox, in particular their
# flags. The +set+ parameter is a number, an array of numbers, or a Range
# object. Each number is a message sequence number. +attr+ is the name of a
# data item to store: <tt>"FLAGS"</tt> will replace the message's flag list
# with the provided one, <tt>"+FLAGS"</tt> will add the provided flags, and
# <tt>"-FLAGS"</tt> will remove them. +flags+ is a list of flags.
# flags.
#
# +set+ is a number, an array of numbers, or a Range object. Each number is
# a message sequence number.
#
# +attr+ is the name of a data item to store. The semantics of +value+
# varies based on +attr+:
# * When +attr+ is <tt>"FLAGS"</tt>, the flags in +value+ replace the
# message's flag list.
# * When +attr+ is <tt>"+FLAGS"</tt>, the flags in +value+ are added to
# the flags for the message.
# * When +attr+ is <tt>"-FLAGS"</tt>, the flags in +value+ are removed
# from the message.
#
# +unchangedsince+ is an optional integer mod-sequence. It prohibits any
# changes to messages with +mod-sequence+ greater than the specified
# +unchangedsince+ value. A SequenceSet of any messages that fail this
# check will be returned in a +MODIFIED+ ResponseCode.
#
# The return value is an array of FetchData.
#
Expand All @@ -2081,13 +2099,25 @@ def uid_fetch(set, attr, mod = nil, changedsince: nil)
# ===== For example:
#
# p imap.store(6..8, "+FLAGS", [:Deleted])
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
# #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
# #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
# #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
# #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
def store(set, attr, flags)
return store_internal("STORE", set, attr, flags)
#
# ===== Capabilities
#
# Extensions may define new data items to be used with #store.
#
# The server's capabilities must include +CONDSTORE+
# {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
# +unchangedsince+ argument. Using +unchangedsince+ implicitly enables the
# +CONDSTORE+ extension.
def store(set, attr, flags, unchangedsince: nil)
store_internal("STORE", set, attr, flags, unchangedsince: unchangedsince)
end

# :call-seq:
# uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData
#
# Sends a {UID STORE command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
# to alter data associated with messages in the mailbox, in particular their
# flags.
Expand All @@ -2096,8 +2126,11 @@ def store(set, attr, flags)
# message sequence numbers.
#
# Related: #store
def uid_store(set, attr, flags)
return store_internal("UID STORE", set, attr, flags)
#
# ===== Capabilities
# Same as #store.
def uid_store(set, attr, flags, unchangedsince: nil)
store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
end

# Sends a {COPY command [IMAP4rev1 §6.4.7]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.7]
Expand Down Expand Up @@ -2809,13 +2842,14 @@ def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
end
end

def store_internal(cmd, set, attr, flags)
if attr.instance_of?(String)
attr = RawData.new(attr)
end
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
attr = RawData.new(attr) if attr.instance_of?(String)
args = [MessageSet.new(set)]
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
args << attr << flags
synchronize do
clear_responses("FETCH")
send_command(cmd, MessageSet.new(set), attr, flags)
send_command(cmd, *args)
clear_responses("FETCH")
end
end
Expand Down
22 changes: 22 additions & 0 deletions test/net/imap/test_imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,28 @@ def test_clear_responses
end
end

test "#store with unchangedsince" do
with_fake_server select: "inbox" do |server, imap|
server.on("STORE", &:done_ok)
imap.store 1..-1, "FLAGS", %i[Deleted], unchangedsince: 12345
assert_equal(
"RUBY0002 STORE 1:* (UNCHANGEDSINCE 12345) FLAGS (\\Deleted)",
server.commands.pop.raw.strip
)
end
end

test "#uid_store with changedsince" do
with_fake_server select: "inbox" do |server, imap|
server.on("UID STORE", &:done_ok)
imap.uid_store 1..-1, "FLAGS", %i[Deleted], unchangedsince: 987
assert_equal(
"RUBY0002 UID STORE 1:* (UNCHANGEDSINCE 987) FLAGS (\\Deleted)",
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 f40e758

Please sign in to comment.