net-imap 0.4.16 → 0.5.0

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.

@@ -14,13 +14,6 @@ module Net
14
14
  # receive a SequenceSet as an argument, for example IMAP#search, IMAP#fetch,
15
15
  # and IMAP#store.
16
16
  #
17
- # == EXPERIMENTAL API
18
- #
19
- # SequenceSet is currently experimental. Only two methods, ::[] and
20
- # #valid_string, are considered stable. Although the API isn't expected to
21
- # change much, any other methods may be removed or changed without
22
- # deprecation.
23
- #
24
17
  # == Creating sequence sets
25
18
  #
26
19
  # SequenceSet.new with no arguments creates an empty sequence set. Note
@@ -37,7 +30,8 @@ module Net
37
30
  #
38
31
  # SequenceSet.new may receive a single optional argument: a non-zero 32 bit
39
32
  # unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
40
- # another sequence set, or an enumerable containing any of these.
33
+ # another sequence set, a Set (containing only numbers or <tt>*</tt>), or an
34
+ # Array containing any of these (array inputs may be nested).
41
35
  #
42
36
  # set = Net::IMAP::SequenceSet.new(1)
43
37
  # set.valid_string #=> "1"
@@ -289,8 +283,7 @@ module Net
289
283
  private_constant :STAR_INT, :STARS
290
284
 
291
285
  COERCIBLE = ->{ _1.respond_to? :to_sequence_set }
292
- ENUMABLE = ->{ _1.respond_to?(:each) && _1.respond_to?(:empty?) }
293
- private_constant :COERCIBLE, :ENUMABLE
286
+ private_constant :COERCIBLE
294
287
 
295
288
  class << self
296
289
 
@@ -304,7 +297,7 @@ module Net
304
297
  # Use ::new to create a mutable or empty SequenceSet.
305
298
  def [](first, *rest)
306
299
  if rest.empty?
307
- if first.is_a?(SequenceSet) && set.frozen? && set.valid?
300
+ if first.is_a?(SequenceSet) && first.frozen? && first.valid?
308
301
  first
309
302
  else
310
303
  new(first).validate.freeze
@@ -682,6 +675,7 @@ module Net
682
675
  # Unlike #add, #merge, or #union, the new value is appended to #string.
683
676
  # This may result in a #string which has duplicates or is out-of-order.
684
677
  def append(object)
678
+ modifying!
685
679
  tuple = input_to_tuple object
686
680
  entry = tuple_to_str tuple
687
681
  tuple_add tuple
@@ -1271,7 +1265,8 @@ module Net
1271
1265
  when *STARS, Integer, Range then [input_to_tuple(obj)]
1272
1266
  when String then str_to_tuples obj
1273
1267
  when SequenceSet then obj.tuples
1274
- when ENUMABLE then obj.flat_map { input_to_tuples _1 }
1268
+ when Set then obj.map { [to_tuple_int(_1)] * 2 }
1269
+ when Array then obj.flat_map { input_to_tuples _1 }
1275
1270
  when nil then []
1276
1271
  else
1277
1272
  raise DataFormatError,
@@ -1284,8 +1279,7 @@ module Net
1284
1279
  # String, Set, Array, or... any type of object.
1285
1280
  def input_try_convert(input)
1286
1281
  SequenceSet.try_convert(input) ||
1287
- # Integer.try_convert(input) || # ruby 3.1+
1288
- input.respond_to?(:to_int) && Integer(input.to_int) ||
1282
+ Integer.try_convert(input) ||
1289
1283
  String.try_convert(input) ||
1290
1284
  input
1291
1285
  end
@@ -1317,6 +1311,12 @@ module Net
1317
1311
  range.include?(min) || range.include?(max) || (min..max).cover?(range)
1318
1312
  end
1319
1313
 
1314
+ def modifying!
1315
+ if frozen?
1316
+ raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
1317
+ end
1318
+ end
1319
+
1320
1320
  def tuples_add(tuples) tuples.each do tuple_add _1 end; self end
1321
1321
  def tuples_subtract(tuples) tuples.each do tuple_subtract _1 end; self end
1322
1322
 
@@ -1331,6 +1331,7 @@ module Net
1331
1331
  # ---------??===lower==|--|==|----|===upper===|-- join until upper
1332
1332
  # ---------??===lower==|--|==|--|=====upper===|-- join to upper
1333
1333
  def tuple_add(tuple)
1334
+ modifying!
1334
1335
  min, max = tuple
1335
1336
  lower, lower_idx = tuple_gte_with_index(min - 1)
1336
1337
  if lower.nil? then tuples << tuple
@@ -1367,6 +1368,7 @@ module Net
1367
1368
  # -------??=====lower====|--|====|---|====upper====|-- 7. delete until
1368
1369
  # -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
1369
1370
  def tuple_subtract(tuple)
1371
+ modifying!
1370
1372
  min, max = tuple
1371
1373
  lower, idx = tuple_gte_with_index(min)
1372
1374
  if lower.nil? then nil # case 1.
@@ -1407,12 +1409,11 @@ module Net
1407
1409
  end
1408
1410
 
1409
1411
  def nz_number(num)
1410
- case num
1411
- when Integer, /\A[1-9]\d*\z/ then num = Integer(num)
1412
- else raise DataFormatError, "%p is not a valid nz-number" % [num]
1413
- end
1414
- NumValidator.ensure_nz_number(num)
1415
- num
1412
+ String === num && !/\A[1-9]\d*\z/.match?(num) and
1413
+ raise DataFormatError, "%p is not a valid nz-number" % [num]
1414
+ NumValidator.ensure_nz_number Integer num
1415
+ rescue TypeError # To catch errors from Integer()
1416
+ raise DataFormatError, $!.message
1416
1417
  end
1417
1418
 
1418
1419
  # intentionally defined after the class implementation
data/lib/net/imap.rb CHANGED
@@ -288,6 +288,8 @@ module Net
288
288
  # pre-authenticated connection.
289
289
  # - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
290
290
  # ResponseCode#data.
291
+ # - #extract_responses: Removes and returns the responses for which the block
292
+ # returns a true value.
291
293
  # - #clear_responses: Deletes unhandled data from #responses and returns it.
292
294
  # - #add_response_handler: Add a block to be called inside the receiver thread
293
295
  # with every server response.
@@ -717,7 +719,7 @@ module Net
717
719
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
718
720
  #
719
721
  class IMAP < Protocol
720
- VERSION = "0.4.16"
722
+ VERSION = "0.5.0"
721
723
 
722
724
  # Aliases for supported capabilities, to be used with the #enable command.
723
725
  ENABLE_ALIASES = {
@@ -944,9 +946,6 @@ module Net
944
946
  @sock = tcp_socket(@host, @port)
945
947
  start_tls_session if ssl_ctx
946
948
  start_imap_connection
947
-
948
- # DEPRECATED: to remove in next version
949
- @client_thread = Thread.current
950
949
  end
951
950
 
952
951
  # Returns true after the TLS negotiation has completed and the remote
@@ -954,11 +953,6 @@ module Net
954
953
  # but peer verification was disabled.
955
954
  def tls_verified?; @tls_verified end
956
955
 
957
- def client_thread # :nodoc:
958
- warn "Net::IMAP#client_thread is deprecated and will be removed soon."
959
- @client_thread
960
- end
961
-
962
956
  # Disconnects from the server.
963
957
  #
964
958
  # Related: #logout, #logout!
@@ -1242,6 +1236,9 @@ module Net
1242
1236
  # +SASL-IR+ capability, below). Defaults to the #config value for
1243
1237
  # {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
1244
1238
  #
1239
+ # The +registry+ kwarg can be used to select the mechanism implementation
1240
+ # from a custom registry. See SASL.authenticator and SASL::Authenticators.
1241
+ #
1245
1242
  # All other arguments are forwarded to the registered SASL authenticator for
1246
1243
  # the requested mechanism. <em>The documentation for each individual
1247
1244
  # mechanism must be consulted for its specific parameters.</em>
@@ -1336,29 +1333,9 @@ module Net
1336
1333
  # Previously cached #capabilities will be cleared when this method
1337
1334
  # completes. If the TaggedResponse to #authenticate includes updated
1338
1335
  # capabilities, they will be cached.
1339
- def authenticate(mechanism, *creds,
1340
- sasl_ir: config.sasl_ir,
1341
- **props, &callback)
1342
- mechanism = mechanism.to_s.tr("_", "-").upcase
1343
- authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
1344
- cmdargs = ["AUTHENTICATE", mechanism]
1345
- if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
1346
- authenticator.respond_to?(:initial_response?) &&
1347
- authenticator.initial_response?
1348
- response = authenticator.process(nil)
1349
- cmdargs << (response.empty? ? "=" : [response].pack("m0"))
1350
- end
1351
- result = send_command_with_continuations(*cmdargs) {|data|
1352
- challenge = data.unpack1("m")
1353
- response = authenticator.process challenge
1354
- [response].pack("m0")
1355
- }
1356
- if authenticator.respond_to?(:done?) && !authenticator.done?
1357
- logout!
1358
- raise SASL::AuthenticationIncomplete, result
1359
- end
1360
- @capabilities = capabilities_from_resp_code result
1361
- result
1336
+ def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
1337
+ sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
1338
+ .tap { @capabilities = capabilities_from_resp_code _1 }
1362
1339
  end
1363
1340
 
1364
1341
  # Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
@@ -1378,13 +1355,9 @@ module Net
1378
1355
  # ===== Capabilities
1379
1356
  #
1380
1357
  # An IMAP client MUST NOT call #login when the server advertises the
1381
- # +LOGINDISABLED+ capability.
1382
- #
1383
- # if imap.capability? "LOGINDISABLED"
1384
- # raise "Remote server has disabled the login command"
1385
- # else
1386
- # imap.login username, password
1387
- # end
1358
+ # +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
1359
+ # LoginDisabledError when that capability is present. See
1360
+ # Config#enforce_logindisabled.
1388
1361
  #
1389
1362
  # Server capabilities may change after #starttls, #login, and #authenticate.
1390
1363
  # Cached capabilities _must_ be invalidated after this method completes.
@@ -1392,6 +1365,9 @@ module Net
1392
1365
  # ResponseCode.
1393
1366
  #
1394
1367
  def login(user, password)
1368
+ if enforce_logindisabled? && capability?("LOGINDISABLED")
1369
+ raise LoginDisabledError
1370
+ end
1395
1371
  send_command("LOGIN", user, password)
1396
1372
  .tap { @capabilities = capabilities_from_resp_code _1 }
1397
1373
  end
@@ -1948,7 +1924,7 @@ module Net
1948
1924
  # [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
1949
1925
  def uid_expunge(uid_set)
1950
1926
  synchronize do
1951
- send_command("UID EXPUNGE", MessageSet.new(uid_set))
1927
+ send_command("UID EXPUNGE", SequenceSet.new(uid_set))
1952
1928
  clear_responses("EXPUNGE")
1953
1929
  end
1954
1930
  end
@@ -2494,41 +2470,98 @@ module Net
2494
2470
  end
2495
2471
  end
2496
2472
 
2473
+ RESPONSES_DEPRECATION_MSG =
2474
+ "Pass a type or block to #responses, " \
2475
+ "set config.responses_without_block to :frozen_dup " \
2476
+ "or :silence_deprecation_warning, " \
2477
+ "or use #extract_responses or #clear_responses."
2478
+ private_constant :RESPONSES_DEPRECATION_MSG
2479
+
2497
2480
  # :call-seq:
2481
+ # responses -> hash of {String => Array} (see config.responses_without_block)
2482
+ # responses(type) -> frozen array
2498
2483
  # responses {|hash| ...} -> block result
2499
2484
  # responses(type) {|array| ...} -> block result
2500
2485
  #
2501
- # Yields unhandled responses and returns the result of the block.
2486
+ # Yields or returns unhandled server responses. Unhandled responses are
2487
+ # stored in a hash, with arrays of UntaggedResponse#data keyed by
2488
+ # UntaggedResponse#name and <em>non-+nil+</em> untagged ResponseCode#data
2489
+ # keyed by ResponseCode#name.
2490
+ #
2491
+ # When a block is given, yields unhandled responses and returns the block's
2492
+ # result. Without a block, returns the unhandled responses.
2493
+ #
2494
+ # [With +type+]
2495
+ # Yield or return only the array of responses for that +type+.
2496
+ # When no block is given, the returned array is a frozen copy.
2497
+ # [Without +type+]
2498
+ # Yield or return the entire responses hash.
2499
+ #
2500
+ # When no block is given, the behavior is determined by
2501
+ # Config#responses_without_block:
2502
+ # >>>
2503
+ # [+:silence_deprecation_warning+ <em>(original behavior)</em>]
2504
+ # Returns the mutable responses hash (without any warnings).
2505
+ # <em>This is not thread-safe.</em>
2506
+ #
2507
+ # [+:warn+ <em>(default since +v0.5+)</em>]
2508
+ # Prints a warning and returns the mutable responses hash.
2509
+ # <em>This is not thread-safe.</em>
2502
2510
  #
2503
- # Unhandled responses are stored in a hash, with arrays of
2504
- # <em>non-+nil+</em> UntaggedResponse#data keyed by UntaggedResponse#name
2505
- # and ResponseCode#data keyed by ResponseCode#name. Call without +type+ to
2506
- # yield the entire responses hash. Call with +type+ to yield only the array
2507
- # of responses for that type.
2511
+ # [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
2512
+ # Returns a frozen copy of the unhandled responses hash, with frozen
2513
+ # array values.
2514
+ #
2515
+ # [+:raise+]
2516
+ # Raise an +ArgumentError+ with the deprecation warning.
2508
2517
  #
2509
2518
  # For example:
2510
2519
  #
2511
2520
  # imap.select("inbox")
2512
- # p imap.responses("EXISTS", &:last)
2521
+ # p imap.responses("EXISTS").last
2513
2522
  # #=> 2
2523
+ # p imap.responses("UIDNEXT", &:last)
2524
+ # #=> 123456
2514
2525
  # p imap.responses("UIDVALIDITY", &:last)
2515
2526
  # #=> 968263756
2527
+ # p imap.responses {|responses|
2528
+ # {
2529
+ # exists: responses.delete("EXISTS").last,
2530
+ # uidnext: responses.delete("UIDNEXT").last,
2531
+ # uidvalidity: responses.delete("UIDVALIDITY").last,
2532
+ # }
2533
+ # }
2534
+ # #=> {:exists=>2, :uidnext=>123456, :uidvalidity=>968263756}
2535
+ # # "EXISTS", "UIDNEXT", and "UIDVALIDITY" have been removed:
2536
+ # p imap.responses(&:keys)
2537
+ # #=> ["FLAGS", "OK", "PERMANENTFLAGS", "RECENT", "HIGHESTMODSEQ"]
2538
+ #
2539
+ # Related: #extract_responses, #clear_responses, #response_handlers, #greeting
2516
2540
  #
2541
+ # ===== Thread safety
2517
2542
  # >>>
2518
2543
  # *Note:* Access to the responses hash is synchronized for thread-safety.
2519
2544
  # The receiver thread and response_handlers cannot process new responses
2520
2545
  # until the block completes. Accessing either the response hash or its
2521
- # response type arrays outside of the block is unsafe.
2546
+ # response type arrays outside of the block is unsafe. They can be safely
2547
+ # updated inside the block. Consider using #clear_responses or
2548
+ # #extract_responses instead.
2522
2549
  #
2523
- # Calling without a block is unsafe and deprecated. Future releases will
2524
- # raise ArgumentError unless a block is given.
2525
- # See Config#responses_without_block.
2550
+ # Net::IMAP will add and remove responses from the responses hash and its
2551
+ # array values, in the calling threads for commands and in the receiver
2552
+ # thread, but will not modify any responses after adding them to the
2553
+ # responses hash.
2554
+ #
2555
+ # ===== Clearing responses
2526
2556
  #
2527
2557
  # Previously unhandled responses are automatically cleared before entering a
2528
2558
  # mailbox with #select or #examine. Long-lived connections can receive many
2529
2559
  # unhandled server responses, which must be pruned or they will continually
2530
2560
  # consume more memory. Update or clear the responses hash or arrays inside
2531
- # the block, or use #clear_responses.
2561
+ # the block, or remove responses with #extract_responses, #clear_responses,
2562
+ # or #add_response_handler.
2563
+ #
2564
+ # ===== Missing responses
2532
2565
  #
2533
2566
  # Only non-+nil+ data is stored. Many important response codes have no data
2534
2567
  # of their own, but are used as "tags" on the ResponseText object they are
@@ -2539,19 +2572,24 @@ module Net
2539
2572
  # ResponseCode#data on tagged responses. Although some command methods do
2540
2573
  # return the TaggedResponse directly, #add_response_handler must be used to
2541
2574
  # handle all response codes.
2542
- #
2543
- # Related: #clear_responses, #response_handlers, #greeting
2544
2575
  def responses(type = nil)
2545
2576
  if block_given?
2546
2577
  synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
2547
2578
  elsif type
2548
- raise ArgumentError, "Pass a block or use #clear_responses"
2579
+ synchronize { @responses[type.to_s.upcase].dup.freeze }
2549
2580
  else
2550
2581
  case config.responses_without_block
2551
2582
  when :raise
2552
- raise ArgumentError, "Pass a block or use #clear_responses"
2583
+ raise ArgumentError, RESPONSES_DEPRECATION_MSG
2553
2584
  when :warn
2554
- warn("DEPRECATED: pass a block or use #clear_responses", uplevel: 1)
2585
+ warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
2586
+ when :frozen_dup
2587
+ synchronize {
2588
+ responses = @responses.transform_values(&:freeze)
2589
+ responses.default_proc = nil
2590
+ responses.default = [].freeze
2591
+ return responses.freeze
2592
+ }
2555
2593
  end
2556
2594
  @responses
2557
2595
  end
@@ -2567,7 +2605,7 @@ module Net
2567
2605
  # Clearing responses is synchronized with other threads. The lock is
2568
2606
  # released before returning.
2569
2607
  #
2570
- # Related: #responses, #response_handlers
2608
+ # Related: #extract_responses, #responses, #response_handlers
2571
2609
  def clear_responses(type = nil)
2572
2610
  synchronize {
2573
2611
  if type
@@ -2581,6 +2619,30 @@ module Net
2581
2619
  .freeze
2582
2620
  end
2583
2621
 
2622
+ # :call-seq:
2623
+ # extract_responses(type) {|response| ... } -> array
2624
+ #
2625
+ # Yields all of the unhandled #responses for a single response +type+.
2626
+ # Removes and returns the responses for which the block returns a true
2627
+ # value.
2628
+ #
2629
+ # Extracting responses is synchronized with other threads. The lock is
2630
+ # released before returning.
2631
+ #
2632
+ # Related: #responses, #clear_responses
2633
+ def extract_responses(type)
2634
+ type = String.try_convert(type) or
2635
+ raise ArgumentError, "type must be a string"
2636
+ raise ArgumentError, "must provide a block" unless block_given?
2637
+ extracted = []
2638
+ responses(type) do |all|
2639
+ all.reject! do |response|
2640
+ extracted << response if yield response
2641
+ end
2642
+ end
2643
+ extracted
2644
+ end
2645
+
2584
2646
  # Returns all response handlers, including those that are added internally
2585
2647
  # by commands. Each response handler will be called with every new
2586
2648
  # UntaggedResponse, TaggedResponse, and ContinuationRequest.
@@ -2869,6 +2931,14 @@ module Net
2869
2931
  end
2870
2932
  end
2871
2933
 
2934
+ def enforce_logindisabled?
2935
+ if config.enforce_logindisabled == :when_capabilities_cached
2936
+ capabilities_cached?
2937
+ else
2938
+ config.enforce_logindisabled
2939
+ end
2940
+ end
2941
+
2872
2942
  def search_internal(cmd, keys, charset)
2873
2943
  if keys.instance_of?(String)
2874
2944
  keys = [RawData.new(keys)]
@@ -2902,9 +2972,9 @@ module Net
2902
2972
  synchronize do
2903
2973
  clear_responses("FETCH")
2904
2974
  if mod
2905
- send_command(cmd, MessageSet.new(set), attr, mod)
2975
+ send_command(cmd, SequenceSet.new(set), attr, mod)
2906
2976
  else
2907
- send_command(cmd, MessageSet.new(set), attr)
2977
+ send_command(cmd, SequenceSet.new(set), attr)
2908
2978
  end
2909
2979
  clear_responses("FETCH")
2910
2980
  end
@@ -2912,7 +2982,7 @@ module Net
2912
2982
 
2913
2983
  def store_internal(cmd, set, attr, flags, unchangedsince: nil)
2914
2984
  attr = RawData.new(attr) if attr.instance_of?(String)
2915
- args = [MessageSet.new(set)]
2985
+ args = [SequenceSet.new(set)]
2916
2986
  args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
2917
2987
  args << attr << flags
2918
2988
  synchronize do
@@ -2923,7 +2993,7 @@ module Net
2923
2993
  end
2924
2994
 
2925
2995
  def copy_internal(cmd, set, mailbox)
2926
- send_command(cmd, MessageSet.new(set), mailbox)
2996
+ send_command(cmd, SequenceSet.new(set), mailbox)
2927
2997
  end
2928
2998
 
2929
2999
  def sort_internal(cmd, sort_keys, search_keys, charset)
@@ -2954,7 +3024,7 @@ module Net
2954
3024
  keys.collect! do |i|
2955
3025
  case i
2956
3026
  when -1, Range, Array
2957
- MessageSet.new(i)
3027
+ SequenceSet.new(i)
2958
3028
  else
2959
3029
  i
2960
3030
  end
data/net-imap.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.summary = %q{Ruby client api for Internet Message Access Protocol}
17
17
  spec.description = %q{Ruby client api for Internet Message Access Protocol}
18
18
  spec.homepage = "https://github.com/ruby/net-imap"
19
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.3")
19
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
20
20
  spec.licenses = ["Ruby", "BSD-2-Clause"]
21
21
 
22
22
  spec.metadata["homepage_uri"] = spec.homepage
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.4.16
4
+ version: 0.5.0
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-09-04 00:00:00.000000000 Z
12
+ date: 2024-10-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-protocol
@@ -118,14 +118,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
118
118
  requirements:
119
119
  - - ">="
120
120
  - !ruby/object:Gem::Version
121
- version: 2.7.3
121
+ version: 3.1.0
122
122
  required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  requirements:
124
124
  - - ">="
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubygems_version: 3.5.9
128
+ rubygems_version: 3.5.16
129
129
  signing_key:
130
130
  specification_version: 4
131
131
  summary: Ruby client api for Internet Message Access Protocol