net-imap 0.5.5 → 0.5.7

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,18 @@ 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
+ # See #connection_state.
57
+ #
50
58
  # === Sequence numbers and UIDs
51
59
  #
52
60
  # Messages have two sorts of identifiers: message sequence
@@ -199,6 +207,42 @@ module Net
199
207
  #
200
208
  # This script invokes the FETCH command and the SEARCH command concurrently.
201
209
  #
210
+ # When running multiple commands, care must be taken to avoid ambiguity. For
211
+ # example, SEARCH responses are ambiguous about which command they are
212
+ # responding to, so search commands should not run simultaneously, unless the
213
+ # server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
214
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
215
+ # §5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
216
+ # other examples of command sequences which should not be pipelined.
217
+ #
218
+ # == Unbounded memory use
219
+ #
220
+ # Net::IMAP reads server responses in a separate receiver thread per client.
221
+ # Unhandled response data is saved to #responses, and response_handlers run
222
+ # inside the receiver thread. See the list of methods for {handling server
223
+ # responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
224
+ #
225
+ # Because the receiver thread continuously reads and saves new responses, some
226
+ # scenarios must be careful to avoid unbounded memory use:
227
+ #
228
+ # * Commands such as #list or #fetch can have an enormous number of responses.
229
+ # * Commands such as #fetch can result in an enormous size per response.
230
+ # * Long-lived connections will gradually accumulate unsolicited server
231
+ # responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
232
+ # * A buggy or untrusted server could send inappropriate responses, which
233
+ # could be very numerous, very large, and very rapid.
234
+ #
235
+ # Use paginated or limited versions of commands whenever possible.
236
+ #
237
+ # Use Config#max_response_size to impose a limit on incoming server responses
238
+ # as they are being read. <em>This is especially important for untrusted
239
+ # servers.</em>
240
+ #
241
+ # Use #add_response_handler to handle responses after each one is received.
242
+ # Use the +response_handlers+ argument to ::new to assign response handlers
243
+ # before the receiver thread is started. Use #extract_responses,
244
+ # #clear_responses, or #responses (with a block) to prune responses.
245
+ #
202
246
  # == Errors
203
247
  #
204
248
  # An \IMAP server can send three different types of responses to indicate
@@ -260,8 +304,9 @@ module Net
260
304
  #
261
305
  # - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
262
306
  # waits for a successful server greeting before the method returns.
307
+ # - #connection_state: Returns the connection state.
263
308
  # - #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.
309
+ # - #logout: Tells the server to end the session. Enters the +logout+ state.
265
310
  # - #disconnect: Disconnects the connection (without sending #logout first).
266
311
  # - #disconnected?: True if the connection has been closed.
267
312
  #
@@ -317,37 +362,36 @@ module Net
317
362
  # <em>In general, #capable? should be used rather than explicitly sending a
318
363
  # +CAPABILITY+ command to the server.</em>
319
364
  # - #noop: Allows the server to send unsolicited untagged #responses.
320
- # - #logout: Tells the server to end the session. Enters the "_logout_" state.
365
+ # - #logout: Tells the server to end the session. Enters the +logout+ state.
321
366
  #
322
367
  # ==== Not Authenticated state
323
368
  #
324
369
  # In addition to the commands for any state, the following commands are valid
325
- # in the "<em>not authenticated</em>" state:
370
+ # in the +not_authenticated+ state:
326
371
  #
327
372
  # - #starttls: Upgrades a clear-text connection to use TLS.
328
373
  #
329
374
  # <em>Requires the +STARTTLS+ capability.</em>
330
375
  # - #authenticate: Identifies the client to the server using the given
331
376
  # {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
332
- # and credentials. Enters the "_authenticated_" state.
377
+ # and credentials. Enters the +authenticated+ state.
333
378
  #
334
379
  # <em>The server should list <tt>"AUTH=#{mechanism}"</tt> capabilities for
335
380
  # supported mechanisms.</em>
336
381
  # - #login: Identifies the client to the server using a plain text password.
337
- # Using #authenticate is generally preferred. Enters the "_authenticated_"
338
- # state.
382
+ # Using #authenticate is preferred. Enters the +authenticated+ state.
339
383
  #
340
384
  # <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
341
385
  #
342
386
  # ==== Authenticated state
343
387
  #
344
388
  # In addition to the commands for any state, the following commands are valid
345
- # in the "_authenticated_" state:
389
+ # in the +authenticated+ state:
346
390
  #
347
391
  # - #enable: Enables backwards incompatible server extensions.
348
392
  # <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
349
- # - #select: Open a mailbox and enter the "_selected_" state.
350
- # - #examine: Open a mailbox read-only, and enter the "_selected_" state.
393
+ # - #select: Open a mailbox and enter the +selected+ state.
394
+ # - #examine: Open a mailbox read-only, and enter the +selected+ state.
351
395
  # - #create: Creates a new mailbox.
352
396
  # - #delete: Permanently remove a mailbox.
353
397
  # - #rename: Change the name of a mailbox.
@@ -369,12 +413,12 @@ module Net
369
413
  #
370
414
  # ==== Selected state
371
415
  #
372
- # In addition to the commands for any state and the "_authenticated_"
373
- # commands, the following commands are valid in the "_selected_" state:
416
+ # In addition to the commands for any state and the +authenticated+
417
+ # commands, the following commands are valid in the +selected+ state:
374
418
  #
375
- # - #close: Closes the mailbox and returns to the "_authenticated_" state,
419
+ # - #close: Closes the mailbox and returns to the +authenticated+ state,
376
420
  # expunging deleted messages, unless the mailbox was opened as read-only.
377
- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
421
+ # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
378
422
  # without expunging any messages.
379
423
  # <em>Requires the +UNSELECT+ or +IMAP4rev2+ capability.</em>
380
424
  # - #expunge: Permanently removes messages which have the Deleted flag set.
@@ -395,7 +439,7 @@ module Net
395
439
  #
396
440
  # ==== Logout state
397
441
  #
398
- # No \IMAP commands are valid in the "_logout_" state. If the socket is still
442
+ # No \IMAP commands are valid in the +logout+ state. If the socket is still
399
443
  # open, Net::IMAP will close it after receiving server confirmation.
400
444
  # Exceptions will be raised by \IMAP commands that have already started and
401
445
  # are waiting for a response, as well as any that are called after logout.
@@ -449,7 +493,7 @@ module Net
449
493
  # ==== RFC3691: +UNSELECT+
450
494
  # Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
451
495
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
452
- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
496
+ # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
453
497
  # without expunging any messages.
454
498
  #
455
499
  # ==== RFC4314: +ACL+
@@ -744,7 +788,7 @@ module Net
744
788
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
745
789
  #
746
790
  class IMAP < Protocol
747
- VERSION = "0.5.5"
791
+ VERSION = "0.5.7"
748
792
 
749
793
  # Aliases for supported capabilities, to be used with the #enable command.
750
794
  ENABLE_ALIASES = {
@@ -752,9 +796,12 @@ module Net
752
796
  "UTF8=ONLY" => "UTF8=ACCEPT",
753
797
  }.freeze
754
798
 
755
- autoload :SASL, File.expand_path("imap/sasl", __dir__)
756
- autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
757
- autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
799
+ dir = File.expand_path("imap", __dir__)
800
+ autoload :ConnectionState, "#{dir}/connection_state"
801
+ autoload :ResponseReader, "#{dir}/response_reader"
802
+ autoload :SASL, "#{dir}/sasl"
803
+ autoload :SASLAdapter, "#{dir}/sasl_adapter"
804
+ autoload :StringPrep, "#{dir}/stringprep"
758
805
 
759
806
  include MonitorMixin
760
807
  if defined?(OpenSSL::SSL)
@@ -766,9 +813,11 @@ module Net
766
813
  def self.config; Config.global end
767
814
 
768
815
  # Returns the global debug mode.
816
+ # Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
769
817
  def self.debug; config.debug end
770
818
 
771
819
  # Sets the global debug mode.
820
+ # Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
772
821
  def self.debug=(val)
773
822
  config.debug = val
774
823
  end
@@ -789,7 +838,7 @@ module Net
789
838
  alias default_ssl_port default_tls_port
790
839
  end
791
840
 
792
- # Returns the initial greeting the server, an UntaggedResponse.
841
+ # Returns the initial greeting sent by the server, an UntaggedResponse.
793
842
  attr_reader :greeting
794
843
 
795
844
  # The client configuration. See Net::IMAP::Config.
@@ -798,13 +847,28 @@ module Net
798
847
  # Net::IMAP.config.
799
848
  attr_reader :config
800
849
 
801
- # Seconds to wait until a connection is opened.
802
- # If the IMAP object cannot open a connection within this time,
803
- # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
804
- def open_timeout; config.open_timeout end
850
+ ##
851
+ # :attr_reader: open_timeout
852
+ # Seconds to wait until a connection is opened. Also used by #starttls.
853
+ # Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout].
805
854
 
855
+ ##
856
+ # :attr_reader: idle_response_timeout
806
857
  # Seconds to wait until an IDLE response is received.
807
- def idle_response_timeout; config.idle_response_timeout end
858
+ # Delegates to {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout].
859
+
860
+ ##
861
+ # :attr_accessor: max_response_size
862
+ #
863
+ # The maximum allowed server response size, in bytes.
864
+ # Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
865
+
866
+ # :stopdoc:
867
+ def open_timeout; config.open_timeout end
868
+ def idle_response_timeout; config.idle_response_timeout end
869
+ def max_response_size; config.max_response_size end
870
+ def max_response_size=(val) config.max_response_size = val end
871
+ # :startdoc:
808
872
 
809
873
  # The hostname this client connected to
810
874
  attr_reader :host
@@ -827,6 +891,67 @@ module Net
827
891
  # Returns +false+ for a plaintext connection.
828
892
  attr_reader :ssl_ctx_params
829
893
 
894
+ # Returns the current connection state.
895
+ #
896
+ # Once an IMAP connection is established, the connection is in one of four
897
+ # states: +not_authenticated+, +authenticated+, +selected+, and +logout+.
898
+ # Most commands are valid only in certain states.
899
+ #
900
+ # The connection state object responds to +to_sym+ and +name+ with the name
901
+ # of the current connection state, as a Symbol or String. Future versions
902
+ # of +net-imap+ may store additional information on the state object.
903
+ #
904
+ # From {RFC9051}[https://www.rfc-editor.org/rfc/rfc9051#section-3]:
905
+ # +----------------------+
906
+ # |connection established|
907
+ # +----------------------+
908
+ # ||
909
+ # \/
910
+ # +--------------------------------------+
911
+ # | server greeting |
912
+ # +--------------------------------------+
913
+ # || (1) || (2) || (3)
914
+ # \/ || ||
915
+ # +-----------------+ || ||
916
+ # |Not Authenticated| || ||
917
+ # +-----------------+ || ||
918
+ # || (7) || (4) || ||
919
+ # || \/ \/ ||
920
+ # || +----------------+ ||
921
+ # || | Authenticated |<=++ ||
922
+ # || +----------------+ || ||
923
+ # || || (7) || (5) || (6) ||
924
+ # || || \/ || ||
925
+ # || || +--------+ || ||
926
+ # || || |Selected|==++ ||
927
+ # || || +--------+ ||
928
+ # || || || (7) ||
929
+ # \/ \/ \/ \/
930
+ # +--------------------------------------+
931
+ # | Logout |
932
+ # +--------------------------------------+
933
+ # ||
934
+ # \/
935
+ # +-------------------------------+
936
+ # |both sides close the connection|
937
+ # +-------------------------------+
938
+ #
939
+ # >>>
940
+ # Legend for the above diagram:
941
+ #
942
+ # 1. connection without pre-authentication (+OK+ #greeting)
943
+ # 2. pre-authenticated connection (+PREAUTH+ #greeting)
944
+ # 3. rejected connection (+BYE+ #greeting)
945
+ # 4. successful #login or #authenticate command
946
+ # 5. successful #select or #examine command
947
+ # 6. #close or #unselect command, unsolicited +CLOSED+ response code, or
948
+ # failed #select or #examine command
949
+ # 7. #logout command, server shutdown, or connection closed
950
+ #
951
+ # Before the server greeting, the state is +not_authenticated+.
952
+ # After the connection closes, the state remains +logout+.
953
+ attr_reader :connection_state
954
+
830
955
  # Creates a new Net::IMAP object and connects it to the specified
831
956
  # +host+.
832
957
  #
@@ -860,6 +985,12 @@ module Net
860
985
  #
861
986
  # See DeprecatedClientOptions.new for deprecated SSL arguments.
862
987
  #
988
+ # [response_handlers]
989
+ # A list of response handlers to be added before the receiver thread is
990
+ # started. This ensures every server response is handled, including the
991
+ # #greeting. Note that the greeting is handled in the current thread, but
992
+ # all other responses are handled in the receiver thread.
993
+ #
863
994
  # [config]
864
995
  # A Net::IMAP::Config object to use as the basis for #config. By default,
865
996
  # the global Net::IMAP.config is used.
@@ -931,7 +1062,7 @@ module Net
931
1062
  # [Net::IMAP::ByeResponseError]
932
1063
  # Connected to the host successfully, but it immediately said goodbye.
933
1064
  #
934
- def initialize(host, port: nil, ssl: nil,
1065
+ def initialize(host, port: nil, ssl: nil, response_handlers: nil,
935
1066
  config: Config.global, **config_options)
936
1067
  super()
937
1068
  # Config options
@@ -946,6 +1077,8 @@ module Net
946
1077
  @exception = nil
947
1078
  @greeting = nil
948
1079
  @capabilities = nil
1080
+ @tls_verified = false
1081
+ @connection_state = ConnectionState::NotAuthenticated.new
949
1082
 
950
1083
  # Client Protocol Receiver
951
1084
  @parser = ResponseParser.new(config: @config)
@@ -954,6 +1087,7 @@ module Net
954
1087
  @receiver_thread = nil
955
1088
  @receiver_thread_exception = nil
956
1089
  @receiver_thread_terminating = false
1090
+ response_handlers&.each do add_response_handler(_1) end
957
1091
 
958
1092
  # Client Protocol Sender (including state for currently running commands)
959
1093
  @tag_prefix = "RUBY"
@@ -967,8 +1101,8 @@ module Net
967
1101
  @logout_command_tag = nil
968
1102
 
969
1103
  # Connection
970
- @tls_verified = false
971
1104
  @sock = tcp_socket(@host, @port)
1105
+ @reader = ResponseReader.new(self, @sock)
972
1106
  start_tls_session if ssl_ctx
973
1107
  start_imap_connection
974
1108
  end
@@ -983,6 +1117,7 @@ module Net
983
1117
  # Related: #logout, #logout!
984
1118
  def disconnect
985
1119
  return if disconnected?
1120
+ state_logout!
986
1121
  begin
987
1122
  begin
988
1123
  # try to call SSL::SSLSocket#io.
@@ -1221,6 +1356,10 @@ module Net
1221
1356
  # both successful. Any error indicates that the connection has not been
1222
1357
  # secured.
1223
1358
  #
1359
+ # After the server agrees to start a TLS connection, this method waits up to
1360
+ # {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
1361
+ # +Net::OpenTimeout+.
1362
+ #
1224
1363
  # *Note:*
1225
1364
  # >>>
1226
1365
  # Any #response_handlers added before STARTTLS should be aware that the
@@ -1239,13 +1378,21 @@ module Net
1239
1378
  #
1240
1379
  def starttls(**options)
1241
1380
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
1242
- send_command("STARTTLS") do |resp|
1381
+ error = nil
1382
+ ok = send_command("STARTTLS") do |resp|
1243
1383
  if resp.kind_of?(TaggedResponse) && resp.name == "OK"
1244
1384
  clear_cached_capabilities
1245
1385
  clear_responses
1246
1386
  start_tls_session
1247
1387
  end
1388
+ rescue Exception => error
1389
+ raise # note that the error backtrace is in the receiver_thread
1248
1390
  end
1391
+ if error
1392
+ disconnect
1393
+ raise error
1394
+ end
1395
+ ok
1249
1396
  end
1250
1397
 
1251
1398
  # :call-seq:
@@ -1360,7 +1507,7 @@ module Net
1360
1507
  # capabilities, they will be cached.
1361
1508
  def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
1362
1509
  sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
1363
- .tap { @capabilities = capabilities_from_resp_code _1 }
1510
+ .tap do state_authenticated! _1 end
1364
1511
  end
1365
1512
 
1366
1513
  # Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
@@ -1394,7 +1541,7 @@ module Net
1394
1541
  raise LoginDisabledError
1395
1542
  end
1396
1543
  send_command("LOGIN", user, password)
1397
- .tap { @capabilities = capabilities_from_resp_code _1 }
1544
+ .tap do state_authenticated! _1 end
1398
1545
  end
1399
1546
 
1400
1547
  # Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
@@ -1434,8 +1581,10 @@ module Net
1434
1581
  args = ["SELECT", mailbox]
1435
1582
  args << ["CONDSTORE"] if condstore
1436
1583
  synchronize do
1584
+ state_unselected! # implicitly closes current mailbox
1437
1585
  @responses.clear
1438
1586
  send_command(*args)
1587
+ .tap do state_selected! end
1439
1588
  end
1440
1589
  end
1441
1590
 
@@ -1452,8 +1601,10 @@ module Net
1452
1601
  args = ["EXAMINE", mailbox]
1453
1602
  args << ["CONDSTORE"] if condstore
1454
1603
  synchronize do
1604
+ state_unselected! # implicitly closes current mailbox
1455
1605
  @responses.clear
1456
1606
  send_command(*args)
1607
+ .tap do state_selected! end
1457
1608
  end
1458
1609
  end
1459
1610
 
@@ -1892,6 +2043,7 @@ module Net
1892
2043
  # Related: #unselect
1893
2044
  def close
1894
2045
  send_command("CLOSE")
2046
+ .tap do state_authenticated! end
1895
2047
  end
1896
2048
 
1897
2049
  # Sends an {UNSELECT command [RFC3691 §2]}[https://www.rfc-editor.org/rfc/rfc3691#section-3]
@@ -1908,6 +2060,7 @@ module Net
1908
2060
  # [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
1909
2061
  def unselect
1910
2062
  send_command("UNSELECT")
2063
+ .tap do state_authenticated! end
1911
2064
  end
1912
2065
 
1913
2066
  # call-seq:
@@ -3138,6 +3291,10 @@ module Net
3138
3291
  # end
3139
3292
  # }
3140
3293
  #
3294
+ # Response handlers can also be added when the client is created before the
3295
+ # receiver thread is started, by the +response_handlers+ argument to ::new.
3296
+ # This ensures every server response is handled, including the #greeting.
3297
+ #
3141
3298
  # Related: #remove_response_handler, #response_handlers
3142
3299
  def add_response_handler(handler = nil, &block)
3143
3300
  raise ArgumentError, "two Procs are passed" if handler && block
@@ -3164,8 +3321,10 @@ module Net
3164
3321
  def start_imap_connection
3165
3322
  @greeting = get_server_greeting
3166
3323
  @capabilities = capabilities_from_resp_code @greeting
3324
+ @response_handlers.each do |handler| handler.call(@greeting) end
3167
3325
  @receiver_thread = start_receiver_thread
3168
3326
  rescue Exception
3327
+ state_logout!
3169
3328
  @sock.close
3170
3329
  raise
3171
3330
  end
@@ -3174,7 +3333,10 @@ module Net
3174
3333
  greeting = get_response
3175
3334
  raise Error, "No server greeting - connection closed" unless greeting
3176
3335
  record_untagged_response_code greeting
3177
- raise ByeResponseError, greeting if greeting.name == "BYE"
3336
+ case greeting.name
3337
+ when "PREAUTH" then state_authenticated!
3338
+ when "BYE" then state_logout!; raise ByeResponseError, greeting
3339
+ end
3178
3340
  greeting
3179
3341
  end
3180
3342
 
@@ -3184,6 +3346,8 @@ module Net
3184
3346
  rescue Exception => ex
3185
3347
  @receiver_thread_exception = ex
3186
3348
  # don't exit the thread with an exception
3349
+ ensure
3350
+ state_logout!
3187
3351
  end
3188
3352
  end
3189
3353
 
@@ -3206,6 +3370,7 @@ module Net
3206
3370
  resp = get_response
3207
3371
  rescue Exception => e
3208
3372
  synchronize do
3373
+ state_logout!
3209
3374
  @sock.close
3210
3375
  @exception = e
3211
3376
  end
@@ -3225,6 +3390,7 @@ module Net
3225
3390
  @tagged_response_arrival.broadcast
3226
3391
  case resp.tag
3227
3392
  when @logout_command_tag
3393
+ state_logout!
3228
3394
  return
3229
3395
  when @continued_command_tag
3230
3396
  @continuation_request_exception =
@@ -3234,6 +3400,7 @@ module Net
3234
3400
  when UntaggedResponse
3235
3401
  record_untagged_response(resp)
3236
3402
  if resp.name == "BYE" && @logout_command_tag.nil?
3403
+ state_logout!
3237
3404
  @sock.close
3238
3405
  @exception = ByeResponseError.new(resp)
3239
3406
  connection_closed = true
@@ -3241,6 +3408,7 @@ module Net
3241
3408
  when ContinuationRequest
3242
3409
  @continuation_request_arrival.signal
3243
3410
  end
3411
+ state_unselected! if resp in {data: {code: {name: "CLOSED"}}}
3244
3412
  @response_handlers.each do |handler|
3245
3413
  handler.call(resp)
3246
3414
  end
@@ -3292,23 +3460,10 @@ module Net
3292
3460
  end
3293
3461
 
3294
3462
  def get_response
3295
- buff = String.new
3296
- while true
3297
- s = @sock.gets(CRLF)
3298
- break unless s
3299
- buff.concat(s)
3300
- if /\{(\d+)\}\r\n/n =~ s
3301
- s = @sock.read($1.to_i)
3302
- buff.concat(s)
3303
- else
3304
- break
3305
- end
3306
- end
3463
+ buff = @reader.read_response_buffer
3307
3464
  return nil if buff.length == 0
3308
- if config.debug?
3309
- $stderr.print(buff.gsub(/^/n, "S: "))
3310
- end
3311
- return @parser.parse(buff)
3465
+ $stderr.print(buff.gsub(/^/n, "S: ")) if config.debug?
3466
+ @parser.parse(buff)
3312
3467
  end
3313
3468
 
3314
3469
  #############################
@@ -3612,6 +3767,7 @@ module Net
3612
3767
  raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
3613
3768
  raise "cannot start TLS without SSLContext" unless ssl_ctx
3614
3769
  @sock = SSLSocket.new(@sock, ssl_ctx)
3770
+ @reader = ResponseReader.new(self, @sock)
3615
3771
  @sock.sync_close = true
3616
3772
  @sock.hostname = @host if @sock.respond_to? :hostname=
3617
3773
  ssl_socket_connect(@sock, open_timeout)
@@ -3621,6 +3777,29 @@ module Net
3621
3777
  end
3622
3778
  end
3623
3779
 
3780
+ def state_authenticated!(resp = nil)
3781
+ synchronize do
3782
+ @capabilities = capabilities_from_resp_code resp if resp
3783
+ @connection_state = ConnectionState::Authenticated.new
3784
+ end
3785
+ end
3786
+
3787
+ def state_selected!
3788
+ synchronize do
3789
+ @connection_state = ConnectionState::Selected.new
3790
+ end
3791
+ end
3792
+
3793
+ def state_unselected!
3794
+ state_authenticated! if connection_state.to_sym == :selected
3795
+ end
3796
+
3797
+ def state_logout!
3798
+ synchronize do
3799
+ @connection_state = ConnectionState::Logout.new
3800
+ end
3801
+ end
3802
+
3624
3803
  def sasl_adapter
3625
3804
  SASLAdapter.new(self, &method(:send_command_with_continuations))
3626
3805
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  - nicholas a. evans
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-04 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-protocol
@@ -60,6 +60,7 @@ files:
60
60
  - lib/net/imap/config/attr_accessors.rb
61
61
  - lib/net/imap/config/attr_inheritance.rb
62
62
  - lib/net/imap/config/attr_type_coercion.rb
63
+ - lib/net/imap/connection_state.rb
63
64
  - lib/net/imap/data_encoding.rb
64
65
  - lib/net/imap/data_lite.rb
65
66
  - lib/net/imap/deprecated_client_options.rb
@@ -70,6 +71,7 @@ files:
70
71
  - lib/net/imap/response_data.rb
71
72
  - lib/net/imap/response_parser.rb
72
73
  - lib/net/imap/response_parser/parser_utils.rb
74
+ - lib/net/imap/response_reader.rb
73
75
  - lib/net/imap/sasl.rb
74
76
  - lib/net/imap/sasl/anonymous_authenticator.rb
75
77
  - lib/net/imap/sasl/authentication_exchange.rb
@@ -96,6 +98,7 @@ files:
96
98
  - lib/net/imap/stringprep/saslprep_tables.rb
97
99
  - lib/net/imap/stringprep/tables.rb
98
100
  - lib/net/imap/stringprep/trace.rb
101
+ - lib/net/imap/uidplus_data.rb
99
102
  - lib/net/imap/vanished_data.rb
100
103
  - net-imap.gemspec
101
104
  - rakelib/benchmarks.rake
@@ -126,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
129
  - !ruby/object:Gem::Version
127
130
  version: '0'
128
131
  requirements: []
129
- rubygems_version: 3.6.2
132
+ rubygems_version: 3.6.7
130
133
  specification_version: 4
131
134
  summary: Ruby client api for Internet Message Access Protocol
132
135
  test_files: []