net-imap 0.5.6 → 0.5.8

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.6"
791
+ VERSION = "0.5.8"
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
@@ -1368,7 +1507,7 @@ module Net
1368
1507
  # capabilities, they will be cached.
1369
1508
  def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
1370
1509
  sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
1371
- .tap { @capabilities = capabilities_from_resp_code _1 }
1510
+ .tap do state_authenticated! _1 end
1372
1511
  end
1373
1512
 
1374
1513
  # Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
@@ -1402,7 +1541,7 @@ module Net
1402
1541
  raise LoginDisabledError
1403
1542
  end
1404
1543
  send_command("LOGIN", user, password)
1405
- .tap { @capabilities = capabilities_from_resp_code _1 }
1544
+ .tap do state_authenticated! _1 end
1406
1545
  end
1407
1546
 
1408
1547
  # Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
@@ -1442,8 +1581,10 @@ module Net
1442
1581
  args = ["SELECT", mailbox]
1443
1582
  args << ["CONDSTORE"] if condstore
1444
1583
  synchronize do
1584
+ state_unselected! # implicitly closes current mailbox
1445
1585
  @responses.clear
1446
1586
  send_command(*args)
1587
+ .tap do state_selected! end
1447
1588
  end
1448
1589
  end
1449
1590
 
@@ -1460,8 +1601,10 @@ module Net
1460
1601
  args = ["EXAMINE", mailbox]
1461
1602
  args << ["CONDSTORE"] if condstore
1462
1603
  synchronize do
1604
+ state_unselected! # implicitly closes current mailbox
1463
1605
  @responses.clear
1464
1606
  send_command(*args)
1607
+ .tap do state_selected! end
1465
1608
  end
1466
1609
  end
1467
1610
 
@@ -1900,6 +2043,7 @@ module Net
1900
2043
  # Related: #unselect
1901
2044
  def close
1902
2045
  send_command("CLOSE")
2046
+ .tap do state_authenticated! end
1903
2047
  end
1904
2048
 
1905
2049
  # Sends an {UNSELECT command [RFC3691 §2]}[https://www.rfc-editor.org/rfc/rfc3691#section-3]
@@ -1916,6 +2060,7 @@ module Net
1916
2060
  # [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
1917
2061
  def unselect
1918
2062
  send_command("UNSELECT")
2063
+ .tap do state_authenticated! end
1919
2064
  end
1920
2065
 
1921
2066
  # call-seq:
@@ -3146,6 +3291,10 @@ module Net
3146
3291
  # end
3147
3292
  # }
3148
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
+ #
3149
3298
  # Related: #remove_response_handler, #response_handlers
3150
3299
  def add_response_handler(handler = nil, &block)
3151
3300
  raise ArgumentError, "two Procs are passed" if handler && block
@@ -3172,8 +3321,10 @@ module Net
3172
3321
  def start_imap_connection
3173
3322
  @greeting = get_server_greeting
3174
3323
  @capabilities = capabilities_from_resp_code @greeting
3324
+ @response_handlers.each do |handler| handler.call(@greeting) end
3175
3325
  @receiver_thread = start_receiver_thread
3176
3326
  rescue Exception
3327
+ state_logout!
3177
3328
  @sock.close
3178
3329
  raise
3179
3330
  end
@@ -3182,7 +3333,10 @@ module Net
3182
3333
  greeting = get_response
3183
3334
  raise Error, "No server greeting - connection closed" unless greeting
3184
3335
  record_untagged_response_code greeting
3185
- 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
3186
3340
  greeting
3187
3341
  end
3188
3342
 
@@ -3192,6 +3346,8 @@ module Net
3192
3346
  rescue Exception => ex
3193
3347
  @receiver_thread_exception = ex
3194
3348
  # don't exit the thread with an exception
3349
+ ensure
3350
+ state_logout!
3195
3351
  end
3196
3352
  end
3197
3353
 
@@ -3214,6 +3370,7 @@ module Net
3214
3370
  resp = get_response
3215
3371
  rescue Exception => e
3216
3372
  synchronize do
3373
+ state_logout!
3217
3374
  @sock.close
3218
3375
  @exception = e
3219
3376
  end
@@ -3233,6 +3390,7 @@ module Net
3233
3390
  @tagged_response_arrival.broadcast
3234
3391
  case resp.tag
3235
3392
  when @logout_command_tag
3393
+ state_logout!
3236
3394
  return
3237
3395
  when @continued_command_tag
3238
3396
  @continuation_request_exception =
@@ -3242,6 +3400,7 @@ module Net
3242
3400
  when UntaggedResponse
3243
3401
  record_untagged_response(resp)
3244
3402
  if resp.name == "BYE" && @logout_command_tag.nil?
3403
+ state_logout!
3245
3404
  @sock.close
3246
3405
  @exception = ByeResponseError.new(resp)
3247
3406
  connection_closed = true
@@ -3249,6 +3408,7 @@ module Net
3249
3408
  when ContinuationRequest
3250
3409
  @continuation_request_arrival.signal
3251
3410
  end
3411
+ state_unselected! if resp in {data: {code: {name: "CLOSED"}}}
3252
3412
  @response_handlers.each do |handler|
3253
3413
  handler.call(resp)
3254
3414
  end
@@ -3300,23 +3460,10 @@ module Net
3300
3460
  end
3301
3461
 
3302
3462
  def get_response
3303
- buff = String.new
3304
- while true
3305
- s = @sock.gets(CRLF)
3306
- break unless s
3307
- buff.concat(s)
3308
- if /\{(\d+)\}\r\n/n =~ s
3309
- s = @sock.read($1.to_i)
3310
- buff.concat(s)
3311
- else
3312
- break
3313
- end
3314
- end
3463
+ buff = @reader.read_response_buffer
3315
3464
  return nil if buff.length == 0
3316
- if config.debug?
3317
- $stderr.print(buff.gsub(/^/n, "S: "))
3318
- end
3319
- return @parser.parse(buff)
3465
+ $stderr.print(buff.gsub(/^/n, "S: ")) if config.debug?
3466
+ @parser.parse(buff)
3320
3467
  end
3321
3468
 
3322
3469
  #############################
@@ -3620,6 +3767,7 @@ module Net
3620
3767
  raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
3621
3768
  raise "cannot start TLS without SSLContext" unless ssl_ctx
3622
3769
  @sock = SSLSocket.new(@sock, ssl_ctx)
3770
+ @reader = ResponseReader.new(self, @sock)
3623
3771
  @sock.sync_close = true
3624
3772
  @sock.hostname = @host if @sock.respond_to? :hostname=
3625
3773
  ssl_socket_connect(@sock, open_timeout)
@@ -3629,6 +3777,29 @@ module Net
3629
3777
  end
3630
3778
  end
3631
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
+
3632
3803
  def sasl_adapter
3633
3804
  SASLAdapter.new(self, &method(:send_command_with_continuations))
3634
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.6
4
+ version: 0.5.8
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-02-07 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
@@ -127,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
129
  - !ruby/object:Gem::Version
128
130
  version: '0'
129
131
  requirements: []
130
- rubygems_version: 3.6.2
132
+ rubygems_version: 3.6.7
131
133
  specification_version: 4
132
134
  summary: Ruby client api for Internet Message Access Protocol
133
135
  test_files: []