net-imap 0.4.14 → 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.
data/lib/net/imap.rb CHANGED
@@ -43,10 +43,16 @@ module Net
43
43
  # To work on the messages within a mailbox, the client must
44
44
  # first select that mailbox, using either #select or #examine
45
45
  # (for read-only access). Once the client has successfully
46
- # selected a mailbox, they enter the "_selected_" state, and that
46
+ # selected a mailbox, they enter the +selected+ state, and that
47
47
  # mailbox becomes the _current_ mailbox, on which mail-item
48
48
  # related commands implicitly operate.
49
49
  #
50
+ # === Connection state
51
+ #
52
+ # Once an IMAP connection is established, the connection is in one of four
53
+ # states: <tt>not authenticated</tt>, +authenticated+, +selected+, and
54
+ # +logout+. Most commands are valid only in certain states.
55
+ #
50
56
  # === Sequence numbers and UIDs
51
57
  #
52
58
  # Messages have two sorts of identifiers: message sequence
@@ -199,6 +205,42 @@ module Net
199
205
  #
200
206
  # This script invokes the FETCH command and the SEARCH command concurrently.
201
207
  #
208
+ # When running multiple commands, care must be taken to avoid ambiguity. For
209
+ # example, SEARCH responses are ambiguous about which command they are
210
+ # responding to, so search commands should not run simultaneously, unless the
211
+ # server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
212
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
213
+ # §5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
214
+ # other examples of command sequences which should not be pipelined.
215
+ #
216
+ # == Unbounded memory use
217
+ #
218
+ # Net::IMAP reads server responses in a separate receiver thread per client.
219
+ # Unhandled response data is saved to #responses, and response_handlers run
220
+ # inside the receiver thread. See the list of methods for {handling server
221
+ # responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
222
+ #
223
+ # Because the receiver thread continuously reads and saves new responses, some
224
+ # scenarios must be careful to avoid unbounded memory use:
225
+ #
226
+ # * Commands such as #list or #fetch can have an enormous number of responses.
227
+ # * Commands such as #fetch can result in an enormous size per response.
228
+ # * Long-lived connections will gradually accumulate unsolicited server
229
+ # responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
230
+ # * A buggy or untrusted server could send inappropriate responses, which
231
+ # could be very numerous, very large, and very rapid.
232
+ #
233
+ # Use paginated or limited versions of commands whenever possible.
234
+ #
235
+ # Use Config#max_response_size to impose a limit on incoming server responses
236
+ # as they are being read. <em>This is especially important for untrusted
237
+ # servers.</em>
238
+ #
239
+ # Use #add_response_handler to handle responses after each one is received.
240
+ # Use the +response_handlers+ argument to ::new to assign response handlers
241
+ # before the receiver thread is started. Use #extract_responses,
242
+ # #clear_responses, or #responses (with a block) to prune responses.
243
+ #
202
244
  # == Errors
203
245
  #
204
246
  # An \IMAP server can send three different types of responses to indicate
@@ -260,8 +302,9 @@ module Net
260
302
  #
261
303
  # - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
262
304
  # waits for a successful server greeting before the method returns.
305
+ # - #connection_state: Returns the connection state.
263
306
  # - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
264
- # - #logout: Tells the server to end the session. Enters the "_logout_" state.
307
+ # - #logout: Tells the server to end the session. Enters the +logout+ state.
265
308
  # - #disconnect: Disconnects the connection (without sending #logout first).
266
309
  # - #disconnected?: True if the connection has been closed.
267
310
  #
@@ -288,6 +331,8 @@ module Net
288
331
  # pre-authenticated connection.
289
332
  # - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
290
333
  # ResponseCode#data.
334
+ # - #extract_responses: Removes and returns the responses for which the block
335
+ # returns a true value.
291
336
  # - #clear_responses: Deletes unhandled data from #responses and returns it.
292
337
  # - #add_response_handler: Add a block to be called inside the receiver thread
293
338
  # with every server response.
@@ -315,37 +360,36 @@ module Net
315
360
  # <em>In general, #capable? should be used rather than explicitly sending a
316
361
  # +CAPABILITY+ command to the server.</em>
317
362
  # - #noop: Allows the server to send unsolicited untagged #responses.
318
- # - #logout: Tells the server to end the session. Enters the "_logout_" state.
363
+ # - #logout: Tells the server to end the session. Enters the +logout+ state.
319
364
  #
320
365
  # ==== Not Authenticated state
321
366
  #
322
367
  # In addition to the commands for any state, the following commands are valid
323
- # in the "<em>not authenticated</em>" state:
368
+ # in the +not_authenticated+ state:
324
369
  #
325
370
  # - #starttls: Upgrades a clear-text connection to use TLS.
326
371
  #
327
372
  # <em>Requires the +STARTTLS+ capability.</em>
328
373
  # - #authenticate: Identifies the client to the server using the given
329
374
  # {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
330
- # and credentials. Enters the "_authenticated_" state.
375
+ # and credentials. Enters the +authenticated+ state.
331
376
  #
332
377
  # <em>The server should list <tt>"AUTH=#{mechanism}"</tt> capabilities for
333
378
  # supported mechanisms.</em>
334
379
  # - #login: Identifies the client to the server using a plain text password.
335
- # Using #authenticate is generally preferred. Enters the "_authenticated_"
336
- # state.
380
+ # Using #authenticate is preferred. Enters the +authenticated+ state.
337
381
  #
338
382
  # <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
339
383
  #
340
384
  # ==== Authenticated state
341
385
  #
342
386
  # In addition to the commands for any state, the following commands are valid
343
- # in the "_authenticated_" state:
387
+ # in the +authenticated+ state:
344
388
  #
345
389
  # - #enable: Enables backwards incompatible server extensions.
346
390
  # <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
347
- # - #select: Open a mailbox and enter the "_selected_" state.
348
- # - #examine: Open a mailbox read-only, and enter the "_selected_" state.
391
+ # - #select: Open a mailbox and enter the +selected+ state.
392
+ # - #examine: Open a mailbox read-only, and enter the +selected+ state.
349
393
  # - #create: Creates a new mailbox.
350
394
  # - #delete: Permanently remove a mailbox.
351
395
  # - #rename: Change the name of a mailbox.
@@ -367,12 +411,12 @@ module Net
367
411
  #
368
412
  # ==== Selected state
369
413
  #
370
- # In addition to the commands for any state and the "_authenticated_"
371
- # commands, the following commands are valid in the "_selected_" state:
414
+ # In addition to the commands for any state and the +authenticated+
415
+ # commands, the following commands are valid in the +selected+ state:
372
416
  #
373
- # - #close: Closes the mailbox and returns to the "_authenticated_" state,
417
+ # - #close: Closes the mailbox and returns to the +authenticated+ state,
374
418
  # expunging deleted messages, unless the mailbox was opened as read-only.
375
- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
419
+ # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
376
420
  # without expunging any messages.
377
421
  # <em>Requires the +UNSELECT+ or +IMAP4rev2+ capability.</em>
378
422
  # - #expunge: Permanently removes messages which have the Deleted flag set.
@@ -393,7 +437,7 @@ module Net
393
437
  #
394
438
  # ==== Logout state
395
439
  #
396
- # No \IMAP commands are valid in the "_logout_" state. If the socket is still
440
+ # No \IMAP commands are valid in the +logout+ state. If the socket is still
397
441
  # open, Net::IMAP will close it after receiving server confirmation.
398
442
  # Exceptions will be raised by \IMAP commands that have already started and
399
443
  # are waiting for a response, as well as any that are called after logout.
@@ -447,7 +491,7 @@ module Net
447
491
  # ==== RFC3691: +UNSELECT+
448
492
  # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
449
493
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
450
- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
494
+ # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
451
495
  # without expunging any messages.
452
496
  #
453
497
  # ==== RFC4314: +ACL+
@@ -717,7 +761,7 @@ module Net
717
761
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
718
762
  #
719
763
  class IMAP < Protocol
720
- VERSION = "0.4.14"
764
+ VERSION = "0.4.21"
721
765
 
722
766
  # Aliases for supported capabilities, to be used with the #enable command.
723
767
  ENABLE_ALIASES = {
@@ -725,6 +769,7 @@ module Net
725
769
  "UTF8=ONLY" => "UTF8=ACCEPT",
726
770
  }.freeze
727
771
 
772
+ autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__)
728
773
  autoload :SASL, File.expand_path("imap/sasl", __dir__)
729
774
  autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
730
775
  autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
@@ -739,9 +784,11 @@ module Net
739
784
  def self.config; Config.global end
740
785
 
741
786
  # Returns the global debug mode.
787
+ # Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
742
788
  def self.debug; config.debug end
743
789
 
744
790
  # Sets the global debug mode.
791
+ # Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
745
792
  def self.debug=(val)
746
793
  config.debug = val
747
794
  end
@@ -762,7 +809,7 @@ module Net
762
809
  alias default_ssl_port default_tls_port
763
810
  end
764
811
 
765
- # Returns the initial greeting the server, an UntaggedResponse.
812
+ # Returns the initial greeting sent by the server, an UntaggedResponse.
766
813
  attr_reader :greeting
767
814
 
768
815
  # The client configuration. See Net::IMAP::Config.
@@ -771,13 +818,28 @@ module Net
771
818
  # Net::IMAP.config.
772
819
  attr_reader :config
773
820
 
774
- # Seconds to wait until a connection is opened.
775
- # If the IMAP object cannot open a connection within this time,
776
- # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
777
- def open_timeout; config.open_timeout end
821
+ ##
822
+ # :attr_reader: open_timeout
823
+ # Seconds to wait until a connection is opened. Also used by #starttls.
824
+ # Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout].
778
825
 
826
+ ##
827
+ # :attr_reader: idle_response_timeout
779
828
  # Seconds to wait until an IDLE response is received.
780
- def idle_response_timeout; config.idle_response_timeout end
829
+ # Delegates to {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout].
830
+
831
+ ##
832
+ # :attr_accessor: max_response_size
833
+ #
834
+ # The maximum allowed server response size, in bytes.
835
+ # Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
836
+
837
+ # :stopdoc:
838
+ def open_timeout; config.open_timeout end
839
+ def idle_response_timeout; config.idle_response_timeout end
840
+ def max_response_size; config.max_response_size end
841
+ def max_response_size=(val) config.max_response_size = val end
842
+ # :startdoc:
781
843
 
782
844
  # The hostname this client connected to
783
845
  attr_reader :host
@@ -833,6 +895,12 @@ module Net
833
895
  #
834
896
  # See DeprecatedClientOptions.new for deprecated SSL arguments.
835
897
  #
898
+ # [response_handlers]
899
+ # A list of response handlers to be added before the receiver thread is
900
+ # started. This ensures every server response is handled, including the
901
+ # #greeting. Note that the greeting is handled in the current thread, but
902
+ # all other responses are handled in the receiver thread.
903
+ #
836
904
  # [config]
837
905
  # A Net::IMAP::Config object to use as the basis for #config. By default,
838
906
  # the global Net::IMAP.config is used.
@@ -904,7 +972,7 @@ module Net
904
972
  # [Net::IMAP::ByeResponseError]
905
973
  # Connected to the host successfully, but it immediately said goodbye.
906
974
  #
907
- def initialize(host, port: nil, ssl: nil,
975
+ def initialize(host, port: nil, ssl: nil, response_handlers: nil,
908
976
  config: Config.global, **config_options)
909
977
  super()
910
978
  # Config options
@@ -927,6 +995,7 @@ module Net
927
995
  @receiver_thread = nil
928
996
  @receiver_thread_exception = nil
929
997
  @receiver_thread_terminating = false
998
+ response_handlers&.each do add_response_handler(_1) end
930
999
 
931
1000
  # Client Protocol Sender (including state for currently running commands)
932
1001
  @tag_prefix = "RUBY"
@@ -942,6 +1011,7 @@ module Net
942
1011
  # Connection
943
1012
  @tls_verified = false
944
1013
  @sock = tcp_socket(@host, @port)
1014
+ @reader = ResponseReader.new(self, @sock)
945
1015
  start_tls_session if ssl_ctx
946
1016
  start_imap_connection
947
1017
 
@@ -1202,6 +1272,10 @@ module Net
1202
1272
  # both successful. Any error indicates that the connection has not been
1203
1273
  # secured.
1204
1274
  #
1275
+ # After the server agrees to start a TLS connection, this method waits up to
1276
+ # {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
1277
+ # +Net::OpenTimeout+.
1278
+ #
1205
1279
  # *Note:*
1206
1280
  # >>>
1207
1281
  # Any #response_handlers added before STARTTLS should be aware that the
@@ -1220,13 +1294,21 @@ module Net
1220
1294
  #
1221
1295
  def starttls(**options)
1222
1296
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
1223
- send_command("STARTTLS") do |resp|
1297
+ error = nil
1298
+ ok = send_command("STARTTLS") do |resp|
1224
1299
  if resp.kind_of?(TaggedResponse) && resp.name == "OK"
1225
1300
  clear_cached_capabilities
1226
1301
  clear_responses
1227
1302
  start_tls_session
1228
1303
  end
1304
+ rescue Exception => error
1305
+ raise # note that the error backtrace is in the receiver_thread
1229
1306
  end
1307
+ if error
1308
+ disconnect
1309
+ raise error
1310
+ end
1311
+ ok
1230
1312
  end
1231
1313
 
1232
1314
  # :call-seq:
@@ -2494,41 +2576,98 @@ module Net
2494
2576
  end
2495
2577
  end
2496
2578
 
2579
+ RESPONSES_DEPRECATION_MSG =
2580
+ "Pass a type or block to #responses, " \
2581
+ "set config.responses_without_block to :frozen_dup " \
2582
+ "or :silence_deprecation_warning, " \
2583
+ "or use #extract_responses or #clear_responses."
2584
+ private_constant :RESPONSES_DEPRECATION_MSG
2585
+
2497
2586
  # :call-seq:
2587
+ # responses -> hash of {String => Array} (see config.responses_without_block)
2588
+ # responses(type) -> frozen array
2498
2589
  # responses {|hash| ...} -> block result
2499
2590
  # responses(type) {|array| ...} -> block result
2500
2591
  #
2501
- # Yields unhandled responses and returns the result of the block.
2592
+ # Yields or returns unhandled server responses. Unhandled responses are
2593
+ # stored in a hash, with arrays of UntaggedResponse#data keyed by
2594
+ # UntaggedResponse#name and <em>non-+nil+</em> untagged ResponseCode#data
2595
+ # keyed by ResponseCode#name.
2596
+ #
2597
+ # When a block is given, yields unhandled responses and returns the block's
2598
+ # result. Without a block, returns the unhandled responses.
2599
+ #
2600
+ # [With +type+]
2601
+ # Yield or return only the array of responses for that +type+.
2602
+ # When no block is given, the returned array is a frozen copy.
2603
+ # [Without +type+]
2604
+ # Yield or return the entire responses hash.
2605
+ #
2606
+ # When no block is given, the behavior is determined by
2607
+ # Config#responses_without_block:
2608
+ # >>>
2609
+ # [+:silence_deprecation_warning+ <em>(original behavior)</em>]
2610
+ # Returns the mutable responses hash (without any warnings).
2611
+ # <em>This is not thread-safe.</em>
2612
+ #
2613
+ # [+:warn+ <em>(default since +v0.5+)</em>]
2614
+ # Prints a warning and returns the mutable responses hash.
2615
+ # <em>This is not thread-safe.</em>
2502
2616
  #
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.
2617
+ # [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
2618
+ # Returns a frozen copy of the unhandled responses hash, with frozen
2619
+ # array values.
2620
+ #
2621
+ # [+:raise+]
2622
+ # Raise an +ArgumentError+ with the deprecation warning.
2508
2623
  #
2509
2624
  # For example:
2510
2625
  #
2511
2626
  # imap.select("inbox")
2512
- # p imap.responses("EXISTS", &:last)
2627
+ # p imap.responses("EXISTS").last
2513
2628
  # #=> 2
2629
+ # p imap.responses("UIDNEXT", &:last)
2630
+ # #=> 123456
2514
2631
  # p imap.responses("UIDVALIDITY", &:last)
2515
2632
  # #=> 968263756
2633
+ # p imap.responses {|responses|
2634
+ # {
2635
+ # exists: responses.delete("EXISTS").last,
2636
+ # uidnext: responses.delete("UIDNEXT").last,
2637
+ # uidvalidity: responses.delete("UIDVALIDITY").last,
2638
+ # }
2639
+ # }
2640
+ # #=> {:exists=>2, :uidnext=>123456, :uidvalidity=>968263756}
2641
+ # # "EXISTS", "UIDNEXT", and "UIDVALIDITY" have been removed:
2642
+ # p imap.responses(&:keys)
2643
+ # #=> ["FLAGS", "OK", "PERMANENTFLAGS", "RECENT", "HIGHESTMODSEQ"]
2644
+ #
2645
+ # Related: #extract_responses, #clear_responses, #response_handlers, #greeting
2516
2646
  #
2647
+ # ===== Thread safety
2517
2648
  # >>>
2518
2649
  # *Note:* Access to the responses hash is synchronized for thread-safety.
2519
2650
  # The receiver thread and response_handlers cannot process new responses
2520
2651
  # until the block completes. Accessing either the response hash or its
2521
- # response type arrays outside of the block is unsafe.
2652
+ # response type arrays outside of the block is unsafe. They can be safely
2653
+ # updated inside the block. Consider using #clear_responses or
2654
+ # #extract_responses instead.
2522
2655
  #
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.
2656
+ # Net::IMAP will add and remove responses from the responses hash and its
2657
+ # array values, in the calling threads for commands and in the receiver
2658
+ # thread, but will not modify any responses after adding them to the
2659
+ # responses hash.
2660
+ #
2661
+ # ===== Clearing responses
2526
2662
  #
2527
2663
  # Previously unhandled responses are automatically cleared before entering a
2528
2664
  # mailbox with #select or #examine. Long-lived connections can receive many
2529
2665
  # unhandled server responses, which must be pruned or they will continually
2530
2666
  # consume more memory. Update or clear the responses hash or arrays inside
2531
- # the block, or use #clear_responses.
2667
+ # the block, or remove responses with #extract_responses, #clear_responses,
2668
+ # or #add_response_handler.
2669
+ #
2670
+ # ===== Missing responses
2532
2671
  #
2533
2672
  # Only non-+nil+ data is stored. Many important response codes have no data
2534
2673
  # of their own, but are used as "tags" on the ResponseText object they are
@@ -2539,19 +2678,24 @@ module Net
2539
2678
  # ResponseCode#data on tagged responses. Although some command methods do
2540
2679
  # return the TaggedResponse directly, #add_response_handler must be used to
2541
2680
  # handle all response codes.
2542
- #
2543
- # Related: #clear_responses, #response_handlers, #greeting
2544
2681
  def responses(type = nil)
2545
2682
  if block_given?
2546
2683
  synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
2547
2684
  elsif type
2548
- raise ArgumentError, "Pass a block or use #clear_responses"
2685
+ synchronize { @responses[type.to_s.upcase].dup.freeze }
2549
2686
  else
2550
2687
  case config.responses_without_block
2551
2688
  when :raise
2552
- raise ArgumentError, "Pass a block or use #clear_responses"
2689
+ raise ArgumentError, RESPONSES_DEPRECATION_MSG
2553
2690
  when :warn
2554
- warn("DEPRECATED: pass a block or use #clear_responses", uplevel: 1)
2691
+ warn(RESPONSES_DEPRECATION_MSG, uplevel: 1)
2692
+ when :frozen_dup
2693
+ synchronize {
2694
+ responses = @responses.transform_values(&:freeze)
2695
+ responses.default_proc = nil
2696
+ responses.default = [].freeze
2697
+ return responses.freeze
2698
+ }
2555
2699
  end
2556
2700
  @responses
2557
2701
  end
@@ -2567,7 +2711,7 @@ module Net
2567
2711
  # Clearing responses is synchronized with other threads. The lock is
2568
2712
  # released before returning.
2569
2713
  #
2570
- # Related: #responses, #response_handlers
2714
+ # Related: #extract_responses, #responses, #response_handlers
2571
2715
  def clear_responses(type = nil)
2572
2716
  synchronize {
2573
2717
  if type
@@ -2581,6 +2725,30 @@ module Net
2581
2725
  .freeze
2582
2726
  end
2583
2727
 
2728
+ # :call-seq:
2729
+ # extract_responses(type) {|response| ... } -> array
2730
+ #
2731
+ # Yields all of the unhandled #responses for a single response +type+.
2732
+ # Removes and returns the responses for which the block returns a true
2733
+ # value.
2734
+ #
2735
+ # Extracting responses is synchronized with other threads. The lock is
2736
+ # released before returning.
2737
+ #
2738
+ # Related: #responses, #clear_responses
2739
+ def extract_responses(type)
2740
+ type = String.try_convert(type) or
2741
+ raise ArgumentError, "type must be a string"
2742
+ raise ArgumentError, "must provide a block" unless block_given?
2743
+ extracted = []
2744
+ responses(type) do |all|
2745
+ all.reject! do |response|
2746
+ extracted << response if yield response
2747
+ end
2748
+ end
2749
+ extracted
2750
+ end
2751
+
2584
2752
  # Returns all response handlers, including those that are added internally
2585
2753
  # by commands. Each response handler will be called with every new
2586
2754
  # UntaggedResponse, TaggedResponse, and ContinuationRequest.
@@ -2610,6 +2778,10 @@ module Net
2610
2778
  # end
2611
2779
  # }
2612
2780
  #
2781
+ # Response handlers can also be added when the client is created before the
2782
+ # receiver thread is started, by the +response_handlers+ argument to ::new.
2783
+ # This ensures every server response is handled, including the #greeting.
2784
+ #
2613
2785
  # Related: #remove_response_handler, #response_handlers
2614
2786
  def add_response_handler(handler = nil, &block)
2615
2787
  raise ArgumentError, "two Procs are passed" if handler && block
@@ -2636,6 +2808,7 @@ module Net
2636
2808
  def start_imap_connection
2637
2809
  @greeting = get_server_greeting
2638
2810
  @capabilities = capabilities_from_resp_code @greeting
2811
+ @response_handlers.each do |handler| handler.call(@greeting) end
2639
2812
  @receiver_thread = start_receiver_thread
2640
2813
  rescue Exception
2641
2814
  @sock.close
@@ -2764,23 +2937,10 @@ module Net
2764
2937
  end
2765
2938
 
2766
2939
  def get_response
2767
- buff = String.new
2768
- while true
2769
- s = @sock.gets(CRLF)
2770
- break unless s
2771
- buff.concat(s)
2772
- if /\{(\d+)\}\r\n/n =~ s
2773
- s = @sock.read($1.to_i)
2774
- buff.concat(s)
2775
- else
2776
- break
2777
- end
2778
- end
2940
+ buff = @reader.read_response_buffer
2779
2941
  return nil if buff.length == 0
2780
- if config.debug?
2781
- $stderr.print(buff.gsub(/^/n, "S: "))
2782
- end
2783
- return @parser.parse(buff)
2942
+ $stderr.print(buff.gsub(/^/n, "S: ")) if config.debug?
2943
+ @parser.parse(buff)
2784
2944
  end
2785
2945
 
2786
2946
  #############################
@@ -2981,6 +3141,7 @@ module Net
2981
3141
  raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
2982
3142
  raise "cannot start TLS without SSLContext" unless ssl_ctx
2983
3143
  @sock = SSLSocket.new(@sock, ssl_ctx)
3144
+ @reader = ResponseReader.new(self, @sock)
2984
3145
  @sock.sync_close = true
2985
3146
  @sock.hostname = @host if @sock.respond_to? :hostname=
2986
3147
  ssl_socket_connect(@sock, open_timeout)
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.14
4
+ version: 0.4.21
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-06-22 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: net-protocol
@@ -69,6 +68,7 @@ files:
69
68
  - lib/net/imap/response_data.rb
70
69
  - lib/net/imap/response_parser.rb
71
70
  - lib/net/imap/response_parser/parser_utils.rb
71
+ - lib/net/imap/response_reader.rb
72
72
  - lib/net/imap/sasl.rb
73
73
  - lib/net/imap/sasl/anonymous_authenticator.rb
74
74
  - lib/net/imap/sasl/authentication_exchange.rb
@@ -95,6 +95,7 @@ files:
95
95
  - lib/net/imap/stringprep/saslprep_tables.rb
96
96
  - lib/net/imap/stringprep/tables.rb
97
97
  - lib/net/imap/stringprep/trace.rb
98
+ - lib/net/imap/uidplus_data.rb
98
99
  - net-imap.gemspec
99
100
  - rakelib/benchmarks.rake
100
101
  - rakelib/rdoc.rake
@@ -110,7 +111,6 @@ metadata:
110
111
  homepage_uri: https://github.com/ruby/net-imap
111
112
  source_code_uri: https://github.com/ruby/net-imap
112
113
  changelog_uri: https://github.com/ruby/net-imap/releases
113
- post_install_message:
114
114
  rdoc_options: []
115
115
  require_paths:
116
116
  - lib
@@ -125,8 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubygems_version: 3.5.9
129
- signing_key:
128
+ rubygems_version: 3.6.7
130
129
  specification_version: 4
131
130
  summary: Ruby client api for Internet Message Access Protocol
132
131
  test_files: []