net-imap 0.4.22 → 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.

data/lib/net/imap.rb CHANGED
@@ -43,16 +43,10 @@ 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
- #
56
50
  # === Sequence numbers and UIDs
57
51
  #
58
52
  # Messages have two sorts of identifiers: message sequence
@@ -205,42 +199,6 @@ module Net
205
199
  #
206
200
  # This script invokes the FETCH command and the SEARCH command concurrently.
207
201
  #
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
- #
244
202
  # == Errors
245
203
  #
246
204
  # An \IMAP server can send three different types of responses to indicate
@@ -302,9 +260,8 @@ module Net
302
260
  #
303
261
  # - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
304
262
  # waits for a successful server greeting before the method returns.
305
- # - #connection_state: Returns the connection state.
306
263
  # - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
307
- # - #logout: Tells the server to end the session. Enters the +logout+ state.
264
+ # - #logout: Tells the server to end the session. Enters the "_logout_" state.
308
265
  # - #disconnect: Disconnects the connection (without sending #logout first).
309
266
  # - #disconnected?: True if the connection has been closed.
310
267
  #
@@ -360,36 +317,37 @@ module Net
360
317
  # <em>In general, #capable? should be used rather than explicitly sending a
361
318
  # +CAPABILITY+ command to the server.</em>
362
319
  # - #noop: Allows the server to send unsolicited untagged #responses.
363
- # - #logout: Tells the server to end the session. Enters the +logout+ state.
320
+ # - #logout: Tells the server to end the session. Enters the "_logout_" state.
364
321
  #
365
322
  # ==== Not Authenticated state
366
323
  #
367
324
  # In addition to the commands for any state, the following commands are valid
368
- # in the +not_authenticated+ state:
325
+ # in the "<em>not authenticated</em>" state:
369
326
  #
370
327
  # - #starttls: Upgrades a clear-text connection to use TLS.
371
328
  #
372
329
  # <em>Requires the +STARTTLS+ capability.</em>
373
330
  # - #authenticate: Identifies the client to the server using the given
374
331
  # {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
375
- # and credentials. Enters the +authenticated+ state.
332
+ # and credentials. Enters the "_authenticated_" state.
376
333
  #
377
334
  # <em>The server should list <tt>"AUTH=#{mechanism}"</tt> capabilities for
378
335
  # supported mechanisms.</em>
379
336
  # - #login: Identifies the client to the server using a plain text password.
380
- # Using #authenticate is preferred. Enters the +authenticated+ state.
337
+ # Using #authenticate is generally preferred. Enters the "_authenticated_"
338
+ # state.
381
339
  #
382
340
  # <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
383
341
  #
384
342
  # ==== Authenticated state
385
343
  #
386
344
  # In addition to the commands for any state, the following commands are valid
387
- # in the +authenticated+ state:
345
+ # in the "_authenticated_" state:
388
346
  #
389
347
  # - #enable: Enables backwards incompatible server extensions.
390
348
  # <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
391
- # - #select: Open a mailbox and enter the +selected+ state.
392
- # - #examine: Open a mailbox read-only, and enter the +selected+ state.
349
+ # - #select: Open a mailbox and enter the "_selected_" state.
350
+ # - #examine: Open a mailbox read-only, and enter the "_selected_" state.
393
351
  # - #create: Creates a new mailbox.
394
352
  # - #delete: Permanently remove a mailbox.
395
353
  # - #rename: Change the name of a mailbox.
@@ -411,12 +369,12 @@ module Net
411
369
  #
412
370
  # ==== Selected state
413
371
  #
414
- # In addition to the commands for any state and the +authenticated+
415
- # commands, the following commands are valid in the +selected+ state:
372
+ # In addition to the commands for any state and the "_authenticated_"
373
+ # commands, the following commands are valid in the "_selected_" state:
416
374
  #
417
- # - #close: Closes the mailbox and returns to the +authenticated+ state,
375
+ # - #close: Closes the mailbox and returns to the "_authenticated_" state,
418
376
  # expunging deleted messages, unless the mailbox was opened as read-only.
419
- # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
377
+ # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
420
378
  # without expunging any messages.
421
379
  # <em>Requires the +UNSELECT+ or +IMAP4rev2+ capability.</em>
422
380
  # - #expunge: Permanently removes messages which have the Deleted flag set.
@@ -437,7 +395,7 @@ module Net
437
395
  #
438
396
  # ==== Logout state
439
397
  #
440
- # No \IMAP commands are valid in the +logout+ state. If the socket is still
398
+ # No \IMAP commands are valid in the "_logout_" state. If the socket is still
441
399
  # open, Net::IMAP will close it after receiving server confirmation.
442
400
  # Exceptions will be raised by \IMAP commands that have already started and
443
401
  # are waiting for a response, as well as any that are called after logout.
@@ -491,7 +449,7 @@ module Net
491
449
  # ==== RFC3691: +UNSELECT+
492
450
  # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
493
451
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
494
- # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
452
+ # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
495
453
  # without expunging any messages.
496
454
  #
497
455
  # ==== RFC4314: +ACL+
@@ -761,7 +719,7 @@ module Net
761
719
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
762
720
  #
763
721
  class IMAP < Protocol
764
- VERSION = "0.4.22"
722
+ VERSION = "0.5.0"
765
723
 
766
724
  # Aliases for supported capabilities, to be used with the #enable command.
767
725
  ENABLE_ALIASES = {
@@ -769,7 +727,6 @@ module Net
769
727
  "UTF8=ONLY" => "UTF8=ACCEPT",
770
728
  }.freeze
771
729
 
772
- autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__)
773
730
  autoload :SASL, File.expand_path("imap/sasl", __dir__)
774
731
  autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
775
732
  autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
@@ -784,11 +741,9 @@ module Net
784
741
  def self.config; Config.global end
785
742
 
786
743
  # Returns the global debug mode.
787
- # Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
788
744
  def self.debug; config.debug end
789
745
 
790
746
  # Sets the global debug mode.
791
- # Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
792
747
  def self.debug=(val)
793
748
  config.debug = val
794
749
  end
@@ -809,7 +764,7 @@ module Net
809
764
  alias default_ssl_port default_tls_port
810
765
  end
811
766
 
812
- # Returns the initial greeting sent by the server, an UntaggedResponse.
767
+ # Returns the initial greeting the server, an UntaggedResponse.
813
768
  attr_reader :greeting
814
769
 
815
770
  # The client configuration. See Net::IMAP::Config.
@@ -818,28 +773,13 @@ module Net
818
773
  # Net::IMAP.config.
819
774
  attr_reader :config
820
775
 
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].
776
+ # Seconds to wait until a connection is opened.
777
+ # If the IMAP object cannot open a connection within this time,
778
+ # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
779
+ def open_timeout; config.open_timeout end
825
780
 
826
- ##
827
- # :attr_reader: idle_response_timeout
828
781
  # Seconds to wait until an IDLE response is received.
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:
782
+ def idle_response_timeout; config.idle_response_timeout end
843
783
 
844
784
  # The hostname this client connected to
845
785
  attr_reader :host
@@ -895,12 +835,6 @@ module Net
895
835
  #
896
836
  # See DeprecatedClientOptions.new for deprecated SSL arguments.
897
837
  #
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
- #
904
838
  # [config]
905
839
  # A Net::IMAP::Config object to use as the basis for #config. By default,
906
840
  # the global Net::IMAP.config is used.
@@ -972,7 +906,7 @@ module Net
972
906
  # [Net::IMAP::ByeResponseError]
973
907
  # Connected to the host successfully, but it immediately said goodbye.
974
908
  #
975
- def initialize(host, port: nil, ssl: nil, response_handlers: nil,
909
+ def initialize(host, port: nil, ssl: nil,
976
910
  config: Config.global, **config_options)
977
911
  super()
978
912
  # Config options
@@ -995,7 +929,6 @@ module Net
995
929
  @receiver_thread = nil
996
930
  @receiver_thread_exception = nil
997
931
  @receiver_thread_terminating = false
998
- response_handlers&.each do add_response_handler(_1) end
999
932
 
1000
933
  # Client Protocol Sender (including state for currently running commands)
1001
934
  @tag_prefix = "RUBY"
@@ -1011,12 +944,8 @@ module Net
1011
944
  # Connection
1012
945
  @tls_verified = false
1013
946
  @sock = tcp_socket(@host, @port)
1014
- @reader = ResponseReader.new(self, @sock)
1015
947
  start_tls_session if ssl_ctx
1016
948
  start_imap_connection
1017
-
1018
- # DEPRECATED: to remove in next version
1019
- @client_thread = Thread.current
1020
949
  end
1021
950
 
1022
951
  # Returns true after the TLS negotiation has completed and the remote
@@ -1024,11 +953,6 @@ module Net
1024
953
  # but peer verification was disabled.
1025
954
  def tls_verified?; @tls_verified end
1026
955
 
1027
- def client_thread # :nodoc:
1028
- warn "Net::IMAP#client_thread is deprecated and will be removed soon."
1029
- @client_thread
1030
- end
1031
-
1032
956
  # Disconnects from the server.
1033
957
  #
1034
958
  # Related: #logout, #logout!
@@ -1272,10 +1196,6 @@ module Net
1272
1196
  # both successful. Any error indicates that the connection has not been
1273
1197
  # secured.
1274
1198
  #
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
- #
1279
1199
  # *Note:*
1280
1200
  # >>>
1281
1201
  # Any #response_handlers added before STARTTLS should be aware that the
@@ -1294,21 +1214,13 @@ module Net
1294
1214
  #
1295
1215
  def starttls(**options)
1296
1216
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
1297
- error = nil
1298
- ok = send_command("STARTTLS") do |resp|
1217
+ send_command("STARTTLS") do |resp|
1299
1218
  if resp.kind_of?(TaggedResponse) && resp.name == "OK"
1300
1219
  clear_cached_capabilities
1301
1220
  clear_responses
1302
1221
  start_tls_session
1303
1222
  end
1304
- rescue Exception => error
1305
- raise # note that the error backtrace is in the receiver_thread
1306
1223
  end
1307
- if error
1308
- disconnect
1309
- raise error
1310
- end
1311
- ok
1312
1224
  end
1313
1225
 
1314
1226
  # :call-seq:
@@ -1324,6 +1236,9 @@ module Net
1324
1236
  # +SASL-IR+ capability, below). Defaults to the #config value for
1325
1237
  # {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
1326
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
+ #
1327
1242
  # All other arguments are forwarded to the registered SASL authenticator for
1328
1243
  # the requested mechanism. <em>The documentation for each individual
1329
1244
  # mechanism must be consulted for its specific parameters.</em>
@@ -1418,29 +1333,9 @@ module Net
1418
1333
  # Previously cached #capabilities will be cleared when this method
1419
1334
  # completes. If the TaggedResponse to #authenticate includes updated
1420
1335
  # capabilities, they will be cached.
1421
- def authenticate(mechanism, *creds,
1422
- sasl_ir: config.sasl_ir,
1423
- **props, &callback)
1424
- mechanism = mechanism.to_s.tr("_", "-").upcase
1425
- authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
1426
- cmdargs = ["AUTHENTICATE", mechanism]
1427
- if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
1428
- authenticator.respond_to?(:initial_response?) &&
1429
- authenticator.initial_response?
1430
- response = authenticator.process(nil)
1431
- cmdargs << (response.empty? ? "=" : [response].pack("m0"))
1432
- end
1433
- result = send_command_with_continuations(*cmdargs) {|data|
1434
- challenge = data.unpack1("m")
1435
- response = authenticator.process challenge
1436
- [response].pack("m0")
1437
- }
1438
- if authenticator.respond_to?(:done?) && !authenticator.done?
1439
- logout!
1440
- raise SASL::AuthenticationIncomplete, result
1441
- end
1442
- @capabilities = capabilities_from_resp_code result
1443
- 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 }
1444
1339
  end
1445
1340
 
1446
1341
  # Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
@@ -1460,13 +1355,9 @@ module Net
1460
1355
  # ===== Capabilities
1461
1356
  #
1462
1357
  # An IMAP client MUST NOT call #login when the server advertises the
1463
- # +LOGINDISABLED+ capability.
1464
- #
1465
- # if imap.capability? "LOGINDISABLED"
1466
- # raise "Remote server has disabled the login command"
1467
- # else
1468
- # imap.login username, password
1469
- # end
1358
+ # +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
1359
+ # LoginDisabledError when that capability is present. See
1360
+ # Config#enforce_logindisabled.
1470
1361
  #
1471
1362
  # Server capabilities may change after #starttls, #login, and #authenticate.
1472
1363
  # Cached capabilities _must_ be invalidated after this method completes.
@@ -1474,6 +1365,9 @@ module Net
1474
1365
  # ResponseCode.
1475
1366
  #
1476
1367
  def login(user, password)
1368
+ if enforce_logindisabled? && capability?("LOGINDISABLED")
1369
+ raise LoginDisabledError
1370
+ end
1477
1371
  send_command("LOGIN", user, password)
1478
1372
  .tap { @capabilities = capabilities_from_resp_code _1 }
1479
1373
  end
@@ -2030,7 +1924,7 @@ module Net
2030
1924
  # [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
2031
1925
  def uid_expunge(uid_set)
2032
1926
  synchronize do
2033
- send_command("UID EXPUNGE", MessageSet.new(uid_set))
1927
+ send_command("UID EXPUNGE", SequenceSet.new(uid_set))
2034
1928
  clear_responses("EXPUNGE")
2035
1929
  end
2036
1930
  end
@@ -2688,7 +2582,7 @@ module Net
2688
2582
  when :raise
2689
2583
  raise ArgumentError, RESPONSES_DEPRECATION_MSG
2690
2584
  when :warn
2691
- warn(RESPONSES_DEPRECATION_MSG, uplevel: 1)
2585
+ warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
2692
2586
  when :frozen_dup
2693
2587
  synchronize {
2694
2588
  responses = @responses.transform_values(&:freeze)
@@ -2778,10 +2672,6 @@ module Net
2778
2672
  # end
2779
2673
  # }
2780
2674
  #
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
- #
2785
2675
  # Related: #remove_response_handler, #response_handlers
2786
2676
  def add_response_handler(handler = nil, &block)
2787
2677
  raise ArgumentError, "two Procs are passed" if handler && block
@@ -2808,7 +2698,6 @@ module Net
2808
2698
  def start_imap_connection
2809
2699
  @greeting = get_server_greeting
2810
2700
  @capabilities = capabilities_from_resp_code @greeting
2811
- @response_handlers.each do |handler| handler.call(@greeting) end
2812
2701
  @receiver_thread = start_receiver_thread
2813
2702
  rescue Exception
2814
2703
  @sock.close
@@ -2937,10 +2826,23 @@ module Net
2937
2826
  end
2938
2827
 
2939
2828
  def get_response
2940
- buff = @reader.read_response_buffer
2829
+ buff = String.new
2830
+ while true
2831
+ s = @sock.gets(CRLF)
2832
+ break unless s
2833
+ buff.concat(s)
2834
+ if /\{(\d+)\}\r\n/n =~ s
2835
+ s = @sock.read($1.to_i)
2836
+ buff.concat(s)
2837
+ else
2838
+ break
2839
+ end
2840
+ end
2941
2841
  return nil if buff.length == 0
2942
- $stderr.print(buff.gsub(/^/n, "S: ")) if config.debug?
2943
- @parser.parse(buff)
2842
+ if config.debug?
2843
+ $stderr.print(buff.gsub(/^/n, "S: "))
2844
+ end
2845
+ return @parser.parse(buff)
2944
2846
  end
2945
2847
 
2946
2848
  #############################
@@ -3029,6 +2931,14 @@ module Net
3029
2931
  end
3030
2932
  end
3031
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
+
3032
2942
  def search_internal(cmd, keys, charset)
3033
2943
  if keys.instance_of?(String)
3034
2944
  keys = [RawData.new(keys)]
@@ -3062,9 +2972,9 @@ module Net
3062
2972
  synchronize do
3063
2973
  clear_responses("FETCH")
3064
2974
  if mod
3065
- send_command(cmd, MessageSet.new(set), attr, mod)
2975
+ send_command(cmd, SequenceSet.new(set), attr, mod)
3066
2976
  else
3067
- send_command(cmd, MessageSet.new(set), attr)
2977
+ send_command(cmd, SequenceSet.new(set), attr)
3068
2978
  end
3069
2979
  clear_responses("FETCH")
3070
2980
  end
@@ -3072,7 +2982,7 @@ module Net
3072
2982
 
3073
2983
  def store_internal(cmd, set, attr, flags, unchangedsince: nil)
3074
2984
  attr = RawData.new(attr) if attr.instance_of?(String)
3075
- args = [MessageSet.new(set)]
2985
+ args = [SequenceSet.new(set)]
3076
2986
  args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
3077
2987
  args << attr << flags
3078
2988
  synchronize do
@@ -3083,7 +2993,7 @@ module Net
3083
2993
  end
3084
2994
 
3085
2995
  def copy_internal(cmd, set, mailbox)
3086
- send_command(cmd, MessageSet.new(set), mailbox)
2996
+ send_command(cmd, SequenceSet.new(set), mailbox)
3087
2997
  end
3088
2998
 
3089
2999
  def sort_internal(cmd, sort_keys, search_keys, charset)
@@ -3114,7 +3024,7 @@ module Net
3114
3024
  keys.collect! do |i|
3115
3025
  case i
3116
3026
  when -1, Range, Array
3117
- MessageSet.new(i)
3027
+ SequenceSet.new(i)
3118
3028
  else
3119
3029
  i
3120
3030
  end
@@ -3141,7 +3051,6 @@ module Net
3141
3051
  raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
3142
3052
  raise "cannot start TLS without SSLContext" unless ssl_ctx
3143
3053
  @sock = SSLSocket.new(@sock, ssl_ctx)
3144
- @reader = ResponseReader.new(self, @sock)
3145
3054
  @sock.sync_close = true
3146
3055
  @sock.hostname = @host if @sock.respond_to? :hostname=
3147
3056
  ssl_socket_connect(@sock, open_timeout)
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,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.22
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  - nicholas a. evans
9
+ autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 1980-01-02 00:00:00.000000000 Z
12
+ date: 2024-10-16 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: net-protocol
@@ -68,7 +69,6 @@ files:
68
69
  - lib/net/imap/response_data.rb
69
70
  - lib/net/imap/response_parser.rb
70
71
  - 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,7 +95,6 @@ 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
99
98
  - net-imap.gemspec
100
99
  - rakelib/benchmarks.rake
101
100
  - rakelib/rdoc.rake
@@ -111,6 +110,7 @@ metadata:
111
110
  homepage_uri: https://github.com/ruby/net-imap
112
111
  source_code_uri: https://github.com/ruby/net-imap
113
112
  changelog_uri: https://github.com/ruby/net-imap/releases
113
+ post_install_message:
114
114
  rdoc_options: []
115
115
  require_paths:
116
116
  - lib
@@ -118,14 +118,15 @@ 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.6.7
128
+ rubygems_version: 3.5.16
129
+ signing_key:
129
130
  specification_version: 4
130
131
  summary: Ruby client api for Internet Message Access Protocol
131
132
  test_files: []
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Net
4
- class IMAP
5
- # See https://www.rfc-editor.org/rfc/rfc9051#section-2.2.2
6
- class ResponseReader # :nodoc:
7
- attr_reader :client
8
-
9
- def initialize(client, sock)
10
- @client, @sock = client, sock
11
- end
12
-
13
- def read_response_buffer
14
- @buff = String.new
15
- catch :eof do
16
- while true
17
- read_line
18
- break unless (@literal_size = get_literal_size)
19
- read_literal
20
- end
21
- end
22
- buff
23
- ensure
24
- @buff = nil
25
- end
26
-
27
- private
28
-
29
- attr_reader :buff, :literal_size
30
-
31
- def bytes_read; buff.bytesize end
32
- def empty?; buff.empty? end
33
- def done?; line_done? && !get_literal_size end
34
- def line_done?; buff.end_with?(CRLF) end
35
- def get_literal_size; /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i end
36
-
37
- def read_line
38
- buff << (@sock.gets(CRLF, read_limit) or throw :eof)
39
- max_response_remaining! unless line_done?
40
- end
41
-
42
- def read_literal
43
- # check before allocating memory for literal
44
- max_response_remaining!
45
- literal = String.new(capacity: literal_size)
46
- buff << (@sock.read(read_limit(literal_size), literal) or throw :eof)
47
- ensure
48
- @literal_size = nil
49
- end
50
-
51
- def read_limit(limit = nil)
52
- [limit, max_response_remaining!].compact.min
53
- end
54
-
55
- def max_response_size; client.max_response_size end
56
- def max_response_remaining; max_response_size &.- bytes_read end
57
- def response_too_large?; max_response_size &.< min_response_size end
58
- def min_response_size; bytes_read + min_response_remaining end
59
-
60
- def min_response_remaining
61
- empty? ? 3 : done? ? 0 : (literal_size || 0) + 2
62
- end
63
-
64
- def max_response_remaining!
65
- return max_response_remaining unless response_too_large?
66
- raise ResponseTooLargeError.new(
67
- max_response_size: max_response_size,
68
- bytes_read: bytes_read,
69
- literal_size: literal_size,
70
- )
71
- end
72
-
73
- end
74
- end
75
- end