net-imap 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/docs/styles.css +1 -6
- data/lib/net/imap/command_data.rb +32 -0
- data/lib/net/imap/esearch_result.rb +44 -4
- data/lib/net/imap/response_data.rb +1 -0
- data/lib/net/imap/response_parser.rb +65 -1
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +161 -67
- data/rakelib/rfcs.rake +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a871e99f51067e3a509aa5a74fe2c49f4d79496bcec2f21ac50c3c5793d2cdbf
|
4
|
+
data.tar.gz: 302c03e65954f9a118d877f97ea2296f34a7f50c8b1299e3e4f8b589cb620880
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85d97c9729220265da03588f175fea22bcdd27d40cd06910ad35bc01ed64bd0c27b2769790464712572166761f3e82fc4eb6330fef64f45cceb0b3e1007f7126
|
7
|
+
data.tar.gz: aa18d53b81e57ddfbdde2b8295ca7324f965fa744846fdd756ec4ca1c30e6e72092b106ff454d100f70d1078ee239f2b3e643bd4650606a2fc3b4ab5a2d0cc1f
|
data/docs/styles.css
CHANGED
@@ -6,9 +6,7 @@
|
|
6
6
|
|
7
7
|
main .method-detail {
|
8
8
|
display: grid;
|
9
|
-
grid-template-
|
10
|
-
"description description";
|
11
|
-
grid-template-columns: 1fr min-content;
|
9
|
+
grid-template-columns: 1fr auto;
|
12
10
|
justify-content: space-between;
|
13
11
|
}
|
14
12
|
|
@@ -20,19 +18,16 @@ main .method-header, main .method-controls {
|
|
20
18
|
}
|
21
19
|
|
22
20
|
main .method-header {
|
23
|
-
grid-area: "header";
|
24
21
|
border-right: none;
|
25
22
|
border-radius: 4px 0 0 4px;
|
26
23
|
}
|
27
24
|
|
28
25
|
main .method-controls {
|
29
|
-
grid-area: "controls";
|
30
26
|
border-left: none;
|
31
27
|
border-radius: 0 4px 4px 0;
|
32
28
|
}
|
33
29
|
|
34
30
|
main .method-description, main .aliases {
|
35
|
-
grid-area: "description";
|
36
31
|
grid-column: 1 / span 2;
|
37
32
|
padding-left: 1em;
|
38
33
|
}
|
@@ -153,6 +153,38 @@ module Net
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
+
class PartialRange < CommandData # :nodoc:
|
157
|
+
uint32_max = 2**32 - 1
|
158
|
+
POS_RANGE = 1..uint32_max
|
159
|
+
NEG_RANGE = -uint32_max..-1
|
160
|
+
Positive = ->{ (_1 in Range) and POS_RANGE.cover?(_1) }
|
161
|
+
Negative = ->{ (_1 in Range) and NEG_RANGE.cover?(_1) }
|
162
|
+
|
163
|
+
def initialize(data:)
|
164
|
+
min, max = case data
|
165
|
+
in Range
|
166
|
+
data.minmax.map { Integer _1 }
|
167
|
+
in ResponseParser::Patterns::PARTIAL_RANGE
|
168
|
+
data.split(":").map { Integer _1 }.minmax
|
169
|
+
else
|
170
|
+
raise ArgumentError, "invalid partial range input: %p" % [data]
|
171
|
+
end
|
172
|
+
data = min..max
|
173
|
+
unless data in Positive | Negative
|
174
|
+
raise ArgumentError, "invalid partial-range: %p" % [data]
|
175
|
+
end
|
176
|
+
super
|
177
|
+
rescue TypeError, RangeError
|
178
|
+
raise ArgumentError, "expected range min/max to be Integers"
|
179
|
+
end
|
180
|
+
|
181
|
+
def formatted = "%d:%d" % data.minmax
|
182
|
+
|
183
|
+
def send_data(imap, tag)
|
184
|
+
imap.__send__(:put_string, formatted)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
156
188
|
# *DEPRECATED*. Replaced by SequenceSet.
|
157
189
|
class MessageSet < CommandData # :nodoc:
|
158
190
|
def send_data(imap, tag)
|
@@ -35,16 +35,16 @@ module Net
|
|
35
35
|
|
36
36
|
# :call-seq: to_a -> Array of integers
|
37
37
|
#
|
38
|
-
# When #all contains a SequenceSet of message sequence
|
38
|
+
# When either #all or #partial contains a SequenceSet of message sequence
|
39
39
|
# numbers or UIDs, +to_a+ returns that set as an array of integers.
|
40
40
|
#
|
41
|
-
# When #all
|
42
|
-
# returned no results or because +ALL+
|
41
|
+
# When both #all and #partial are +nil+, either because the server
|
42
|
+
# returned no results or because +ALL+ and +PARTIAL+ were not included in
|
43
43
|
# the IMAP#search +RETURN+ options, #to_a returns an empty array.
|
44
44
|
#
|
45
45
|
# Note that SearchResult also implements +to_a+, so it can be used without
|
46
46
|
# checking if the server returned +SEARCH+ or +ESEARCH+ data.
|
47
|
-
def to_a; all&.numbers || [] end
|
47
|
+
def to_a; all&.numbers || partial&.to_a || [] end
|
48
48
|
|
49
49
|
##
|
50
50
|
# attr_reader: tag
|
@@ -135,6 +135,46 @@ module Net
|
|
135
135
|
# and +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.2].
|
136
136
|
def modseq; data.assoc("MODSEQ")&.last end
|
137
137
|
|
138
|
+
# Returned by ESearchResult#partial.
|
139
|
+
#
|
140
|
+
# Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
|
141
|
+
# or <tt>CONTEXT=SEARCH</tt>/<tt>CONTEXT=SORT</tt>
|
142
|
+
# {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
|
143
|
+
#
|
144
|
+
# See also: #to_a
|
145
|
+
class PartialResult < Data.define(:range, :results)
|
146
|
+
def initialize(range:, results:)
|
147
|
+
range => Range
|
148
|
+
results = SequenceSet[results] unless results.nil?
|
149
|
+
super
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# method: range
|
154
|
+
# :call-seq: range -> range
|
155
|
+
|
156
|
+
##
|
157
|
+
# method: results
|
158
|
+
# :call-seq: results -> sequence set or nil
|
159
|
+
|
160
|
+
# Converts #results to an array of integers.
|
161
|
+
#
|
162
|
+
# See also: ESearchResult#to_a.
|
163
|
+
def to_a; results&.numbers || [] end
|
164
|
+
end
|
165
|
+
|
166
|
+
# :call-seq: partial -> PartialResult or nil
|
167
|
+
#
|
168
|
+
# A PartialResult containing a subset of the message sequence numbers or
|
169
|
+
# UIDs that satisfy the SEARCH criteria.
|
170
|
+
#
|
171
|
+
# Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
|
172
|
+
# or <tt>CONTEXT=SEARCH</tt>/<tt>CONTEXT=SORT</tt>
|
173
|
+
# {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
|
174
|
+
#
|
175
|
+
# See also: #to_a
|
176
|
+
def partial; data.assoc("PARTIAL")&.last end
|
177
|
+
|
138
178
|
end
|
139
179
|
end
|
140
180
|
end
|
@@ -6,6 +6,7 @@ module Net
|
|
6
6
|
autoload :FetchData, "#{__dir__}/fetch_data"
|
7
7
|
autoload :SearchResult, "#{__dir__}/search_result"
|
8
8
|
autoload :SequenceSet, "#{__dir__}/sequence_set"
|
9
|
+
autoload :VanishedData, "#{__dir__}/vanished_data"
|
9
10
|
|
10
11
|
# Net::IMAP::ContinuationRequest represents command continuation requests.
|
11
12
|
#
|
@@ -321,6 +321,24 @@ module Net
|
|
321
321
|
SEQUENCE_SET = /#{SEQUENCE_SET_ITEM}(?:,#{SEQUENCE_SET_ITEM})*/n
|
322
322
|
SEQUENCE_SET_STR = /\A#{SEQUENCE_SET}\z/n
|
323
323
|
|
324
|
+
# partial-range-first = nz-number ":" nz-number
|
325
|
+
# ;; Request to search from oldest (lowest UIDs) to
|
326
|
+
# ;; more recent messages.
|
327
|
+
# ;; A range 500:400 is the same as 400:500.
|
328
|
+
# ;; This is similar to <seq-range> from [RFC3501]
|
329
|
+
# ;; but cannot contain "*".
|
330
|
+
PARTIAL_RANGE_FIRST = /\A(#{NZ_NUMBER}):(#{NZ_NUMBER})\z/n
|
331
|
+
|
332
|
+
# partial-range-last = MINUS nz-number ":" MINUS nz-number
|
333
|
+
# ;; Request to search from newest (highest UIDs) to
|
334
|
+
# ;; oldest messages.
|
335
|
+
# ;; A range -500:-400 is the same as -400:-500.
|
336
|
+
PARTIAL_RANGE_LAST = /\A(-#{NZ_NUMBER}):(-#{NZ_NUMBER})\z/n
|
337
|
+
|
338
|
+
# partial-range = partial-range-first / partial-range-last
|
339
|
+
PARTIAL_RANGE = Regexp.union(PARTIAL_RANGE_FIRST,
|
340
|
+
PARTIAL_RANGE_LAST)
|
341
|
+
|
324
342
|
# RFC3501:
|
325
343
|
# literal = "{" number "}" CRLF *CHAR8
|
326
344
|
# ; Number represents the number of CHAR8s
|
@@ -769,7 +787,6 @@ module Net
|
|
769
787
|
def response_data__ignored; response_data__unhandled(IgnoredResponse) end
|
770
788
|
alias response_data__noop response_data__ignored
|
771
789
|
|
772
|
-
alias expunged_resp response_data__unhandled
|
773
790
|
alias uidfetch_resp response_data__unhandled
|
774
791
|
alias listrights_data response_data__unhandled
|
775
792
|
alias myrights_data response_data__unhandled
|
@@ -841,6 +858,20 @@ module Net
|
|
841
858
|
alias mailbox_data__exists response_data__simple_numeric
|
842
859
|
alias mailbox_data__recent response_data__simple_numeric
|
843
860
|
|
861
|
+
# The name for this is confusing, because it *replaces* EXPUNGE
|
862
|
+
# >>>
|
863
|
+
# expunged-resp = "VANISHED" [SP "(EARLIER)"] SP known-uids
|
864
|
+
def expunged_resp
|
865
|
+
name = label "VANISHED"; SP!
|
866
|
+
earlier = if lpar? then label("EARLIER"); rpar; SP!; true else false end
|
867
|
+
uids = known_uids
|
868
|
+
data = VanishedData[uids, earlier]
|
869
|
+
UntaggedResponse.new name, data, @str
|
870
|
+
end
|
871
|
+
|
872
|
+
# TODO: replace with uid_set
|
873
|
+
alias known_uids sequence_set
|
874
|
+
|
844
875
|
# RFC3501 & RFC9051:
|
845
876
|
# msg-att = "(" (msg-att-dynamic / msg-att-static)
|
846
877
|
# *(SP (msg-att-dynamic / msg-att-static)) ")"
|
@@ -1504,6 +1535,9 @@ module Net
|
|
1504
1535
|
# From RFC4731 (ESEARCH):
|
1505
1536
|
# search-return-data =/ "MODSEQ" SP mod-sequence-value
|
1506
1537
|
#
|
1538
|
+
# From RFC9394 (PARTIAL):
|
1539
|
+
# search-return-data =/ ret-data-partial
|
1540
|
+
#
|
1507
1541
|
def search_return_data
|
1508
1542
|
label = search_modifier_name; SP!
|
1509
1543
|
value =
|
@@ -1513,11 +1547,41 @@ module Net
|
|
1513
1547
|
when "ALL" then sequence_set
|
1514
1548
|
when "COUNT" then number
|
1515
1549
|
when "MODSEQ" then mod_sequence_value # RFC7162: CONDSTORE
|
1550
|
+
when "PARTIAL" then ret_data_partial__value # RFC9394: PARTIAL
|
1516
1551
|
else search_return_value
|
1517
1552
|
end
|
1518
1553
|
[label, value]
|
1519
1554
|
end
|
1520
1555
|
|
1556
|
+
# From RFC5267 (CONTEXT=SEARCH, CONTEXT=SORT) and RFC9394 (PARTIAL):
|
1557
|
+
# ret-data-partial = "PARTIAL"
|
1558
|
+
# SP "(" partial-range SP partial-results ")"
|
1559
|
+
def ret_data_partial__value
|
1560
|
+
lpar
|
1561
|
+
range = partial_range; SP!
|
1562
|
+
results = partial_results
|
1563
|
+
rpar
|
1564
|
+
ESearchResult::PartialResult.new(range, results)
|
1565
|
+
end
|
1566
|
+
|
1567
|
+
# partial-range = partial-range-first / partial-range-last
|
1568
|
+
# tagged-ext-simple =/ partial-range-last
|
1569
|
+
def partial_range
|
1570
|
+
case (str = atom)
|
1571
|
+
when Patterns::PARTIAL_RANGE_FIRST, Patterns::PARTIAL_RANGE_LAST
|
1572
|
+
min, max = [Integer($1), Integer($2)].minmax
|
1573
|
+
min..max
|
1574
|
+
else
|
1575
|
+
parse_error("unexpected atom %p, expected partial-range", str)
|
1576
|
+
end
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
# partial-results = sequence-set / "NIL"
|
1580
|
+
# ;; <sequence-set> from [RFC3501].
|
1581
|
+
# ;; NIL indicates that no results correspond to
|
1582
|
+
# ;; the requested range.
|
1583
|
+
def partial_results; NIL? ? nil : sequence_set end
|
1584
|
+
|
1521
1585
|
# search-modifier-name = tagged-ext-label
|
1522
1586
|
alias search_modifier_name tagged_ext_label
|
1523
1587
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP < Protocol
|
5
|
+
|
6
|
+
# Net::IMAP::VanishedData represents the contents of a +VANISHED+ response,
|
7
|
+
# which is described by the
|
8
|
+
# {QRESYNC}[https://www.rfc-editor.org/rfc/rfc7162.html] extension.
|
9
|
+
# [{RFC7162 §3.2.10}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.2.10]].
|
10
|
+
#
|
11
|
+
# +VANISHED+ responses replace +EXPUNGE+ responses when either the
|
12
|
+
# {QRESYNC}[https://www.rfc-editor.org/rfc/rfc7162.html] or the
|
13
|
+
# {UIDONLY}[https://www.rfc-editor.org/rfc/rfc9586.html] extension has been
|
14
|
+
# enabled.
|
15
|
+
class VanishedData < Data.define(:uids, :earlier)
|
16
|
+
|
17
|
+
# Returns a new VanishedData object.
|
18
|
+
#
|
19
|
+
# * +uids+ will be converted by SequenceSet.[].
|
20
|
+
# * +earlier+ will be converted to +true+ or +false+
|
21
|
+
def initialize(uids:, earlier:)
|
22
|
+
uids = SequenceSet[uids]
|
23
|
+
earlier = !!earlier
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# :attr_reader: uids
|
29
|
+
#
|
30
|
+
# SequenceSet of UIDs that have been permanently removed from the mailbox.
|
31
|
+
|
32
|
+
##
|
33
|
+
# :attr_reader: earlier
|
34
|
+
#
|
35
|
+
# +true+ when the response was caused by Net::IMAP#uid_fetch with
|
36
|
+
# <tt>vanished: true</tt> or Net::IMAP#select/Net::IMAP#examine with
|
37
|
+
# <tt>qresync: true</tt>.
|
38
|
+
#
|
39
|
+
# +false+ when the response is used to announce message removals within an
|
40
|
+
# already selected mailbox.
|
41
|
+
|
42
|
+
# rdoc doesn't handle attr aliases nicely. :(
|
43
|
+
alias earlier? earlier # :nodoc:
|
44
|
+
##
|
45
|
+
# :attr_reader: earlier?
|
46
|
+
#
|
47
|
+
# Alias for #earlier.
|
48
|
+
|
49
|
+
# Returns an Array of all of the UIDs in #uids.
|
50
|
+
#
|
51
|
+
# See SequenceSet#numbers.
|
52
|
+
def to_a; uids.numbers end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/net/imap.rb
CHANGED
@@ -534,6 +534,11 @@ module Net
|
|
534
534
|
# See FetchData#emailid and FetchData#emailid.
|
535
535
|
# - Updates #status with support for the +MAILBOXID+ status attribute.
|
536
536
|
#
|
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
|
+
#
|
537
542
|
# == References
|
538
543
|
#
|
539
544
|
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
|
@@ -701,6 +706,11 @@ module Net
|
|
701
706
|
# Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
|
702
707
|
# RFC 8474, DOI 10.17487/RFC8474, September 2018,
|
703
708
|
# <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>.
|
704
714
|
#
|
705
715
|
# === IANA registries
|
706
716
|
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
|
@@ -723,7 +733,7 @@ module Net
|
|
723
733
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
724
734
|
#
|
725
735
|
class IMAP < Protocol
|
726
|
-
VERSION = "0.5.
|
736
|
+
VERSION = "0.5.4"
|
727
737
|
|
728
738
|
# Aliases for supported capabilities, to be used with the #enable command.
|
729
739
|
ENABLE_ALIASES = {
|
@@ -1889,48 +1899,64 @@ module Net
|
|
1889
1899
|
send_command("UNSELECT")
|
1890
1900
|
end
|
1891
1901
|
|
1902
|
+
# call-seq:
|
1903
|
+
# expunge -> array of message sequence numbers
|
1904
|
+
# expunge -> VanishedData of UIDs
|
1905
|
+
#
|
1892
1906
|
# Sends an {EXPUNGE command [IMAP4rev1 §6.4.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.3]
|
1893
|
-
#
|
1894
|
-
# selected mailbox
|
1907
|
+
# to permanently remove all messages with the +\Deleted+ flag from the
|
1908
|
+
# currently selected mailbox.
|
1909
|
+
#
|
1910
|
+
# Returns either an array of expunged message <em>sequence numbers</em> or
|
1911
|
+
# (when the appropriate capability is enabled) VanishedData of expunged
|
1912
|
+
# UIDs. Previously unhandled +EXPUNGE+ or +VANISHED+ responses are merged
|
1913
|
+
# with the direct response to this command. <tt>VANISHED (EARLIER)</tt>
|
1914
|
+
# responses will _not_ be merged.
|
1915
|
+
#
|
1916
|
+
# When no messages have been expunged, an empty array is returned,
|
1917
|
+
# regardless of which extensions are enabled. In a future release, an empty
|
1918
|
+
# VanishedData may be returned, based on the currently enabled extensions.
|
1895
1919
|
#
|
1896
1920
|
# Related: #uid_expunge
|
1921
|
+
#
|
1922
|
+
# ==== Capabilities
|
1923
|
+
#
|
1924
|
+
# When either QRESYNC[https://tools.ietf.org/html/rfc7162] or
|
1925
|
+
# UIDONLY[https://tools.ietf.org/html/rfc9586] are enabled, #expunge
|
1926
|
+
# returns VanishedData, which contains UIDs---<em>not message sequence
|
1927
|
+
# numbers</em>.
|
1897
1928
|
def expunge
|
1898
|
-
|
1899
|
-
send_command("EXPUNGE")
|
1900
|
-
clear_responses("EXPUNGE")
|
1901
|
-
end
|
1929
|
+
expunge_internal("EXPUNGE")
|
1902
1930
|
end
|
1903
1931
|
|
1932
|
+
# call-seq:
|
1933
|
+
# uid_expunge{uid_set) -> array of message sequence numbers
|
1934
|
+
# uid_expunge{uid_set) -> VanishedData of UIDs
|
1935
|
+
#
|
1904
1936
|
# Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
|
1905
1937
|
# {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
|
1906
1938
|
# to permanently remove all messages that have both the <tt>\\Deleted</tt>
|
1907
1939
|
# flag set and a UID that is included in +uid_set+.
|
1908
1940
|
#
|
1941
|
+
# Returns the same result type as #expunge.
|
1942
|
+
#
|
1909
1943
|
# By using #uid_expunge instead of #expunge when resynchronizing with
|
1910
1944
|
# the server, the client can ensure that it does not inadvertantly
|
1911
1945
|
# remove any messages that have been marked as <tt>\\Deleted</tt> by other
|
1912
1946
|
# clients between the time that the client was last connected and
|
1913
1947
|
# the time the client resynchronizes.
|
1914
1948
|
#
|
1915
|
-
# *Note:*
|
1916
|
-
# >>>
|
1917
|
-
# Although the command takes a set of UIDs for its argument, the
|
1918
|
-
# server still returns regular EXPUNGE responses, which contain
|
1919
|
-
# a <em>sequence number</em>. These will be deleted from
|
1920
|
-
# #responses and this method returns them as an array of
|
1921
|
-
# <em>sequence number</em> integers.
|
1922
|
-
#
|
1923
1949
|
# Related: #expunge
|
1924
1950
|
#
|
1925
1951
|
# ==== Capabilities
|
1926
1952
|
#
|
1927
|
-
# The server's capabilities must include +UIDPLUS+
|
1953
|
+
# The server's capabilities must include either +IMAP4rev2+ or +UIDPLUS+
|
1928
1954
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
1955
|
+
#
|
1956
|
+
# Otherwise, #uid_expunge is updated by extensions in the same way as
|
1957
|
+
# #expunge.
|
1929
1958
|
def uid_expunge(uid_set)
|
1930
|
-
|
1931
|
-
send_command("UID EXPUNGE", SequenceSet.new(uid_set))
|
1932
|
-
clear_responses("EXPUNGE")
|
1933
|
-
end
|
1959
|
+
expunge_internal("UID EXPUNGE", SequenceSet.new(uid_set))
|
1934
1960
|
end
|
1935
1961
|
|
1936
1962
|
# :call-seq:
|
@@ -1942,7 +1968,7 @@ module Net
|
|
1942
1968
|
# and returns either a SearchResult or an ESearchResult. SearchResult
|
1943
1969
|
# inherits from Array (for backward compatibility) but adds
|
1944
1970
|
# SearchResult#modseq when the +CONDSTORE+ capability has been enabled.
|
1945
|
-
# ESearchResult also implements to_a
|
1971
|
+
# ESearchResult also implements {#to_a}[rdoc-ref:ESearchResult#to_a], for
|
1946
1972
|
# compatibility with SearchResult.
|
1947
1973
|
#
|
1948
1974
|
# +criteria+ is one or more search keys and their arguments, which may be
|
@@ -1955,8 +1981,9 @@ module Net
|
|
1955
1981
|
# the server to return an ESearchResult instead of a SearchResult, but some
|
1956
1982
|
# servers disobey this requirement. <em>Requires an extended search
|
1957
1983
|
# capability, such as +ESEARCH+ or +IMAP4rev2+.</em>
|
1958
|
-
# See {"Argument translation"}[rdoc-ref:#search@Argument+translation]
|
1959
|
-
#
|
1984
|
+
# See {"Argument translation"}[rdoc-ref:#search@Argument+translation] and
|
1985
|
+
# {"Supported return options"}[rdoc-ref:#search@Supported+return+options],
|
1986
|
+
# below.
|
1960
1987
|
#
|
1961
1988
|
# +charset+ is the name of the {registered character
|
1962
1989
|
# set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
@@ -2066,33 +2093,58 @@ module Net
|
|
2066
2093
|
# <em>*WARNING:* This is vulnerable to injection attacks when external
|
2067
2094
|
# inputs are used.</em>
|
2068
2095
|
#
|
2069
|
-
# ====
|
2096
|
+
# ==== Supported return options
|
2070
2097
|
#
|
2071
2098
|
# For full definitions of the standard return options and return data, see
|
2072
2099
|
# the relevant RFCs.
|
2073
2100
|
#
|
2074
|
-
# ===== +ESEARCH+ or +IMAP4rev2+
|
2075
|
-
#
|
2076
|
-
# The following return options require either +ESEARCH+ or +IMAP4rev2+.
|
2077
|
-
# See [{RFC4731 §3.1}[https://rfc-editor.org/rfc/rfc4731#section-3.1]] or
|
2078
|
-
# [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]].
|
2079
|
-
#
|
2080
2101
|
# [+ALL+]
|
2081
2102
|
# Returns ESearchResult#all with a SequenceSet of all matching sequence
|
2082
2103
|
# numbers or UIDs. This is the default, when return options are empty.
|
2083
2104
|
#
|
2084
2105
|
# For compatibility with SearchResult, ESearchResult#to_a returns an
|
2085
2106
|
# 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
|
+
#
|
2086
2112
|
# [+COUNT+]
|
2087
2113
|
# 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
|
+
#
|
2088
2119
|
# [+MAX+]
|
2089
2120
|
# Returns ESearchResult#max with the highest matching sequence number or
|
2090
2121
|
# 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
|
+
#
|
2091
2127
|
# [+MIN+]
|
2092
2128
|
# Returns ESearchResult#min with the lowest matching sequence number or
|
2093
2129
|
# UID.
|
2094
2130
|
#
|
2095
|
-
#
|
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
|
2096
2148
|
#
|
2097
2149
|
# ESearchResult#modseq return data does not have a corresponding return
|
2098
2150
|
# option. Instead, it is returned if the +MODSEQ+ search key is used or
|
@@ -2104,8 +2156,8 @@ module Net
|
|
2104
2156
|
#
|
2105
2157
|
# {RFC4466 §2.6}[https://www.rfc-editor.org/rfc/rfc4466.html#section-2.6]
|
2106
2158
|
# defines standard syntax for search extensions. Net::IMAP allows sending
|
2107
|
-
#
|
2108
|
-
# 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
|
2109
2161
|
# intentionally _unstable_ API. Future releases may return different
|
2110
2162
|
# (incompatible) objects, <em>without deprecation or warning</em>.
|
2111
2163
|
#
|
@@ -2341,14 +2393,9 @@ module Net
|
|
2341
2393
|
# Sends a {FETCH command [IMAP4rev1 §6.4.5]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.5]
|
2342
2394
|
# to retrieve data associated with a message in the mailbox.
|
2343
2395
|
#
|
2344
|
-
#
|
2345
|
-
#
|
2346
|
-
#
|
2347
|
-
# being interpreted as '100:*'. Beware that the +exclude_end?+
|
2348
|
-
# property of a Range object is ignored, and the contents of a
|
2349
|
-
# range are independent of the order of the range endpoints as per
|
2350
|
-
# the protocol specification, so 1...5, 5..1 and 5...1 are all
|
2351
|
-
# equivalent to 1..5.
|
2396
|
+
# +set+ is the message sequence numbers to fetch, and may be any valid input
|
2397
|
+
# to {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
|
2398
|
+
# (For UIDs, use #uid_fetch instead.)
|
2352
2399
|
#
|
2353
2400
|
# +attr+ is a list of attributes to fetch; see the documentation
|
2354
2401
|
# for FetchData for a list of valid attributes.
|
@@ -2358,7 +2405,7 @@ module Net
|
|
2358
2405
|
#
|
2359
2406
|
# The return value is an array of FetchData.
|
2360
2407
|
#
|
2361
|
-
# Related: #
|
2408
|
+
# Related: #uid_fetch, FetchData
|
2362
2409
|
#
|
2363
2410
|
# ==== For example:
|
2364
2411
|
#
|
@@ -2387,30 +2434,66 @@ module Net
|
|
2387
2434
|
# {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
|
2388
2435
|
# +changedsince+ argument. Using +changedsince+ implicitly enables the
|
2389
2436
|
# +CONDSTORE+ extension.
|
2390
|
-
def fetch(
|
2391
|
-
fetch_internal("FETCH",
|
2437
|
+
def fetch(...)
|
2438
|
+
fetch_internal("FETCH", ...)
|
2392
2439
|
end
|
2393
2440
|
|
2394
2441
|
# :call-seq:
|
2395
|
-
# uid_fetch(set, attr, changedsince: nil) -> array of FetchData
|
2442
|
+
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData
|
2396
2443
|
#
|
2397
2444
|
# Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
2398
2445
|
# to retrieve data associated with a message in the mailbox.
|
2399
2446
|
#
|
2400
|
-
#
|
2401
|
-
#
|
2447
|
+
# +set+ is the message UIDs to fetch, and may be any valid input to
|
2448
|
+
# {SequenceSet[...]}[rdoc-ref:SequenceSet@Creating+sequence+sets].
|
2449
|
+
# (For message sequence numbers, use #fetch instead.)
|
2402
2450
|
#
|
2451
|
+
# +attr+ behaves the same as with #fetch.
|
2403
2452
|
# >>>
|
2404
2453
|
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
|
2405
2454
|
# part of any +FETCH+ response caused by a +UID+ command, regardless of
|
2406
2455
|
# whether a +UID+ was specified as a message data item to the +FETCH+.
|
2407
2456
|
#
|
2457
|
+
# +changedsince+ (optional) behaves the same as with #fetch.
|
2458
|
+
#
|
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
|
+
#
|
2408
2486
|
# Related: #fetch, FetchData
|
2409
2487
|
#
|
2410
2488
|
# ==== Capabilities
|
2411
|
-
#
|
2412
|
-
|
2413
|
-
|
2489
|
+
#
|
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", ...)
|
2414
2497
|
end
|
2415
2498
|
|
2416
2499
|
# :call-seq:
|
@@ -3261,6 +3344,22 @@ module Net
|
|
3261
3344
|
end
|
3262
3345
|
end
|
3263
3346
|
|
3347
|
+
def expunge_internal(...)
|
3348
|
+
synchronize do
|
3349
|
+
send_command(...)
|
3350
|
+
expunged_array = clear_responses("EXPUNGE")
|
3351
|
+
vanished_array = extract_responses("VANISHED") { !_1.earlier? }
|
3352
|
+
if vanished_array.empty?
|
3353
|
+
expunged_array
|
3354
|
+
elsif vanished_array.length == 1
|
3355
|
+
vanished_array.first
|
3356
|
+
else
|
3357
|
+
merged_uids = SequenceSet[*vanished_array.map(&:uids)]
|
3358
|
+
VanishedData[uids: merged_uids, earlier: false]
|
3359
|
+
end
|
3360
|
+
end
|
3361
|
+
end
|
3362
|
+
|
3264
3363
|
RETURN_WHOLE = /\ARETURN\z/i
|
3265
3364
|
RETURN_START = /\ARETURN\b/i
|
3266
3365
|
private_constant :RETURN_WHOLE, :RETURN_START
|
@@ -3306,24 +3405,14 @@ module Net
|
|
3306
3405
|
]
|
3307
3406
|
return_opts.map {|opt|
|
3308
3407
|
case opt
|
3309
|
-
when Symbol
|
3310
|
-
when
|
3311
|
-
|
3408
|
+
when Symbol then opt.to_s
|
3409
|
+
when PartialRange::Negative then PartialRange[opt]
|
3410
|
+
when Range then SequenceSet[opt]
|
3411
|
+
else opt
|
3312
3412
|
end
|
3313
3413
|
}
|
3314
3414
|
end
|
3315
3415
|
|
3316
|
-
def partial_range_last_or_seqset(range)
|
3317
|
-
case [range.begin, range.end]
|
3318
|
-
in [Integer => first, Integer => last] if first.negative? && last.negative?
|
3319
|
-
# partial-range-last [RFC9394]
|
3320
|
-
first <= last or raise DataFormatError, "empty range: %p" % [range]
|
3321
|
-
"#{first}:#{last}"
|
3322
|
-
else
|
3323
|
-
SequenceSet[range]
|
3324
|
-
end
|
3325
|
-
end
|
3326
|
-
|
3327
3416
|
def search_internal(cmd, ...)
|
3328
3417
|
args, esearch = search_args(...)
|
3329
3418
|
synchronize do
|
@@ -3350,7 +3439,12 @@ module Net
|
|
3350
3439
|
end
|
3351
3440
|
end
|
3352
3441
|
|
3353
|
-
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
|
3354
3448
|
if changedsince
|
3355
3449
|
mod ||= []
|
3356
3450
|
mod << "CHANGEDSINCE" << Integer(changedsince)
|
@@ -3367,9 +3461,9 @@ module Net
|
|
3367
3461
|
synchronize do
|
3368
3462
|
clear_responses("FETCH")
|
3369
3463
|
if mod
|
3370
|
-
send_command(cmd,
|
3464
|
+
send_command(cmd, set, attr, mod)
|
3371
3465
|
else
|
3372
|
-
send_command(cmd,
|
3466
|
+
send_command(cmd, set, attr)
|
3373
3467
|
end
|
3374
3468
|
clear_responses("FETCH")
|
3375
3469
|
end
|
data/rakelib/rfcs.rake
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-imap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-12-
|
12
|
+
date: 2024-12-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-protocol
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- lib/net/imap/stringprep/saslprep_tables.rb
|
98
98
|
- lib/net/imap/stringprep/tables.rb
|
99
99
|
- lib/net/imap/stringprep/trace.rb
|
100
|
+
- lib/net/imap/vanished_data.rb
|
100
101
|
- net-imap.gemspec
|
101
102
|
- rakelib/benchmarks.rake
|
102
103
|
- rakelib/rdoc.rake
|