net-imap 0.4.12 → 0.4.21
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.
- checksums.yaml +4 -4
- data/lib/net/imap/command_data.rb +2 -2
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +65 -0
- data/lib/net/imap/config.rb +524 -0
- data/lib/net/imap/deprecated_client_options.rb +2 -2
- data/lib/net/imap/errors.rb +33 -0
- data/lib/net/imap/response_data.rb +3 -54
- data/lib/net/imap/response_parser/parser_utils.rb +6 -6
- data/lib/net/imap/response_parser.rb +34 -15
- data/lib/net/imap/response_reader.rb +75 -0
- data/lib/net/imap/sequence_set.rb +274 -123
- data/lib/net/imap/uidplus_data.rb +326 -0
- data/lib/net/imap.rb +297 -86
- data/net-imap.gemspec +2 -2
- metadata +9 -9
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/pages.yml +0 -46
- data/.github/workflows/push_gem.yml +0 -48
- data/.github/workflows/test.yml +0 -31
- data/.gitignore +0 -12
- data/.mailmap +0 -13
@@ -11,12 +11,19 @@ module Net
|
|
11
11
|
include ParserUtils
|
12
12
|
extend ParserUtils::Generator
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
attr_reader :config
|
15
|
+
|
16
|
+
# Creates a new ResponseParser.
|
17
|
+
#
|
18
|
+
# When +config+ is frozen or global, the parser #config inherits from it.
|
19
|
+
# Otherwise, +config+ will be used directly.
|
20
|
+
def initialize(config: Config.global)
|
16
21
|
@str = nil
|
17
22
|
@pos = nil
|
18
23
|
@lex_state = nil
|
19
24
|
@token = nil
|
25
|
+
@config = Config[config]
|
26
|
+
@config = @config.new if @config == Config.global || @config.frozen?
|
20
27
|
end
|
21
28
|
|
22
29
|
# :call-seq:
|
@@ -1336,7 +1343,8 @@ module Net
|
|
1336
1343
|
assert_no_lookahead
|
1337
1344
|
start = @pos
|
1338
1345
|
astring
|
1339
|
-
@
|
1346
|
+
end_pos = @token ? @pos - 1 : @pos
|
1347
|
+
@str[start...end_pos]
|
1340
1348
|
end
|
1341
1349
|
|
1342
1350
|
# mailbox-data = "FLAGS" SP flag-list / "LIST" SP mailbox-list /
|
@@ -1859,11 +1867,10 @@ module Net
|
|
1859
1867
|
#
|
1860
1868
|
# n.b, uniqueid ⊂ uid-set. To avoid inconsistent return types, we always
|
1861
1869
|
# match uid_set even if that returns a single-member array.
|
1862
|
-
#
|
1863
1870
|
def resp_code_apnd__data
|
1864
1871
|
validity = number; SP!
|
1865
1872
|
dst_uids = uid_set # uniqueid ⊂ uid-set
|
1866
|
-
|
1873
|
+
AppendUID(validity, dst_uids)
|
1867
1874
|
end
|
1868
1875
|
|
1869
1876
|
# already matched: "COPYUID"
|
@@ -1873,7 +1880,25 @@ module Net
|
|
1873
1880
|
validity = number; SP!
|
1874
1881
|
src_uids = uid_set; SP!
|
1875
1882
|
dst_uids = uid_set
|
1876
|
-
|
1883
|
+
CopyUID(validity, src_uids, dst_uids)
|
1884
|
+
end
|
1885
|
+
|
1886
|
+
def AppendUID(...) DeprecatedUIDPlus(...) || AppendUIDData.new(...) end
|
1887
|
+
def CopyUID(...) DeprecatedUIDPlus(...) || CopyUIDData.new(...) end
|
1888
|
+
|
1889
|
+
# TODO: remove this code in the v0.6.0 release
|
1890
|
+
def DeprecatedUIDPlus(validity, src_uids = nil, dst_uids)
|
1891
|
+
return unless config.parser_use_deprecated_uidplus_data
|
1892
|
+
compact_uid_sets = [src_uids, dst_uids].compact
|
1893
|
+
count = compact_uid_sets.map { _1.count_with_duplicates }.max
|
1894
|
+
max = config.parser_max_deprecated_uidplus_data_size
|
1895
|
+
if count <= max
|
1896
|
+
src_uids &&= src_uids.each_ordered_number.to_a
|
1897
|
+
dst_uids = dst_uids.each_ordered_number.to_a
|
1898
|
+
UIDPlusData.new(validity, src_uids, dst_uids)
|
1899
|
+
elsif config.parser_use_deprecated_uidplus_data != :up_to_max_size
|
1900
|
+
parse_error("uid-set is too large: %d > %d", count, max)
|
1901
|
+
end
|
1877
1902
|
end
|
1878
1903
|
|
1879
1904
|
ADDRESS_REGEXP = /\G
|
@@ -1999,15 +2024,9 @@ module Net
|
|
1999
2024
|
# uniqueid = nz-number
|
2000
2025
|
# ; Strictly ascending
|
2001
2026
|
def uid_set
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
when T_ATOM
|
2006
|
-
token.value.split(",").flat_map {|range|
|
2007
|
-
range = range.split(":").map {|uniqueid| Integer(uniqueid) }
|
2008
|
-
range.size == 1 ? range : Range.new(range.min, range.max).to_a
|
2009
|
-
}
|
2010
|
-
end
|
2027
|
+
set = sequence_set
|
2028
|
+
parse_error("uid-set cannot contain '*'") if set.include_star?
|
2029
|
+
set
|
2011
2030
|
end
|
2012
2031
|
|
2013
2032
|
def nil_atom
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP
|
5
|
+
# See https://www.rfc-editor.org/rfc/rfc9051#section-2.2.2
|
6
|
+
class ResponseReader # :nodoc:
|
7
|
+
attr_reader :client
|
8
|
+
|
9
|
+
def initialize(client, sock)
|
10
|
+
@client, @sock = client, sock
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_response_buffer
|
14
|
+
@buff = String.new
|
15
|
+
catch :eof do
|
16
|
+
while true
|
17
|
+
read_line
|
18
|
+
break unless (@literal_size = get_literal_size)
|
19
|
+
read_literal
|
20
|
+
end
|
21
|
+
end
|
22
|
+
buff
|
23
|
+
ensure
|
24
|
+
@buff = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :buff, :literal_size
|
30
|
+
|
31
|
+
def bytes_read; buff.bytesize end
|
32
|
+
def empty?; buff.empty? end
|
33
|
+
def done?; line_done? && !get_literal_size end
|
34
|
+
def line_done?; buff.end_with?(CRLF) end
|
35
|
+
def get_literal_size; /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i end
|
36
|
+
|
37
|
+
def read_line
|
38
|
+
buff << (@sock.gets(CRLF, read_limit) or throw :eof)
|
39
|
+
max_response_remaining! unless line_done?
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_literal
|
43
|
+
# check before allocating memory for literal
|
44
|
+
max_response_remaining!
|
45
|
+
literal = String.new(capacity: literal_size)
|
46
|
+
buff << (@sock.read(read_limit(literal_size), literal) or throw :eof)
|
47
|
+
ensure
|
48
|
+
@literal_size = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def read_limit(limit = nil)
|
52
|
+
[limit, max_response_remaining!].compact.min
|
53
|
+
end
|
54
|
+
|
55
|
+
def max_response_size; client.max_response_size end
|
56
|
+
def max_response_remaining; max_response_size &.- bytes_read end
|
57
|
+
def response_too_large?; max_response_size &.< min_response_size end
|
58
|
+
def min_response_size; bytes_read + min_response_remaining end
|
59
|
+
|
60
|
+
def min_response_remaining
|
61
|
+
empty? ? 3 : done? ? 0 : (literal_size || 0) + 2
|
62
|
+
end
|
63
|
+
|
64
|
+
def max_response_remaining!
|
65
|
+
return max_response_remaining unless response_too_large?
|
66
|
+
raise ResponseTooLargeError.new(
|
67
|
+
max_response_size: max_response_size,
|
68
|
+
bytes_read: bytes_read,
|
69
|
+
literal_size: literal_size,
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|