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.
@@ -11,12 +11,19 @@ module Net
11
11
  include ParserUtils
12
12
  extend ParserUtils::Generator
13
13
 
14
- # :call-seq: Net::IMAP::ResponseParser.new -> Net::IMAP::ResponseParser
15
- def initialize
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
- @str[start...@pos - 1]
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
- UIDPlusData.new(validity, nil, dst_uids)
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
- UIDPlusData.new(validity, src_uids, dst_uids)
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
- token = match(T_NUMBER, T_ATOM)
2003
- case token.symbol
2004
- when T_NUMBER then [Integer(token.value)]
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