net-imap 0.4.18 → 0.4.19

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f71a5476fad0d1bbe6dd52a510fa083ef6b35cb98f27af536a5991ebe9f31244
4
- data.tar.gz: 10082ff774cfccf2b0b43c58cf62bdb99b3e2e75cf7ff7fcb9c508cde8dc33ed
3
+ metadata.gz: 0d13d2ec5aeab6f5fce9d77841e77c690a2849d2d6393d4bac0847fcec6dcf2b
4
+ data.tar.gz: 2821ba41ca70465fdfc38005908ae2fa2828ecd5c3425bf407df27f2e949ea2b
5
5
  SHA512:
6
- metadata.gz: c98013c4ad6493bb0e455cced96388e35214567eb0359085e5023beffc9d748472ff729dc70718a3e66ba86a78e5a1d5de803fc68e5012a6da852e57f951398c
7
- data.tar.gz: bd5b766443689dad66eb1caf4479763f8181d38e64b3db7fdf415b277d27721fda9d4ce634e4638ab4f0860e3a82f63a37581830e81a617fe08baaff07262694
6
+ metadata.gz: 45bb4a741d80d2097e487b3d1ca31196f97322d6104967c6cad6410d65321e372667a6ff5bbb5bf9aafbdddc7906c7fc05b88c2fff06984dc9efd8a309802ecc
7
+ data.tar.gz: '08acdba3fdc323f01bc593ac7fde03ed5e06f1265be821263213fd53714b647e81305713d6829b67adba55dfb541a231e8c0a2303ad83e3efada4088e10c8419'
@@ -262,6 +262,67 @@ module Net
262
262
  #
263
263
  # Alias for responses_without_block
264
264
 
265
+ # Whether ResponseParser should use the deprecated UIDPlusData or
266
+ # CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
267
+ # AppendUIDData for +APPENDUID+ response codes.
268
+ #
269
+ # UIDPlusData stores its data in arrays of numbers, which is vulnerable to
270
+ # a memory exhaustion denial of service attack from an untrusted or
271
+ # compromised server. Set this option to +false+ to completely block this
272
+ # vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
273
+ # mitigates this vulnerability.
274
+ #
275
+ # AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
276
+ # UIDPlusData. Most applications should be able to upgrade with little
277
+ # or no changes.
278
+ #
279
+ # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
280
+ #
281
+ # <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
282
+ #
283
+ # <em>UIDPlusData will be removed in +v0.6+ and this config setting will
284
+ # be ignored.</em>
285
+ #
286
+ # ==== Valid options
287
+ #
288
+ # [+true+ <em>(original default)</em>]
289
+ # ResponseParser only uses UIDPlusData.
290
+ #
291
+ # [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
292
+ # ResponseParser uses UIDPlusData when the +uid-set+ size is below
293
+ # parser_max_deprecated_uidplus_data_size. Above that size,
294
+ # ResponseParser uses AppendUIDData or CopyUIDData.
295
+ #
296
+ # [+false+ <em>(planned default for +v0.6+)</em>]
297
+ # ResponseParser _only_ uses AppendUIDData and CopyUIDData.
298
+ attr_accessor :parser_use_deprecated_uidplus_data, type: [
299
+ true, :up_to_max_size, false
300
+ ]
301
+
302
+ # The maximum +uid-set+ size that ResponseParser will parse into
303
+ # deprecated UIDPlusData. This limit only applies when
304
+ # parser_use_deprecated_uidplus_data is not +false+.
305
+ #
306
+ # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
307
+ #
308
+ # <em>Support for limiting UIDPlusData to a maximum size was added in
309
+ # +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
310
+ #
311
+ # <em>UIDPlusData will be removed in +v0.6+.</em>
312
+ #
313
+ # ==== Versioned Defaults
314
+ #
315
+ # Because this limit guards against a remote server causing catastrophic
316
+ # memory exhaustion, the versioned default (used by #load_defaults) also
317
+ # applies to versions without the feature.
318
+ #
319
+ # * +0.3+ and prior: <tt>10,000</tt>
320
+ # * +0.4+: <tt>1,000</tt>
321
+ # * +0.5+: <tt>100</tt>
322
+ # * +0.6+: <tt>0</tt>
323
+ #
324
+ attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
325
+
265
326
  # Creates a new config object and initialize its attribute with +attrs+.
266
327
  #
267
328
  # If +parent+ is not given, the global config is used by default.
@@ -341,6 +402,8 @@ module Net
341
402
  idle_response_timeout: 5,
342
403
  sasl_ir: true,
343
404
  responses_without_block: :silence_deprecation_warning,
405
+ parser_use_deprecated_uidplus_data: true,
406
+ parser_max_deprecated_uidplus_data_size: 1000,
344
407
  ).freeze
345
408
 
346
409
  @global = default.new
@@ -349,6 +412,8 @@ module Net
349
412
 
350
413
  version_defaults[0] = Config[0.4].dup.update(
351
414
  sasl_ir: false,
415
+ parser_use_deprecated_uidplus_data: true,
416
+ parser_max_deprecated_uidplus_data_size: 10_000,
352
417
  ).freeze
353
418
  version_defaults[0.0] = Config[0]
354
419
  version_defaults[0.1] = Config[0]
@@ -357,6 +422,8 @@ module Net
357
422
 
358
423
  version_defaults[0.5] = Config[0.4].dup.update(
359
424
  responses_without_block: :warn,
425
+ parser_use_deprecated_uidplus_data: :up_to_max_size,
426
+ parser_max_deprecated_uidplus_data_size: 100,
360
427
  ).freeze
361
428
 
362
429
  version_defaults[:default] = Config[0.4]
@@ -365,6 +432,8 @@ module Net
365
432
 
366
433
  version_defaults[0.6] = Config[0.5].dup.update(
367
434
  responses_without_block: :frozen_dup,
435
+ parser_use_deprecated_uidplus_data: false,
436
+ parser_max_deprecated_uidplus_data_size: 0,
368
437
  ).freeze
369
438
  version_defaults[:future] = Config[0.6]
370
439
 
@@ -5,6 +5,9 @@ module Net
5
5
  autoload :FetchData, "#{__dir__}/fetch_data"
6
6
  autoload :SearchResult, "#{__dir__}/search_result"
7
7
  autoload :SequenceSet, "#{__dir__}/sequence_set"
8
+ autoload :UIDPlusData, "#{__dir__}/uidplus_data"
9
+ autoload :AppendUIDData, "#{__dir__}/uidplus_data"
10
+ autoload :CopyUIDData, "#{__dir__}/uidplus_data"
8
11
 
9
12
  # Net::IMAP::ContinuationRequest represents command continuation requests.
10
13
  #
@@ -324,60 +327,6 @@ module Net
324
327
  # code data can take.
325
328
  end
326
329
 
327
- # Net::IMAP::UIDPlusData represents the ResponseCode#data that accompanies
328
- # the +APPENDUID+ and +COPYUID+ response codes.
329
- #
330
- # See [[UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]].
331
- #
332
- # ==== Capability requirement
333
- #
334
- # The +UIDPLUS+ capability[rdoc-ref:Net::IMAP#capability] must be supported.
335
- # A server that supports +UIDPLUS+ should send a UIDPlusData object inside
336
- # every TaggedResponse returned by the append[rdoc-ref:Net::IMAP#append],
337
- # copy[rdoc-ref:Net::IMAP#copy], move[rdoc-ref:Net::IMAP#move], {uid
338
- # copy}[rdoc-ref:Net::IMAP#uid_copy], and {uid
339
- # move}[rdoc-ref:Net::IMAP#uid_move] commands---unless the destination
340
- # mailbox reports +UIDNOTSTICKY+.
341
- #
342
- #--
343
- # TODO: support MULTIAPPEND
344
- #++
345
- #
346
- class UIDPlusData < Struct.new(:uidvalidity, :source_uids, :assigned_uids)
347
- ##
348
- # method: uidvalidity
349
- # :call-seq: uidvalidity -> nonzero uint32
350
- #
351
- # The UIDVALIDITY of the destination mailbox.
352
-
353
- ##
354
- # method: source_uids
355
- # :call-seq: source_uids -> nil or an array of nonzero uint32
356
- #
357
- # The UIDs of the copied or moved messages.
358
- #
359
- # Note:: Returns +nil+ for Net::IMAP#append.
360
-
361
- ##
362
- # method: assigned_uids
363
- # :call-seq: assigned_uids -> an array of nonzero uint32
364
- #
365
- # The newly assigned UIDs of the copied, moved, or appended messages.
366
- #
367
- # Note:: This always returns an array, even when it contains only one UID.
368
-
369
- ##
370
- # :call-seq: uid_mapping -> nil or a hash
371
- #
372
- # Returns a hash mapping each source UID to the newly assigned destination
373
- # UID.
374
- #
375
- # Note:: Returns +nil+ for Net::IMAP#append.
376
- def uid_mapping
377
- source_uids&.zip(assigned_uids)&.to_h
378
- end
379
- end
380
-
381
330
  # Net::IMAP::MailboxList represents contents of the LIST response,
382
331
  # representing a single mailbox path.
383
332
  #
@@ -13,13 +13,17 @@ module Net
13
13
 
14
14
  attr_reader :config
15
15
 
16
- # :call-seq: Net::IMAP::ResponseParser.new -> Net::IMAP::ResponseParser
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.
17
20
  def initialize(config: Config.global)
18
21
  @str = nil
19
22
  @pos = nil
20
23
  @lex_state = nil
21
24
  @token = nil
22
25
  @config = Config[config]
26
+ @config = @config.new if @config == Config.global || @config.frozen?
23
27
  end
24
28
 
25
29
  # :call-seq:
@@ -1863,11 +1867,10 @@ module Net
1863
1867
  #
1864
1868
  # n.b, uniqueid ⊂ uid-set. To avoid inconsistent return types, we always
1865
1869
  # match uid_set even if that returns a single-member array.
1866
- #
1867
1870
  def resp_code_apnd__data
1868
1871
  validity = number; SP!
1869
1872
  dst_uids = uid_set # uniqueid ⊂ uid-set
1870
- UIDPlusData.new(validity, nil, dst_uids)
1873
+ AppendUID(validity, dst_uids)
1871
1874
  end
1872
1875
 
1873
1876
  # already matched: "COPYUID"
@@ -1877,7 +1880,25 @@ module Net
1877
1880
  validity = number; SP!
1878
1881
  src_uids = uid_set; SP!
1879
1882
  dst_uids = uid_set
1880
- 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
1881
1902
  end
1882
1903
 
1883
1904
  ADDRESS_REGEXP = /\G
@@ -2003,15 +2024,9 @@ module Net
2003
2024
  # uniqueid = nz-number
2004
2025
  # ; Strictly ascending
2005
2026
  def uid_set
2006
- token = match(T_NUMBER, T_ATOM)
2007
- case token.symbol
2008
- when T_NUMBER then [Integer(token.value)]
2009
- when T_ATOM
2010
- token.value.split(",").flat_map {|range|
2011
- range = range.split(":").map {|uniqueid| Integer(uniqueid) }
2012
- range.size == 1 ? range : Range.new(range.min, range.max).to_a
2013
- }
2014
- end
2027
+ set = sequence_set
2028
+ parse_error("uid-set cannot contain '*'") if set.include_star?
2029
+ set
2015
2030
  end
2016
2031
 
2017
2032
  def nil_atom
@@ -60,18 +60,20 @@ module Net
60
60
  # set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
61
61
  # set.valid_string #=> "1:10,55,1024:2048"
62
62
  #
63
- # == Normalized form
63
+ # == Ordered and Normalized sets
64
64
  #
65
- # When a sequence set is created with a single String value, that #string
66
- # representation is preserved. SequenceSet's internal representation
67
- # implicitly sorts all entries, de-duplicates numbers, and coalesces
68
- # adjacent or overlapping ranges. Most enumeration methods and offset-based
69
- # methods use this normalized representation. Most modification methods
70
- # will convert #string to its normalized form.
65
+ # Sometimes the order of the set's members is significant, such as with the
66
+ # +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
67
+ # sequence set is created by the parser or with a single string value, that
68
+ # #string representation is preserved.
71
69
  #
72
- # In some cases the order of the string representation is significant, such
73
- # as the +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. Use
74
- # #entries or #each_entry to enumerate the set in its original order. To
70
+ # Internally, SequenceSet stores a normalized representation which sorts all
71
+ # entries, de-duplicates numbers, and coalesces adjacent or overlapping
72
+ # ranges. Most methods use this normalized representation to achieve
73
+ # <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
74
+ # the set in its original order.
75
+ #
76
+ # Most modification methods convert #string to its normalized form. To
75
77
  # preserve #string order while modifying a set, use #append, #string=, or
76
78
  # #replace.
77
79
  #
@@ -164,7 +166,7 @@ module Net
164
166
  # - #===:
165
167
  # Returns whether a given object is fully contained within +self+, or
166
168
  # +nil+ if the object cannot be converted to a compatible type.
167
- # - #cover? (aliased as #===):
169
+ # - #cover?:
168
170
  # Returns whether a given object is fully contained within +self+.
169
171
  # - #intersect? (aliased as #overlap?):
170
172
  # Returns whether +self+ and a given object have any common elements.
@@ -185,30 +187,41 @@ module Net
185
187
  # - #max: Returns the maximum number in the set.
186
188
  # - #minmax: Returns the minimum and maximum numbers in the set.
187
189
  #
188
- # <i>Accessing value by offset:</i>
190
+ # <i>Accessing value by offset in sorted set:</i>
189
191
  # - #[] (aliased as #slice): Returns the number or consecutive subset at a
190
- # given offset or range of offsets.
191
- # - #at: Returns the number at a given offset.
192
- # - #find_index: Returns the given number's offset in the set
192
+ # given offset or range of offsets in the sorted set.
193
+ # - #at: Returns the number at a given offset in the sorted set.
194
+ # - #find_index: Returns the given number's offset in the sorted set.
195
+ #
196
+ # <i>Accessing value by offset in ordered entries</i>
197
+ # - #ordered_at: Returns the number at a given offset in the ordered entries.
198
+ # - #find_ordered_index: Returns the index of the given number's first
199
+ # occurrence in entries.
193
200
  #
194
201
  # <i>Set cardinality:</i>
195
202
  # - #count (aliased as #size): Returns the count of numbers in the set.
203
+ # Duplicated numbers are not counted.
196
204
  # - #empty?: Returns whether the set has no members. \IMAP syntax does not
197
205
  # allow empty sequence sets.
198
206
  # - #valid?: Returns whether the set has any members.
199
207
  # - #full?: Returns whether the set contains every possible value, including
200
208
  # <tt>*</tt>.
201
209
  #
210
+ # <i>Denormalized properties:</i>
211
+ # - #has_duplicates?: Returns whether the ordered entries repeat any
212
+ # numbers.
213
+ # - #count_duplicates: Returns the count of repeated numbers in the ordered
214
+ # entries.
215
+ # - #count_with_duplicates: Returns the count of numbers in the ordered
216
+ # entries, including any repeated numbers.
217
+ #
202
218
  # === Methods for Iterating
203
219
  #
220
+ # <i>Normalized (sorted and coalesced):</i>
204
221
  # - #each_element: Yields each number and range in the set, sorted and
205
222
  # coalesced, and returns +self+.
206
223
  # - #elements (aliased as #to_a): Returns an Array of every number and range
207
224
  # in the set, sorted and coalesced.
208
- # - #each_entry: Yields each number and range in the set, unsorted and
209
- # without deduplicating numbers or coalescing ranges, and returns +self+.
210
- # - #entries: Returns an Array of every number and range in the set,
211
- # unsorted and without deduplicating numbers or coalescing ranges.
212
225
  # - #each_range:
213
226
  # Yields each element in the set as a Range and returns +self+.
214
227
  # - #ranges: Returns an Array of every element in the set, converting
@@ -218,6 +231,14 @@ module Net
218
231
  # ranges into all of their contained numbers.
219
232
  # - #to_set: Returns a Set containing all of the #numbers in the set.
220
233
  #
234
+ # <i>Order preserving:</i>
235
+ # - #each_entry: Yields each number and range in the set, unsorted and
236
+ # without deduplicating numbers or coalescing ranges, and returns +self+.
237
+ # - #entries: Returns an Array of every number and range in the set,
238
+ # unsorted and without deduplicating numbers or coalescing ranges.
239
+ # - #each_ordered_number: Yields each number in the ordered entries and
240
+ # returns +self+.
241
+ #
221
242
  # === Methods for \Set Operations
222
243
  # These methods do not modify +self+.
223
244
  #
@@ -237,19 +258,29 @@ module Net
237
258
  # === Methods for Assigning
238
259
  # These methods add or replace elements in +self+.
239
260
  #
261
+ # <i>Normalized (sorted and coalesced):</i>
262
+ #
263
+ # These methods always update #string to be fully sorted and coalesced.
264
+ #
240
265
  # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
241
266
  # - #add?: If the given object is not an element in the set, adds it and
242
267
  # returns +self+; otherwise, returns +nil+.
243
268
  # - #merge: Merges multiple elements into the set; returns +self+.
269
+ # - #complement!: Replaces the contents of the set with its own #complement.
270
+ #
271
+ # <i>Order preserving:</i>
272
+ #
273
+ # These methods _may_ cause #string to not be sorted or coalesced.
274
+ #
244
275
  # - #append: Adds a given object to the set, appending it to the existing
245
276
  # string, and returns +self+.
246
277
  # - #string=: Assigns a new #string value and replaces #elements to match.
247
278
  # - #replace: Replaces the contents of the set with the contents
248
279
  # of a given object.
249
- # - #complement!: Replaces the contents of the set with its own #complement.
250
280
  #
251
281
  # === Methods for Deleting
252
- # These methods remove elements from +self+.
282
+ # These methods remove elements from +self+, and update #string to be fully
283
+ # sorted and coalesced.
253
284
  #
254
285
  # - #clear: Removes all elements in the set; returns +self+.
255
286
  # - #delete: Removes a given object from the set; returns +self+.
@@ -685,8 +716,9 @@ module Net
685
716
  modifying!
686
717
  tuple = input_to_tuple object
687
718
  entry = tuple_to_str tuple
719
+ string unless empty? # write @string before tuple_add
688
720
  tuple_add tuple
689
- @string = -(string ? "#{@string},#{entry}" : entry)
721
+ @string = -(@string ? "#{@string},#{entry}" : entry)
690
722
  self
691
723
  end
692
724
 
@@ -842,8 +874,8 @@ module Net
842
874
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
843
875
  # cases to a maximum value.
844
876
  #
845
- # If the original input was unordered or contains overlapping ranges, the
846
- # returned ranges will be ordered and coalesced.
877
+ # The returned elements will be sorted and coalesced, even when the input
878
+ # #string is not. <tt>*</tt> will sort last. See #normalize.
847
879
  #
848
880
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
849
881
  # #=> [2, 5..9, 11..12, :*]
@@ -861,7 +893,7 @@ module Net
861
893
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
862
894
  # value.
863
895
  #
864
- # The returned ranges will be ordered and coalesced, even when the input
896
+ # The returned ranges will be sorted and coalesced, even when the input
865
897
  # #string is not. <tt>*</tt> will sort last. See #normalize.
866
898
  #
867
899
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
@@ -910,9 +942,7 @@ module Net
910
942
  # Related: #entries, #each_element
911
943
  def each_entry(&block) # :yields: integer or range or :*
912
944
  return to_enum(__method__) unless block_given?
913
- return each_element(&block) unless @string
914
- @string.split(",").each do yield tuple_to_entry str_to_tuple _1 end
915
- self
945
+ each_entry_tuple do yield tuple_to_entry _1 end
916
946
  end
917
947
 
918
948
  # Yields each number or range (or <tt>:*</tt>) in #elements to the block
@@ -930,6 +960,16 @@ module Net
930
960
 
931
961
  private
932
962
 
963
+ def each_entry_tuple(&block)
964
+ return to_enum(__method__) unless block_given?
965
+ if @string
966
+ @string.split(",") do block.call str_to_tuple _1 end
967
+ else
968
+ @tuples.each(&block)
969
+ end
970
+ self
971
+ end
972
+
933
973
  def tuple_to_entry((min, max))
934
974
  if min == STAR_INT then :*
935
975
  elsif max == STAR_INT then min..
@@ -961,19 +1001,36 @@ module Net
961
1001
  # Returns an enumerator when called without a block (even if the set
962
1002
  # contains <tt>*</tt>).
963
1003
  #
964
- # Related: #numbers
1004
+ # Related: #numbers, #each_ordered_number
965
1005
  def each_number(&block) # :yields: integer
966
1006
  return to_enum(__method__) unless block_given?
967
1007
  raise RangeError, '%s contains "*"' % [self.class] if include_star?
968
- each_element do |elem|
969
- case elem
970
- when Range then elem.each(&block)
971
- when Integer then block.(elem)
972
- end
973
- end
1008
+ @tuples.each do each_number_in_tuple _1, _2, &block end
974
1009
  self
975
1010
  end
976
1011
 
1012
+ # Yields each number in #entries to the block and returns self.
1013
+ # If the set contains a <tt>*</tt>, RangeError will be raised.
1014
+ #
1015
+ # Returns an enumerator when called without a block (even if the set
1016
+ # contains <tt>*</tt>).
1017
+ #
1018
+ # Related: #entries, #each_number
1019
+ def each_ordered_number(&block)
1020
+ return to_enum(__method__) unless block_given?
1021
+ raise RangeError, '%s contains "*"' % [self.class] if include_star?
1022
+ each_entry_tuple do each_number_in_tuple _1, _2, &block end
1023
+ end
1024
+
1025
+ private def each_number_in_tuple(min, max, &block)
1026
+ if min == STAR_INT then yield :*
1027
+ elsif min == max then yield min
1028
+ elsif max != STAR_INT then (min..max).each(&block)
1029
+ else
1030
+ raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
1031
+ end
1032
+ end
1033
+
977
1034
  # Returns a Set with all of the #numbers in the sequence set.
978
1035
  #
979
1036
  # If the set contains a <tt>*</tt>, RangeError will be raised.
@@ -985,8 +1042,10 @@ module Net
985
1042
 
986
1043
  # Returns the count of #numbers in the set.
987
1044
  #
988
- # If <tt>*</tt> and <tt>2**32 - 1</tt> (the maximum 32-bit unsigned
989
- # integer value) are both in the set, they will only be counted once.
1045
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1046
+ # unsigned integer value).
1047
+ #
1048
+ # Related: #count_with_duplicates
990
1049
  def count
991
1050
  @tuples.sum(@tuples.count) { _2 - _1 } +
992
1051
  (include_star? && include?(UINT32_MAX) ? -1 : 0)
@@ -994,33 +1053,87 @@ module Net
994
1053
 
995
1054
  alias size count
996
1055
 
997
- # Returns the index of +number+ in the set, or +nil+ if +number+ isn't in
998
- # the set.
1056
+ # Returns the count of numbers in the ordered #entries, including any
1057
+ # repeated numbers.
1058
+ #
1059
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1060
+ # unsigned integer value).
1061
+ #
1062
+ # When #string is normalized, this behaves the same as #count.
999
1063
  #
1000
- # Related: #[]
1064
+ # Related: #entries, #count_duplicates, #has_duplicates?
1065
+ def count_with_duplicates
1066
+ return count unless @string
1067
+ each_entry_tuple.sum {|min, max|
1068
+ max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
1069
+ }
1070
+ end
1071
+
1072
+ # Returns the count of repeated numbers in the ordered #entries, the
1073
+ # difference between #count_with_duplicates and #count.
1074
+ #
1075
+ # When #string is normalized, this is zero.
1076
+ #
1077
+ # Related: #entries, #count_with_duplicates, #has_duplicates?
1078
+ def count_duplicates
1079
+ return 0 unless @string
1080
+ count_with_duplicates - count
1081
+ end
1082
+
1083
+ # :call-seq: has_duplicates? -> true | false
1084
+ #
1085
+ # Returns whether or not the ordered #entries repeat any numbers.
1086
+ #
1087
+ # Always returns +false+ when #string is normalized.
1088
+ #
1089
+ # Related: #entries, #count_with_duplicates, #count_duplicates?
1090
+ def has_duplicates?
1091
+ return false unless @string
1092
+ count_with_duplicates != count
1093
+ end
1094
+
1095
+ # Returns the (sorted and deduplicated) index of +number+ in the set, or
1096
+ # +nil+ if +number+ isn't in the set.
1097
+ #
1098
+ # Related: #[], #at, #find_ordered_index
1001
1099
  def find_index(number)
1002
1100
  number = to_tuple_int number
1003
- each_tuple_with_index do |min, max, idx_min|
1101
+ each_tuple_with_index(@tuples) do |min, max, idx_min|
1004
1102
  number < min and return nil
1005
1103
  number <= max and return from_tuple_int(idx_min + (number - min))
1006
1104
  end
1007
1105
  nil
1008
1106
  end
1009
1107
 
1108
+ # Returns the first index of +number+ in the ordered #entries, or
1109
+ # +nil+ if +number+ isn't in the set.
1110
+ #
1111
+ # Related: #find_index
1112
+ def find_ordered_index(number)
1113
+ number = to_tuple_int number
1114
+ each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
1115
+ if min <= number && number <= max
1116
+ return from_tuple_int(idx_min + (number - min))
1117
+ end
1118
+ end
1119
+ nil
1120
+ end
1121
+
1010
1122
  private
1011
1123
 
1012
- def each_tuple_with_index
1124
+ def each_tuple_with_index(tuples)
1013
1125
  idx_min = 0
1014
- @tuples.each do |min, max|
1015
- yield min, max, idx_min, (idx_max = idx_min + (max - min))
1126
+ tuples.each do |min, max|
1127
+ idx_max = idx_min + (max - min)
1128
+ yield min, max, idx_min, idx_max
1016
1129
  idx_min = idx_max + 1
1017
1130
  end
1018
1131
  idx_min
1019
1132
  end
1020
1133
 
1021
- def reverse_each_tuple_with_index
1134
+ def reverse_each_tuple_with_index(tuples)
1022
1135
  idx_max = -1
1023
- @tuples.reverse_each do |min, max|
1136
+ tuples.reverse_each do |min, max|
1024
1137
  yield min, max, (idx_min = idx_max - (max - min)), idx_max
1025
1138
  idx_max = idx_min - 1
1026
1139
  end
@@ -1031,18 +1144,38 @@ module Net
1031
1144
 
1032
1145
  # :call-seq: at(index) -> integer or nil
1033
1146
  #
1034
- # Returns a number from +self+, without modifying the set. Behaves the
1035
- # same as #[], except that #at only allows a single integer argument.
1147
+ # Returns the number at the given +index+ in the sorted set, without
1148
+ # modifying the set.
1036
1149
  #
1037
- # Related: #[], #slice
1150
+ # +index+ is interpreted the same as in #[], except that #at only allows a
1151
+ # single integer argument.
1152
+ #
1153
+ # Related: #[], #slice, #ordered_at
1038
1154
  def at(index)
1155
+ lookup_number_by_tuple_index(tuples, index)
1156
+ end
1157
+
1158
+ # :call-seq: ordered_at(index) -> integer or nil
1159
+ #
1160
+ # Returns the number at the given +index+ in the ordered #entries, without
1161
+ # modifying the set.
1162
+ #
1163
+ # +index+ is interpreted the same as in #at (and #[]), except that
1164
+ # #ordered_at applies to the ordered #entries, not the sorted set.
1165
+ #
1166
+ # Related: #[], #slice, #ordered_at
1167
+ def ordered_at(index)
1168
+ lookup_number_by_tuple_index(each_entry_tuple, index)
1169
+ end
1170
+
1171
+ private def lookup_number_by_tuple_index(tuples, index)
1039
1172
  index = Integer(index.to_int)
1040
1173
  if index.negative?
1041
- reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
1174
+ reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
1042
1175
  idx_min <= index and return from_tuple_int(min + (index - idx_min))
1043
1176
  end
1044
1177
  else
1045
- each_tuple_with_index do |min, _, idx_min, idx_max|
1178
+ each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
1046
1179
  index <= idx_max and return from_tuple_int(min + (index - idx_min))
1047
1180
  end
1048
1181
  end
@@ -1057,17 +1190,18 @@ module Net
1057
1190
  # seqset[range] -> sequence set or nil
1058
1191
  # slice(range) -> sequence set or nil
1059
1192
  #
1060
- # Returns a number or a subset from +self+, without modifying the set.
1193
+ # Returns a number or a subset from the _sorted_ set, without modifying
1194
+ # the set.
1061
1195
  #
1062
1196
  # When an Integer argument +index+ is given, the number at offset +index+
1063
- # is returned:
1197
+ # in the sorted set is returned:
1064
1198
  #
1065
1199
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1066
1200
  # set[0] #=> 10
1067
1201
  # set[5] #=> 15
1068
1202
  # set[10] #=> 26
1069
1203
  #
1070
- # If +index+ is negative, it counts relative to the end of +self+:
1204
+ # If +index+ is negative, it counts relative to the end of the sorted set:
1071
1205
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1072
1206
  # set[-1] #=> 26
1073
1207
  # set[-3] #=> 22
@@ -1079,13 +1213,14 @@ module Net
1079
1213
  # set[11] #=> nil
1080
1214
  # set[-12] #=> nil
1081
1215
  #
1082
- # The result is based on the normalized set—sorted and de-duplicatednot
1083
- # on the assigned value of #string.
1216
+ # The result is based on the sorted and de-duplicated set, not on the
1217
+ # ordered #entries in #string.
1084
1218
  #
1085
1219
  # set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
1086
1220
  # set[0] #=> 11
1087
1221
  # set[-1] #=> 23
1088
1222
  #
1223
+ # Related: #at
1089
1224
  def [](index, length = nil)
1090
1225
  if length then slice_length(index, length)
1091
1226
  elsif index.is_a?(Range) then slice_range(index)
@@ -1341,8 +1476,8 @@ module Net
1341
1476
  modifying!
1342
1477
  min, max = tuple
1343
1478
  lower, lower_idx = tuple_gte_with_index(min - 1)
1344
- if lower.nil? then tuples << tuple
1345
- elsif (max + 1) < lower.first then tuples.insert(lower_idx, tuple)
1479
+ if lower.nil? then tuples << [min, max]
1480
+ elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max])
1346
1481
  else tuple_coalesce(lower, lower_idx, min, max)
1347
1482
  end
1348
1483
  end
@@ -0,0 +1,326 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP < Protocol
5
+
6
+ # *NOTE:* <em>UIDPlusData is deprecated and will be removed in the +0.6.0+
7
+ # release.</em> To use AppendUIDData and CopyUIDData before +0.6.0+, set
8
+ # Config#parser_use_deprecated_uidplus_data to +false+.
9
+ #
10
+ # UIDPlusData represents the ResponseCode#data that accompanies the
11
+ # +APPENDUID+ and +COPYUID+ {response codes}[rdoc-ref:ResponseCode].
12
+ #
13
+ # A server that supports +UIDPLUS+ should send UIDPlusData in response to
14
+ # the append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy],
15
+ # move[rdoc-ref:Net::IMAP#move], {uid copy}[rdoc-ref:Net::IMAP#uid_copy],
16
+ # and {uid move}[rdoc-ref:Net::IMAP#uid_move] commands---unless the
17
+ # destination mailbox reports +UIDNOTSTICKY+.
18
+ #
19
+ # Note that append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy]
20
+ # and {uid_copy}[rdoc-ref:Net::IMAP#uid_copy] return UIDPlusData in their
21
+ # TaggedResponse. But move[rdoc-ref:Net::IMAP#copy] and
22
+ # {uid_move}[rdoc-ref:Net::IMAP#uid_move] _should_ send UIDPlusData in an
23
+ # UntaggedResponse response before sending their TaggedResponse. However
24
+ # some servers do send UIDPlusData in the TaggedResponse for +MOVE+
25
+ # commands---this complies with the older +UIDPLUS+ specification but is
26
+ # discouraged by the +MOVE+ extension and disallowed by +IMAP4rev2+.
27
+ #
28
+ # == Required capability
29
+ # Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
30
+ # or +IMAP4rev2+ capability.
31
+ #
32
+ class UIDPlusData < Struct.new(:uidvalidity, :source_uids, :assigned_uids)
33
+ ##
34
+ # method: uidvalidity
35
+ # :call-seq: uidvalidity -> nonzero uint32
36
+ #
37
+ # The UIDVALIDITY of the destination mailbox.
38
+
39
+ ##
40
+ # method: source_uids
41
+ # :call-seq: source_uids -> nil or an array of nonzero uint32
42
+ #
43
+ # The UIDs of the copied or moved messages.
44
+ #
45
+ # Note:: Returns +nil+ for Net::IMAP#append.
46
+
47
+ ##
48
+ # method: assigned_uids
49
+ # :call-seq: assigned_uids -> an array of nonzero uint32
50
+ #
51
+ # The newly assigned UIDs of the copied, moved, or appended messages.
52
+ #
53
+ # Note:: This always returns an array, even when it contains only one UID.
54
+
55
+ ##
56
+ # :call-seq: uid_mapping -> nil or a hash
57
+ #
58
+ # Returns a hash mapping each source UID to the newly assigned destination
59
+ # UID.
60
+ #
61
+ # Note:: Returns +nil+ for Net::IMAP#append.
62
+ def uid_mapping
63
+ source_uids&.zip(assigned_uids)&.to_h
64
+ end
65
+ end
66
+
67
+ # This replaces the `Data.define` polyfill that's used by net-imap 0.5.
68
+ class Data_define__uidvalidity___assigned_uids_ # :no-doc:
69
+ attr_reader :uidvalidity, :assigned_uids
70
+
71
+ def self.[](...) new(...) end
72
+ def self.new(uidvalidity = (args = false; nil),
73
+ assigned_uids = nil,
74
+ **kwargs)
75
+ if kwargs.empty?
76
+ super(uidvalidity: uidvalidity, assigned_uids: assigned_uids)
77
+ elsif !args
78
+ super
79
+ else
80
+ raise ArgumentError, "sent both positional and keyword args"
81
+ end
82
+ end
83
+
84
+ def ==(other)
85
+ self.class == other.class &&
86
+ self.uidvalidity == other.uidvalidity &&
87
+ self.assigned_uids == other.assigned_uids
88
+ end
89
+
90
+ def eql?(other)
91
+ self.class.eql?(other.class) &&
92
+ self.uidvalidity.eql?(other.uidvalidity) &&
93
+ self.assigned_uids.eql?(other.assigned_uids)
94
+ end
95
+
96
+ def hash; [self.class, uidvalidity, assigned_uids].hash end
97
+
98
+ def initialize(uidvalidity:, assigned_uids:)
99
+ @uidvalidity = uidvalidity
100
+ @assigned_uids = assigned_uids
101
+ freeze
102
+ end
103
+ end
104
+
105
+ # >>>
106
+ # *NOTE:* <em>AppendUIDData will replace UIDPlusData for +APPENDUID+ in the
107
+ # +0.6.0+ release.</em> To use AppendUIDData before +0.6.0+, set
108
+ # Config#parser_use_deprecated_uidplus_data to +false+.
109
+ #
110
+ # AppendUIDData represents the ResponseCode#data that accompanies the
111
+ # +APPENDUID+ {response code}[rdoc-ref:ResponseCode].
112
+ #
113
+ # A server that supports +UIDPLUS+ (or +IMAP4rev2+) should send
114
+ # AppendUIDData inside every TaggedResponse returned by the
115
+ # append[rdoc-ref:Net::IMAP#append] command---unless the target mailbox
116
+ # reports +UIDNOTSTICKY+.
117
+ #
118
+ # == Required capability
119
+ # Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
120
+ # or +IMAP4rev2+ capability.
121
+ class AppendUIDData < Data_define__uidvalidity___assigned_uids_
122
+ def initialize(uidvalidity:, assigned_uids:)
123
+ uidvalidity = Integer(uidvalidity)
124
+ assigned_uids = SequenceSet[assigned_uids]
125
+ NumValidator.ensure_nz_number(uidvalidity)
126
+ if assigned_uids.include_star?
127
+ raise DataFormatError, "uid-set cannot contain '*'"
128
+ end
129
+ super
130
+ end
131
+
132
+ ##
133
+ # attr_reader: uidvalidity
134
+ # :call-seq: uidvalidity -> nonzero uint32
135
+ #
136
+ # The UIDVALIDITY of the destination mailbox.
137
+
138
+ ##
139
+ # attr_reader: assigned_uids
140
+ #
141
+ # A SequenceSet with the newly assigned UIDs of the appended messages.
142
+
143
+ # Returns the number of messages that have been appended.
144
+ def size
145
+ assigned_uids.count_with_duplicates
146
+ end
147
+ end
148
+
149
+ # This replaces the `Data.define` polyfill that's used by net-imap 0.5.
150
+ class Data_define__uidvalidity___source_uids___assigned_uids_ # :no-doc:
151
+ attr_reader :uidvalidity, :source_uids, :assigned_uids
152
+
153
+ def self.[](...) new(...) end
154
+ def self.new(uidvalidity = (args = false; nil),
155
+ source_uids = nil,
156
+ assigned_uids = nil,
157
+ **kwargs)
158
+ if kwargs.empty?
159
+ super(uidvalidity: uidvalidity,
160
+ source_uids: source_uids,
161
+ assigned_uids: assigned_uids)
162
+ elsif !args
163
+ super(**kwargs)
164
+ else
165
+ raise ArgumentError, "sent both positional and keyword args"
166
+ end
167
+ end
168
+
169
+ def initialize(uidvalidity:, source_uids:, assigned_uids:)
170
+ @uidvalidity = uidvalidity
171
+ @source_uids = source_uids
172
+ @assigned_uids = assigned_uids
173
+ freeze
174
+ end
175
+
176
+ def ==(other)
177
+ self.class == other.class &&
178
+ self.uidvalidity == other.uidvalidity &&
179
+ self.source_uids == other.source_uids
180
+ self.assigned_uids == other.assigned_uids
181
+ end
182
+
183
+ def eql?(other)
184
+ self.class.eql?(other.class) &&
185
+ self.uidvalidity.eql?(other.uidvalidity) &&
186
+ self.source_uids.eql?(other.source_uids)
187
+ self.assigned_uids.eql?(other.assigned_uids)
188
+ end
189
+
190
+ def hash; [self.class, uidvalidity, source_uids, assigned_uids].hash end
191
+ end
192
+
193
+ # >>>
194
+ # *NOTE:* <em>CopyUIDData will replace UIDPlusData for +COPYUID+ in the
195
+ # +0.6.0+ release.</em> To use CopyUIDData before +0.6.0+, set
196
+ # Config#parser_use_deprecated_uidplus_data to +false+.
197
+ #
198
+ # CopyUIDData represents the ResponseCode#data that accompanies the
199
+ # +COPYUID+ {response code}[rdoc-ref:ResponseCode].
200
+ #
201
+ # A server that supports +UIDPLUS+ (or +IMAP4rev2+) should send CopyUIDData
202
+ # in response to
203
+ # copy[rdoc-ref:Net::IMAP#copy], {uid_copy}[rdoc-ref:Net::IMAP#uid_copy],
204
+ # move[rdoc-ref:Net::IMAP#copy], and {uid_move}[rdoc-ref:Net::IMAP#uid_move]
205
+ # commands---unless the destination mailbox reports +UIDNOTSTICKY+.
206
+ #
207
+ # Note that copy[rdoc-ref:Net::IMAP#copy] and
208
+ # {uid_copy}[rdoc-ref:Net::IMAP#uid_copy] return CopyUIDData in their
209
+ # TaggedResponse. But move[rdoc-ref:Net::IMAP#copy] and
210
+ # {uid_move}[rdoc-ref:Net::IMAP#uid_move] _should_ send CopyUIDData in an
211
+ # UntaggedResponse response before sending their TaggedResponse. However
212
+ # some servers do send CopyUIDData in the TaggedResponse for +MOVE+
213
+ # commands---this complies with the older +UIDPLUS+ specification but is
214
+ # discouraged by the +MOVE+ extension and disallowed by +IMAP4rev2+.
215
+ #
216
+ # == Required capability
217
+ # Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
218
+ # or +IMAP4rev2+ capability.
219
+ class CopyUIDData < Data_define__uidvalidity___source_uids___assigned_uids_
220
+ def initialize(uidvalidity:, source_uids:, assigned_uids:)
221
+ uidvalidity = Integer(uidvalidity)
222
+ source_uids = SequenceSet[source_uids]
223
+ assigned_uids = SequenceSet[assigned_uids]
224
+ NumValidator.ensure_nz_number(uidvalidity)
225
+ if source_uids.include_star? || assigned_uids.include_star?
226
+ raise DataFormatError, "uid-set cannot contain '*'"
227
+ elsif source_uids.count_with_duplicates != assigned_uids.count_with_duplicates
228
+ raise DataFormatError, "mismatched uid-set sizes for %s and %s" % [
229
+ source_uids, assigned_uids
230
+ ]
231
+ end
232
+ super
233
+ end
234
+
235
+ ##
236
+ # attr_reader: uidvalidity
237
+ #
238
+ # The +UIDVALIDITY+ of the destination mailbox (a nonzero unsigned 32 bit
239
+ # integer).
240
+
241
+ ##
242
+ # attr_reader: source_uids
243
+ #
244
+ # A SequenceSet with the original UIDs of the copied or moved messages.
245
+
246
+ ##
247
+ # attr_reader: assigned_uids
248
+ #
249
+ # A SequenceSet with the newly assigned UIDs of the copied or moved
250
+ # messages.
251
+
252
+ # Returns the number of messages that have been copied or moved.
253
+ # source_uids and the assigned_uids will both the same number of UIDs.
254
+ def size
255
+ assigned_uids.count_with_duplicates
256
+ end
257
+
258
+ # :call-seq:
259
+ # assigned_uid_for(source_uid) -> uid
260
+ # self[source_uid] -> uid
261
+ #
262
+ # Returns the UID in the destination mailbox for the message that was
263
+ # copied from +source_uid+ in the source mailbox.
264
+ #
265
+ # This is the reverse of #source_uid_for.
266
+ #
267
+ # Related: source_uid_for, each_uid_pair, uid_mapping
268
+ def assigned_uid_for(source_uid)
269
+ idx = source_uids.find_ordered_index(source_uid) and
270
+ assigned_uids.ordered_at(idx)
271
+ end
272
+ alias :[] :assigned_uid_for
273
+
274
+ # :call-seq:
275
+ # source_uid_for(assigned_uid) -> uid
276
+ #
277
+ # Returns the UID in the source mailbox for the message that was copied to
278
+ # +assigned_uid+ in the source mailbox.
279
+ #
280
+ # This is the reverse of #assigned_uid_for.
281
+ #
282
+ # Related: assigned_uid_for, each_uid_pair, uid_mapping
283
+ def source_uid_for(assigned_uid)
284
+ idx = assigned_uids.find_ordered_index(assigned_uid) and
285
+ source_uids.ordered_at(idx)
286
+ end
287
+
288
+ # Yields a pair of UIDs for each copied message. The first is the
289
+ # message's UID in the source mailbox and the second is the UID in the
290
+ # destination mailbox.
291
+ #
292
+ # Returns an enumerator when no block is given.
293
+ #
294
+ # Please note the warning on uid_mapping before calling methods like
295
+ # +to_h+ or +to_a+ on the returned enumerator.
296
+ #
297
+ # Related: uid_mapping, assigned_uid_for, source_uid_for
298
+ def each_uid_pair
299
+ return enum_for(__method__) unless block_given?
300
+ source_uids.each_ordered_number.lazy
301
+ .zip(assigned_uids.each_ordered_number.lazy) do
302
+ |source_uid, assigned_uid|
303
+ yield source_uid, assigned_uid
304
+ end
305
+ end
306
+ alias each_pair each_uid_pair
307
+ alias each each_uid_pair
308
+
309
+ # :call-seq: uid_mapping -> hash
310
+ #
311
+ # Returns a hash mapping each source UID to the newly assigned destination
312
+ # UID.
313
+ #
314
+ # <em>*Warning:*</em> The hash that is created may consume _much_ more
315
+ # memory than the data used to create it. When handling responses from an
316
+ # untrusted server, check #size before calling this method.
317
+ #
318
+ # Related: each_uid_pair, assigned_uid_for, source_uid_for
319
+ def uid_mapping
320
+ each_uid_pair.to_h
321
+ end
322
+
323
+ end
324
+
325
+ end
326
+ end
data/lib/net/imap.rb CHANGED
@@ -719,7 +719,7 @@ module Net
719
719
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
720
720
  #
721
721
  class IMAP < Protocol
722
- VERSION = "0.4.18"
722
+ VERSION = "0.4.19"
723
723
 
724
724
  # Aliases for supported capabilities, to be used with the #enable command.
725
725
  ENABLE_ALIASES = {
@@ -1222,13 +1222,21 @@ module Net
1222
1222
  #
1223
1223
  def starttls(**options)
1224
1224
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
1225
- send_command("STARTTLS") do |resp|
1225
+ error = nil
1226
+ ok = send_command("STARTTLS") do |resp|
1226
1227
  if resp.kind_of?(TaggedResponse) && resp.name == "OK"
1227
1228
  clear_cached_capabilities
1228
1229
  clear_responses
1229
1230
  start_tls_session
1230
1231
  end
1232
+ rescue Exception => error
1233
+ raise # note that the error backtrace is in the receiver_thread
1231
1234
  end
1235
+ if error
1236
+ disconnect
1237
+ raise error
1238
+ end
1239
+ ok
1232
1240
  end
1233
1241
 
1234
1242
  # :call-seq:
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.18
4
+ version: 0.4.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  - nicholas a. evans
9
- autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2024-11-08 00:00:00.000000000 Z
11
+ date: 2025-02-07 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: net-protocol
@@ -95,6 +94,7 @@ files:
95
94
  - lib/net/imap/stringprep/saslprep_tables.rb
96
95
  - lib/net/imap/stringprep/tables.rb
97
96
  - lib/net/imap/stringprep/trace.rb
97
+ - lib/net/imap/uidplus_data.rb
98
98
  - net-imap.gemspec
99
99
  - rakelib/benchmarks.rake
100
100
  - rakelib/rdoc.rake
@@ -110,7 +110,6 @@ metadata:
110
110
  homepage_uri: https://github.com/ruby/net-imap
111
111
  source_code_uri: https://github.com/ruby/net-imap
112
112
  changelog_uri: https://github.com/ruby/net-imap/releases
113
- post_install_message:
114
113
  rdoc_options: []
115
114
  require_paths:
116
115
  - lib
@@ -125,8 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
124
  - !ruby/object:Gem::Version
126
125
  version: '0'
127
126
  requirements: []
128
- rubygems_version: 3.5.22
129
- signing_key:
127
+ rubygems_version: 3.6.2
130
128
  specification_version: 4
131
129
  summary: Ruby client api for Internet Message Access Protocol
132
130
  test_files: []