From f40e758a91926e1f0b89a9315f6d9c45520cb0e3 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Fri, 24 Nov 2023 15:44:01 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20`unchangedsince`=20kwarg=20to?= =?UTF-8?q?=20`#store`,=20`#uid=5Fstore`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #237. --- lib/net/imap.rb | 66 +++++++++++++++++++++++++++++--------- test/net/imap/test_imap.rb | 22 +++++++++++++ 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 30451697..7f7da6d0 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -513,6 +513,8 @@ module Net # (but thread responses are unchanged). # - 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: STATUS=SIZE # - Updates #status with the +SIZE+ status attribute. @@ -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: "FLAGS" will replace the message's flag list - # with the provided one, "+FLAGS" will add the provided flags, and - # "-FLAGS" 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 "FLAGS", the flags in +value+ replace the + # message's flag list. + # * When +attr+ is "+FLAGS", the flags in +value+ are added to + # the flags for the message. + # * When +attr+ is "-FLAGS", 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. # @@ -2081,13 +2099,25 @@ def uid_fetch(set, attr, mod = nil, changedsince: nil) # ===== For example: # # p imap.store(6..8, "+FLAGS", [:Deleted]) - # #=> [#[:Seen, :Deleted]}>, \\ - # #[:Seen, :Deleted]}>, \\ + # #=> [#[:Seen, :Deleted]}>, + # #[:Seen, :Deleted]}>, # #[: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. @@ -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] @@ -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 diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb index 5fdcb807..a08ec105 100644 --- a/test/net/imap/test_imap.rb +++ b/test/net/imap/test_imap.rb @@ -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