net-imap 0.4.12 → 0.5.1
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.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +7 -1
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +13 -2
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +61 -0
- data/lib/net/imap/config.rb +400 -0
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/deprecated_client_options.rb +8 -5
- data/lib/net/imap/errors.rb +6 -0
- data/lib/net/imap/response_data.rb +6 -93
- data/lib/net/imap/response_parser/parser_utils.rb +6 -6
- data/lib/net/imap/response_parser.rb +9 -17
- 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.rb +6 -3
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/sequence_set.rb +28 -24
- data/lib/net/imap.rb +467 -152
- data/net-imap.gemspec +3 -3
- data/rakelib/string_prep_tables_generator.rb +2 -0
- metadata +11 -10
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/pages.yml +0 -46
- data/.github/workflows/push_gem.yml +0 -48
- data/.github/workflows/test.yml +0 -31
- data/.gitignore +0 -12
- data/.mailmap +0 -13
data/lib/net/imap.rb
CHANGED
@@ -288,6 +288,8 @@ module Net
|
|
288
288
|
# pre-authenticated connection.
|
289
289
|
# - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
|
290
290
|
# ResponseCode#data.
|
291
|
+
# - #extract_responses: Removes and returns the responses for which the block
|
292
|
+
# returns a true value.
|
291
293
|
# - #clear_responses: Deletes unhandled data from #responses and returns it.
|
292
294
|
# - #add_response_handler: Add a block to be called inside the receiver thread
|
293
295
|
# with every server response.
|
@@ -717,7 +719,7 @@ module Net
|
|
717
719
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
718
720
|
#
|
719
721
|
class IMAP < Protocol
|
720
|
-
VERSION = "0.
|
722
|
+
VERSION = "0.5.1"
|
721
723
|
|
722
724
|
# Aliases for supported capabilities, to be used with the #enable command.
|
723
725
|
ENABLE_ALIASES = {
|
@@ -735,14 +737,15 @@ module Net
|
|
735
737
|
include SSL
|
736
738
|
end
|
737
739
|
|
738
|
-
# Returns the
|
739
|
-
def self.
|
740
|
-
|
741
|
-
|
740
|
+
# Returns the global Config object
|
741
|
+
def self.config; Config.global end
|
742
|
+
|
743
|
+
# Returns the global debug mode.
|
744
|
+
def self.debug; config.debug end
|
742
745
|
|
743
|
-
# Sets the debug mode.
|
746
|
+
# Sets the global debug mode.
|
744
747
|
def self.debug=(val)
|
745
|
-
|
748
|
+
config.debug = val
|
746
749
|
end
|
747
750
|
|
748
751
|
# The default port for IMAP connections, port 143
|
@@ -764,13 +767,19 @@ module Net
|
|
764
767
|
# Returns the initial greeting the server, an UntaggedResponse.
|
765
768
|
attr_reader :greeting
|
766
769
|
|
770
|
+
# The client configuration. See Net::IMAP::Config.
|
771
|
+
#
|
772
|
+
# By default, the client's local configuration inherits from the global
|
773
|
+
# Net::IMAP.config.
|
774
|
+
attr_reader :config
|
775
|
+
|
767
776
|
# Seconds to wait until a connection is opened.
|
768
777
|
# If the IMAP object cannot open a connection within this time,
|
769
778
|
# it raises a Net::OpenTimeout exception. The default value is 30 seconds.
|
770
|
-
|
779
|
+
def open_timeout; config.open_timeout end
|
771
780
|
|
772
781
|
# Seconds to wait until an IDLE response is received.
|
773
|
-
|
782
|
+
def idle_response_timeout; config.idle_response_timeout end
|
774
783
|
|
775
784
|
# The hostname this client connected to
|
776
785
|
attr_reader :host
|
@@ -809,14 +818,40 @@ module Net
|
|
809
818
|
# If +ssl+ is a hash, it's passed to
|
810
819
|
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
|
811
820
|
# the keys are names of attribute assignment methods on
|
812
|
-
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
|
821
|
+
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]. For example:
|
822
|
+
#
|
823
|
+
# [{ca_file}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_file]]
|
824
|
+
# The path to a file containing a PEM-format CA certificate.
|
825
|
+
# [{ca_path}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-ca_path]]
|
826
|
+
# The path to a directory containing CA certificates in PEM format.
|
827
|
+
# [{min_version}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D]]
|
828
|
+
# Sets the lower bound on the supported SSL/TLS protocol version. Set to
|
829
|
+
# an +OpenSSL+ constant such as +OpenSSL::SSL::TLS1_2_VERSION+,
|
830
|
+
# [{verify_mode}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-verify_mode]]
|
831
|
+
# SSL session verification mode. Valid modes include
|
832
|
+
# +OpenSSL::SSL::VERIFY_PEER+ and +OpenSSL::SSL::VERIFY_NONE+.
|
833
|
+
#
|
834
|
+
# See {OpenSSL::SSL::SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html] for other valid SSL context params.
|
813
835
|
#
|
814
|
-
#
|
815
|
-
# Seconds to wait until a connection is opened
|
816
|
-
# [idle_response_timeout]
|
817
|
-
# Seconds to wait until an IDLE response is received
|
836
|
+
# See DeprecatedClientOptions.new for deprecated SSL arguments.
|
818
837
|
#
|
819
|
-
#
|
838
|
+
# [config]
|
839
|
+
# A Net::IMAP::Config object to use as the basis for #config. By default,
|
840
|
+
# the global Net::IMAP.config is used.
|
841
|
+
#
|
842
|
+
# >>>
|
843
|
+
# *NOTE:* +config+ does not set #config directly---it sets the _parent_
|
844
|
+
# config for inheritance. Every client creates its own unique #config.
|
845
|
+
#
|
846
|
+
# All other keyword arguments are forwarded to Net::IMAP::Config.new, to
|
847
|
+
# initialize the client's #config. For example:
|
848
|
+
#
|
849
|
+
# [{open_timeout}[rdoc-ref:Config#open_timeout]]
|
850
|
+
# Seconds to wait until a connection is opened
|
851
|
+
# [{idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]]
|
852
|
+
# Seconds to wait until an IDLE response is received
|
853
|
+
#
|
854
|
+
# See Net::IMAP::Config for other valid options.
|
820
855
|
#
|
821
856
|
# ==== Examples
|
822
857
|
#
|
@@ -872,13 +907,12 @@ module Net
|
|
872
907
|
# Connected to the host successfully, but it immediately said goodbye.
|
873
908
|
#
|
874
909
|
def initialize(host, port: nil, ssl: nil,
|
875
|
-
|
910
|
+
config: Config.global, **config_options)
|
876
911
|
super()
|
877
912
|
# Config options
|
878
913
|
@host = host
|
914
|
+
@config = Config.new(config, **config_options)
|
879
915
|
@port = port || (ssl ? SSL_PORT : PORT)
|
880
|
-
@open_timeout = Integer(open_timeout)
|
881
|
-
@idle_response_timeout = Integer(idle_response_timeout)
|
882
916
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
|
883
917
|
|
884
918
|
# Basic Client State
|
@@ -889,7 +923,7 @@ module Net
|
|
889
923
|
@capabilities = nil
|
890
924
|
|
891
925
|
# Client Protocol Receiver
|
892
|
-
@parser = ResponseParser.new
|
926
|
+
@parser = ResponseParser.new(config: @config)
|
893
927
|
@responses = Hash.new {|h, k| h[k] = [] }
|
894
928
|
@response_handlers = []
|
895
929
|
@receiver_thread = nil
|
@@ -912,9 +946,6 @@ module Net
|
|
912
946
|
@sock = tcp_socket(@host, @port)
|
913
947
|
start_tls_session if ssl_ctx
|
914
948
|
start_imap_connection
|
915
|
-
|
916
|
-
# DEPRECATED: to remove in next version
|
917
|
-
@client_thread = Thread.current
|
918
949
|
end
|
919
950
|
|
920
951
|
# Returns true after the TLS negotiation has completed and the remote
|
@@ -922,11 +953,6 @@ module Net
|
|
922
953
|
# but peer verification was disabled.
|
923
954
|
def tls_verified?; @tls_verified end
|
924
955
|
|
925
|
-
def client_thread # :nodoc:
|
926
|
-
warn "Net::IMAP#client_thread is deprecated and will be removed soon."
|
927
|
-
@client_thread
|
928
|
-
end
|
929
|
-
|
930
956
|
# Disconnects from the server.
|
931
957
|
#
|
932
958
|
# Related: #logout, #logout!
|
@@ -1198,7 +1224,7 @@ module Net
|
|
1198
1224
|
end
|
1199
1225
|
|
1200
1226
|
# :call-seq:
|
1201
|
-
# authenticate(mechanism, *, sasl_ir:
|
1227
|
+
# authenticate(mechanism, *, sasl_ir: config.sasl_ir, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
|
1202
1228
|
#
|
1203
1229
|
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
|
1204
1230
|
# to authenticate the client. If successful, the connection enters the
|
@@ -1207,7 +1233,11 @@ module Net
|
|
1207
1233
|
# +mechanism+ is the name of the \SASL authentication mechanism to be used.
|
1208
1234
|
#
|
1209
1235
|
# +sasl_ir+ allows or disallows sending an "initial response" (see the
|
1210
|
-
# +SASL-IR+ capability, below).
|
1236
|
+
# +SASL-IR+ capability, below). Defaults to the #config value for
|
1237
|
+
# {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
|
1238
|
+
#
|
1239
|
+
# The +registry+ kwarg can be used to select the mechanism implementation
|
1240
|
+
# from a custom registry. See SASL.authenticator and SASL::Authenticators.
|
1211
1241
|
#
|
1212
1242
|
# All other arguments are forwarded to the registered SASL authenticator for
|
1213
1243
|
# the requested mechanism. <em>The documentation for each individual
|
@@ -1303,27 +1333,9 @@ module Net
|
|
1303
1333
|
# Previously cached #capabilities will be cleared when this method
|
1304
1334
|
# completes. If the TaggedResponse to #authenticate includes updated
|
1305
1335
|
# capabilities, they will be cached.
|
1306
|
-
def authenticate(
|
1307
|
-
|
1308
|
-
|
1309
|
-
cmdargs = ["AUTHENTICATE", mechanism]
|
1310
|
-
if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
|
1311
|
-
authenticator.respond_to?(:initial_response?) &&
|
1312
|
-
authenticator.initial_response?
|
1313
|
-
response = authenticator.process(nil)
|
1314
|
-
cmdargs << (response.empty? ? "=" : [response].pack("m0"))
|
1315
|
-
end
|
1316
|
-
result = send_command_with_continuations(*cmdargs) {|data|
|
1317
|
-
challenge = data.unpack1("m")
|
1318
|
-
response = authenticator.process challenge
|
1319
|
-
[response].pack("m0")
|
1320
|
-
}
|
1321
|
-
if authenticator.respond_to?(:done?) && !authenticator.done?
|
1322
|
-
logout!
|
1323
|
-
raise SASL::AuthenticationIncomplete, result
|
1324
|
-
end
|
1325
|
-
@capabilities = capabilities_from_resp_code result
|
1326
|
-
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 }
|
1327
1339
|
end
|
1328
1340
|
|
1329
1341
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1343,13 +1355,9 @@ module Net
|
|
1343
1355
|
# ===== Capabilities
|
1344
1356
|
#
|
1345
1357
|
# An IMAP client MUST NOT call #login when the server advertises the
|
1346
|
-
# +LOGINDISABLED+ capability.
|
1347
|
-
#
|
1348
|
-
#
|
1349
|
-
# raise "Remote server has disabled the login command"
|
1350
|
-
# else
|
1351
|
-
# imap.login username, password
|
1352
|
-
# end
|
1358
|
+
# +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
|
1359
|
+
# LoginDisabledError when that capability is present. See
|
1360
|
+
# Config#enforce_logindisabled.
|
1353
1361
|
#
|
1354
1362
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1355
1363
|
# Cached capabilities _must_ be invalidated after this method completes.
|
@@ -1357,6 +1365,9 @@ module Net
|
|
1357
1365
|
# ResponseCode.
|
1358
1366
|
#
|
1359
1367
|
def login(user, password)
|
1368
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
1369
|
+
raise LoginDisabledError
|
1370
|
+
end
|
1360
1371
|
send_command("LOGIN", user, password)
|
1361
1372
|
.tap { @capabilities = capabilities_from_resp_code _1 }
|
1362
1373
|
end
|
@@ -1913,82 +1924,274 @@ module Net
|
|
1913
1924
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
1914
1925
|
def uid_expunge(uid_set)
|
1915
1926
|
synchronize do
|
1916
|
-
send_command("UID EXPUNGE",
|
1927
|
+
send_command("UID EXPUNGE", SequenceSet.new(uid_set))
|
1917
1928
|
clear_responses("EXPUNGE")
|
1918
1929
|
end
|
1919
1930
|
end
|
1920
1931
|
|
1921
|
-
#
|
1922
|
-
#
|
1923
|
-
# criteria, and returns message sequence numbers. +keys+ can either be a
|
1924
|
-
# string holding the entire search string, or a single-dimension array of
|
1925
|
-
# search keywords and arguments.
|
1932
|
+
# :call-seq:
|
1933
|
+
# search(criteria, charset = nil) -> result
|
1926
1934
|
#
|
1927
|
-
#
|
1935
|
+
# Sends a {SEARCH command [IMAP4rev1 §6.4.4]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4]
|
1936
|
+
# to search the mailbox for messages that match the given search +criteria+,
|
1937
|
+
# and returns a SearchResult. SearchResult inherits from Array (for
|
1928
1938
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
1929
1939
|
# capability has been enabled.
|
1930
1940
|
#
|
1941
|
+
# +criteria+ is one or more search keys and their arguments, which may be
|
1942
|
+
# provided as an array or a string.
|
1943
|
+
# See {"Search criteria"}[rdoc-ref:#search@Search+criteria], below.
|
1944
|
+
#
|
1945
|
+
# * When +criteria+ is an array, each member is a +SEARCH+ command argument:
|
1946
|
+
# * Any SequenceSet sends SequenceSet#valid_string.
|
1947
|
+
# These types are converted to SequenceSet for validation and encoding:
|
1948
|
+
# * +Set+
|
1949
|
+
# * +Range+
|
1950
|
+
# * <tt>-1</tt> and +:*+ -- both translate to <tt>*</tt>
|
1951
|
+
# * responds to +#to_sequence_set+
|
1952
|
+
# * +Array+, when each element is one of the above types, a positive
|
1953
|
+
# +Integer+, a sequence-set formatted +String+, or a deeply nested
|
1954
|
+
# +Array+ of these same types.
|
1955
|
+
# * Any +String+ is sent verbatim when it is a valid \IMAP atom,
|
1956
|
+
# and encoded as an \IMAP quoted or literal string otherwise.
|
1957
|
+
# * Any other nested +Array+ is encoded as a parenthesized list, to group
|
1958
|
+
# multiple search keys (e.g., for use with +OR+ and +NOT+).
|
1959
|
+
# * Any other +Integer+ (besides <tt>-1</tt>) will be sent as +#to_s+.
|
1960
|
+
# * +Date+ objects will be encoded as an \IMAP date (see ::encode_date).
|
1961
|
+
#
|
1962
|
+
# * When +criteria+ is a string, it will be sent directly to the server
|
1963
|
+
# <em>without any validation or encoding</em>. *WARNING:* This is
|
1964
|
+
# vulnerable to injection attacks when external inputs are used.
|
1965
|
+
#
|
1966
|
+
# +charset+ is the name of the {registered character
|
1967
|
+
# set}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
1968
|
+
# used by strings in the search +criteria+. When +charset+ isn't specified,
|
1969
|
+
# either <tt>"US-ASCII"</tt> or <tt>"UTF-8"</tt> is assumed, depending on
|
1970
|
+
# the server's capabilities. +charset+ may be sent inside +criteria+
|
1971
|
+
# instead of as a separate argument.
|
1972
|
+
#
|
1931
1973
|
# Related: #uid_search
|
1932
1974
|
#
|
1933
|
-
# =====
|
1975
|
+
# ===== For example:
|
1976
|
+
#
|
1977
|
+
# p imap.search(["SUBJECT", "hello", "NOT", "SEEN"])
|
1978
|
+
# #=> [1, 6, 7, 8]
|
1979
|
+
#
|
1980
|
+
# The following searches send the exact same command to the server:
|
1981
|
+
#
|
1982
|
+
# # criteria array, charset arg
|
1983
|
+
# imap.search(["OR", "UNSEEN", %w(FLAGGED SUBJECT foo)], "UTF-8")
|
1984
|
+
# # criteria string, charset arg
|
1985
|
+
# imap.search("OR UNSEEN (FLAGGED SUBJECT foo)", "UTF-8")
|
1986
|
+
# # criteria array contains charset arg
|
1987
|
+
# imap.search([*%w[CHARSET UTF-8], "OR", "UNSEEN", %w(FLAGGED SUBJECT foo)])
|
1988
|
+
# # criteria string contains charset arg
|
1989
|
+
# imap.search("CHARSET UTF-8 OR UNSEEN (FLAGGED SUBJECT foo)")
|
1934
1990
|
#
|
1935
|
-
#
|
1991
|
+
# ===== Search keys
|
1992
|
+
#
|
1993
|
+
# For full definitions of the standard search +criteria+,
|
1936
1994
|
# see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
|
1937
1995
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
1938
1996
|
# in addition to documentation for
|
1939
|
-
# any
|
1940
|
-
# reported by #capabilities which may define additional search filters, e.g:
|
1997
|
+
# any #capabilities which may define additional search filters, such as
|
1941
1998
|
# +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
|
1942
|
-
# +SAVEDATE+.
|
1999
|
+
# +SAVEDATE+.
|
2000
|
+
#
|
2001
|
+
# With the exception of <em>sequence-set</em> and <em>parenthesized
|
2002
|
+
# list</em>, all search keys are composed of prefix label with zero or more
|
2003
|
+
# arguments. The number and type of arguments is specific to each search
|
2004
|
+
# key.
|
2005
|
+
#
|
2006
|
+
# +ALL+::
|
2007
|
+
# Matches every message in the mailbox.
|
2008
|
+
#
|
2009
|
+
# (_search-key_ _search-key_...)::
|
2010
|
+
# Combines one or more _search-key_ arguments to match
|
2011
|
+
# messages which match all contained search keys. Useful for +OR+, +NOT+,
|
2012
|
+
# and other search keys with _search-key_ arguments.
|
2013
|
+
#
|
2014
|
+
# _Note:_ this search key has no label.
|
2015
|
+
#
|
2016
|
+
# +OR+ _search-key_ _search-key_::
|
2017
|
+
# Matches messages which match either _search-key_ argument.
|
2018
|
+
#
|
2019
|
+
# +NOT+ _search-key_::
|
2020
|
+
# Matches messages which do not match _search-key_.
|
2021
|
+
#
|
2022
|
+
# _sequence-set_::
|
2023
|
+
# Matches messages with message sequence numbers in _sequence-set_.
|
2024
|
+
#
|
2025
|
+
# _Note:_ this search key has no label.
|
2026
|
+
#
|
2027
|
+
# <em>+UIDONLY+ must *not* be enabled.</em>
|
2028
|
+
# {[RFC9586]}[https://www.rfc-editor.org/rfc/rfc9586.html]
|
2029
|
+
#
|
2030
|
+
# +UID+ _sequence-set_::
|
2031
|
+
# Matches messages with a UID in _sequence-set_.
|
2032
|
+
#
|
2033
|
+
# +ANSWERED+::
|
2034
|
+
# +UNANSWERED+::
|
2035
|
+
# Matches messages with or without the <tt>\\Answered</tt> flag.
|
2036
|
+
# +DELETED+::
|
2037
|
+
# +UNDELETED+::
|
2038
|
+
# Matches messages with or without the <tt>\\Deleted</tt> flag.
|
2039
|
+
# +DRAFT+::
|
2040
|
+
# +UNDRAFT+::
|
2041
|
+
# Matches messages with or without the <tt>\\Draft</tt> flag.
|
2042
|
+
# +FLAGGED+::
|
2043
|
+
# +UNFLAGGED+::
|
2044
|
+
# Matches messages with or without the <tt>\\Flagged</tt> flag.
|
2045
|
+
# +SEEN+::
|
2046
|
+
# +UNSEEN+::
|
2047
|
+
# Matches messages with or without the <tt>\\Seen</tt> flag.
|
2048
|
+
#
|
2049
|
+
# +KEYWORD+ _keyword_::
|
2050
|
+
# +UNKEYWORD+ _keyword_::
|
2051
|
+
# Matches messages with or without the specified _keyword_.
|
2052
|
+
#
|
2053
|
+
# +BCC+ _substring_::
|
2054
|
+
# Matches when _substring_ is in the envelope's BCC field.
|
2055
|
+
# +CC+ _substring_::
|
2056
|
+
# Matches when _substring_ is in the envelope's CC field.
|
2057
|
+
# +FROM+ _substring_::
|
2058
|
+
# Matches when _substring_ is in the envelope's FROM field.
|
2059
|
+
# +SUBJECT+ _substring_::
|
2060
|
+
# Matches when _substring_ is in the envelope's SUBJECT field.
|
2061
|
+
# +TO+ _substring_::
|
2062
|
+
# Matches when _substring_ is in the envelope's TO field.
|
2063
|
+
#
|
2064
|
+
# +HEADER+ _field_ _substring_::
|
2065
|
+
# Matches when _substring_ is in the specified header _field_.
|
2066
|
+
#
|
2067
|
+
# +BODY+ _string_::
|
2068
|
+
# Matches when _string_ is in the body of the message.
|
2069
|
+
# Does not match on header fields.
|
2070
|
+
#
|
2071
|
+
# The server _may_ use flexible matching, rather than simple substring
|
2072
|
+
# matches. For example, this may use stemming or match only full words.
|
2073
|
+
#
|
2074
|
+
# +TEXT+ _string_::
|
2075
|
+
# Matches when _string_ is in the header or body of the message.
|
2076
|
+
#
|
2077
|
+
# The server _may_ use flexible matching, rather than simple substring
|
2078
|
+
# matches. For example, this may use stemming or match only full words.
|
2079
|
+
#
|
2080
|
+
# +BEFORE+ _date_::
|
2081
|
+
# +ON+ _date_::
|
2082
|
+
# +SINCE+ _date_::
|
2083
|
+
# Matches when the +INTERNALDATE+ is earlier than, on, or later than
|
2084
|
+
# _date_.
|
2085
|
+
#
|
2086
|
+
# +SENTBEFORE+ _date_::
|
2087
|
+
# +SENTON+ _date_::
|
2088
|
+
# +SENTSINCE+ _date_::
|
2089
|
+
# Matches when the +Date+ header is earlier than, on, or later than _date_.
|
2090
|
+
#
|
2091
|
+
# +SMALLER+ _bytes_::
|
2092
|
+
# +LARGER+ _bytes_::
|
2093
|
+
# Matches when +RFC822.SIZE+ is smaller/larger than _bytes_.
|
2094
|
+
#
|
2095
|
+
# ====== Removed from +IMAP4rev2+
|
2096
|
+
#
|
2097
|
+
# The <tt>\\Recent</tt> flag has been removed from +IMAP4rev2+. So these
|
2098
|
+
# search keys require the +IMAP4rev1+ capability.
|
1943
2099
|
#
|
1944
|
-
#
|
1945
|
-
#
|
1946
|
-
#
|
2100
|
+
# +RECENT+::
|
2101
|
+
# +UNRECENT+::
|
2102
|
+
# Matches messages with or without the <tt>\\Recent</tt> flag.
|
1947
2103
|
#
|
1948
|
-
#
|
1949
|
-
#
|
1950
|
-
# to <tt>8-Aug-2002</tt>, and can be formatted using
|
1951
|
-
# Net::IMAP.format_date.
|
2104
|
+
# +NEW+::
|
2105
|
+
# Equivalent to <tt>(RECENT UNSEEN)</tt>.
|
1952
2106
|
#
|
1953
|
-
#
|
2107
|
+
# ====== Extension search keys
|
1954
2108
|
#
|
1955
|
-
#
|
2109
|
+
# The search keys described below are defined by standard \IMAP extensions.
|
1956
2110
|
#
|
1957
|
-
#
|
2111
|
+
# +OLDER+ _interval_::
|
2112
|
+
# +YOUNGER+ _interval_::
|
2113
|
+
# Matches when +INTERNALDATE+ is more/less than _interval_ seconds ago.
|
1958
2114
|
#
|
1959
|
-
#
|
2115
|
+
# <em>Requires the +WITHIN+ capability</em>.
|
2116
|
+
# {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
|
1960
2117
|
#
|
1961
|
-
#
|
2118
|
+
# +ANNOTATION+ _entry_ _attr_ _value_::
|
2119
|
+
# Matches messages that have annotations with entries matching _entry_,
|
2120
|
+
# attributes matching _attr_, and _value_ in the attribute's values.
|
1962
2121
|
#
|
1963
|
-
#
|
2122
|
+
# <em>Requires the +ANNOTATE-EXPERIMENT-1+ capability</em>.
|
2123
|
+
# {[RFC5257]}[https://www.rfc-editor.org/rfc/rfc5257.html].
|
1964
2124
|
#
|
1965
|
-
#
|
1966
|
-
#
|
2125
|
+
# +FILTER+ _filter_::
|
2126
|
+
# References a _filter_ that is stored on the server and matches all
|
2127
|
+
# messages which would be matched by that filter's search criteria.
|
1967
2128
|
#
|
1968
|
-
#
|
2129
|
+
# <em>Requires the +FILTERS+ capability</em>.
|
2130
|
+
# {[RFC5466]}[https://www.rfc-editor.org/rfc/rfc5466.html#section-3.1]
|
1969
2131
|
#
|
1970
|
-
#
|
2132
|
+
# +FUZZY+ _search-key_::
|
2133
|
+
# Uses fuzzy matching for the specified search key.
|
1971
2134
|
#
|
1972
|
-
#
|
2135
|
+
# <em>Requires the <tt>SEARCH=FUZZY</tt> capability.</em>
|
2136
|
+
# {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
|
1973
2137
|
#
|
1974
|
-
#
|
2138
|
+
# +MODSEQ+ _modseq_::
|
2139
|
+
# Matches when +MODSEQ+ is greater than or equal to _modseq_.
|
1975
2140
|
#
|
1976
|
-
#
|
1977
|
-
#
|
2141
|
+
# <em>Requires the +CONDSTORE+ capability</em>.
|
2142
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
|
2143
|
+
#
|
2144
|
+
# +MODSEQ+ _entry_ _entry-type_ _modseq_::
|
2145
|
+
# Matches when a specific metadata _entry_ has been updated since
|
2146
|
+
# _modseq_.
|
2147
|
+
#
|
2148
|
+
# For flags, the corresponding _entry_ name is
|
2149
|
+
# <tt>"/flags/#{flag_name}"</tt>, where _flag_name_ includes the
|
2150
|
+
# <tt>\\</tt> prefix. _entry-type_ can be one of <tt>"shared"</tt>,
|
2151
|
+
# <tt>"priv"</tt> (private), or <tt>"all"</tt>.
|
2152
|
+
#
|
2153
|
+
# <em>Requires the +CONDSTORE+ capability</em>.
|
2154
|
+
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.5].
|
2155
|
+
#
|
2156
|
+
# +EMAILID+ _objectid_::
|
2157
|
+
# +THREADID+ _objectid_::
|
2158
|
+
# Matches when +EMAILID+/+THREADID+ is equal to _objectid_
|
2159
|
+
# (substring matches are not supported).
|
2160
|
+
#
|
2161
|
+
# <em>Requires the +OBJECTID+ capability</em>.
|
2162
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-6]
|
2163
|
+
#
|
2164
|
+
# +SAVEDATESUPPORTED+::
|
2165
|
+
# Matches every message in the mailbox when the mailbox supports the save
|
2166
|
+
# date attribute. Otherwise, it matches no messages.
|
2167
|
+
#
|
2168
|
+
# <em>Requires the +SAVEDATE+ capability</em>.
|
2169
|
+
# {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
|
2170
|
+
#
|
2171
|
+
# +SAVEDBEFORE+ _date_::
|
2172
|
+
# +SAVEDON+ _date_::
|
2173
|
+
# +SAVEDSINCE+ _date_::
|
2174
|
+
# Matches when the save date is earlier than, on, or later than _date_.
|
2175
|
+
#
|
2176
|
+
# <em>Requires the +SAVEDATE+ capability.</em>
|
2177
|
+
# {[RFC8514]}[https://www.rfc-editor.org/rfc/rfc8514.html#section-4.3]
|
1978
2178
|
#
|
1979
2179
|
# ===== Capabilities
|
1980
2180
|
#
|
1981
|
-
# If
|
2181
|
+
# If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
|
1982
2182
|
# and enabled for the selected mailbox, a non-empty SearchResult will
|
1983
2183
|
# include a +MODSEQ+ value.
|
1984
2184
|
# imap.select("mbox", condstore: true)
|
1985
|
-
# result = imap.search(["SUBJECT", "hi there", "not", "new")
|
2185
|
+
# result = imap.search(["SUBJECT", "hi there", "not", "new"])
|
1986
2186
|
# #=> Net::IMAP::SearchResult[1, 6, 7, 8, modseq: 5594]
|
1987
2187
|
# result.modseq # => 5594
|
1988
|
-
def search(
|
1989
|
-
|
2188
|
+
def search(...)
|
2189
|
+
search_internal("SEARCH", ...)
|
1990
2190
|
end
|
1991
2191
|
|
2192
|
+
# :call-seq:
|
2193
|
+
# uid_search(criteria, charset = nil) -> result
|
2194
|
+
#
|
1992
2195
|
# Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
1993
2196
|
# to search the mailbox for messages that match the given searching
|
1994
2197
|
# criteria, and returns unique identifiers (<tt>UID</tt>s).
|
@@ -1997,9 +2200,9 @@ module Net
|
|
1997
2200
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
1998
2201
|
# capability has been enabled.
|
1999
2202
|
#
|
2000
|
-
# See #search for documentation of
|
2001
|
-
def uid_search(
|
2002
|
-
|
2203
|
+
# See #search for documentation of parameters.
|
2204
|
+
def uid_search(...)
|
2205
|
+
search_internal("UID SEARCH", ...)
|
2003
2206
|
end
|
2004
2207
|
|
2005
2208
|
# :call-seq:
|
@@ -2397,11 +2600,17 @@ module Net
|
|
2397
2600
|
# checks the connection for each 60 seconds.
|
2398
2601
|
#
|
2399
2602
|
# loop do
|
2400
|
-
# imap.idle(60) do |
|
2401
|
-
#
|
2603
|
+
# imap.idle(60) do |response|
|
2604
|
+
# do_something_with(response)
|
2605
|
+
# imap.idle_done if some_condition?(response)
|
2402
2606
|
# end
|
2403
2607
|
# end
|
2404
2608
|
#
|
2609
|
+
# Returns the server's response to indicate the IDLE state has ended.
|
2610
|
+
# Returns +nil+ if the server does not respond to #idle_done within
|
2611
|
+
# {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
|
2612
|
+
# seconds.
|
2613
|
+
#
|
2405
2614
|
# Related: #idle_done, #noop, #check
|
2406
2615
|
#
|
2407
2616
|
# ===== Capabilities
|
@@ -2429,7 +2638,7 @@ module Net
|
|
2429
2638
|
unless @receiver_thread_terminating
|
2430
2639
|
remove_response_handler(response_handler)
|
2431
2640
|
put_string("DONE#{CRLF}")
|
2432
|
-
response = get_tagged_response(tag, "IDLE",
|
2641
|
+
response = get_tagged_response(tag, "IDLE", idle_response_timeout)
|
2433
2642
|
end
|
2434
2643
|
end
|
2435
2644
|
end
|
@@ -2437,7 +2646,11 @@ module Net
|
|
2437
2646
|
return response
|
2438
2647
|
end
|
2439
2648
|
|
2440
|
-
# Leaves IDLE.
|
2649
|
+
# Leaves IDLE, allowing #idle to return.
|
2650
|
+
#
|
2651
|
+
# If the server does not respond within
|
2652
|
+
# {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout]
|
2653
|
+
# seconds, #idle will return +nil+.
|
2441
2654
|
#
|
2442
2655
|
# Related: #idle
|
2443
2656
|
def idle_done
|
@@ -2449,40 +2662,98 @@ module Net
|
|
2449
2662
|
end
|
2450
2663
|
end
|
2451
2664
|
|
2665
|
+
RESPONSES_DEPRECATION_MSG =
|
2666
|
+
"Pass a type or block to #responses, " \
|
2667
|
+
"set config.responses_without_block to :frozen_dup " \
|
2668
|
+
"or :silence_deprecation_warning, " \
|
2669
|
+
"or use #extract_responses or #clear_responses."
|
2670
|
+
private_constant :RESPONSES_DEPRECATION_MSG
|
2671
|
+
|
2452
2672
|
# :call-seq:
|
2673
|
+
# responses -> hash of {String => Array} (see config.responses_without_block)
|
2674
|
+
# responses(type) -> frozen array
|
2453
2675
|
# responses {|hash| ...} -> block result
|
2454
2676
|
# responses(type) {|array| ...} -> block result
|
2455
2677
|
#
|
2456
|
-
# Yields
|
2678
|
+
# Yields or returns unhandled server responses. Unhandled responses are
|
2679
|
+
# stored in a hash, with arrays of UntaggedResponse#data keyed by
|
2680
|
+
# UntaggedResponse#name and <em>non-+nil+</em> untagged ResponseCode#data
|
2681
|
+
# keyed by ResponseCode#name.
|
2682
|
+
#
|
2683
|
+
# When a block is given, yields unhandled responses and returns the block's
|
2684
|
+
# result. Without a block, returns the unhandled responses.
|
2685
|
+
#
|
2686
|
+
# [With +type+]
|
2687
|
+
# Yield or return only the array of responses for that +type+.
|
2688
|
+
# When no block is given, the returned array is a frozen copy.
|
2689
|
+
# [Without +type+]
|
2690
|
+
# Yield or return the entire responses hash.
|
2691
|
+
#
|
2692
|
+
# When no block is given, the behavior is determined by
|
2693
|
+
# Config#responses_without_block:
|
2694
|
+
# >>>
|
2695
|
+
# [+:silence_deprecation_warning+ <em>(original behavior)</em>]
|
2696
|
+
# Returns the mutable responses hash (without any warnings).
|
2697
|
+
# <em>This is not thread-safe.</em>
|
2698
|
+
#
|
2699
|
+
# [+:warn+ <em>(default since +v0.5+)</em>]
|
2700
|
+
# Prints a warning and returns the mutable responses hash.
|
2701
|
+
# <em>This is not thread-safe.</em>
|
2457
2702
|
#
|
2458
|
-
#
|
2459
|
-
#
|
2460
|
-
#
|
2461
|
-
#
|
2462
|
-
#
|
2703
|
+
# [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
|
2704
|
+
# Returns a frozen copy of the unhandled responses hash, with frozen
|
2705
|
+
# array values.
|
2706
|
+
#
|
2707
|
+
# [+:raise+]
|
2708
|
+
# Raise an +ArgumentError+ with the deprecation warning.
|
2463
2709
|
#
|
2464
2710
|
# For example:
|
2465
2711
|
#
|
2466
2712
|
# imap.select("inbox")
|
2467
|
-
# p imap.responses("EXISTS"
|
2713
|
+
# p imap.responses("EXISTS").last
|
2468
2714
|
# #=> 2
|
2715
|
+
# p imap.responses("UIDNEXT", &:last)
|
2716
|
+
# #=> 123456
|
2469
2717
|
# p imap.responses("UIDVALIDITY", &:last)
|
2470
2718
|
# #=> 968263756
|
2719
|
+
# p imap.responses {|responses|
|
2720
|
+
# {
|
2721
|
+
# exists: responses.delete("EXISTS").last,
|
2722
|
+
# uidnext: responses.delete("UIDNEXT").last,
|
2723
|
+
# uidvalidity: responses.delete("UIDVALIDITY").last,
|
2724
|
+
# }
|
2725
|
+
# }
|
2726
|
+
# #=> {:exists=>2, :uidnext=>123456, :uidvalidity=>968263756}
|
2727
|
+
# # "EXISTS", "UIDNEXT", and "UIDVALIDITY" have been removed:
|
2728
|
+
# p imap.responses(&:keys)
|
2729
|
+
# #=> ["FLAGS", "OK", "PERMANENTFLAGS", "RECENT", "HIGHESTMODSEQ"]
|
2730
|
+
#
|
2731
|
+
# Related: #extract_responses, #clear_responses, #response_handlers, #greeting
|
2471
2732
|
#
|
2733
|
+
# ===== Thread safety
|
2472
2734
|
# >>>
|
2473
2735
|
# *Note:* Access to the responses hash is synchronized for thread-safety.
|
2474
2736
|
# The receiver thread and response_handlers cannot process new responses
|
2475
2737
|
# until the block completes. Accessing either the response hash or its
|
2476
|
-
# response type arrays outside of the block is unsafe.
|
2738
|
+
# response type arrays outside of the block is unsafe. They can be safely
|
2739
|
+
# updated inside the block. Consider using #clear_responses or
|
2740
|
+
# #extract_responses instead.
|
2477
2741
|
#
|
2478
|
-
#
|
2479
|
-
#
|
2742
|
+
# Net::IMAP will add and remove responses from the responses hash and its
|
2743
|
+
# array values, in the calling threads for commands and in the receiver
|
2744
|
+
# thread, but will not modify any responses after adding them to the
|
2745
|
+
# responses hash.
|
2746
|
+
#
|
2747
|
+
# ===== Clearing responses
|
2480
2748
|
#
|
2481
2749
|
# Previously unhandled responses are automatically cleared before entering a
|
2482
2750
|
# mailbox with #select or #examine. Long-lived connections can receive many
|
2483
2751
|
# unhandled server responses, which must be pruned or they will continually
|
2484
2752
|
# consume more memory. Update or clear the responses hash or arrays inside
|
2485
|
-
# the block, or
|
2753
|
+
# the block, or remove responses with #extract_responses, #clear_responses,
|
2754
|
+
# or #add_response_handler.
|
2755
|
+
#
|
2756
|
+
# ===== Missing responses
|
2486
2757
|
#
|
2487
2758
|
# Only non-+nil+ data is stored. Many important response codes have no data
|
2488
2759
|
# of their own, but are used as "tags" on the ResponseText object they are
|
@@ -2493,15 +2764,25 @@ module Net
|
|
2493
2764
|
# ResponseCode#data on tagged responses. Although some command methods do
|
2494
2765
|
# return the TaggedResponse directly, #add_response_handler must be used to
|
2495
2766
|
# handle all response codes.
|
2496
|
-
#
|
2497
|
-
# Related: #clear_responses, #response_handlers, #greeting
|
2498
2767
|
def responses(type = nil)
|
2499
2768
|
if block_given?
|
2500
2769
|
synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
|
2501
2770
|
elsif type
|
2502
|
-
|
2771
|
+
synchronize { @responses[type.to_s.upcase].dup.freeze }
|
2503
2772
|
else
|
2504
|
-
|
2773
|
+
case config.responses_without_block
|
2774
|
+
when :raise
|
2775
|
+
raise ArgumentError, RESPONSES_DEPRECATION_MSG
|
2776
|
+
when :warn
|
2777
|
+
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
2778
|
+
when :frozen_dup
|
2779
|
+
synchronize {
|
2780
|
+
responses = @responses.transform_values(&:freeze)
|
2781
|
+
responses.default_proc = nil
|
2782
|
+
responses.default = [].freeze
|
2783
|
+
return responses.freeze
|
2784
|
+
}
|
2785
|
+
end
|
2505
2786
|
@responses
|
2506
2787
|
end
|
2507
2788
|
end
|
@@ -2516,7 +2797,7 @@ module Net
|
|
2516
2797
|
# Clearing responses is synchronized with other threads. The lock is
|
2517
2798
|
# released before returning.
|
2518
2799
|
#
|
2519
|
-
# Related: #responses, #response_handlers
|
2800
|
+
# Related: #extract_responses, #responses, #response_handlers
|
2520
2801
|
def clear_responses(type = nil)
|
2521
2802
|
synchronize {
|
2522
2803
|
if type
|
@@ -2530,6 +2811,30 @@ module Net
|
|
2530
2811
|
.freeze
|
2531
2812
|
end
|
2532
2813
|
|
2814
|
+
# :call-seq:
|
2815
|
+
# extract_responses(type) {|response| ... } -> array
|
2816
|
+
#
|
2817
|
+
# Yields all of the unhandled #responses for a single response +type+.
|
2818
|
+
# Removes and returns the responses for which the block returns a true
|
2819
|
+
# value.
|
2820
|
+
#
|
2821
|
+
# Extracting responses is synchronized with other threads. The lock is
|
2822
|
+
# released before returning.
|
2823
|
+
#
|
2824
|
+
# Related: #responses, #clear_responses
|
2825
|
+
def extract_responses(type)
|
2826
|
+
type = String.try_convert(type) or
|
2827
|
+
raise ArgumentError, "type must be a string"
|
2828
|
+
raise ArgumentError, "must provide a block" unless block_given?
|
2829
|
+
extracted = []
|
2830
|
+
responses(type) do |all|
|
2831
|
+
all.reject! do |response|
|
2832
|
+
extracted << response if yield response
|
2833
|
+
end
|
2834
|
+
end
|
2835
|
+
extracted
|
2836
|
+
end
|
2837
|
+
|
2533
2838
|
# Returns all response handlers, including those that are added internally
|
2534
2839
|
# by commands. Each response handler will be called with every new
|
2535
2840
|
# UntaggedResponse, TaggedResponse, and ContinuationRequest.
|
@@ -2582,8 +2887,6 @@ module Net
|
|
2582
2887
|
PORT = 143 # :nodoc:
|
2583
2888
|
SSL_PORT = 993 # :nodoc:
|
2584
2889
|
|
2585
|
-
@@debug = false
|
2586
|
-
|
2587
2890
|
def start_imap_connection
|
2588
2891
|
@greeting = get_server_greeting
|
2589
2892
|
@capabilities = capabilities_from_resp_code @greeting
|
@@ -2611,12 +2914,12 @@ module Net
|
|
2611
2914
|
end
|
2612
2915
|
|
2613
2916
|
def tcp_socket(host, port)
|
2614
|
-
s = Socket.tcp(host, port, :connect_timeout =>
|
2917
|
+
s = Socket.tcp(host, port, :connect_timeout => open_timeout)
|
2615
2918
|
s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
|
2616
2919
|
s
|
2617
2920
|
rescue Errno::ETIMEDOUT
|
2618
2921
|
raise Net::OpenTimeout, "Timeout to open TCP connection to " +
|
2619
|
-
"#{host}:#{port} (exceeds #{
|
2922
|
+
"#{host}:#{port} (exceeds #{open_timeout} seconds)"
|
2620
2923
|
end
|
2621
2924
|
|
2622
2925
|
def receive_responses
|
@@ -2728,7 +3031,7 @@ module Net
|
|
2728
3031
|
end
|
2729
3032
|
end
|
2730
3033
|
return nil if buff.length == 0
|
2731
|
-
if
|
3034
|
+
if config.debug?
|
2732
3035
|
$stderr.print(buff.gsub(/^/n, "S: "))
|
2733
3036
|
end
|
2734
3037
|
return @parser.parse(buff)
|
@@ -2807,7 +3110,7 @@ module Net
|
|
2807
3110
|
|
2808
3111
|
def put_string(str)
|
2809
3112
|
@sock.print(str)
|
2810
|
-
if
|
3113
|
+
if config.debug?
|
2811
3114
|
if @debug_output_bol
|
2812
3115
|
$stderr.print("C: ")
|
2813
3116
|
end
|
@@ -2820,18 +3123,19 @@ module Net
|
|
2820
3123
|
end
|
2821
3124
|
end
|
2822
3125
|
|
2823
|
-
def
|
2824
|
-
if
|
2825
|
-
|
3126
|
+
def enforce_logindisabled?
|
3127
|
+
if config.enforce_logindisabled == :when_capabilities_cached
|
3128
|
+
capabilities_cached?
|
2826
3129
|
else
|
2827
|
-
|
3130
|
+
config.enforce_logindisabled
|
2828
3131
|
end
|
3132
|
+
end
|
3133
|
+
|
3134
|
+
def search_internal(cmd, keys, charset = nil)
|
3135
|
+
keys = normalize_searching_criteria(keys)
|
3136
|
+
args = charset ? ["CHARSET", charset, *keys] : keys
|
2829
3137
|
synchronize do
|
2830
|
-
|
2831
|
-
send_command(cmd, "CHARSET", charset, *keys)
|
2832
|
-
else
|
2833
|
-
send_command(cmd, *keys)
|
2834
|
-
end
|
3138
|
+
send_command(cmd, *args)
|
2835
3139
|
clear_responses("SEARCH").last || []
|
2836
3140
|
end
|
2837
3141
|
end
|
@@ -2853,9 +3157,9 @@ module Net
|
|
2853
3157
|
synchronize do
|
2854
3158
|
clear_responses("FETCH")
|
2855
3159
|
if mod
|
2856
|
-
send_command(cmd,
|
3160
|
+
send_command(cmd, SequenceSet.new(set), attr, mod)
|
2857
3161
|
else
|
2858
|
-
send_command(cmd,
|
3162
|
+
send_command(cmd, SequenceSet.new(set), attr)
|
2859
3163
|
end
|
2860
3164
|
clear_responses("FETCH")
|
2861
3165
|
end
|
@@ -2863,7 +3167,7 @@ module Net
|
|
2863
3167
|
|
2864
3168
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
2865
3169
|
attr = RawData.new(attr) if attr.instance_of?(String)
|
2866
|
-
args = [
|
3170
|
+
args = [SequenceSet.new(set)]
|
2867
3171
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
2868
3172
|
args << attr << flags
|
2869
3173
|
synchronize do
|
@@ -2874,15 +3178,11 @@ module Net
|
|
2874
3178
|
end
|
2875
3179
|
|
2876
3180
|
def copy_internal(cmd, set, mailbox)
|
2877
|
-
send_command(cmd,
|
3181
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
2878
3182
|
end
|
2879
3183
|
|
2880
3184
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
2881
|
-
|
2882
|
-
search_keys = [RawData.new(search_keys)]
|
2883
|
-
else
|
2884
|
-
normalize_searching_criteria(search_keys)
|
2885
|
-
end
|
3185
|
+
search_keys = normalize_searching_criteria(search_keys)
|
2886
3186
|
synchronize do
|
2887
3187
|
send_command(cmd, sort_keys, charset, *search_keys)
|
2888
3188
|
clear_responses("SORT").last || []
|
@@ -2890,25 +3190,39 @@ module Net
|
|
2890
3190
|
end
|
2891
3191
|
|
2892
3192
|
def thread_internal(cmd, algorithm, search_keys, charset)
|
2893
|
-
|
2894
|
-
search_keys = [RawData.new(search_keys)]
|
2895
|
-
else
|
2896
|
-
normalize_searching_criteria(search_keys)
|
2897
|
-
end
|
3193
|
+
search_keys = normalize_searching_criteria(search_keys)
|
2898
3194
|
synchronize do
|
2899
3195
|
send_command(cmd, algorithm, charset, *search_keys)
|
2900
3196
|
clear_responses("THREAD").last || []
|
2901
3197
|
end
|
2902
3198
|
end
|
2903
3199
|
|
2904
|
-
def normalize_searching_criteria(
|
2905
|
-
|
2906
|
-
|
2907
|
-
|
2908
|
-
|
3200
|
+
def normalize_searching_criteria(criteria)
|
3201
|
+
return RawData.new(criteria) if criteria.is_a?(String)
|
3202
|
+
criteria.map {|i|
|
3203
|
+
if coerce_search_arg_to_seqset?(i)
|
3204
|
+
SequenceSet[i]
|
2909
3205
|
else
|
2910
3206
|
i
|
2911
3207
|
end
|
3208
|
+
}
|
3209
|
+
end
|
3210
|
+
|
3211
|
+
def coerce_search_arg_to_seqset?(obj)
|
3212
|
+
case obj
|
3213
|
+
when Set, -1, :* then true
|
3214
|
+
when Range then true
|
3215
|
+
when Array then obj.all? { coerce_search_array_arg_to_seqset? _1 }
|
3216
|
+
else obj.respond_to?(:to_sequence_set)
|
3217
|
+
end
|
3218
|
+
end
|
3219
|
+
|
3220
|
+
def coerce_search_array_arg_to_seqset?(obj)
|
3221
|
+
case obj
|
3222
|
+
when Integer then obj.positive? || obj == -1
|
3223
|
+
when String then ResponseParser::Patterns::SEQUENCE_SET_STR.match?(obj.b)
|
3224
|
+
else
|
3225
|
+
coerce_search_arg_to_seqset?(obj)
|
2912
3226
|
end
|
2913
3227
|
end
|
2914
3228
|
|
@@ -2934,7 +3248,7 @@ module Net
|
|
2934
3248
|
@sock = SSLSocket.new(@sock, ssl_ctx)
|
2935
3249
|
@sock.sync_close = true
|
2936
3250
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
2937
|
-
ssl_socket_connect(@sock,
|
3251
|
+
ssl_socket_connect(@sock, open_timeout)
|
2938
3252
|
if ssl_ctx.verify_mode != VERIFY_NONE
|
2939
3253
|
@sock.post_connection_check(@host)
|
2940
3254
|
@tls_verified = true
|
@@ -2959,6 +3273,7 @@ module Net
|
|
2959
3273
|
end
|
2960
3274
|
|
2961
3275
|
require_relative "imap/errors"
|
3276
|
+
require_relative "imap/config"
|
2962
3277
|
require_relative "imap/command_data"
|
2963
3278
|
require_relative "imap/data_encoding"
|
2964
3279
|
require_relative "imap/flags"
|