diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 4ac9d7a8..551dc534 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -1556,6 +1556,9 @@ def login(user, password) .tap do state_authenticated! _1 end end + # :call-seq: + # select(mailbox, condstore: false, qresync: nil) -> result + # # Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1] # to select a +mailbox+ so that messages in the +mailbox+ can be accessed. # @@ -1569,8 +1572,29 @@ def login(user, password) # 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. + # #select and all +FETCH+ responses will include FetchStruct#modseq. # Otherwise, the +NOMODSEQ+ ResponseCode will be sent. + # Requires the +CONDSTORE+ capabability. + # {[RFC7162]}[https://rfc-editor.org/rfc/rfc7162] + # + # imap.select("mbox", condstore: true) + # modseq = imap.responses("HIGHESTMODSEQ", &:last) + # + # The optional +qresync+ argument can provide quick resynchronization + # parameters: the last known UIDVALIDITY, the last known MODSEQ, + # known UIDs (optional), and message sequence match data + # (optional). When +qresync+ is provided, a SelectResult object + # is returned with +UIDVALIDITY+, +HIGHESTMODSEQ+, +VANISHED+, and +FETCH+ + # results. Sending +qresync+ implicitly enables +condstore+, too. + # Requires the +QRESYNC+ capabability. + # {[RFC7162]}[https://rfc-editor.org/rfc/rfc7162] + # + # imap.enable("QRESYNC") # must enable before selecting the mailbox + # qresync = imap.select("INBOX", qresync: [uidvalidity, modseq, known_uids]) + # qresync => { uidvalidity: ^uidvalidity, highestmodseq: modseq, + # vanished:, updated: } + # vanished.each do delete_message _1 end + # updated .each do update_message _1.uid, flags: _1.flags, modseq: _1.modseq end # # A Net::IMAP::NoResponseError is raised if the mailbox does not # exist or is for some reason non-selectable. @@ -1579,7 +1603,8 @@ def login(user, password) # # ==== Capabilities # - # If [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] is supported, + # If either [UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]] + # or [IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html]] is supported, # the server may return an untagged "NO" response with a "UIDNOTSTICKY" # response code indicating that the mailstore does not support persistent # UIDs: @@ -1587,19 +1612,16 @@ def login(user, password) # # 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 - state_unselected! # implicitly closes current mailbox - @responses.clear - send_command(*args) - .tap do state_selected! end - end + # + # If [QRESYNC[https://www.rfc-editor.org/rfc/rfc7162.html]] is enabled, + # the +qresync+ keyword parameter may be used. + def select(...) + select_internal("SELECT", ...) end + # :call-seq: + # examine(mailbox, condstore: false, qresync: nil) -> result + # # Sends a {EXAMINE command [IMAP4rev1 §6.3.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.2] # to select a +mailbox+ so that messages in the +mailbox+ can be accessed. # Behaves the same as #select, except that the selected +mailbox+ is @@ -1609,15 +1631,8 @@ def select(mailbox, condstore: false) # exist or is for some reason non-examinable. # # Related: #select - def examine(mailbox, condstore: false) - args = ["EXAMINE", mailbox] - args << ["CONDSTORE"] if condstore - synchronize do - state_unselected! # implicitly closes current mailbox - @responses.clear - send_command(*args) - .tap do state_selected! end - end + def examine(...) + select_internal("EXAMINE", ...) end # Sends a {CREATE command [IMAP4rev1 §6.3.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.3] @@ -2959,9 +2974,7 @@ def uid_thread(algorithm, search_keys, charset) # See {[RFC7162 §3.1]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1]. # # [+QRESYNC+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html]] - # *NOTE:* Enabling QRESYNC will replace +EXPUNGE+ with +VANISHED+, but - # the extension arguments to #select, #examine, and #uid_fetch are not - # supported yet. + # *NOTE:* The +QRESYNC+ argument to #uid_fetch is not supported yet. # # Adds quick resynchronization options to #select, #examine, and # #uid_fetch. +QRESYNC+ _must_ be explicitly enabled before using any of @@ -3585,6 +3598,20 @@ def enforce_logindisabled? end end + def select_internal(command, mailbox, condstore: false, qresync: nil) + args = [command, mailbox] + params = [] + params << "CONDSTORE" if condstore + params << "QRESYNC" << qresync if qresync # TODO: validate qresync params + args << params unless params.empty? + synchronize do + state_unselected! # implicitly closes current mailbox + @responses.clear + send_command(*args) + .tap do state_selected! end + end + end + def expunge_internal(...) synchronize do send_command(...)