net-imap 0.4.24 → 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 +32 -182
- 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 +18 -45
- 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 +97 -269
- data/net-imap.gemspec +1 -1
- metadata +7 -6
- data/lib/net/imap/response_reader.rb +0 -82
- 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,11 +953,6 @@ 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
958
|
# Related: #logout, #logout!
|
|
@@ -1290,10 +1196,6 @@ module Net
|
|
|
1290
1196
|
# both successful. Any error indicates that the connection has not been
|
|
1291
1197
|
# secured.
|
|
1292
1198
|
#
|
|
1293
|
-
# After the server agrees to start a TLS connection, this method waits up to
|
|
1294
|
-
# {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
|
|
1295
|
-
# +Net::OpenTimeout+.
|
|
1296
|
-
#
|
|
1297
1199
|
# *Note:*
|
|
1298
1200
|
# >>>
|
|
1299
1201
|
# Any #response_handlers added before STARTTLS should be aware that the
|
|
@@ -1312,30 +1214,13 @@ module Net
|
|
|
1312
1214
|
#
|
|
1313
1215
|
def starttls(**options)
|
|
1314
1216
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
|
1315
|
-
|
|
1316
|
-
error = nil
|
|
1317
|
-
ok = send_command("STARTTLS") do |resp|
|
|
1217
|
+
send_command("STARTTLS") do |resp|
|
|
1318
1218
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
|
1319
|
-
handled = true
|
|
1320
1219
|
clear_cached_capabilities
|
|
1321
1220
|
clear_responses
|
|
1322
1221
|
start_tls_session
|
|
1323
1222
|
end
|
|
1324
|
-
rescue Exception => error
|
|
1325
|
-
raise # note that the error backtrace is in the receiver_thread
|
|
1326
|
-
end
|
|
1327
|
-
if error
|
|
1328
|
-
disconnect
|
|
1329
|
-
raise error
|
|
1330
1223
|
end
|
|
1331
|
-
unless handled
|
|
1332
|
-
disconnect
|
|
1333
|
-
raise InvalidResponseError,
|
|
1334
|
-
"STARTTLS handler was bypassed, although server responded %p" % [
|
|
1335
|
-
ok.raw_data.chomp
|
|
1336
|
-
]
|
|
1337
|
-
end
|
|
1338
|
-
ok
|
|
1339
1224
|
end
|
|
1340
1225
|
|
|
1341
1226
|
# :call-seq:
|
|
@@ -1351,6 +1236,9 @@ module Net
|
|
|
1351
1236
|
# +SASL-IR+ capability, below). Defaults to the #config value for
|
|
1352
1237
|
# {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
|
|
1353
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
|
+
#
|
|
1354
1242
|
# All other arguments are forwarded to the registered SASL authenticator for
|
|
1355
1243
|
# the requested mechanism. <em>The documentation for each individual
|
|
1356
1244
|
# mechanism must be consulted for its specific parameters.</em>
|
|
@@ -1445,29 +1333,9 @@ module Net
|
|
|
1445
1333
|
# Previously cached #capabilities will be cleared when this method
|
|
1446
1334
|
# completes. If the TaggedResponse to #authenticate includes updated
|
|
1447
1335
|
# capabilities, they will be cached.
|
|
1448
|
-
def authenticate(
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
mechanism = mechanism.to_s.tr("_", "-").upcase
|
|
1452
|
-
authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
|
|
1453
|
-
cmdargs = ["AUTHENTICATE", mechanism]
|
|
1454
|
-
if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
|
|
1455
|
-
authenticator.respond_to?(:initial_response?) &&
|
|
1456
|
-
authenticator.initial_response?
|
|
1457
|
-
response = authenticator.process(nil)
|
|
1458
|
-
cmdargs << (response.empty? ? "=" : [response].pack("m0"))
|
|
1459
|
-
end
|
|
1460
|
-
result = send_command_with_continuations(*cmdargs) {|data|
|
|
1461
|
-
challenge = data.unpack1("m")
|
|
1462
|
-
response = authenticator.process challenge
|
|
1463
|
-
[response].pack("m0")
|
|
1464
|
-
}
|
|
1465
|
-
if authenticator.respond_to?(:done?) && !authenticator.done?
|
|
1466
|
-
logout!
|
|
1467
|
-
raise SASL::AuthenticationIncomplete, result
|
|
1468
|
-
end
|
|
1469
|
-
@capabilities = capabilities_from_resp_code result
|
|
1470
|
-
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 }
|
|
1471
1339
|
end
|
|
1472
1340
|
|
|
1473
1341
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
|
@@ -1487,13 +1355,9 @@ module Net
|
|
|
1487
1355
|
# ===== Capabilities
|
|
1488
1356
|
#
|
|
1489
1357
|
# An IMAP client MUST NOT call #login when the server advertises the
|
|
1490
|
-
# +LOGINDISABLED+ capability.
|
|
1491
|
-
#
|
|
1492
|
-
#
|
|
1493
|
-
# raise "Remote server has disabled the login command"
|
|
1494
|
-
# else
|
|
1495
|
-
# imap.login username, password
|
|
1496
|
-
# end
|
|
1358
|
+
# +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
|
|
1359
|
+
# LoginDisabledError when that capability is present. See
|
|
1360
|
+
# Config#enforce_logindisabled.
|
|
1497
1361
|
#
|
|
1498
1362
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
|
1499
1363
|
# Cached capabilities _must_ be invalidated after this method completes.
|
|
@@ -1501,6 +1365,9 @@ module Net
|
|
|
1501
1365
|
# ResponseCode.
|
|
1502
1366
|
#
|
|
1503
1367
|
def login(user, password)
|
|
1368
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
|
1369
|
+
raise LoginDisabledError
|
|
1370
|
+
end
|
|
1504
1371
|
send_command("LOGIN", user, password)
|
|
1505
1372
|
.tap { @capabilities = capabilities_from_resp_code _1 }
|
|
1506
1373
|
end
|
|
@@ -1769,18 +1636,12 @@ module Net
|
|
|
1769
1636
|
# to both admin and user. If this mailbox exists, it returns an array
|
|
1770
1637
|
# containing objects of type MailboxQuotaRoot and MailboxQuota.
|
|
1771
1638
|
#
|
|
1772
|
-
# *NOTE:* Currently, Net::IMAP only supports +QUOTA+ responses with a single
|
|
1773
|
-
# resource type. This is usually +STORAGE+, but you may need to verify this
|
|
1774
|
-
# with UntaggedResponse#raw_data.
|
|
1775
|
-
#
|
|
1776
1639
|
# Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
|
|
1777
1640
|
#
|
|
1778
1641
|
# ===== Capabilities
|
|
1779
1642
|
#
|
|
1780
|
-
#
|
|
1781
|
-
#
|
|
1782
|
-
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208] for each supported
|
|
1783
|
-
# resource type.
|
|
1643
|
+
# The server's capabilities must include +QUOTA+
|
|
1644
|
+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
|
|
1784
1645
|
def getquotaroot(mailbox)
|
|
1785
1646
|
synchronize do
|
|
1786
1647
|
send_command("GETQUOTAROOT", mailbox)
|
|
@@ -1792,59 +1653,41 @@ module Net
|
|
|
1792
1653
|
end
|
|
1793
1654
|
|
|
1794
1655
|
# Sends a {GETQUOTA command [RFC2087 §4.2]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.2]
|
|
1795
|
-
#
|
|
1796
|
-
# containing a MailboxQuota object is returned.
|
|
1797
|
-
#
|
|
1798
|
-
# The names of quota roots that are applicable to a particular mailbox can
|
|
1799
|
-
# be discovered with #getquotaroot.
|
|
1800
|
-
#
|
|
1801
|
-
# *NOTE:* Currently, Net::IMAP only supports +QUOTA+ responses with a single
|
|
1802
|
-
# resource type. This is usually +STORAGE+, but you may need to verify this
|
|
1803
|
-
# 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.
|
|
1804
1659
|
#
|
|
1805
1660
|
# Related: #getquotaroot, #setquota, MailboxQuota
|
|
1806
1661
|
#
|
|
1807
1662
|
# ===== Capabilities
|
|
1808
1663
|
#
|
|
1809
|
-
#
|
|
1810
|
-
#
|
|
1811
|
-
|
|
1812
|
-
# resource type.
|
|
1813
|
-
def getquota(quota_root)
|
|
1664
|
+
# The server's capabilities must include +QUOTA+
|
|
1665
|
+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
|
|
1666
|
+
def getquota(mailbox)
|
|
1814
1667
|
synchronize do
|
|
1815
|
-
send_command("GETQUOTA",
|
|
1668
|
+
send_command("GETQUOTA", mailbox)
|
|
1816
1669
|
clear_responses("QUOTA")
|
|
1817
1670
|
end
|
|
1818
1671
|
end
|
|
1819
1672
|
|
|
1820
1673
|
# Sends a {SETQUOTA command [RFC2087 §4.1]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.1]
|
|
1821
|
-
# along with the specified +
|
|
1822
|
-
# +
|
|
1823
|
-
#
|
|
1824
|
-
#
|
|
1825
|
-
# imap.setquota "#user/alice", 100
|
|
1826
|
-
# imap.getquota "#user/alice"
|
|
1827
|
-
# # => [#<struct Net::IMAP::MailboxQuota mailbox="#user/alice" usage=54 quota=100>]
|
|
1828
|
-
#
|
|
1829
|
-
# Typically one needs to be logged in as a server admin for this to work.
|
|
1830
|
-
#
|
|
1831
|
-
# *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.
|
|
1832
1677
|
#
|
|
1833
1678
|
# Related: #getquota, #getquotaroot
|
|
1834
1679
|
#
|
|
1835
1680
|
# ===== Capabilities
|
|
1836
1681
|
#
|
|
1837
|
-
#
|
|
1838
|
-
#
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
if storage_limit.nil?
|
|
1843
|
-
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 = '()'
|
|
1844
1687
|
else
|
|
1845
|
-
|
|
1688
|
+
data = '(STORAGE ' + quota.to_s + ')'
|
|
1846
1689
|
end
|
|
1847
|
-
send_command("SETQUOTA",
|
|
1690
|
+
send_command("SETQUOTA", mailbox, RawData.new(data))
|
|
1848
1691
|
end
|
|
1849
1692
|
|
|
1850
1693
|
# Sends a {SETACL command [RFC4314 §3.1]}[https://www.rfc-editor.org/rfc/rfc4314#section-3.1]
|
|
@@ -1951,10 +1794,7 @@ module Net
|
|
|
1951
1794
|
# <tt>STATUS=SIZE</tt>
|
|
1952
1795
|
# {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
|
|
1953
1796
|
#
|
|
1954
|
-
# +DELETED+
|
|
1955
|
-
# +IMAP4rev2+.
|
|
1956
|
-
# or <tt>QUOTA=RES-MESSAGES</tt>
|
|
1957
|
-
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208.html].
|
|
1797
|
+
# +DELETED+ requires the server's capabilities to include +IMAP4rev2+.
|
|
1958
1798
|
#
|
|
1959
1799
|
# +HIGHESTMODSEQ+ requires the server's capabilities to include +CONDSTORE+
|
|
1960
1800
|
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
|
@@ -2084,7 +1924,7 @@ module Net
|
|
|
2084
1924
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
|
2085
1925
|
def uid_expunge(uid_set)
|
|
2086
1926
|
synchronize do
|
|
2087
|
-
send_command("UID EXPUNGE",
|
|
1927
|
+
send_command("UID EXPUNGE", SequenceSet.new(uid_set))
|
|
2088
1928
|
clear_responses("EXPUNGE")
|
|
2089
1929
|
end
|
|
2090
1930
|
end
|
|
@@ -2103,14 +1943,6 @@ module Net
|
|
|
2103
1943
|
#
|
|
2104
1944
|
# ===== Search criteria
|
|
2105
1945
|
#
|
|
2106
|
-
# >>>
|
|
2107
|
-
# When +criteria+ is an Array, elements in the array will be validated and
|
|
2108
|
-
# formatted. When +criteria+ is a String, it will be sent <em>with
|
|
2109
|
-
# minimal validation and no encoding or formatting</em>.
|
|
2110
|
-
#
|
|
2111
|
-
# <em>*WARNING:* Although CRLF is prohibited, this is vulnerable to other
|
|
2112
|
-
# types of attribute injection attack if unvetted user input is used.</em>
|
|
2113
|
-
#
|
|
2114
1946
|
# For a full list of search criteria,
|
|
2115
1947
|
# see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
|
|
2116
1948
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
|
@@ -2198,13 +2030,6 @@ module Net
|
|
|
2198
2030
|
#
|
|
2199
2031
|
# +attr+ is a list of attributes to fetch; see the documentation
|
|
2200
2032
|
# for FetchData for a list of valid attributes.
|
|
2201
|
-
# >>>
|
|
2202
|
-
# When +attr+ is a String, it will be sent <em>with minimal validation and
|
|
2203
|
-
# no encoding or formatting</em>. When +attr+ is an Array, each String in
|
|
2204
|
-
# +attr+ will be sent this way.
|
|
2205
|
-
#
|
|
2206
|
-
# <em>*WARNING:* Although CRLF is prohibited, this is vulnerable to other
|
|
2207
|
-
# types of attribute injection attack if unvetted user input is used.</em>
|
|
2208
2033
|
#
|
|
2209
2034
|
# +changedsince+ is an optional integer mod-sequence. It limits results to
|
|
2210
2035
|
# messages with a mod-sequence greater than +changedsince+.
|
|
@@ -2757,10 +2582,10 @@ module Net
|
|
|
2757
2582
|
when :raise
|
|
2758
2583
|
raise ArgumentError, RESPONSES_DEPRECATION_MSG
|
|
2759
2584
|
when :warn
|
|
2760
|
-
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1)
|
|
2585
|
+
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
|
2761
2586
|
when :frozen_dup
|
|
2762
2587
|
synchronize {
|
|
2763
|
-
responses = @responses.transform_values
|
|
2588
|
+
responses = @responses.transform_values(&:freeze)
|
|
2764
2589
|
responses.default_proc = nil
|
|
2765
2590
|
responses.default = [].freeze
|
|
2766
2591
|
return responses.freeze
|
|
@@ -2847,10 +2672,6 @@ module Net
|
|
|
2847
2672
|
# end
|
|
2848
2673
|
# }
|
|
2849
2674
|
#
|
|
2850
|
-
# Response handlers can also be added when the client is created before the
|
|
2851
|
-
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
|
2852
|
-
# This ensures every server response is handled, including the #greeting.
|
|
2853
|
-
#
|
|
2854
2675
|
# Related: #remove_response_handler, #response_handlers
|
|
2855
2676
|
def add_response_handler(handler = nil, &block)
|
|
2856
2677
|
raise ArgumentError, "two Procs are passed" if handler && block
|
|
@@ -2877,7 +2698,6 @@ module Net
|
|
|
2877
2698
|
def start_imap_connection
|
|
2878
2699
|
@greeting = get_server_greeting
|
|
2879
2700
|
@capabilities = capabilities_from_resp_code @greeting
|
|
2880
|
-
@response_handlers.each do |handler| handler.call(@greeting) end
|
|
2881
2701
|
@receiver_thread = start_receiver_thread
|
|
2882
2702
|
rescue Exception
|
|
2883
2703
|
@sock.close
|
|
@@ -3006,10 +2826,23 @@ module Net
|
|
|
3006
2826
|
end
|
|
3007
2827
|
|
|
3008
2828
|
def get_response
|
|
3009
|
-
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
|
|
3010
2841
|
return nil if buff.length == 0
|
|
3011
|
-
|
|
3012
|
-
|
|
2842
|
+
if config.debug?
|
|
2843
|
+
$stderr.print(buff.gsub(/^/n, "S: "))
|
|
2844
|
+
end
|
|
2845
|
+
return @parser.parse(buff)
|
|
3013
2846
|
end
|
|
3014
2847
|
|
|
3015
2848
|
#############################
|
|
@@ -3061,7 +2894,6 @@ module Net
|
|
|
3061
2894
|
put_string(" ")
|
|
3062
2895
|
send_data(i, tag)
|
|
3063
2896
|
end
|
|
3064
|
-
guard_against_tagged_response_skipping_handler!(tag)
|
|
3065
2897
|
put_string(CRLF)
|
|
3066
2898
|
if cmd == "LOGOUT"
|
|
3067
2899
|
@logout_command_tag = tag
|
|
@@ -3077,17 +2909,6 @@ module Net
|
|
|
3077
2909
|
end
|
|
3078
2910
|
end
|
|
3079
2911
|
end
|
|
3080
|
-
rescue InvalidResponseError
|
|
3081
|
-
disconnect
|
|
3082
|
-
raise
|
|
3083
|
-
end
|
|
3084
|
-
|
|
3085
|
-
def guard_against_tagged_response_skipping_handler!(tag)
|
|
3086
|
-
return unless (resp = @tagged_responses[tag])&.name&.upcase == "OK"
|
|
3087
|
-
raise(InvalidResponseError,
|
|
3088
|
-
"Server sent tagged 'OK' before command was finished: %p. " \
|
|
3089
|
-
"This could indicate a malicious server or client-side " \
|
|
3090
|
-
"command injection. Disconnecting." % [resp.raw_data.chomp])
|
|
3091
2912
|
end
|
|
3092
2913
|
|
|
3093
2914
|
def generate_tag
|
|
@@ -3110,6 +2931,14 @@ module Net
|
|
|
3110
2931
|
end
|
|
3111
2932
|
end
|
|
3112
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
|
+
|
|
3113
2942
|
def search_internal(cmd, keys, charset)
|
|
3114
2943
|
if keys.instance_of?(String)
|
|
3115
2944
|
keys = [RawData.new(keys)]
|
|
@@ -3143,17 +2972,17 @@ module Net
|
|
|
3143
2972
|
synchronize do
|
|
3144
2973
|
clear_responses("FETCH")
|
|
3145
2974
|
if mod
|
|
3146
|
-
send_command(cmd,
|
|
2975
|
+
send_command(cmd, SequenceSet.new(set), attr, mod)
|
|
3147
2976
|
else
|
|
3148
|
-
send_command(cmd,
|
|
2977
|
+
send_command(cmd, SequenceSet.new(set), attr)
|
|
3149
2978
|
end
|
|
3150
2979
|
clear_responses("FETCH")
|
|
3151
2980
|
end
|
|
3152
2981
|
end
|
|
3153
2982
|
|
|
3154
2983
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
|
3155
|
-
attr =
|
|
3156
|
-
args = [
|
|
2984
|
+
attr = RawData.new(attr) if attr.instance_of?(String)
|
|
2985
|
+
args = [SequenceSet.new(set)]
|
|
3157
2986
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
|
3158
2987
|
args << attr << flags
|
|
3159
2988
|
synchronize do
|
|
@@ -3164,7 +2993,7 @@ module Net
|
|
|
3164
2993
|
end
|
|
3165
2994
|
|
|
3166
2995
|
def copy_internal(cmd, set, mailbox)
|
|
3167
|
-
send_command(cmd,
|
|
2996
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
|
3168
2997
|
end
|
|
3169
2998
|
|
|
3170
2999
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
|
@@ -3195,7 +3024,7 @@ module Net
|
|
|
3195
3024
|
keys.collect! do |i|
|
|
3196
3025
|
case i
|
|
3197
3026
|
when -1, Range, Array
|
|
3198
|
-
|
|
3027
|
+
SequenceSet.new(i)
|
|
3199
3028
|
else
|
|
3200
3029
|
i
|
|
3201
3030
|
end
|
|
@@ -3222,7 +3051,6 @@ module Net
|
|
|
3222
3051
|
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
|
3223
3052
|
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
|
3224
3053
|
@sock = SSLSocket.new(@sock, ssl_ctx)
|
|
3225
|
-
@reader = ResponseReader.new(self, @sock)
|
|
3226
3054
|
@sock.sync_close = true
|
|
3227
3055
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
|
3228
3056
|
ssl_socket_connect(@sock, open_timeout)
|