@@ -534,6 +534,11 @@ module Net
534534 # See FetchData#emailid and FetchData#emailid.
535535 # - Updates #status with support for the +MAILBOXID+ status attribute.
536536 #
537+ # ==== RFC9394: +PARTIAL+
538+ # - Updates #search, #uid_search with the +PARTIAL+ return option which adds
539+ # ESearchResult#partial return data.
540+ # - Updates #uid_fetch with the +partial+ modifier.
541+ #
537542 # == References
538543 #
539544 # [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
@@ -701,6 +706,11 @@ module Net
701706 # Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
702707 # RFC 8474, DOI 10.17487/RFC8474, September 2018,
703708 # <https://www.rfc-editor.org/info/rfc8474>.
709+ # [PARTIAL[https://www.rfc-editor.org/info/rfc9394]]::
710+ # Melnikov, A., Achuthan, A., Nagulakonda, V., and L. Alves,
711+ # "IMAP PARTIAL Extension for Paged SEARCH and FETCH", RFC 9394,
712+ # DOI 10.17487/RFC9394, June 2023,
713+ # <https://www.rfc-editor.org/info/rfc9394>.
704714 #
705715 # === IANA registries
706716 # * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
@@ -1971,8 +1981,9 @@ def uid_expunge(uid_set)
19711981 # the server to return an ESearchResult instead of a SearchResult, but some
19721982 # servers disobey this requirement. <em>Requires an extended search
19731983 # capability, such as +ESEARCH+ or +IMAP4rev2+.</em>
1974- # See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
1975- # and {"Return options"}[rdoc-ref:#search@Return+options], below.
1984+ # See {"Argument translation"}[rdoc-ref:#search@Argument+translation] and
1985+ # {"Supported return options"}[rdoc-ref:#search@Supported+return+options],
1986+ # below.
19761987 #
19771988 # +charset+ is the name of the {registered character
19781989 # set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
@@ -2082,33 +2093,58 @@ def uid_expunge(uid_set)
20822093 # <em>*WARNING:* This is vulnerable to injection attacks when external
20832094 # inputs are used.</em>
20842095 #
2085- # ==== Return options
2096+ # ==== Supported return options
20862097 #
20872098 # For full definitions of the standard return options and return data, see
20882099 # the relevant RFCs.
20892100 #
2090- # ===== +ESEARCH+ or +IMAP4rev2+
2091- #
2092- # The following return options require either +ESEARCH+ or +IMAP4rev2+.
2093- # See [{RFC4731 §3.1}[https://rfc-editor.org/rfc/rfc4731#section-3.1]] or
2094- # [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]].
2095- #
20962101 # [+ALL+]
20972102 # Returns ESearchResult#all with a SequenceSet of all matching sequence
20982103 # numbers or UIDs. This is the default, when return options are empty.
20992104 #
21002105 # For compatibility with SearchResult, ESearchResult#to_a returns an
21012106 # Array of message sequence numbers or UIDs.
2107+ #
2108+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2109+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2110+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2111+ #
21022112 # [+COUNT+]
21032113 # Returns ESearchResult#count with the number of matching messages.
2114+ #
2115+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2116+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2117+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2118+ #
21042119 # [+MAX+]
21052120 # Returns ESearchResult#max with the highest matching sequence number or
21062121 # UID.
2122+ #
2123+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2124+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2125+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2126+ #
21072127 # [+MIN+]
21082128 # Returns ESearchResult#min with the lowest matching sequence number or
21092129 # UID.
21102130 #
2111- # ===== +CONDSTORE+
2131+ # <em>Requires either the +ESEARCH+ or +IMAP4rev2+ capabability.</em>
2132+ # {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731]
2133+ # {[RFC9051]}[https://rfc-editor.org/rfc/rfc9051]
2134+ #
2135+ # [+PARTIAL+ _range_]
2136+ # Returns ESearchResult#partial with a SequenceSet of a subset of
2137+ # matching sequence numbers or UIDs, as selected by _range_. As with
2138+ # sequence numbers, the first result is +1+: <tt>1..500</tt> selects the
2139+ # first 500 search results (in mailbox order), <tt>501..1000</tt> the
2140+ # second 500, and so on. _range_ may also be negative: <tt>-500..-1</tt>
2141+ # selects the last 500 search results.
2142+ #
2143+ # <em>Requires either the <tt>CONTEXT=SEARCH</tt> or +PARTIAL+ capabability.</em>
2144+ # {[RFC5267]}[https://rfc-editor.org/rfc/rfc5267]
2145+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2146+ #
2147+ # ===== +MODSEQ+ return data
21122148 #
21132149 # ESearchResult#modseq return data does not have a corresponding return
21142150 # option. Instead, it is returned if the +MODSEQ+ search key is used or
@@ -2120,8 +2156,8 @@ def uid_expunge(uid_set)
21202156 #
21212157 # {RFC4466 §2.6}[https://www.rfc-editor.org/rfc/rfc4466.html#section-2.6]
21222158 # defines standard syntax for search extensions. Net::IMAP allows sending
2123- # unknown search return options and will parse unknown search extensions'
2124- # return values into ExtensionData. Please note that this is an
2159+ # unsupported search return options and will parse unsupported search
2160+ # extensions' return values into ExtensionData. Please note that this is an
21252161 # intentionally _unstable_ API. Future releases may return different
21262162 # (incompatible) objects, <em>without deprecation or warning</em>.
21272163 #
@@ -2398,12 +2434,12 @@ def uid_search(...)
23982434 # {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
23992435 # +changedsince+ argument. Using +changedsince+ implicitly enables the
24002436 # +CONDSTORE+ extension.
2401- def fetch ( set , attr , mod = nil , changedsince : nil )
2402- fetch_internal ( "FETCH" , set , attr , mod , changedsince : changedsince )
2437+ def fetch ( ... )
2438+ fetch_internal ( "FETCH" , ... )
24032439 end
24042440
24052441 # :call-seq:
2406- # uid_fetch(set, attr, changedsince: nil) -> array of FetchData
2442+ # uid_fetch(set, attr, changedsince: nil, partial: nil ) -> array of FetchData
24072443 #
24082444 # Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
24092445 # to retrieve data associated with a message in the mailbox.
@@ -2420,13 +2456,44 @@ def fetch(set, attr, mod = nil, changedsince: nil)
24202456 #
24212457 # +changedsince+ (optional) behaves the same as with #fetch.
24222458 #
2459+ # +partial+ is an optional range to limit the number of results returned.
2460+ # It's useful when +set+ contains an unknown number of messages.
2461+ # <tt>1..500</tt> returns the first 500 messages in +set+ (in mailbox
2462+ # order), <tt>501..1000</tt> the second 500, and so on. +partial+ may also
2463+ # be negative: <tt>-500..-1</tt> selects the last 500 messages in +set+.
2464+ # <em>Requires the +PARTIAL+ capabability.</em>
2465+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2466+ #
2467+ # For example:
2468+ #
2469+ # # Without partial, the size of the results may be unknown beforehand:
2470+ # results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
2471+ # # ... maybe wait for a long time ... and allocate a lot of memory ...
2472+ # results.size # => 0..2**32-1
2473+ # process results # may also take a long time and use a lot of memory...
2474+ #
2475+ # # Using partial, the results may be paginated:
2476+ # loop do
2477+ # results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
2478+ # partial: 1..500)
2479+ # # fetch should return quickly and allocate little memory
2480+ # results.size # => 0..500
2481+ # break if results.empty?
2482+ # next_uid_to_fetch = results.last.uid + 1
2483+ # process results
2484+ # end
2485+ #
24232486 # Related: #fetch, FetchData
24242487 #
24252488 # ==== Capabilities
24262489 #
2427- # Same as #fetch.
2428- def uid_fetch ( set , attr , mod = nil , changedsince : nil )
2429- fetch_internal ( "UID FETCH" , set , attr , mod , changedsince : changedsince )
2490+ # The server's capabilities must include +PARTIAL+
2491+ # {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394] in order to use the
2492+ # +partial+ argument.
2493+ #
2494+ # Otherwise, the same as #fetch.
2495+ def uid_fetch ( ...)
2496+ fetch_internal ( "UID FETCH" , ...)
24302497 end
24312498
24322499 # :call-seq:
@@ -3372,7 +3439,12 @@ def search_internal(cmd, ...)
33723439 end
33733440 end
33743441
3375- def fetch_internal ( cmd , set , attr , mod = nil , changedsince : nil )
3442+ def fetch_internal ( cmd , set , attr , mod = nil , partial : nil , changedsince : nil )
3443+ set = SequenceSet [ set ]
3444+ if partial
3445+ mod ||= [ ]
3446+ mod << "PARTIAL" << PartialRange [ partial ]
3447+ end
33763448 if changedsince
33773449 mod ||= [ ]
33783450 mod << "CHANGEDSINCE" << Integer ( changedsince )
@@ -3389,9 +3461,9 @@ def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
33893461 synchronize do
33903462 clear_responses ( "FETCH" )
33913463 if mod
3392- send_command ( cmd , SequenceSet . new ( set ) , attr , mod )
3464+ send_command ( cmd , set , attr , mod )
33933465 else
3394- send_command ( cmd , SequenceSet . new ( set ) , attr )
3466+ send_command ( cmd , set , attr )
33953467 end
33963468 clear_responses ( "FETCH" )
33973469 end
0 commit comments