net-imap 0.5.6 → 0.5.9
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/net/imap/config/attr_type_coercion.rb +23 -22
- data/lib/net/imap/config.rb +101 -19
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/errors.rb +33 -0
- data/lib/net/imap/response_data.rb +0 -1
- data/lib/net/imap/response_reader.rb +73 -0
- data/lib/net/imap/sequence_set.rb +471 -165
- data/lib/net/imap.rb +259 -58
- data/rakelib/string_prep_tables_generator.rb +4 -2
- metadata +5 -3
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
|
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.
|
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
|
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
|
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
|
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
|
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
|
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
|
350
|
-
# - #examine: Open a mailbox read-only, and enter the
|
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
|
373
|
-
# commands, the following commands are valid in the
|
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
|
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
|
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
|
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
|
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.
|
791
|
+
VERSION = "0.5.9"
|
748
792
|
|
749
793
|
# Aliases for supported capabilities, to be used with the #enable command.
|
750
794
|
ENABLE_ALIASES = {
|
@@ -752,9 +796,13 @@ module Net
|
|
752
796
|
"UTF8=ONLY" => "UTF8=ACCEPT",
|
753
797
|
}.freeze
|
754
798
|
|
755
|
-
|
756
|
-
autoload :
|
757
|
-
autoload :
|
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 :SequenceSet, "#{dir}/sequence_set"
|
805
|
+
autoload :StringPrep, "#{dir}/stringprep"
|
758
806
|
|
759
807
|
include MonitorMixin
|
760
808
|
if defined?(OpenSSL::SSL)
|
@@ -762,13 +810,31 @@ module Net
|
|
762
810
|
include SSL
|
763
811
|
end
|
764
812
|
|
813
|
+
# :call-seq:
|
814
|
+
# Net::IMAP::SequenceSet(set = nil) -> SequenceSet
|
815
|
+
#
|
816
|
+
# Coerces +set+ into a SequenceSet, using either SequenceSet.try_convert or
|
817
|
+
# SequenceSet.new.
|
818
|
+
#
|
819
|
+
# * When +set+ is a SequenceSet, that same set is returned.
|
820
|
+
# * When +set+ responds to +to_sequence_set+, +set.to_sequence_set+ is
|
821
|
+
# returned.
|
822
|
+
# * Otherwise, returns the result from calling SequenceSet.new with +set+.
|
823
|
+
#
|
824
|
+
# Related: SequenceSet.try_convert, SequenceSet.new, SequenceSet::[]
|
825
|
+
def self.SequenceSet(set = nil)
|
826
|
+
SequenceSet.try_convert(set) || SequenceSet.new(set)
|
827
|
+
end
|
828
|
+
|
765
829
|
# Returns the global Config object
|
766
830
|
def self.config; Config.global end
|
767
831
|
|
768
832
|
# Returns the global debug mode.
|
833
|
+
# Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
|
769
834
|
def self.debug; config.debug end
|
770
835
|
|
771
836
|
# Sets the global debug mode.
|
837
|
+
# Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
|
772
838
|
def self.debug=(val)
|
773
839
|
config.debug = val
|
774
840
|
end
|
@@ -789,7 +855,7 @@ module Net
|
|
789
855
|
alias default_ssl_port default_tls_port
|
790
856
|
end
|
791
857
|
|
792
|
-
# Returns the initial greeting the server, an UntaggedResponse.
|
858
|
+
# Returns the initial greeting sent by the server, an UntaggedResponse.
|
793
859
|
attr_reader :greeting
|
794
860
|
|
795
861
|
# The client configuration. See Net::IMAP::Config.
|
@@ -798,13 +864,28 @@ module Net
|
|
798
864
|
# Net::IMAP.config.
|
799
865
|
attr_reader :config
|
800
866
|
|
801
|
-
|
802
|
-
#
|
803
|
-
#
|
804
|
-
|
867
|
+
##
|
868
|
+
# :attr_reader: open_timeout
|
869
|
+
# Seconds to wait until a connection is opened. Also used by #starttls.
|
870
|
+
# Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout].
|
805
871
|
|
872
|
+
##
|
873
|
+
# :attr_reader: idle_response_timeout
|
806
874
|
# Seconds to wait until an IDLE response is received.
|
807
|
-
|
875
|
+
# Delegates to {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout].
|
876
|
+
|
877
|
+
##
|
878
|
+
# :attr_accessor: max_response_size
|
879
|
+
#
|
880
|
+
# The maximum allowed server response size, in bytes.
|
881
|
+
# Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
|
882
|
+
|
883
|
+
# :stopdoc:
|
884
|
+
def open_timeout; config.open_timeout end
|
885
|
+
def idle_response_timeout; config.idle_response_timeout end
|
886
|
+
def max_response_size; config.max_response_size end
|
887
|
+
def max_response_size=(val) config.max_response_size = val end
|
888
|
+
# :startdoc:
|
808
889
|
|
809
890
|
# The hostname this client connected to
|
810
891
|
attr_reader :host
|
@@ -827,6 +908,67 @@ module Net
|
|
827
908
|
# Returns +false+ for a plaintext connection.
|
828
909
|
attr_reader :ssl_ctx_params
|
829
910
|
|
911
|
+
# Returns the current connection state.
|
912
|
+
#
|
913
|
+
# Once an IMAP connection is established, the connection is in one of four
|
914
|
+
# states: +not_authenticated+, +authenticated+, +selected+, and +logout+.
|
915
|
+
# Most commands are valid only in certain states.
|
916
|
+
#
|
917
|
+
# The connection state object responds to +to_sym+ and +name+ with the name
|
918
|
+
# of the current connection state, as a Symbol or String. Future versions
|
919
|
+
# of +net-imap+ may store additional information on the state object.
|
920
|
+
#
|
921
|
+
# From {RFC9051}[https://www.rfc-editor.org/rfc/rfc9051#section-3]:
|
922
|
+
# +----------------------+
|
923
|
+
# |connection established|
|
924
|
+
# +----------------------+
|
925
|
+
# ||
|
926
|
+
# \/
|
927
|
+
# +--------------------------------------+
|
928
|
+
# | server greeting |
|
929
|
+
# +--------------------------------------+
|
930
|
+
# || (1) || (2) || (3)
|
931
|
+
# \/ || ||
|
932
|
+
# +-----------------+ || ||
|
933
|
+
# |Not Authenticated| || ||
|
934
|
+
# +-----------------+ || ||
|
935
|
+
# || (7) || (4) || ||
|
936
|
+
# || \/ \/ ||
|
937
|
+
# || +----------------+ ||
|
938
|
+
# || | Authenticated |<=++ ||
|
939
|
+
# || +----------------+ || ||
|
940
|
+
# || || (7) || (5) || (6) ||
|
941
|
+
# || || \/ || ||
|
942
|
+
# || || +--------+ || ||
|
943
|
+
# || || |Selected|==++ ||
|
944
|
+
# || || +--------+ ||
|
945
|
+
# || || || (7) ||
|
946
|
+
# \/ \/ \/ \/
|
947
|
+
# +--------------------------------------+
|
948
|
+
# | Logout |
|
949
|
+
# +--------------------------------------+
|
950
|
+
# ||
|
951
|
+
# \/
|
952
|
+
# +-------------------------------+
|
953
|
+
# |both sides close the connection|
|
954
|
+
# +-------------------------------+
|
955
|
+
#
|
956
|
+
# >>>
|
957
|
+
# Legend for the above diagram:
|
958
|
+
#
|
959
|
+
# 1. connection without pre-authentication (+OK+ #greeting)
|
960
|
+
# 2. pre-authenticated connection (+PREAUTH+ #greeting)
|
961
|
+
# 3. rejected connection (+BYE+ #greeting)
|
962
|
+
# 4. successful #login or #authenticate command
|
963
|
+
# 5. successful #select or #examine command
|
964
|
+
# 6. #close or #unselect command, unsolicited +CLOSED+ response code, or
|
965
|
+
# failed #select or #examine command
|
966
|
+
# 7. #logout command, server shutdown, or connection closed
|
967
|
+
#
|
968
|
+
# Before the server greeting, the state is +not_authenticated+.
|
969
|
+
# After the connection closes, the state remains +logout+.
|
970
|
+
attr_reader :connection_state
|
971
|
+
|
830
972
|
# Creates a new Net::IMAP object and connects it to the specified
|
831
973
|
# +host+.
|
832
974
|
#
|
@@ -860,6 +1002,12 @@ module Net
|
|
860
1002
|
#
|
861
1003
|
# See DeprecatedClientOptions.new for deprecated SSL arguments.
|
862
1004
|
#
|
1005
|
+
# [response_handlers]
|
1006
|
+
# A list of response handlers to be added before the receiver thread is
|
1007
|
+
# started. This ensures every server response is handled, including the
|
1008
|
+
# #greeting. Note that the greeting is handled in the current thread, but
|
1009
|
+
# all other responses are handled in the receiver thread.
|
1010
|
+
#
|
863
1011
|
# [config]
|
864
1012
|
# A Net::IMAP::Config object to use as the basis for #config. By default,
|
865
1013
|
# the global Net::IMAP.config is used.
|
@@ -931,7 +1079,7 @@ module Net
|
|
931
1079
|
# [Net::IMAP::ByeResponseError]
|
932
1080
|
# Connected to the host successfully, but it immediately said goodbye.
|
933
1081
|
#
|
934
|
-
def initialize(host, port: nil, ssl:
|
1082
|
+
def initialize(host, port: nil, ssl: nil, response_handlers: nil,
|
935
1083
|
config: Config.global, **config_options)
|
936
1084
|
super()
|
937
1085
|
# Config options
|
@@ -946,6 +1094,8 @@ module Net
|
|
946
1094
|
@exception = nil
|
947
1095
|
@greeting = nil
|
948
1096
|
@capabilities = nil
|
1097
|
+
@tls_verified = false
|
1098
|
+
@connection_state = ConnectionState::NotAuthenticated.new
|
949
1099
|
|
950
1100
|
# Client Protocol Receiver
|
951
1101
|
@parser = ResponseParser.new(config: @config)
|
@@ -954,6 +1104,7 @@ module Net
|
|
954
1104
|
@receiver_thread = nil
|
955
1105
|
@receiver_thread_exception = nil
|
956
1106
|
@receiver_thread_terminating = false
|
1107
|
+
response_handlers&.each do add_response_handler(_1) end
|
957
1108
|
|
958
1109
|
# Client Protocol Sender (including state for currently running commands)
|
959
1110
|
@tag_prefix = "RUBY"
|
@@ -967,8 +1118,8 @@ module Net
|
|
967
1118
|
@logout_command_tag = nil
|
968
1119
|
|
969
1120
|
# Connection
|
970
|
-
@tls_verified = false
|
971
1121
|
@sock = tcp_socket(@host, @port)
|
1122
|
+
@reader = ResponseReader.new(self, @sock)
|
972
1123
|
start_tls_session if ssl_ctx
|
973
1124
|
start_imap_connection
|
974
1125
|
end
|
@@ -980,27 +1131,27 @@ module Net
|
|
980
1131
|
|
981
1132
|
# Disconnects from the server.
|
982
1133
|
#
|
1134
|
+
# Waits for receiver thread to close before returning. Slow or stuck
|
1135
|
+
# response handlers can cause #disconnect to hang until they complete.
|
1136
|
+
#
|
983
1137
|
# Related: #logout, #logout!
|
984
1138
|
def disconnect
|
1139
|
+
in_logout_state = try_state_logout?
|
985
1140
|
return if disconnected?
|
986
1141
|
begin
|
987
|
-
|
988
|
-
# try to call SSL::SSLSocket#io.
|
989
|
-
@sock.io.shutdown
|
990
|
-
rescue NoMethodError
|
991
|
-
# @sock is not an SSL::SSLSocket.
|
992
|
-
@sock.shutdown
|
993
|
-
end
|
1142
|
+
@sock.to_io.shutdown
|
994
1143
|
rescue Errno::ENOTCONN
|
995
1144
|
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
|
996
1145
|
rescue Exception => e
|
997
1146
|
@receiver_thread.raise(e)
|
998
1147
|
end
|
1148
|
+
@sock.close
|
999
1149
|
@receiver_thread.join
|
1000
|
-
synchronize do
|
1001
|
-
@sock.close
|
1002
|
-
end
|
1003
1150
|
raise e if e
|
1151
|
+
ensure
|
1152
|
+
# Try again after shutting down the receiver thread. With no reciever
|
1153
|
+
# left to wait for, any remaining locks should be _very_ brief.
|
1154
|
+
state_logout! unless in_logout_state
|
1004
1155
|
end
|
1005
1156
|
|
1006
1157
|
# Returns true if disconnected from the server.
|
@@ -1221,6 +1372,10 @@ module Net
|
|
1221
1372
|
# both successful. Any error indicates that the connection has not been
|
1222
1373
|
# secured.
|
1223
1374
|
#
|
1375
|
+
# After the server agrees to start a TLS connection, this method waits up to
|
1376
|
+
# {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
|
1377
|
+
# +Net::OpenTimeout+.
|
1378
|
+
#
|
1224
1379
|
# *Note:*
|
1225
1380
|
# >>>
|
1226
1381
|
# Any #response_handlers added before STARTTLS should be aware that the
|
@@ -1368,7 +1523,7 @@ module Net
|
|
1368
1523
|
# capabilities, they will be cached.
|
1369
1524
|
def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
|
1370
1525
|
sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
|
1371
|
-
.tap
|
1526
|
+
.tap do state_authenticated! _1 end
|
1372
1527
|
end
|
1373
1528
|
|
1374
1529
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1402,7 +1557,7 @@ module Net
|
|
1402
1557
|
raise LoginDisabledError
|
1403
1558
|
end
|
1404
1559
|
send_command("LOGIN", user, password)
|
1405
|
-
.tap
|
1560
|
+
.tap do state_authenticated! _1 end
|
1406
1561
|
end
|
1407
1562
|
|
1408
1563
|
# Sends a {SELECT command [IMAP4rev1 §6.3.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.1]
|
@@ -1442,8 +1597,10 @@ module Net
|
|
1442
1597
|
args = ["SELECT", mailbox]
|
1443
1598
|
args << ["CONDSTORE"] if condstore
|
1444
1599
|
synchronize do
|
1600
|
+
state_unselected! # implicitly closes current mailbox
|
1445
1601
|
@responses.clear
|
1446
1602
|
send_command(*args)
|
1603
|
+
.tap do state_selected! end
|
1447
1604
|
end
|
1448
1605
|
end
|
1449
1606
|
|
@@ -1460,8 +1617,10 @@ module Net
|
|
1460
1617
|
args = ["EXAMINE", mailbox]
|
1461
1618
|
args << ["CONDSTORE"] if condstore
|
1462
1619
|
synchronize do
|
1620
|
+
state_unselected! # implicitly closes current mailbox
|
1463
1621
|
@responses.clear
|
1464
1622
|
send_command(*args)
|
1623
|
+
.tap do state_selected! end
|
1465
1624
|
end
|
1466
1625
|
end
|
1467
1626
|
|
@@ -1900,6 +2059,7 @@ module Net
|
|
1900
2059
|
# Related: #unselect
|
1901
2060
|
def close
|
1902
2061
|
send_command("CLOSE")
|
2062
|
+
.tap do state_authenticated! end
|
1903
2063
|
end
|
1904
2064
|
|
1905
2065
|
# Sends an {UNSELECT command [RFC3691 §2]}[https://www.rfc-editor.org/rfc/rfc3691#section-3]
|
@@ -1916,6 +2076,7 @@ module Net
|
|
1916
2076
|
# [RFC3691[https://www.rfc-editor.org/rfc/rfc3691]].
|
1917
2077
|
def unselect
|
1918
2078
|
send_command("UNSELECT")
|
2079
|
+
.tap do state_authenticated! end
|
1919
2080
|
end
|
1920
2081
|
|
1921
2082
|
# call-seq:
|
@@ -2917,8 +3078,8 @@ module Net
|
|
2917
3078
|
raise @exception || Net::IMAP::Error.new("connection closed")
|
2918
3079
|
end
|
2919
3080
|
ensure
|
3081
|
+
remove_response_handler(response_handler)
|
2920
3082
|
unless @receiver_thread_terminating
|
2921
|
-
remove_response_handler(response_handler)
|
2922
3083
|
put_string("DONE#{CRLF}")
|
2923
3084
|
response = get_tagged_response(tag, "IDLE", idle_response_timeout)
|
2924
3085
|
end
|
@@ -3146,6 +3307,10 @@ module Net
|
|
3146
3307
|
# end
|
3147
3308
|
# }
|
3148
3309
|
#
|
3310
|
+
# Response handlers can also be added when the client is created before the
|
3311
|
+
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
3312
|
+
# This ensures every server response is handled, including the #greeting.
|
3313
|
+
#
|
3149
3314
|
# Related: #remove_response_handler, #response_handlers
|
3150
3315
|
def add_response_handler(handler = nil, &block)
|
3151
3316
|
raise ArgumentError, "two Procs are passed" if handler && block
|
@@ -3172,8 +3337,10 @@ module Net
|
|
3172
3337
|
def start_imap_connection
|
3173
3338
|
@greeting = get_server_greeting
|
3174
3339
|
@capabilities = capabilities_from_resp_code @greeting
|
3340
|
+
@response_handlers.each do |handler| handler.call(@greeting) end
|
3175
3341
|
@receiver_thread = start_receiver_thread
|
3176
3342
|
rescue Exception
|
3343
|
+
state_logout!
|
3177
3344
|
@sock.close
|
3178
3345
|
raise
|
3179
3346
|
end
|
@@ -3182,7 +3349,10 @@ module Net
|
|
3182
3349
|
greeting = get_response
|
3183
3350
|
raise Error, "No server greeting - connection closed" unless greeting
|
3184
3351
|
record_untagged_response_code greeting
|
3185
|
-
|
3352
|
+
case greeting.name
|
3353
|
+
when "PREAUTH" then state_authenticated!
|
3354
|
+
when "BYE" then state_logout!; raise ByeResponseError, greeting
|
3355
|
+
end
|
3186
3356
|
greeting
|
3187
3357
|
end
|
3188
3358
|
|
@@ -3214,6 +3384,7 @@ module Net
|
|
3214
3384
|
resp = get_response
|
3215
3385
|
rescue Exception => e
|
3216
3386
|
synchronize do
|
3387
|
+
state_logout!
|
3217
3388
|
@sock.close
|
3218
3389
|
@exception = e
|
3219
3390
|
end
|
@@ -3233,6 +3404,7 @@ module Net
|
|
3233
3404
|
@tagged_response_arrival.broadcast
|
3234
3405
|
case resp.tag
|
3235
3406
|
when @logout_command_tag
|
3407
|
+
state_logout!
|
3236
3408
|
return
|
3237
3409
|
when @continued_command_tag
|
3238
3410
|
@continuation_request_exception =
|
@@ -3242,6 +3414,7 @@ module Net
|
|
3242
3414
|
when UntaggedResponse
|
3243
3415
|
record_untagged_response(resp)
|
3244
3416
|
if resp.name == "BYE" && @logout_command_tag.nil?
|
3417
|
+
state_logout!
|
3245
3418
|
@sock.close
|
3246
3419
|
@exception = ByeResponseError.new(resp)
|
3247
3420
|
connection_closed = true
|
@@ -3249,6 +3422,7 @@ module Net
|
|
3249
3422
|
when ContinuationRequest
|
3250
3423
|
@continuation_request_arrival.signal
|
3251
3424
|
end
|
3425
|
+
state_unselected! if resp in {data: {code: {name: "CLOSED"}}}
|
3252
3426
|
@response_handlers.each do |handler|
|
3253
3427
|
handler.call(resp)
|
3254
3428
|
end
|
@@ -3269,6 +3443,8 @@ module Net
|
|
3269
3443
|
@idle_done_cond.signal
|
3270
3444
|
end
|
3271
3445
|
end
|
3446
|
+
ensure
|
3447
|
+
state_logout!
|
3272
3448
|
end
|
3273
3449
|
|
3274
3450
|
def get_tagged_response(tag, cmd, timeout = nil)
|
@@ -3300,23 +3476,10 @@ module Net
|
|
3300
3476
|
end
|
3301
3477
|
|
3302
3478
|
def get_response
|
3303
|
-
buff =
|
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
|
3479
|
+
buff = @reader.read_response_buffer
|
3315
3480
|
return nil if buff.length == 0
|
3316
|
-
if config.debug?
|
3317
|
-
|
3318
|
-
end
|
3319
|
-
return @parser.parse(buff)
|
3481
|
+
$stderr.print(buff.gsub(/^/n, "S: ")) if config.debug?
|
3482
|
+
@parser.parse(buff)
|
3320
3483
|
end
|
3321
3484
|
|
3322
3485
|
#############################
|
@@ -3620,6 +3783,7 @@ module Net
|
|
3620
3783
|
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
3621
3784
|
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
3622
3785
|
@sock = SSLSocket.new(@sock, ssl_ctx)
|
3786
|
+
@reader = ResponseReader.new(self, @sock)
|
3623
3787
|
@sock.sync_close = true
|
3624
3788
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
3625
3789
|
ssl_socket_connect(@sock, open_timeout)
|
@@ -3629,6 +3793,43 @@ module Net
|
|
3629
3793
|
end
|
3630
3794
|
end
|
3631
3795
|
|
3796
|
+
def state_authenticated!(resp = nil)
|
3797
|
+
synchronize do
|
3798
|
+
@capabilities = capabilities_from_resp_code resp if resp
|
3799
|
+
@connection_state = ConnectionState::Authenticated.new
|
3800
|
+
end
|
3801
|
+
end
|
3802
|
+
|
3803
|
+
def state_selected!
|
3804
|
+
synchronize do
|
3805
|
+
@connection_state = ConnectionState::Selected.new
|
3806
|
+
end
|
3807
|
+
end
|
3808
|
+
|
3809
|
+
def state_unselected!
|
3810
|
+
synchronize do
|
3811
|
+
state_authenticated! if connection_state.to_sym == :selected
|
3812
|
+
end
|
3813
|
+
end
|
3814
|
+
|
3815
|
+
def state_logout!
|
3816
|
+
return true if connection_state in [:logout, *]
|
3817
|
+
synchronize do
|
3818
|
+
return true if connection_state in [:logout, *]
|
3819
|
+
@connection_state = ConnectionState::Logout.new
|
3820
|
+
end
|
3821
|
+
end
|
3822
|
+
|
3823
|
+
# don't wait to aqcuire the lock
|
3824
|
+
def try_state_logout?
|
3825
|
+
return true if connection_state in [:logout, *]
|
3826
|
+
return false unless acquired_lock = mon_try_enter
|
3827
|
+
state_logout!
|
3828
|
+
true
|
3829
|
+
ensure
|
3830
|
+
mon_exit if acquired_lock
|
3831
|
+
end
|
3832
|
+
|
3632
3833
|
def sasl_adapter
|
3633
3834
|
SASLAdapter.new(self, &method(:send_command_with_continuations))
|
3634
3835
|
end
|
@@ -388,9 +388,11 @@ class StringPrepTablesGenerator
|
|
388
388
|
end
|
389
389
|
|
390
390
|
def asgn_mapping(name, replacement = to_map(tables[name]))
|
391
|
+
indent = " " * 2
|
392
|
+
replacement = replacement.inspect.gsub(/" => "/, '"=>"')
|
391
393
|
cname = name.tr(?., ?_).upcase
|
392
|
-
"# Replacements for %s\n%s%s = %
|
393
|
-
"IN_#{name}",
|
394
|
+
"# Replacements for %s\n%s%s = %s.freeze" % [
|
395
|
+
"IN_#{name}", indent, "MAP_#{cname}", replacement,
|
394
396
|
]
|
395
397
|
end
|
396
398
|
|
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.
|
4
|
+
version: 0.5.9
|
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:
|
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.
|
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: []
|