Skip to content

Commit 2fdc80f

Browse files
authored
🔀 Merge pull request #366 from avdi/RFC9586-UIDONLY
RFC9586 UIDONLY support
2 parents 22d860a + f9303b2 commit 2fdc80f

File tree

9 files changed

+457
-138
lines changed

9 files changed

+457
-138
lines changed

lib/net/imap.rb

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,12 @@ module Net
539539
# ESearchResult#partial return data.
540540
# - Updates #uid_fetch with the +partial+ modifier.
541541
#
542+
# ==== RFC9586: +UIDONLY+
543+
# - Updates #enable with +UIDONLY+ parameter.
544+
# - Updates #uid_fetch and #uid_store to return +UIDFETCH+ response.
545+
# - Updates #expunge and #uid_expunge to return +VANISHED+ response.
546+
# - Prohibits use of message sequence numbers in responses or requests.
547+
#
542548
# == References
543549
#
544550
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
@@ -711,6 +717,11 @@ module Net
711717
# "IMAP PARTIAL Extension for Paged SEARCH and FETCH", RFC 9394,
712718
# DOI 10.17487/RFC9394, June 2023,
713719
# <https://www.rfc-editor.org/info/rfc9394>.
720+
# [UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.pdf]]::
721+
# Melnikov, A., Achuthan, A., Nagulakonda, V., Singh, A., and L. Alves,
722+
# "\IMAP Extension for Using and Returning Unique Identifiers (UIDs) Only",
723+
# RFC 9586, DOI 10.17487/RFC9586, May 2024,
724+
# <https://www.rfc-editor.org/info/rfc9586>.
714725
#
715726
# === IANA registries
716727
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
@@ -724,7 +735,7 @@ module Net
724735
# * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
725736
# +imap+
726737
# * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
727-
# ===== For currently unsupported features:
738+
# ==== For currently unsupported features:
728739
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
729740
# * {LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
730741
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
@@ -2366,6 +2377,9 @@ def uid_expunge(uid_set)
23662377
# result = imap.search(["SUBJECT", "hi there", "not", "new"])
23672378
# #=> Net::IMAP::SearchResult[1, 6, 7, 8, modseq: 5594]
23682379
# result.modseq # => 5594
2380+
#
2381+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2382+
# the +SEARCH+ command is prohibited. Use #uid_search instead.
23692383
def search(...)
23702384
search_internal("SEARCH", ...)
23712385
end
@@ -2383,6 +2397,16 @@ def search(...)
23832397
# capability has been enabled.
23842398
#
23852399
# See #search for documentation of parameters.
2400+
#
2401+
# ==== Capabilities
2402+
#
2403+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2404+
# #uid_search must be used instead of #search, and the <tt><message
2405+
# set></tt> search criterion is prohibited. Use +ALL+ or <tt>UID
2406+
# sequence-set</tt> instead.
2407+
#
2408+
# Otherwise, #uid_search is updated by extensions in the same way as
2409+
# #search.
23862410
def uid_search(...)
23872411
search_internal("UID SEARCH", ...)
23882412
end
@@ -2397,8 +2421,8 @@ def uid_search(...)
23972421
# to {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
23982422
# (For UIDs, use #uid_fetch instead.)
23992423
#
2400-
# +attr+ is a list of attributes to fetch; see the documentation
2401-
# for FetchData for a list of valid attributes.
2424+
# +attr+ is a list of attributes to fetch; see FetchStruct documentation for
2425+
# a list of supported attributes.
24022426
#
24032427
# +changedsince+ is an optional integer mod-sequence. It limits results to
24042428
# messages with a mod-sequence greater than +changedsince+.
@@ -2427,19 +2451,22 @@ def uid_search(...)
24272451
#
24282452
# ==== Capabilities
24292453
#
2430-
# Many extensions define new message +attr+ names. See FetchData for a list
2431-
# of supported extension fields.
2454+
# Many extensions define new message +attr+ names. See FetchStruct for a
2455+
# list of supported extension fields.
24322456
#
24332457
# The server's capabilities must include +CONDSTORE+
24342458
# {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
24352459
# +changedsince+ argument. Using +changedsince+ implicitly enables the
24362460
# +CONDSTORE+ extension.
2461+
#
2462+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2463+
# +FETCH+ command is prohibited. Use #uid_fetch instead.
24372464
def fetch(...)
24382465
fetch_internal("FETCH", ...)
24392466
end
24402467

24412468
# :call-seq:
2442-
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData
2469+
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData (or UIDFetchData)
24432470
#
24442471
# Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
24452472
# to retrieve data associated with a message in the mailbox.
@@ -2491,7 +2518,11 @@ def fetch(...)
24912518
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394] in order to use the
24922519
# +partial+ argument.
24932520
#
2494-
# Otherwise, the same as #fetch.
2521+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2522+
# #uid_fetch must be used instead of #fetch, and UIDFetchData will be
2523+
# returned instead of FetchData.
2524+
#
2525+
# Otherwise, #uid_fetch is updated by extensions in the same way as #fetch.
24952526
def uid_fetch(...)
24962527
fetch_internal("UID FETCH", ...)
24972528
end
@@ -2539,12 +2570,15 @@ def uid_fetch(...)
25392570
# {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
25402571
# +unchangedsince+ argument. Using +unchangedsince+ implicitly enables the
25412572
# +CONDSTORE+ extension.
2573+
#
2574+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2575+
# +STORE+ command is prohibited. Use #uid_store instead.
25422576
def store(set, attr, flags, unchangedsince: nil)
25432577
store_internal("STORE", set, attr, flags, unchangedsince: unchangedsince)
25442578
end
25452579

25462580
# :call-seq:
2547-
# uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData
2581+
# uid_store(set, attr, value, unchangedsince: nil) -> array of FetchData (or UIDFetchData)
25482582
#
25492583
# Sends a {UID STORE command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
25502584
# to alter data associated with messages in the mailbox, in particular their
@@ -2556,7 +2590,12 @@ def store(set, attr, flags, unchangedsince: nil)
25562590
# Related: #store
25572591
#
25582592
# ==== Capabilities
2559-
# Same as #store.
2593+
#
2594+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2595+
# #uid_store must be used instead of #store, and UIDFetchData will be
2596+
# returned instead of FetchData.
2597+
#
2598+
# Otherwise, #uid_store is updated by extensions in the same way as #store.
25602599
def uid_store(set, attr, flags, unchangedsince: nil)
25612600
store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
25622601
end
@@ -2575,6 +2614,9 @@ def uid_store(set, attr, flags, unchangedsince: nil)
25752614
# with UIDPlusData. This will report the UIDVALIDITY of the destination
25762615
# mailbox, the UID set of the source messages, and the assigned UID set of
25772616
# the moved messages.
2617+
#
2618+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2619+
# +COPY+ command is prohibited. Use #uid_copy instead.
25782620
def copy(set, mailbox)
25792621
copy_internal("COPY", set, mailbox)
25802622
end
@@ -2587,7 +2629,10 @@ def copy(set, mailbox)
25872629
#
25882630
# ==== Capabilities
25892631
#
2590-
# +UIDPLUS+ affects #uid_copy the same way it affects #copy.
2632+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] in enabled,
2633+
# #uid_copy must be used instead of #copy.
2634+
#
2635+
# Otherwise, #uid_copy is updated by extensions in the same way as #copy.
25912636
def uid_copy(set, mailbox)
25922637
copy_internal("UID COPY", set, mailbox)
25932638
end
@@ -2611,6 +2656,8 @@ def uid_copy(set, mailbox)
26112656
# mailbox, the UID set of the source messages, and the assigned UID set of
26122657
# the moved messages.
26132658
#
2659+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled, the
2660+
# +MOVE+ command is prohibited. Use #uid_move instead.
26142661
def move(set, mailbox)
26152662
copy_internal("MOVE", set, mailbox)
26162663
end
@@ -2626,9 +2673,13 @@ def move(set, mailbox)
26262673
#
26272674
# ==== Capabilities
26282675
#
2629-
# Same as #move: The server's capabilities must include +MOVE+
2630-
# [RFC6851[https://tools.ietf.org/html/rfc6851]]. +UIDPLUS+ also affects
2631-
# #uid_move the same way it affects #move.
2676+
# The server's capabilities must include either +IMAP4rev2+ or +MOVE+
2677+
# [RFC6851[https://tools.ietf.org/html/rfc6851]].
2678+
#
2679+
# When UIDONLY[https://www.rfc-editor.org/rfc/rfc9586.html] is enabled,
2680+
# #uid_move must be used instead of #move.
2681+
#
2682+
# Otherwise, #uid_move is updated by extensions in the same way as #move.
26322683
def uid_move(set, mailbox)
26332684
copy_internal("UID MOVE", set, mailbox)
26342685
end
@@ -2773,6 +2824,16 @@ def uid_thread(algorithm, search_keys, charset)
27732824
# selected. For convenience, <tt>enable("UTF8=ONLY")</tt> is aliased to
27742825
# <tt>enable("UTF8=ACCEPT")</tt>.
27752826
#
2827+
# [+UIDONLY+ {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.pdf]]
2828+
#
2829+
# When UIDONLY is enabled, the #fetch, #store, #search, #copy, and #move
2830+
# commands are prohibited and result in a tagged BAD response. Clients
2831+
# should instead use uid_fetch, uid_store, uid_search, uid_copy, or
2832+
# uid_move, respectively. All +FETCH+ responses that would be returned are
2833+
# replaced by +UIDFETCH+ responses. All +EXPUNGED+ responses that would be
2834+
# returned are replaced by +VANISHED+ responses. The "<sequence set>"
2835+
# uid_search criterion is prohibited.
2836+
#
27762837
# ===== Unsupported capabilities
27772838
#
27782839
# *Note:* Some extensions that use ENABLE permit the server to send syntax
@@ -3458,26 +3519,27 @@ def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
34583519
}
34593520
end
34603521

3461-
synchronize do
3462-
clear_responses("FETCH")
3463-
if mod
3464-
send_command(cmd, set, attr, mod)
3465-
else
3466-
send_command(cmd, set, attr)
3467-
end
3468-
clear_responses("FETCH")
3469-
end
3522+
args = [cmd, set, attr]
3523+
args << mod if mod
3524+
send_command_returning_fetch_results(*args)
34703525
end
34713526

34723527
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
34733528
attr = RawData.new(attr) if attr.instance_of?(String)
34743529
args = [SequenceSet.new(set)]
34753530
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
34763531
args << attr << flags
3532+
send_command_returning_fetch_results(cmd, *args)
3533+
end
3534+
3535+
def send_command_returning_fetch_results(...)
34773536
synchronize do
34783537
clear_responses("FETCH")
3479-
send_command(cmd, *args)
3480-
clear_responses("FETCH")
3538+
clear_responses("UIDFETCH")
3539+
send_command(...)
3540+
fetches = clear_responses("FETCH")
3541+
uidfetches = clear_responses("UIDFETCH")
3542+
uidfetches.any? ? uidfetches : fetches
34813543
end
34823544
end
34833545

0 commit comments

Comments
 (0)