net-imap 0.4.25 → 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.
- checksums.yaml +4 -4
- data/Gemfile +8 -2
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +47 -236
- data/lib/net/imap/config/attr_type_coercion.rb +22 -23
- data/lib/net/imap/config.rb +38 -162
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +6 -33
- data/lib/net/imap/response_data.rb +62 -118
- data/lib/net/imap/response_parser.rb +30 -65
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +1 -1
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +213 -51
- data/lib/net/imap/sasl/login_authenticator.rb +2 -1
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +0 -74
- data/lib/net/imap/sasl.rb +6 -3
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/sequence_set.rb +132 -282
- data/lib/net/imap.rb +118 -301
- data/net-imap.gemspec +1 -1
- metadata +7 -6
- data/lib/net/imap/response_reader.rb +0 -94
- data/lib/net/imap/uidplus_data.rb +0 -326
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
392
|
-
# - #examine: Open a mailbox read-only, and enter the
|
|
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
|
|
415
|
-
# commands, the following commands are valid in the
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
@@ -460,9 +418,6 @@ module Net
|
|
|
460
418
|
# +LITERAL-+, and +SPECIAL-USE+.</em>
|
|
461
419
|
#
|
|
462
420
|
# ==== RFC2087: +QUOTA+
|
|
463
|
-
# +NOTE:+ Only the +STORAGE+ quota resource type is currently supported.
|
|
464
|
-
# - Obsoleted by <tt>QUOTA=RES-*</tt> [RFC9208[https://www.rfc-editor.org/rfc/rfc9208]],
|
|
465
|
-
# although the commands are backward compatible.
|
|
466
421
|
# - #getquota: returns the resource usage and limits for a quota root
|
|
467
422
|
# - #getquotaroot: returns the list of quota roots for a mailbox, as well as
|
|
468
423
|
# their resource usage and limits.
|
|
@@ -494,7 +449,7 @@ module Net
|
|
|
494
449
|
# ==== RFC3691: +UNSELECT+
|
|
495
450
|
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
|
496
451
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
|
497
|
-
# - #unselect: Closes the mailbox and returns to the
|
|
452
|
+
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
|
|
498
453
|
# without expunging any messages.
|
|
499
454
|
#
|
|
500
455
|
# ==== RFC4314: +ACL+
|
|
@@ -575,16 +530,6 @@ module Net
|
|
|
575
530
|
# See FetchData#emailid and FetchData#emailid.
|
|
576
531
|
# - Updates #status with support for the +MAILBOXID+ status attribute.
|
|
577
532
|
#
|
|
578
|
-
# ==== RFC9208: <tt>QUOTA=RES-*</tt>
|
|
579
|
-
# +NOTE:+ Only the +STORAGE+ quota resource type is currently supported.
|
|
580
|
-
# - Obsoletes the +QUOTA+ [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]]
|
|
581
|
-
# extension and provides strict semantics for different resource types.
|
|
582
|
-
# - #getquota: returns the resource usage and limits for a quota root
|
|
583
|
-
# - #getquotaroot: returns the list of quota roots for a mailbox, as well as
|
|
584
|
-
# their resource usage and limits.
|
|
585
|
-
# - #setquota: sets the resource limits for a given quota root.
|
|
586
|
-
# - Updates #status with <tt>"DELETED"</tt> and +DELETED-STORAGE+ attributes.
|
|
587
|
-
#
|
|
588
533
|
# == References
|
|
589
534
|
#
|
|
590
535
|
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
|
|
@@ -694,13 +639,14 @@ module Net
|
|
|
694
639
|
#
|
|
695
640
|
# === \IMAP Extensions
|
|
696
641
|
#
|
|
697
|
-
# [QUOTA[https://
|
|
698
|
-
#
|
|
699
|
-
#
|
|
642
|
+
# [QUOTA[https://tools.ietf.org/html/rfc9208]]::
|
|
643
|
+
# Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
|
|
644
|
+
# March 2022, <https://www.rfc-editor.org/info/rfc9208>.
|
|
700
645
|
#
|
|
701
|
-
#
|
|
702
|
-
# (
|
|
703
|
-
#
|
|
646
|
+
# <em>Note: obsoletes</em>
|
|
647
|
+
# RFC-2087[https://tools.ietf.org/html/rfc2087]<em> (January 1997)</em>.
|
|
648
|
+
# <em>Net::IMAP does not fully support the RFC9208 updates yet.</em>
|
|
649
|
+
# [IDLE[https://tools.ietf.org/html/rfc2177]]::
|
|
704
650
|
# Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
|
|
705
651
|
# June 1997, <https://www.rfc-editor.org/info/rfc2177>.
|
|
706
652
|
# [NAMESPACE[https://tools.ietf.org/html/rfc2342]]::
|
|
@@ -751,15 +697,9 @@ module Net
|
|
|
751
697
|
# Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
|
|
752
698
|
# RFC 8474, DOI 10.17487/RFC8474, September 2018,
|
|
753
699
|
# <https://www.rfc-editor.org/info/rfc8474>.
|
|
754
|
-
# [{QUOTA=RES-*}[https://www.rfc-editor.org/rfc/rfc9208]]::
|
|
755
|
-
# Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
|
|
756
|
-
# March 2022, <https://www.rfc-editor.org/info/rfc9208>.
|
|
757
|
-
#
|
|
758
|
-
# Obsoletes RFC2087[https://www.rfc-editor.org/rfc/rfc2087].
|
|
759
700
|
#
|
|
760
701
|
# === IANA registries
|
|
761
702
|
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
|
|
762
|
-
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
|
763
703
|
# * {IMAP Response Codes}[https://www.iana.org/assignments/imap-response-codes/imap-response-codes.xhtml]
|
|
764
704
|
# * {IMAP Mailbox Name Attributes}[https://www.iana.org/assignments/imap-mailbox-name-attributes/imap-mailbox-name-attributes.xhtml]
|
|
765
705
|
# * {IMAP and JMAP Keywords}[https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml]
|
|
@@ -779,7 +719,7 @@ module Net
|
|
|
779
719
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
|
780
720
|
#
|
|
781
721
|
class IMAP < Protocol
|
|
782
|
-
VERSION = "0.
|
|
722
|
+
VERSION = "0.5.0"
|
|
783
723
|
|
|
784
724
|
# Aliases for supported capabilities, to be used with the #enable command.
|
|
785
725
|
ENABLE_ALIASES = {
|
|
@@ -787,7 +727,6 @@ module Net
|
|
|
787
727
|
"UTF8=ONLY" => "UTF8=ACCEPT",
|
|
788
728
|
}.freeze
|
|
789
729
|
|
|
790
|
-
autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__)
|
|
791
730
|
autoload :SASL, File.expand_path("imap/sasl", __dir__)
|
|
792
731
|
autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
|
|
793
732
|
autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
|
|
@@ -802,11 +741,9 @@ module Net
|
|
|
802
741
|
def self.config; Config.global end
|
|
803
742
|
|
|
804
743
|
# Returns the global debug mode.
|
|
805
|
-
# Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
|
|
806
744
|
def self.debug; config.debug end
|
|
807
745
|
|
|
808
746
|
# Sets the global debug mode.
|
|
809
|
-
# Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
|
|
810
747
|
def self.debug=(val)
|
|
811
748
|
config.debug = val
|
|
812
749
|
end
|
|
@@ -827,7 +764,7 @@ module Net
|
|
|
827
764
|
alias default_ssl_port default_tls_port
|
|
828
765
|
end
|
|
829
766
|
|
|
830
|
-
# Returns the initial greeting
|
|
767
|
+
# Returns the initial greeting the server, an UntaggedResponse.
|
|
831
768
|
attr_reader :greeting
|
|
832
769
|
|
|
833
770
|
# The client configuration. See Net::IMAP::Config.
|
|
@@ -836,28 +773,13 @@ module Net
|
|
|
836
773
|
# Net::IMAP.config.
|
|
837
774
|
attr_reader :config
|
|
838
775
|
|
|
839
|
-
|
|
840
|
-
#
|
|
841
|
-
#
|
|
842
|
-
|
|
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
|
|
843
780
|
|
|
844
|
-
##
|
|
845
|
-
# :attr_reader: idle_response_timeout
|
|
846
781
|
# Seconds to wait until an IDLE response is received.
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
##
|
|
850
|
-
# :attr_accessor: max_response_size
|
|
851
|
-
#
|
|
852
|
-
# The maximum allowed server response size, in bytes.
|
|
853
|
-
# Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
|
|
854
|
-
|
|
855
|
-
# :stopdoc:
|
|
856
|
-
def open_timeout; config.open_timeout end
|
|
857
|
-
def idle_response_timeout; config.idle_response_timeout end
|
|
858
|
-
def max_response_size; config.max_response_size end
|
|
859
|
-
def max_response_size=(val) config.max_response_size = val end
|
|
860
|
-
# :startdoc:
|
|
782
|
+
def idle_response_timeout; config.idle_response_timeout end
|
|
861
783
|
|
|
862
784
|
# The hostname this client connected to
|
|
863
785
|
attr_reader :host
|
|
@@ -913,12 +835,6 @@ module Net
|
|
|
913
835
|
#
|
|
914
836
|
# See DeprecatedClientOptions.new for deprecated SSL arguments.
|
|
915
837
|
#
|
|
916
|
-
# [response_handlers]
|
|
917
|
-
# A list of response handlers to be added before the receiver thread is
|
|
918
|
-
# started. This ensures every server response is handled, including the
|
|
919
|
-
# #greeting. Note that the greeting is handled in the current thread, but
|
|
920
|
-
# all other responses are handled in the receiver thread.
|
|
921
|
-
#
|
|
922
838
|
# [config]
|
|
923
839
|
# A Net::IMAP::Config object to use as the basis for #config. By default,
|
|
924
840
|
# the global Net::IMAP.config is used.
|
|
@@ -990,7 +906,7 @@ module Net
|
|
|
990
906
|
# [Net::IMAP::ByeResponseError]
|
|
991
907
|
# Connected to the host successfully, but it immediately said goodbye.
|
|
992
908
|
#
|
|
993
|
-
def initialize(host, port: nil, ssl:
|
|
909
|
+
def initialize(host, port: nil, ssl: nil,
|
|
994
910
|
config: Config.global, **config_options)
|
|
995
911
|
super()
|
|
996
912
|
# Config options
|
|
@@ -1013,7 +929,6 @@ module Net
|
|
|
1013
929
|
@receiver_thread = nil
|
|
1014
930
|
@receiver_thread_exception = nil
|
|
1015
931
|
@receiver_thread_terminating = false
|
|
1016
|
-
response_handlers&.each do add_response_handler(_1) end
|
|
1017
932
|
|
|
1018
933
|
# Client Protocol Sender (including state for currently running commands)
|
|
1019
934
|
@tag_prefix = "RUBY"
|
|
@@ -1029,12 +944,8 @@ module Net
|
|
|
1029
944
|
# Connection
|
|
1030
945
|
@tls_verified = false
|
|
1031
946
|
@sock = tcp_socket(@host, @port)
|
|
1032
|
-
@reader = ResponseReader.new(self, @sock)
|
|
1033
947
|
start_tls_session if ssl_ctx
|
|
1034
948
|
start_imap_connection
|
|
1035
|
-
|
|
1036
|
-
# DEPRECATED: to remove in next version
|
|
1037
|
-
@client_thread = Thread.current
|
|
1038
949
|
end
|
|
1039
950
|
|
|
1040
951
|
# Returns true after the TLS negotiation has completed and the remote
|
|
@@ -1042,29 +953,28 @@ module Net
|
|
|
1042
953
|
# but peer verification was disabled.
|
|
1043
954
|
def tls_verified?; @tls_verified end
|
|
1044
955
|
|
|
1045
|
-
def client_thread # :nodoc:
|
|
1046
|
-
warn "Net::IMAP#client_thread is deprecated and will be removed soon."
|
|
1047
|
-
@client_thread
|
|
1048
|
-
end
|
|
1049
|
-
|
|
1050
956
|
# Disconnects from the server.
|
|
1051
957
|
#
|
|
1052
|
-
# Waits for receiver thread to close before returning. Slow or stuck
|
|
1053
|
-
# response handlers can cause #disconnect to hang until they complete.
|
|
1054
|
-
#
|
|
1055
958
|
# Related: #logout, #logout!
|
|
1056
959
|
def disconnect
|
|
1057
960
|
return if disconnected?
|
|
1058
|
-
in_receiver_thread = Thread.current == @receiver_thread
|
|
1059
961
|
begin
|
|
1060
|
-
|
|
962
|
+
begin
|
|
963
|
+
# try to call SSL::SSLSocket#io.
|
|
964
|
+
@sock.io.shutdown
|
|
965
|
+
rescue NoMethodError
|
|
966
|
+
# @sock is not an SSL::SSLSocket.
|
|
967
|
+
@sock.shutdown
|
|
968
|
+
end
|
|
1061
969
|
rescue Errno::ENOTCONN
|
|
1062
970
|
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
|
|
1063
971
|
rescue Exception => e
|
|
1064
|
-
@receiver_thread.raise(e)
|
|
972
|
+
@receiver_thread.raise(e)
|
|
973
|
+
end
|
|
974
|
+
@receiver_thread.join
|
|
975
|
+
synchronize do
|
|
976
|
+
@sock.close
|
|
1065
977
|
end
|
|
1066
|
-
@sock.close
|
|
1067
|
-
@receiver_thread.join unless mon_owned? || in_receiver_thread
|
|
1068
978
|
raise e if e
|
|
1069
979
|
end
|
|
1070
980
|
|
|
@@ -1286,10 +1196,6 @@ module Net
|
|
|
1286
1196
|
# both successful. Any error indicates that the connection has not been
|
|
1287
1197
|
# secured.
|
|
1288
1198
|
#
|
|
1289
|
-
# After the server agrees to start a TLS connection, this method waits up to
|
|
1290
|
-
# {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
|
|
1291
|
-
# +Net::OpenTimeout+.
|
|
1292
|
-
#
|
|
1293
1199
|
# *Note:*
|
|
1294
1200
|
# >>>
|
|
1295
1201
|
# Any #response_handlers added before STARTTLS should be aware that the
|
|
@@ -1308,30 +1214,13 @@ module Net
|
|
|
1308
1214
|
#
|
|
1309
1215
|
def starttls(**options)
|
|
1310
1216
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
|
1311
|
-
|
|
1312
|
-
error = nil
|
|
1313
|
-
ok = send_command("STARTTLS") do |resp|
|
|
1217
|
+
send_command("STARTTLS") do |resp|
|
|
1314
1218
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
|
1315
|
-
handled = true
|
|
1316
1219
|
clear_cached_capabilities
|
|
1317
1220
|
clear_responses
|
|
1318
1221
|
start_tls_session
|
|
1319
1222
|
end
|
|
1320
|
-
rescue Exception => error
|
|
1321
|
-
raise # note that the error backtrace is in the receiver_thread
|
|
1322
1223
|
end
|
|
1323
|
-
if error
|
|
1324
|
-
disconnect
|
|
1325
|
-
raise error
|
|
1326
|
-
end
|
|
1327
|
-
unless handled
|
|
1328
|
-
disconnect
|
|
1329
|
-
raise InvalidResponseError,
|
|
1330
|
-
"STARTTLS handler was bypassed, although server responded %p" % [
|
|
1331
|
-
ok.raw_data.chomp
|
|
1332
|
-
]
|
|
1333
|
-
end
|
|
1334
|
-
ok
|
|
1335
1224
|
end
|
|
1336
1225
|
|
|
1337
1226
|
# :call-seq:
|
|
@@ -1347,6 +1236,9 @@ module Net
|
|
|
1347
1236
|
# +SASL-IR+ capability, below). Defaults to the #config value for
|
|
1348
1237
|
# {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
|
|
1349
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
|
+
#
|
|
1350
1242
|
# All other arguments are forwarded to the registered SASL authenticator for
|
|
1351
1243
|
# the requested mechanism. <em>The documentation for each individual
|
|
1352
1244
|
# mechanism must be consulted for its specific parameters.</em>
|
|
@@ -1441,29 +1333,9 @@ module Net
|
|
|
1441
1333
|
# Previously cached #capabilities will be cleared when this method
|
|
1442
1334
|
# completes. If the TaggedResponse to #authenticate includes updated
|
|
1443
1335
|
# capabilities, they will be cached.
|
|
1444
|
-
def authenticate(
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
mechanism = mechanism.to_s.tr("_", "-").upcase
|
|
1448
|
-
authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
|
|
1449
|
-
cmdargs = ["AUTHENTICATE", mechanism]
|
|
1450
|
-
if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
|
|
1451
|
-
authenticator.respond_to?(:initial_response?) &&
|
|
1452
|
-
authenticator.initial_response?
|
|
1453
|
-
response = authenticator.process(nil)
|
|
1454
|
-
cmdargs << (response.empty? ? "=" : [response].pack("m0"))
|
|
1455
|
-
end
|
|
1456
|
-
result = send_command_with_continuations(*cmdargs) {|data|
|
|
1457
|
-
challenge = data.unpack1("m")
|
|
1458
|
-
response = authenticator.process challenge
|
|
1459
|
-
[response].pack("m0")
|
|
1460
|
-
}
|
|
1461
|
-
if authenticator.respond_to?(:done?) && !authenticator.done?
|
|
1462
|
-
logout!
|
|
1463
|
-
raise SASL::AuthenticationIncomplete, result
|
|
1464
|
-
end
|
|
1465
|
-
@capabilities = capabilities_from_resp_code result
|
|
1466
|
-
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 }
|
|
1467
1339
|
end
|
|
1468
1340
|
|
|
1469
1341
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
|
@@ -1483,13 +1355,9 @@ module Net
|
|
|
1483
1355
|
# ===== Capabilities
|
|
1484
1356
|
#
|
|
1485
1357
|
# An IMAP client MUST NOT call #login when the server advertises the
|
|
1486
|
-
# +LOGINDISABLED+ capability.
|
|
1487
|
-
#
|
|
1488
|
-
#
|
|
1489
|
-
# raise "Remote server has disabled the login command"
|
|
1490
|
-
# else
|
|
1491
|
-
# imap.login username, password
|
|
1492
|
-
# end
|
|
1358
|
+
# +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
|
|
1359
|
+
# LoginDisabledError when that capability is present. See
|
|
1360
|
+
# Config#enforce_logindisabled.
|
|
1493
1361
|
#
|
|
1494
1362
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
|
1495
1363
|
# Cached capabilities _must_ be invalidated after this method completes.
|
|
@@ -1497,6 +1365,9 @@ module Net
|
|
|
1497
1365
|
# ResponseCode.
|
|
1498
1366
|
#
|
|
1499
1367
|
def login(user, password)
|
|
1368
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
|
1369
|
+
raise LoginDisabledError
|
|
1370
|
+
end
|
|
1500
1371
|
send_command("LOGIN", user, password)
|
|
1501
1372
|
.tap { @capabilities = capabilities_from_resp_code _1 }
|
|
1502
1373
|
end
|
|
@@ -1765,18 +1636,12 @@ module Net
|
|
|
1765
1636
|
# to both admin and user. If this mailbox exists, it returns an array
|
|
1766
1637
|
# containing objects of type MailboxQuotaRoot and MailboxQuota.
|
|
1767
1638
|
#
|
|
1768
|
-
# *NOTE:* Currently, Net::IMAP only supports +QUOTA+ responses with a single
|
|
1769
|
-
# resource type. This is usually +STORAGE+, but you may need to verify this
|
|
1770
|
-
# with UntaggedResponse#raw_data.
|
|
1771
|
-
#
|
|
1772
1639
|
# Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
|
|
1773
1640
|
#
|
|
1774
1641
|
# ===== Capabilities
|
|
1775
1642
|
#
|
|
1776
|
-
#
|
|
1777
|
-
#
|
|
1778
|
-
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208] for each supported
|
|
1779
|
-
# resource type.
|
|
1643
|
+
# The server's capabilities must include +QUOTA+
|
|
1644
|
+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
|
|
1780
1645
|
def getquotaroot(mailbox)
|
|
1781
1646
|
synchronize do
|
|
1782
1647
|
send_command("GETQUOTAROOT", mailbox)
|
|
@@ -1788,59 +1653,41 @@ module Net
|
|
|
1788
1653
|
end
|
|
1789
1654
|
|
|
1790
1655
|
# Sends a {GETQUOTA command [RFC2087 §4.2]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.2]
|
|
1791
|
-
#
|
|
1792
|
-
# containing a MailboxQuota object is returned.
|
|
1793
|
-
#
|
|
1794
|
-
# The names of quota roots that are applicable to a particular mailbox can
|
|
1795
|
-
# be discovered with #getquotaroot.
|
|
1796
|
-
#
|
|
1797
|
-
# *NOTE:* Currently, Net::IMAP only supports +QUOTA+ responses with a single
|
|
1798
|
-
# resource type. This is usually +STORAGE+, but you may need to verify this
|
|
1799
|
-
# with UntaggedResponse#raw_data.
|
|
1656
|
+
# along with specified +mailbox+. If this mailbox exists, then an array
|
|
1657
|
+
# containing a MailboxQuota object is returned. This command is generally
|
|
1658
|
+
# only available to server admin.
|
|
1800
1659
|
#
|
|
1801
1660
|
# Related: #getquotaroot, #setquota, MailboxQuota
|
|
1802
1661
|
#
|
|
1803
1662
|
# ===== Capabilities
|
|
1804
1663
|
#
|
|
1805
|
-
#
|
|
1806
|
-
#
|
|
1807
|
-
|
|
1808
|
-
# resource type.
|
|
1809
|
-
def getquota(quota_root)
|
|
1664
|
+
# The server's capabilities must include +QUOTA+
|
|
1665
|
+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
|
|
1666
|
+
def getquota(mailbox)
|
|
1810
1667
|
synchronize do
|
|
1811
|
-
send_command("GETQUOTA",
|
|
1668
|
+
send_command("GETQUOTA", mailbox)
|
|
1812
1669
|
clear_responses("QUOTA")
|
|
1813
1670
|
end
|
|
1814
1671
|
end
|
|
1815
1672
|
|
|
1816
1673
|
# Sends a {SETQUOTA command [RFC2087 §4.1]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.1]
|
|
1817
|
-
# along with the specified +
|
|
1818
|
-
# +
|
|
1819
|
-
#
|
|
1820
|
-
#
|
|
1821
|
-
# imap.setquota "#user/alice", 100
|
|
1822
|
-
# imap.getquota "#user/alice"
|
|
1823
|
-
# # => [#<struct Net::IMAP::MailboxQuota mailbox="#user/alice" usage=54 quota=100>]
|
|
1824
|
-
#
|
|
1825
|
-
# Typically one needs to be logged in as a server admin for this to work.
|
|
1826
|
-
#
|
|
1827
|
-
# *NOTE:* Currently, Net::IMAP only supports setting +STORAGE+ quota limits.
|
|
1674
|
+
# along with the specified +mailbox+ and +quota+. If +quota+ is nil, then
|
|
1675
|
+
# +quota+ will be unset for that mailbox. Typically one needs to be logged
|
|
1676
|
+
# in as a server admin for this to work.
|
|
1828
1677
|
#
|
|
1829
1678
|
# Related: #getquota, #getquotaroot
|
|
1830
1679
|
#
|
|
1831
1680
|
# ===== Capabilities
|
|
1832
1681
|
#
|
|
1833
|
-
#
|
|
1834
|
-
#
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
if storage_limit.nil?
|
|
1839
|
-
list = []
|
|
1682
|
+
# The server's capabilities must include +QUOTA+
|
|
1683
|
+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
|
|
1684
|
+
def setquota(mailbox, quota)
|
|
1685
|
+
if quota.nil?
|
|
1686
|
+
data = '()'
|
|
1840
1687
|
else
|
|
1841
|
-
|
|
1688
|
+
data = '(STORAGE ' + quota.to_s + ')'
|
|
1842
1689
|
end
|
|
1843
|
-
send_command("SETQUOTA",
|
|
1690
|
+
send_command("SETQUOTA", mailbox, RawData.new(data))
|
|
1844
1691
|
end
|
|
1845
1692
|
|
|
1846
1693
|
# Sends a {SETACL command [RFC4314 §3.1]}[https://www.rfc-editor.org/rfc/rfc4314#section-3.1]
|
|
@@ -1947,10 +1794,7 @@ module Net
|
|
|
1947
1794
|
# <tt>STATUS=SIZE</tt>
|
|
1948
1795
|
# {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
|
|
1949
1796
|
#
|
|
1950
|
-
# +DELETED+
|
|
1951
|
-
# +IMAP4rev2+.
|
|
1952
|
-
# or <tt>QUOTA=RES-MESSAGES</tt>
|
|
1953
|
-
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208.html].
|
|
1797
|
+
# +DELETED+ requires the server's capabilities to include +IMAP4rev2+.
|
|
1954
1798
|
#
|
|
1955
1799
|
# +HIGHESTMODSEQ+ requires the server's capabilities to include +CONDSTORE+
|
|
1956
1800
|
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
|
@@ -2080,7 +1924,7 @@ module Net
|
|
|
2080
1924
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
|
2081
1925
|
def uid_expunge(uid_set)
|
|
2082
1926
|
synchronize do
|
|
2083
|
-
send_command("UID EXPUNGE",
|
|
1927
|
+
send_command("UID EXPUNGE", SequenceSet.new(uid_set))
|
|
2084
1928
|
clear_responses("EXPUNGE")
|
|
2085
1929
|
end
|
|
2086
1930
|
end
|
|
@@ -2091,8 +1935,6 @@ module Net
|
|
|
2091
1935
|
# string holding the entire search string, or a single-dimension array of
|
|
2092
1936
|
# search keywords and arguments.
|
|
2093
1937
|
#
|
|
2094
|
-
# <em>Please note</em> the warning (below) when +keys+ is a String.
|
|
2095
|
-
#
|
|
2096
1938
|
# Returns a SearchResult object. SearchResult inherits from Array (for
|
|
2097
1939
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
|
2098
1940
|
# capability has been enabled.
|
|
@@ -2101,14 +1943,6 @@ module Net
|
|
|
2101
1943
|
#
|
|
2102
1944
|
# ===== Search criteria
|
|
2103
1945
|
#
|
|
2104
|
-
# >>>
|
|
2105
|
-
# When +keys+ is an Array, elements in the array will be validated and
|
|
2106
|
-
# formatted. When +keys+ is a String, it will be sent <em>with
|
|
2107
|
-
# minimal validation and no encoding or formatting</em>.
|
|
2108
|
-
#
|
|
2109
|
-
# <em>*WARNING:* Although CRLF is prohibited, this is vulnerable to other
|
|
2110
|
-
# types of attribute injection attack if unvetted user input is used.</em>
|
|
2111
|
-
#
|
|
2112
1946
|
# For a full list of search criteria,
|
|
2113
1947
|
# see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
|
|
2114
1948
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
|
@@ -2174,8 +2008,7 @@ module Net
|
|
|
2174
2008
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
|
2175
2009
|
# capability has been enabled.
|
|
2176
2010
|
#
|
|
2177
|
-
# See #search for documentation of
|
|
2178
|
-
# warning for when +keys+ is a String.
|
|
2011
|
+
# See #search for documentation of search criteria.
|
|
2179
2012
|
def uid_search(keys, charset = nil)
|
|
2180
2013
|
return search_internal("UID SEARCH", keys, charset)
|
|
2181
2014
|
end
|
|
@@ -2197,13 +2030,6 @@ module Net
|
|
|
2197
2030
|
#
|
|
2198
2031
|
# +attr+ is a list of attributes to fetch; see the documentation
|
|
2199
2032
|
# for FetchData for a list of valid attributes.
|
|
2200
|
-
# >>>
|
|
2201
|
-
# When +attr+ is a String, it will be sent <em>with minimal validation and
|
|
2202
|
-
# no encoding or formatting</em>. When +attr+ is an Array, each String in
|
|
2203
|
-
# +attr+ will be sent this way.
|
|
2204
|
-
#
|
|
2205
|
-
# <em>*WARNING:* Although CRLF is prohibited, this is vulnerable to other
|
|
2206
|
-
# types of attribute injection attack if unvetted user input is used.</em>
|
|
2207
2033
|
#
|
|
2208
2034
|
# +changedsince+ is an optional integer mod-sequence. It limits results to
|
|
2209
2035
|
# messages with a mod-sequence greater than +changedsince+.
|
|
@@ -2252,8 +2078,6 @@ module Net
|
|
|
2252
2078
|
# Similar to #fetch, but the +set+ parameter contains unique identifiers
|
|
2253
2079
|
# instead of message sequence numbers.
|
|
2254
2080
|
#
|
|
2255
|
-
# +attr+ behaves the same as with #fetch. <em>Please note</em> the #fetch
|
|
2256
|
-
# warning on the +attr+ argument.
|
|
2257
2081
|
# >>>
|
|
2258
2082
|
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
|
|
2259
2083
|
# part of any +FETCH+ response caused by a +UID+ command, regardless of
|
|
@@ -2406,10 +2230,8 @@ module Net
|
|
|
2406
2230
|
|
|
2407
2231
|
# Sends a {SORT command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2408
2232
|
# to search a mailbox for messages that match +search_keys+ and return an
|
|
2409
|
-
# array of message sequence numbers, sorted by +sort_keys+.
|
|
2410
|
-
#
|
|
2411
|
-
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2412
|
-
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2233
|
+
# array of message sequence numbers, sorted by +sort_keys+. +search_keys+
|
|
2234
|
+
# are interpreted the same as for #search.
|
|
2413
2235
|
#
|
|
2414
2236
|
#--
|
|
2415
2237
|
# TODO: describe +sort_keys+
|
|
@@ -2434,10 +2256,8 @@ module Net
|
|
|
2434
2256
|
|
|
2435
2257
|
# Sends a {UID SORT command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2436
2258
|
# to search a mailbox for messages that match +search_keys+ and return an
|
|
2437
|
-
# array of unique identifiers, sorted by +sort_keys+.
|
|
2438
|
-
#
|
|
2439
|
-
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2440
|
-
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2259
|
+
# array of unique identifiers, sorted by +sort_keys+. +search_keys+ are
|
|
2260
|
+
# interpreted the same as for #search.
|
|
2441
2261
|
#
|
|
2442
2262
|
# Related: #sort, #search, #uid_search, #thread, #uid_thread
|
|
2443
2263
|
#
|
|
@@ -2451,10 +2271,8 @@ module Net
|
|
|
2451
2271
|
|
|
2452
2272
|
# Sends a {THREAD command [RFC5256 §3]}[https://www.rfc-editor.org/rfc/rfc5256#section-3]
|
|
2453
2273
|
# to search a mailbox and return message sequence numbers in threaded
|
|
2454
|
-
# format, as a ThreadMember tree.
|
|
2455
|
-
#
|
|
2456
|
-
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2457
|
-
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2274
|
+
# format, as a ThreadMember tree. +search_keys+ are interpreted the same as
|
|
2275
|
+
# for #search.
|
|
2458
2276
|
#
|
|
2459
2277
|
# The supported algorithms are:
|
|
2460
2278
|
#
|
|
@@ -2480,9 +2298,6 @@ module Net
|
|
|
2480
2298
|
# Similar to #thread, but returns unique identifiers instead of
|
|
2481
2299
|
# message sequence numbers.
|
|
2482
2300
|
#
|
|
2483
|
-
# +search_keys+ are interpreted the same as the +criteria+ argument for
|
|
2484
|
-
# #search. <em>Please note</em> the #search warning for String +criteria+.
|
|
2485
|
-
#
|
|
2486
2301
|
# Related: #thread, #search, #uid_search, #sort, #uid_sort
|
|
2487
2302
|
#
|
|
2488
2303
|
# ===== Capabilities
|
|
@@ -2570,11 +2385,10 @@ module Net
|
|
|
2570
2385
|
capabilities = capabilities
|
|
2571
2386
|
.flatten
|
|
2572
2387
|
.map {|e| ENABLE_ALIASES[e] || e }
|
|
2573
|
-
.flat_map { _1.is_a?(String) && !_1.empty? ? _1.split(/ /, -1) : [_1] }
|
|
2574
2388
|
.uniq
|
|
2575
|
-
.
|
|
2389
|
+
.join(' ')
|
|
2576
2390
|
synchronize do
|
|
2577
|
-
send_command("ENABLE
|
|
2391
|
+
send_command("ENABLE #{capabilities}")
|
|
2578
2392
|
result = clear_responses("ENABLED").last || []
|
|
2579
2393
|
@utf8_strings ||= result.include? "UTF8=ACCEPT"
|
|
2580
2394
|
@utf8_strings ||= result.include? "IMAP4REV2"
|
|
@@ -2768,10 +2582,10 @@ module Net
|
|
|
2768
2582
|
when :raise
|
|
2769
2583
|
raise ArgumentError, RESPONSES_DEPRECATION_MSG
|
|
2770
2584
|
when :warn
|
|
2771
|
-
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1)
|
|
2585
|
+
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
|
2772
2586
|
when :frozen_dup
|
|
2773
2587
|
synchronize {
|
|
2774
|
-
responses = @responses.transform_values
|
|
2588
|
+
responses = @responses.transform_values(&:freeze)
|
|
2775
2589
|
responses.default_proc = nil
|
|
2776
2590
|
responses.default = [].freeze
|
|
2777
2591
|
return responses.freeze
|
|
@@ -2858,10 +2672,6 @@ module Net
|
|
|
2858
2672
|
# end
|
|
2859
2673
|
# }
|
|
2860
2674
|
#
|
|
2861
|
-
# Response handlers can also be added when the client is created before the
|
|
2862
|
-
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
|
2863
|
-
# This ensures every server response is handled, including the #greeting.
|
|
2864
|
-
#
|
|
2865
2675
|
# Related: #remove_response_handler, #response_handlers
|
|
2866
2676
|
def add_response_handler(handler = nil, &block)
|
|
2867
2677
|
raise ArgumentError, "two Procs are passed" if handler && block
|
|
@@ -2888,7 +2698,6 @@ module Net
|
|
|
2888
2698
|
def start_imap_connection
|
|
2889
2699
|
@greeting = get_server_greeting
|
|
2890
2700
|
@capabilities = capabilities_from_resp_code @greeting
|
|
2891
|
-
@response_handlers.each do |handler| handler.call(@greeting) end
|
|
2892
2701
|
@receiver_thread = start_receiver_thread
|
|
2893
2702
|
rescue Exception
|
|
2894
2703
|
@sock.close
|
|
@@ -3017,10 +2826,23 @@ module Net
|
|
|
3017
2826
|
end
|
|
3018
2827
|
|
|
3019
2828
|
def get_response
|
|
3020
|
-
buff =
|
|
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
|
|
3021
2841
|
return nil if buff.length == 0
|
|
3022
|
-
|
|
3023
|
-
|
|
2842
|
+
if config.debug?
|
|
2843
|
+
$stderr.print(buff.gsub(/^/n, "S: "))
|
|
2844
|
+
end
|
|
2845
|
+
return @parser.parse(buff)
|
|
3024
2846
|
end
|
|
3025
2847
|
|
|
3026
2848
|
#############################
|
|
@@ -3072,7 +2894,6 @@ module Net
|
|
|
3072
2894
|
put_string(" ")
|
|
3073
2895
|
send_data(i, tag)
|
|
3074
2896
|
end
|
|
3075
|
-
guard_against_tagged_response_skipping_handler!(tag)
|
|
3076
2897
|
put_string(CRLF)
|
|
3077
2898
|
if cmd == "LOGOUT"
|
|
3078
2899
|
@logout_command_tag = tag
|
|
@@ -3088,17 +2909,6 @@ module Net
|
|
|
3088
2909
|
end
|
|
3089
2910
|
end
|
|
3090
2911
|
end
|
|
3091
|
-
rescue InvalidResponseError
|
|
3092
|
-
disconnect
|
|
3093
|
-
raise
|
|
3094
|
-
end
|
|
3095
|
-
|
|
3096
|
-
def guard_against_tagged_response_skipping_handler!(tag)
|
|
3097
|
-
return unless (resp = @tagged_responses[tag])&.name&.upcase == "OK"
|
|
3098
|
-
raise(InvalidResponseError,
|
|
3099
|
-
"Server sent tagged 'OK' before command was finished: %p. " \
|
|
3100
|
-
"This could indicate a malicious server or client-side " \
|
|
3101
|
-
"command injection. Disconnecting." % [resp.raw_data.chomp])
|
|
3102
2912
|
end
|
|
3103
2913
|
|
|
3104
2914
|
def generate_tag
|
|
@@ -3121,6 +2931,14 @@ module Net
|
|
|
3121
2931
|
end
|
|
3122
2932
|
end
|
|
3123
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
|
+
|
|
3124
2942
|
def search_internal(cmd, keys, charset)
|
|
3125
2943
|
if keys.instance_of?(String)
|
|
3126
2944
|
keys = [RawData.new(keys)]
|
|
@@ -3154,17 +2972,17 @@ module Net
|
|
|
3154
2972
|
synchronize do
|
|
3155
2973
|
clear_responses("FETCH")
|
|
3156
2974
|
if mod
|
|
3157
|
-
send_command(cmd,
|
|
2975
|
+
send_command(cmd, SequenceSet.new(set), attr, mod)
|
|
3158
2976
|
else
|
|
3159
|
-
send_command(cmd,
|
|
2977
|
+
send_command(cmd, SequenceSet.new(set), attr)
|
|
3160
2978
|
end
|
|
3161
2979
|
clear_responses("FETCH")
|
|
3162
2980
|
end
|
|
3163
2981
|
end
|
|
3164
2982
|
|
|
3165
2983
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
|
3166
|
-
attr =
|
|
3167
|
-
args = [
|
|
2984
|
+
attr = RawData.new(attr) if attr.instance_of?(String)
|
|
2985
|
+
args = [SequenceSet.new(set)]
|
|
3168
2986
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
|
3169
2987
|
args << attr << flags
|
|
3170
2988
|
synchronize do
|
|
@@ -3175,7 +2993,7 @@ module Net
|
|
|
3175
2993
|
end
|
|
3176
2994
|
|
|
3177
2995
|
def copy_internal(cmd, set, mailbox)
|
|
3178
|
-
send_command(cmd,
|
|
2996
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
|
3179
2997
|
end
|
|
3180
2998
|
|
|
3181
2999
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
|
@@ -3206,7 +3024,7 @@ module Net
|
|
|
3206
3024
|
keys.collect! do |i|
|
|
3207
3025
|
case i
|
|
3208
3026
|
when -1, Range, Array
|
|
3209
|
-
|
|
3027
|
+
SequenceSet.new(i)
|
|
3210
3028
|
else
|
|
3211
3029
|
i
|
|
3212
3030
|
end
|
|
@@ -3233,7 +3051,6 @@ module Net
|
|
|
3233
3051
|
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
|
3234
3052
|
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
|
3235
3053
|
@sock = SSLSocket.new(@sock, ssl_ctx)
|
|
3236
|
-
@reader = ResponseReader.new(self, @sock)
|
|
3237
3054
|
@sock.sync_close = true
|
|
3238
3055
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
|
3239
3056
|
ssl_socket_connect(@sock, open_timeout)
|