net-imap 0.4.17 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -1
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +11 -0
- data/lib/net/imap/config.rb +36 -10
- 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 -0
- data/lib/net/imap/response_data.rb +6 -93
- data/lib/net/imap/response_parser.rb +5 -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 +18 -23
- data/lib/net/imap.rb +285 -108
- data/net-imap.gemspec +1 -1
- data/rakelib/string_prep_tables_generator.rb +2 -0
- metadata +7 -7
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set" unless defined?(::Set)
|
4
|
+
|
3
5
|
module Net
|
4
6
|
class IMAP
|
5
7
|
|
@@ -14,13 +16,6 @@ module Net
|
|
14
16
|
# receive a SequenceSet as an argument, for example IMAP#search, IMAP#fetch,
|
15
17
|
# and IMAP#store.
|
16
18
|
#
|
17
|
-
# == EXPERIMENTAL API
|
18
|
-
#
|
19
|
-
# SequenceSet is currently experimental. Only two methods, ::[] and
|
20
|
-
# #valid_string, are considered stable. Although the API isn't expected to
|
21
|
-
# change much, any other methods may be removed or changed without
|
22
|
-
# deprecation.
|
23
|
-
#
|
24
19
|
# == Creating sequence sets
|
25
20
|
#
|
26
21
|
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
@@ -37,7 +32,8 @@ module Net
|
|
37
32
|
#
|
38
33
|
# SequenceSet.new may receive a single optional argument: a non-zero 32 bit
|
39
34
|
# unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
|
40
|
-
# another sequence set,
|
35
|
+
# another sequence set, a Set (containing only numbers or <tt>*</tt>), or an
|
36
|
+
# Array containing any of these (array inputs may be nested).
|
41
37
|
#
|
42
38
|
# set = Net::IMAP::SequenceSet.new(1)
|
43
39
|
# set.valid_string #=> "1"
|
@@ -286,11 +282,7 @@ module Net
|
|
286
282
|
|
287
283
|
# valid inputs for "*"
|
288
284
|
STARS = [:*, ?*, -1].freeze
|
289
|
-
private_constant :
|
290
|
-
|
291
|
-
COERCIBLE = ->{ _1.respond_to? :to_sequence_set }
|
292
|
-
ENUMABLE = ->{ _1.respond_to?(:each) && _1.respond_to?(:empty?) }
|
293
|
-
private_constant :COERCIBLE, :ENUMABLE
|
285
|
+
private_constant :STARS
|
294
286
|
|
295
287
|
class << self
|
296
288
|
|
@@ -325,7 +317,7 @@ module Net
|
|
325
317
|
# raised.
|
326
318
|
def try_convert(obj)
|
327
319
|
return obj if obj.is_a?(SequenceSet)
|
328
|
-
return nil unless respond_to?(:to_sequence_set)
|
320
|
+
return nil unless obj.respond_to?(:to_sequence_set)
|
329
321
|
obj = obj.to_sequence_set
|
330
322
|
return obj if obj.is_a?(SequenceSet)
|
331
323
|
raise DataFormatError, "invalid object returned from to_sequence_set"
|
@@ -389,6 +381,10 @@ module Net
|
|
389
381
|
# Related: #valid_string, #normalized_string, #to_s
|
390
382
|
def string; @string ||= normalized_string if valid? end
|
391
383
|
|
384
|
+
# Returns an array with #normalized_string when valid and an empty array
|
385
|
+
# otherwise.
|
386
|
+
def deconstruct; valid? ? [normalized_string] : [] end
|
387
|
+
|
392
388
|
# Assigns a new string to #string and resets #elements to match. It
|
393
389
|
# cannot be set to an empty string—assign +nil+ or use #clear instead.
|
394
390
|
# The string is validated but not normalized.
|
@@ -1272,7 +1268,8 @@ module Net
|
|
1272
1268
|
when *STARS, Integer, Range then [input_to_tuple(obj)]
|
1273
1269
|
when String then str_to_tuples obj
|
1274
1270
|
when SequenceSet then obj.tuples
|
1275
|
-
when
|
1271
|
+
when Set then obj.map { [to_tuple_int(_1)] * 2 }
|
1272
|
+
when Array then obj.flat_map { input_to_tuples _1 }
|
1276
1273
|
when nil then []
|
1277
1274
|
else
|
1278
1275
|
raise DataFormatError,
|
@@ -1285,8 +1282,7 @@ module Net
|
|
1285
1282
|
# String, Set, Array, or... any type of object.
|
1286
1283
|
def input_try_convert(input)
|
1287
1284
|
SequenceSet.try_convert(input) ||
|
1288
|
-
|
1289
|
-
input.respond_to?(:to_int) && Integer(input.to_int) ||
|
1285
|
+
Integer.try_convert(input) ||
|
1290
1286
|
String.try_convert(input) ||
|
1291
1287
|
input
|
1292
1288
|
end
|
@@ -1416,12 +1412,11 @@ module Net
|
|
1416
1412
|
end
|
1417
1413
|
|
1418
1414
|
def nz_number(num)
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
num
|
1415
|
+
String === num && !/\A[1-9]\d*\z/.match?(num) and
|
1416
|
+
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
1417
|
+
NumValidator.ensure_nz_number Integer num
|
1418
|
+
rescue TypeError # To catch errors from Integer()
|
1419
|
+
raise DataFormatError, $!.message
|
1425
1420
|
end
|
1426
1421
|
|
1427
1422
|
# intentionally defined after the class implementation
|
data/lib/net/imap.rb
CHANGED
@@ -719,7 +719,7 @@ module Net
|
|
719
719
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
720
720
|
#
|
721
721
|
class IMAP < Protocol
|
722
|
-
VERSION = "0.
|
722
|
+
VERSION = "0.5.1"
|
723
723
|
|
724
724
|
# Aliases for supported capabilities, to be used with the #enable command.
|
725
725
|
ENABLE_ALIASES = {
|
@@ -946,9 +946,6 @@ module Net
|
|
946
946
|
@sock = tcp_socket(@host, @port)
|
947
947
|
start_tls_session if ssl_ctx
|
948
948
|
start_imap_connection
|
949
|
-
|
950
|
-
# DEPRECATED: to remove in next version
|
951
|
-
@client_thread = Thread.current
|
952
949
|
end
|
953
950
|
|
954
951
|
# Returns true after the TLS negotiation has completed and the remote
|
@@ -956,11 +953,6 @@ module Net
|
|
956
953
|
# but peer verification was disabled.
|
957
954
|
def tls_verified?; @tls_verified end
|
958
955
|
|
959
|
-
def client_thread # :nodoc:
|
960
|
-
warn "Net::IMAP#client_thread is deprecated and will be removed soon."
|
961
|
-
@client_thread
|
962
|
-
end
|
963
|
-
|
964
956
|
# Disconnects from the server.
|
965
957
|
#
|
966
958
|
# Related: #logout, #logout!
|
@@ -1244,6 +1236,9 @@ module Net
|
|
1244
1236
|
# +SASL-IR+ capability, below). Defaults to the #config value for
|
1245
1237
|
# {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
|
1246
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
|
+
#
|
1247
1242
|
# All other arguments are forwarded to the registered SASL authenticator for
|
1248
1243
|
# the requested mechanism. <em>The documentation for each individual
|
1249
1244
|
# mechanism must be consulted for its specific parameters.</em>
|
@@ -1338,29 +1333,9 @@ module Net
|
|
1338
1333
|
# Previously cached #capabilities will be cleared when this method
|
1339
1334
|
# completes. If the TaggedResponse to #authenticate includes updated
|
1340
1335
|
# capabilities, they will be cached.
|
1341
|
-
def authenticate(
|
1342
|
-
|
1343
|
-
|
1344
|
-
mechanism = mechanism.to_s.tr("_", "-").upcase
|
1345
|
-
authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
|
1346
|
-
cmdargs = ["AUTHENTICATE", mechanism]
|
1347
|
-
if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
|
1348
|
-
authenticator.respond_to?(:initial_response?) &&
|
1349
|
-
authenticator.initial_response?
|
1350
|
-
response = authenticator.process(nil)
|
1351
|
-
cmdargs << (response.empty? ? "=" : [response].pack("m0"))
|
1352
|
-
end
|
1353
|
-
result = send_command_with_continuations(*cmdargs) {|data|
|
1354
|
-
challenge = data.unpack1("m")
|
1355
|
-
response = authenticator.process challenge
|
1356
|
-
[response].pack("m0")
|
1357
|
-
}
|
1358
|
-
if authenticator.respond_to?(:done?) && !authenticator.done?
|
1359
|
-
logout!
|
1360
|
-
raise SASL::AuthenticationIncomplete, result
|
1361
|
-
end
|
1362
|
-
@capabilities = capabilities_from_resp_code result
|
1363
|
-
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 }
|
1364
1339
|
end
|
1365
1340
|
|
1366
1341
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1380,13 +1355,9 @@ module Net
|
|
1380
1355
|
# ===== Capabilities
|
1381
1356
|
#
|
1382
1357
|
# An IMAP client MUST NOT call #login when the server advertises the
|
1383
|
-
# +LOGINDISABLED+ capability.
|
1384
|
-
#
|
1385
|
-
#
|
1386
|
-
# raise "Remote server has disabled the login command"
|
1387
|
-
# else
|
1388
|
-
# imap.login username, password
|
1389
|
-
# end
|
1358
|
+
# +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
|
1359
|
+
# LoginDisabledError when that capability is present. See
|
1360
|
+
# Config#enforce_logindisabled.
|
1390
1361
|
#
|
1391
1362
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1392
1363
|
# Cached capabilities _must_ be invalidated after this method completes.
|
@@ -1394,6 +1365,9 @@ module Net
|
|
1394
1365
|
# ResponseCode.
|
1395
1366
|
#
|
1396
1367
|
def login(user, password)
|
1368
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
1369
|
+
raise LoginDisabledError
|
1370
|
+
end
|
1397
1371
|
send_command("LOGIN", user, password)
|
1398
1372
|
.tap { @capabilities = capabilities_from_resp_code _1 }
|
1399
1373
|
end
|
@@ -1950,82 +1924,274 @@ module Net
|
|
1950
1924
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
1951
1925
|
def uid_expunge(uid_set)
|
1952
1926
|
synchronize do
|
1953
|
-
send_command("UID EXPUNGE",
|
1927
|
+
send_command("UID EXPUNGE", SequenceSet.new(uid_set))
|
1954
1928
|
clear_responses("EXPUNGE")
|
1955
1929
|
end
|
1956
1930
|
end
|
1957
1931
|
|
1958
|
-
#
|
1959
|
-
#
|
1960
|
-
# criteria, and returns message sequence numbers. +keys+ can either be a
|
1961
|
-
# string holding the entire search string, or a single-dimension array of
|
1962
|
-
# search keywords and arguments.
|
1932
|
+
# :call-seq:
|
1933
|
+
# search(criteria, charset = nil) -> result
|
1963
1934
|
#
|
1964
|
-
#
|
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
|
1965
1938
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
1966
1939
|
# capability has been enabled.
|
1967
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
|
+
#
|
1968
1973
|
# Related: #uid_search
|
1969
1974
|
#
|
1970
|
-
# =====
|
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)")
|
1990
|
+
#
|
1991
|
+
# ===== Search keys
|
1971
1992
|
#
|
1972
|
-
# For
|
1993
|
+
# For full definitions of the standard search +criteria+,
|
1973
1994
|
# see [{IMAP4rev1 §6.4.4}[https://www.rfc-editor.org/rfc/rfc3501.html#section-6.4.4]],
|
1974
1995
|
# or [{IMAP4rev2 §6.4.4}[https://www.rfc-editor.org/rfc/rfc9051.html#section-6.4.4]],
|
1975
1996
|
# in addition to documentation for
|
1976
|
-
# any
|
1977
|
-
# reported by #capabilities which may define additional search filters, e.g:
|
1997
|
+
# any #capabilities which may define additional search filters, such as
|
1978
1998
|
# +CONDSTORE+, +WITHIN+, +FILTERS+, <tt>SEARCH=FUZZY</tt>, +OBJECTID+, or
|
1979
|
-
# +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.
|
1980
2099
|
#
|
1981
|
-
#
|
1982
|
-
#
|
1983
|
-
#
|
2100
|
+
# +RECENT+::
|
2101
|
+
# +UNRECENT+::
|
2102
|
+
# Matches messages with or without the <tt>\\Recent</tt> flag.
|
1984
2103
|
#
|
1985
|
-
#
|
1986
|
-
#
|
1987
|
-
# to <tt>8-Aug-2002</tt>, and can be formatted using
|
1988
|
-
# Net::IMAP.format_date.
|
2104
|
+
# +NEW+::
|
2105
|
+
# Equivalent to <tt>(RECENT UNSEEN)</tt>.
|
1989
2106
|
#
|
1990
|
-
#
|
2107
|
+
# ====== Extension search keys
|
1991
2108
|
#
|
1992
|
-
#
|
2109
|
+
# The search keys described below are defined by standard \IMAP extensions.
|
1993
2110
|
#
|
1994
|
-
#
|
2111
|
+
# +OLDER+ _interval_::
|
2112
|
+
# +YOUNGER+ _interval_::
|
2113
|
+
# Matches when +INTERNALDATE+ is more/less than _interval_ seconds ago.
|
1995
2114
|
#
|
1996
|
-
#
|
2115
|
+
# <em>Requires the +WITHIN+ capability</em>.
|
2116
|
+
# {[RFC5032]}[https://www.rfc-editor.org/rfc/rfc5032.html]
|
1997
2117
|
#
|
1998
|
-
#
|
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.
|
1999
2121
|
#
|
2000
|
-
#
|
2122
|
+
# <em>Requires the +ANNOTATE-EXPERIMENT-1+ capability</em>.
|
2123
|
+
# {[RFC5257]}[https://www.rfc-editor.org/rfc/rfc5257.html].
|
2001
2124
|
#
|
2002
|
-
#
|
2003
|
-
#
|
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.
|
2004
2128
|
#
|
2005
|
-
#
|
2129
|
+
# <em>Requires the +FILTERS+ capability</em>.
|
2130
|
+
# {[RFC5466]}[https://www.rfc-editor.org/rfc/rfc5466.html#section-3.1]
|
2006
2131
|
#
|
2007
|
-
#
|
2132
|
+
# +FUZZY+ _search-key_::
|
2133
|
+
# Uses fuzzy matching for the specified search key.
|
2008
2134
|
#
|
2009
|
-
#
|
2135
|
+
# <em>Requires the <tt>SEARCH=FUZZY</tt> capability.</em>
|
2136
|
+
# {[RFC6203]}[https://www.rfc-editor.org/rfc/rfc6203.html#section-6].
|
2010
2137
|
#
|
2011
|
-
#
|
2138
|
+
# +MODSEQ+ _modseq_::
|
2139
|
+
# Matches when +MODSEQ+ is greater than or equal to _modseq_.
|
2012
2140
|
#
|
2013
|
-
#
|
2014
|
-
#
|
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]
|
2015
2178
|
#
|
2016
2179
|
# ===== Capabilities
|
2017
2180
|
#
|
2018
|
-
# If
|
2181
|
+
# If CONDSTORE[https://www.rfc-editor.org/rfc/rfc7162.html] is supported
|
2019
2182
|
# and enabled for the selected mailbox, a non-empty SearchResult will
|
2020
2183
|
# include a +MODSEQ+ value.
|
2021
2184
|
# imap.select("mbox", condstore: true)
|
2022
|
-
# result = imap.search(["SUBJECT", "hi there", "not", "new")
|
2185
|
+
# result = imap.search(["SUBJECT", "hi there", "not", "new"])
|
2023
2186
|
# #=> Net::IMAP::SearchResult[1, 6, 7, 8, modseq: 5594]
|
2024
2187
|
# result.modseq # => 5594
|
2025
|
-
def search(
|
2026
|
-
|
2188
|
+
def search(...)
|
2189
|
+
search_internal("SEARCH", ...)
|
2027
2190
|
end
|
2028
2191
|
|
2192
|
+
# :call-seq:
|
2193
|
+
# uid_search(criteria, charset = nil) -> result
|
2194
|
+
#
|
2029
2195
|
# Sends a {UID SEARCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
|
2030
2196
|
# to search the mailbox for messages that match the given searching
|
2031
2197
|
# criteria, and returns unique identifiers (<tt>UID</tt>s).
|
@@ -2034,9 +2200,9 @@ module Net
|
|
2034
2200
|
# backward compatibility) but adds SearchResult#modseq when the +CONDSTORE+
|
2035
2201
|
# capability has been enabled.
|
2036
2202
|
#
|
2037
|
-
# See #search for documentation of
|
2038
|
-
def uid_search(
|
2039
|
-
|
2203
|
+
# See #search for documentation of parameters.
|
2204
|
+
def uid_search(...)
|
2205
|
+
search_internal("UID SEARCH", ...)
|
2040
2206
|
end
|
2041
2207
|
|
2042
2208
|
# :call-seq:
|
@@ -2608,7 +2774,7 @@ module Net
|
|
2608
2774
|
when :raise
|
2609
2775
|
raise ArgumentError, RESPONSES_DEPRECATION_MSG
|
2610
2776
|
when :warn
|
2611
|
-
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1)
|
2777
|
+
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
2612
2778
|
when :frozen_dup
|
2613
2779
|
synchronize {
|
2614
2780
|
responses = @responses.transform_values(&:freeze)
|
@@ -2957,18 +3123,19 @@ module Net
|
|
2957
3123
|
end
|
2958
3124
|
end
|
2959
3125
|
|
2960
|
-
def
|
2961
|
-
if
|
2962
|
-
|
3126
|
+
def enforce_logindisabled?
|
3127
|
+
if config.enforce_logindisabled == :when_capabilities_cached
|
3128
|
+
capabilities_cached?
|
2963
3129
|
else
|
2964
|
-
|
3130
|
+
config.enforce_logindisabled
|
2965
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
|
2966
3137
|
synchronize do
|
2967
|
-
|
2968
|
-
send_command(cmd, "CHARSET", charset, *keys)
|
2969
|
-
else
|
2970
|
-
send_command(cmd, *keys)
|
2971
|
-
end
|
3138
|
+
send_command(cmd, *args)
|
2972
3139
|
clear_responses("SEARCH").last || []
|
2973
3140
|
end
|
2974
3141
|
end
|
@@ -2990,9 +3157,9 @@ module Net
|
|
2990
3157
|
synchronize do
|
2991
3158
|
clear_responses("FETCH")
|
2992
3159
|
if mod
|
2993
|
-
send_command(cmd,
|
3160
|
+
send_command(cmd, SequenceSet.new(set), attr, mod)
|
2994
3161
|
else
|
2995
|
-
send_command(cmd,
|
3162
|
+
send_command(cmd, SequenceSet.new(set), attr)
|
2996
3163
|
end
|
2997
3164
|
clear_responses("FETCH")
|
2998
3165
|
end
|
@@ -3000,7 +3167,7 @@ module Net
|
|
3000
3167
|
|
3001
3168
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
3002
3169
|
attr = RawData.new(attr) if attr.instance_of?(String)
|
3003
|
-
args = [
|
3170
|
+
args = [SequenceSet.new(set)]
|
3004
3171
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
3005
3172
|
args << attr << flags
|
3006
3173
|
synchronize do
|
@@ -3011,15 +3178,11 @@ module Net
|
|
3011
3178
|
end
|
3012
3179
|
|
3013
3180
|
def copy_internal(cmd, set, mailbox)
|
3014
|
-
send_command(cmd,
|
3181
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
3015
3182
|
end
|
3016
3183
|
|
3017
3184
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
3018
|
-
|
3019
|
-
search_keys = [RawData.new(search_keys)]
|
3020
|
-
else
|
3021
|
-
normalize_searching_criteria(search_keys)
|
3022
|
-
end
|
3185
|
+
search_keys = normalize_searching_criteria(search_keys)
|
3023
3186
|
synchronize do
|
3024
3187
|
send_command(cmd, sort_keys, charset, *search_keys)
|
3025
3188
|
clear_responses("SORT").last || []
|
@@ -3027,25 +3190,39 @@ module Net
|
|
3027
3190
|
end
|
3028
3191
|
|
3029
3192
|
def thread_internal(cmd, algorithm, search_keys, charset)
|
3030
|
-
|
3031
|
-
search_keys = [RawData.new(search_keys)]
|
3032
|
-
else
|
3033
|
-
normalize_searching_criteria(search_keys)
|
3034
|
-
end
|
3193
|
+
search_keys = normalize_searching_criteria(search_keys)
|
3035
3194
|
synchronize do
|
3036
3195
|
send_command(cmd, algorithm, charset, *search_keys)
|
3037
3196
|
clear_responses("THREAD").last || []
|
3038
3197
|
end
|
3039
3198
|
end
|
3040
3199
|
|
3041
|
-
def normalize_searching_criteria(
|
3042
|
-
|
3043
|
-
|
3044
|
-
|
3045
|
-
|
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]
|
3046
3205
|
else
|
3047
3206
|
i
|
3048
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)
|
3049
3226
|
end
|
3050
3227
|
end
|
3051
3228
|
|
data/net-imap.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.summary = %q{Ruby client api for Internet Message Access Protocol}
|
17
17
|
spec.description = %q{Ruby client api for Internet Message Access Protocol}
|
18
18
|
spec.homepage = "https://github.com/ruby/net-imap"
|
19
|
-
spec.required_ruby_version = Gem::Requirement.new(">=
|
19
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
|
20
20
|
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
21
21
|
|
22
22
|
spec.metadata["homepage_uri"] = spec.homepage
|