net-imap 0.4.22 → 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.
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 +11 -0
- 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 +60 -96
- 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.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 +68 -159
- data/net-imap.gemspec +1 -1
- metadata +7 -6
- data/lib/net/imap/response_reader.rb +0 -75
- 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.
|
@@ -491,7 +449,7 @@ module Net
|
|
491
449
|
# ==== RFC3691: +UNSELECT+
|
492
450
|
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
493
451
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
494
|
-
# - #unselect: Closes the mailbox and returns to the
|
452
|
+
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
|
495
453
|
# without expunging any messages.
|
496
454
|
#
|
497
455
|
# ==== RFC4314: +ACL+
|
@@ -761,7 +719,7 @@ module Net
|
|
761
719
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
762
720
|
#
|
763
721
|
class IMAP < Protocol
|
764
|
-
VERSION = "0.
|
722
|
+
VERSION = "0.5.0"
|
765
723
|
|
766
724
|
# Aliases for supported capabilities, to be used with the #enable command.
|
767
725
|
ENABLE_ALIASES = {
|
@@ -769,7 +727,6 @@ module Net
|
|
769
727
|
"UTF8=ONLY" => "UTF8=ACCEPT",
|
770
728
|
}.freeze
|
771
729
|
|
772
|
-
autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__)
|
773
730
|
autoload :SASL, File.expand_path("imap/sasl", __dir__)
|
774
731
|
autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
|
775
732
|
autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
|
@@ -784,11 +741,9 @@ module Net
|
|
784
741
|
def self.config; Config.global end
|
785
742
|
|
786
743
|
# Returns the global debug mode.
|
787
|
-
# Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
|
788
744
|
def self.debug; config.debug end
|
789
745
|
|
790
746
|
# Sets the global debug mode.
|
791
|
-
# Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
|
792
747
|
def self.debug=(val)
|
793
748
|
config.debug = val
|
794
749
|
end
|
@@ -809,7 +764,7 @@ module Net
|
|
809
764
|
alias default_ssl_port default_tls_port
|
810
765
|
end
|
811
766
|
|
812
|
-
# Returns the initial greeting
|
767
|
+
# Returns the initial greeting the server, an UntaggedResponse.
|
813
768
|
attr_reader :greeting
|
814
769
|
|
815
770
|
# The client configuration. See Net::IMAP::Config.
|
@@ -818,28 +773,13 @@ module Net
|
|
818
773
|
# Net::IMAP.config.
|
819
774
|
attr_reader :config
|
820
775
|
|
821
|
-
|
822
|
-
#
|
823
|
-
#
|
824
|
-
|
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
|
825
780
|
|
826
|
-
##
|
827
|
-
# :attr_reader: idle_response_timeout
|
828
781
|
# Seconds to wait until an IDLE response is received.
|
829
|
-
|
830
|
-
|
831
|
-
##
|
832
|
-
# :attr_accessor: max_response_size
|
833
|
-
#
|
834
|
-
# The maximum allowed server response size, in bytes.
|
835
|
-
# Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
|
836
|
-
|
837
|
-
# :stopdoc:
|
838
|
-
def open_timeout; config.open_timeout end
|
839
|
-
def idle_response_timeout; config.idle_response_timeout end
|
840
|
-
def max_response_size; config.max_response_size end
|
841
|
-
def max_response_size=(val) config.max_response_size = val end
|
842
|
-
# :startdoc:
|
782
|
+
def idle_response_timeout; config.idle_response_timeout end
|
843
783
|
|
844
784
|
# The hostname this client connected to
|
845
785
|
attr_reader :host
|
@@ -895,12 +835,6 @@ module Net
|
|
895
835
|
#
|
896
836
|
# See DeprecatedClientOptions.new for deprecated SSL arguments.
|
897
837
|
#
|
898
|
-
# [response_handlers]
|
899
|
-
# A list of response handlers to be added before the receiver thread is
|
900
|
-
# started. This ensures every server response is handled, including the
|
901
|
-
# #greeting. Note that the greeting is handled in the current thread, but
|
902
|
-
# all other responses are handled in the receiver thread.
|
903
|
-
#
|
904
838
|
# [config]
|
905
839
|
# A Net::IMAP::Config object to use as the basis for #config. By default,
|
906
840
|
# the global Net::IMAP.config is used.
|
@@ -972,7 +906,7 @@ module Net
|
|
972
906
|
# [Net::IMAP::ByeResponseError]
|
973
907
|
# Connected to the host successfully, but it immediately said goodbye.
|
974
908
|
#
|
975
|
-
def initialize(host, port: nil, ssl:
|
909
|
+
def initialize(host, port: nil, ssl: nil,
|
976
910
|
config: Config.global, **config_options)
|
977
911
|
super()
|
978
912
|
# Config options
|
@@ -995,7 +929,6 @@ module Net
|
|
995
929
|
@receiver_thread = nil
|
996
930
|
@receiver_thread_exception = nil
|
997
931
|
@receiver_thread_terminating = false
|
998
|
-
response_handlers&.each do add_response_handler(_1) end
|
999
932
|
|
1000
933
|
# Client Protocol Sender (including state for currently running commands)
|
1001
934
|
@tag_prefix = "RUBY"
|
@@ -1011,12 +944,8 @@ module Net
|
|
1011
944
|
# Connection
|
1012
945
|
@tls_verified = false
|
1013
946
|
@sock = tcp_socket(@host, @port)
|
1014
|
-
@reader = ResponseReader.new(self, @sock)
|
1015
947
|
start_tls_session if ssl_ctx
|
1016
948
|
start_imap_connection
|
1017
|
-
|
1018
|
-
# DEPRECATED: to remove in next version
|
1019
|
-
@client_thread = Thread.current
|
1020
949
|
end
|
1021
950
|
|
1022
951
|
# Returns true after the TLS negotiation has completed and the remote
|
@@ -1024,11 +953,6 @@ module Net
|
|
1024
953
|
# but peer verification was disabled.
|
1025
954
|
def tls_verified?; @tls_verified end
|
1026
955
|
|
1027
|
-
def client_thread # :nodoc:
|
1028
|
-
warn "Net::IMAP#client_thread is deprecated and will be removed soon."
|
1029
|
-
@client_thread
|
1030
|
-
end
|
1031
|
-
|
1032
956
|
# Disconnects from the server.
|
1033
957
|
#
|
1034
958
|
# Related: #logout, #logout!
|
@@ -1272,10 +1196,6 @@ module Net
|
|
1272
1196
|
# both successful. Any error indicates that the connection has not been
|
1273
1197
|
# secured.
|
1274
1198
|
#
|
1275
|
-
# After the server agrees to start a TLS connection, this method waits up to
|
1276
|
-
# {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
|
1277
|
-
# +Net::OpenTimeout+.
|
1278
|
-
#
|
1279
1199
|
# *Note:*
|
1280
1200
|
# >>>
|
1281
1201
|
# Any #response_handlers added before STARTTLS should be aware that the
|
@@ -1294,21 +1214,13 @@ module Net
|
|
1294
1214
|
#
|
1295
1215
|
def starttls(**options)
|
1296
1216
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
1297
|
-
|
1298
|
-
ok = send_command("STARTTLS") do |resp|
|
1217
|
+
send_command("STARTTLS") do |resp|
|
1299
1218
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
1300
1219
|
clear_cached_capabilities
|
1301
1220
|
clear_responses
|
1302
1221
|
start_tls_session
|
1303
1222
|
end
|
1304
|
-
rescue Exception => error
|
1305
|
-
raise # note that the error backtrace is in the receiver_thread
|
1306
1223
|
end
|
1307
|
-
if error
|
1308
|
-
disconnect
|
1309
|
-
raise error
|
1310
|
-
end
|
1311
|
-
ok
|
1312
1224
|
end
|
1313
1225
|
|
1314
1226
|
# :call-seq:
|
@@ -1324,6 +1236,9 @@ module Net
|
|
1324
1236
|
# +SASL-IR+ capability, below). Defaults to the #config value for
|
1325
1237
|
# {sasl_ir}[rdoc-ref:Config#sasl_ir], which defaults to +true+.
|
1326
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
|
+
#
|
1327
1242
|
# All other arguments are forwarded to the registered SASL authenticator for
|
1328
1243
|
# the requested mechanism. <em>The documentation for each individual
|
1329
1244
|
# mechanism must be consulted for its specific parameters.</em>
|
@@ -1418,29 +1333,9 @@ module Net
|
|
1418
1333
|
# Previously cached #capabilities will be cleared when this method
|
1419
1334
|
# completes. If the TaggedResponse to #authenticate includes updated
|
1420
1335
|
# capabilities, they will be cached.
|
1421
|
-
def authenticate(
|
1422
|
-
|
1423
|
-
|
1424
|
-
mechanism = mechanism.to_s.tr("_", "-").upcase
|
1425
|
-
authenticator = SASL.authenticator(mechanism, *creds, **props, &callback)
|
1426
|
-
cmdargs = ["AUTHENTICATE", mechanism]
|
1427
|
-
if sasl_ir && capable?("SASL-IR") && auth_capable?(mechanism) &&
|
1428
|
-
authenticator.respond_to?(:initial_response?) &&
|
1429
|
-
authenticator.initial_response?
|
1430
|
-
response = authenticator.process(nil)
|
1431
|
-
cmdargs << (response.empty? ? "=" : [response].pack("m0"))
|
1432
|
-
end
|
1433
|
-
result = send_command_with_continuations(*cmdargs) {|data|
|
1434
|
-
challenge = data.unpack1("m")
|
1435
|
-
response = authenticator.process challenge
|
1436
|
-
[response].pack("m0")
|
1437
|
-
}
|
1438
|
-
if authenticator.respond_to?(:done?) && !authenticator.done?
|
1439
|
-
logout!
|
1440
|
-
raise SASL::AuthenticationIncomplete, result
|
1441
|
-
end
|
1442
|
-
@capabilities = capabilities_from_resp_code result
|
1443
|
-
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 }
|
1444
1339
|
end
|
1445
1340
|
|
1446
1341
|
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
|
@@ -1460,13 +1355,9 @@ module Net
|
|
1460
1355
|
# ===== Capabilities
|
1461
1356
|
#
|
1462
1357
|
# An IMAP client MUST NOT call #login when the server advertises the
|
1463
|
-
# +LOGINDISABLED+ capability.
|
1464
|
-
#
|
1465
|
-
#
|
1466
|
-
# raise "Remote server has disabled the login command"
|
1467
|
-
# else
|
1468
|
-
# imap.login username, password
|
1469
|
-
# end
|
1358
|
+
# +LOGINDISABLED+ capability. By default, Net::IMAP will raise a
|
1359
|
+
# LoginDisabledError when that capability is present. See
|
1360
|
+
# Config#enforce_logindisabled.
|
1470
1361
|
#
|
1471
1362
|
# Server capabilities may change after #starttls, #login, and #authenticate.
|
1472
1363
|
# Cached capabilities _must_ be invalidated after this method completes.
|
@@ -1474,6 +1365,9 @@ module Net
|
|
1474
1365
|
# ResponseCode.
|
1475
1366
|
#
|
1476
1367
|
def login(user, password)
|
1368
|
+
if enforce_logindisabled? && capability?("LOGINDISABLED")
|
1369
|
+
raise LoginDisabledError
|
1370
|
+
end
|
1477
1371
|
send_command("LOGIN", user, password)
|
1478
1372
|
.tap { @capabilities = capabilities_from_resp_code _1 }
|
1479
1373
|
end
|
@@ -2030,7 +1924,7 @@ module Net
|
|
2030
1924
|
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
|
2031
1925
|
def uid_expunge(uid_set)
|
2032
1926
|
synchronize do
|
2033
|
-
send_command("UID EXPUNGE",
|
1927
|
+
send_command("UID EXPUNGE", SequenceSet.new(uid_set))
|
2034
1928
|
clear_responses("EXPUNGE")
|
2035
1929
|
end
|
2036
1930
|
end
|
@@ -2688,7 +2582,7 @@ module Net
|
|
2688
2582
|
when :raise
|
2689
2583
|
raise ArgumentError, RESPONSES_DEPRECATION_MSG
|
2690
2584
|
when :warn
|
2691
|
-
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1)
|
2585
|
+
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
2692
2586
|
when :frozen_dup
|
2693
2587
|
synchronize {
|
2694
2588
|
responses = @responses.transform_values(&:freeze)
|
@@ -2778,10 +2672,6 @@ module Net
|
|
2778
2672
|
# end
|
2779
2673
|
# }
|
2780
2674
|
#
|
2781
|
-
# Response handlers can also be added when the client is created before the
|
2782
|
-
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
2783
|
-
# This ensures every server response is handled, including the #greeting.
|
2784
|
-
#
|
2785
2675
|
# Related: #remove_response_handler, #response_handlers
|
2786
2676
|
def add_response_handler(handler = nil, &block)
|
2787
2677
|
raise ArgumentError, "two Procs are passed" if handler && block
|
@@ -2808,7 +2698,6 @@ module Net
|
|
2808
2698
|
def start_imap_connection
|
2809
2699
|
@greeting = get_server_greeting
|
2810
2700
|
@capabilities = capabilities_from_resp_code @greeting
|
2811
|
-
@response_handlers.each do |handler| handler.call(@greeting) end
|
2812
2701
|
@receiver_thread = start_receiver_thread
|
2813
2702
|
rescue Exception
|
2814
2703
|
@sock.close
|
@@ -2937,10 +2826,23 @@ module Net
|
|
2937
2826
|
end
|
2938
2827
|
|
2939
2828
|
def get_response
|
2940
|
-
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
|
2941
2841
|
return nil if buff.length == 0
|
2942
|
-
|
2943
|
-
|
2842
|
+
if config.debug?
|
2843
|
+
$stderr.print(buff.gsub(/^/n, "S: "))
|
2844
|
+
end
|
2845
|
+
return @parser.parse(buff)
|
2944
2846
|
end
|
2945
2847
|
|
2946
2848
|
#############################
|
@@ -3029,6 +2931,14 @@ module Net
|
|
3029
2931
|
end
|
3030
2932
|
end
|
3031
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
|
+
|
3032
2942
|
def search_internal(cmd, keys, charset)
|
3033
2943
|
if keys.instance_of?(String)
|
3034
2944
|
keys = [RawData.new(keys)]
|
@@ -3062,9 +2972,9 @@ module Net
|
|
3062
2972
|
synchronize do
|
3063
2973
|
clear_responses("FETCH")
|
3064
2974
|
if mod
|
3065
|
-
send_command(cmd,
|
2975
|
+
send_command(cmd, SequenceSet.new(set), attr, mod)
|
3066
2976
|
else
|
3067
|
-
send_command(cmd,
|
2977
|
+
send_command(cmd, SequenceSet.new(set), attr)
|
3068
2978
|
end
|
3069
2979
|
clear_responses("FETCH")
|
3070
2980
|
end
|
@@ -3072,7 +2982,7 @@ module Net
|
|
3072
2982
|
|
3073
2983
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
3074
2984
|
attr = RawData.new(attr) if attr.instance_of?(String)
|
3075
|
-
args = [
|
2985
|
+
args = [SequenceSet.new(set)]
|
3076
2986
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
3077
2987
|
args << attr << flags
|
3078
2988
|
synchronize do
|
@@ -3083,7 +2993,7 @@ module Net
|
|
3083
2993
|
end
|
3084
2994
|
|
3085
2995
|
def copy_internal(cmd, set, mailbox)
|
3086
|
-
send_command(cmd,
|
2996
|
+
send_command(cmd, SequenceSet.new(set), mailbox)
|
3087
2997
|
end
|
3088
2998
|
|
3089
2999
|
def sort_internal(cmd, sort_keys, search_keys, charset)
|
@@ -3114,7 +3024,7 @@ module Net
|
|
3114
3024
|
keys.collect! do |i|
|
3115
3025
|
case i
|
3116
3026
|
when -1, Range, Array
|
3117
|
-
|
3027
|
+
SequenceSet.new(i)
|
3118
3028
|
else
|
3119
3029
|
i
|
3120
3030
|
end
|
@@ -3141,7 +3051,6 @@ module Net
|
|
3141
3051
|
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
3142
3052
|
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
3143
3053
|
@sock = SSLSocket.new(@sock, ssl_ctx)
|
3144
|
-
@reader = ResponseReader.new(self, @sock)
|
3145
3054
|
@sock.sync_close = true
|
3146
3055
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
3147
3056
|
ssl_socket_connect(@sock, open_timeout)
|
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
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-imap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
8
8
|
- nicholas a. evans
|
9
|
+
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2024-10-16 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: net-protocol
|
@@ -68,7 +69,6 @@ files:
|
|
68
69
|
- lib/net/imap/response_data.rb
|
69
70
|
- lib/net/imap/response_parser.rb
|
70
71
|
- lib/net/imap/response_parser/parser_utils.rb
|
71
|
-
- lib/net/imap/response_reader.rb
|
72
72
|
- lib/net/imap/sasl.rb
|
73
73
|
- lib/net/imap/sasl/anonymous_authenticator.rb
|
74
74
|
- lib/net/imap/sasl/authentication_exchange.rb
|
@@ -95,7 +95,6 @@ files:
|
|
95
95
|
- lib/net/imap/stringprep/saslprep_tables.rb
|
96
96
|
- lib/net/imap/stringprep/tables.rb
|
97
97
|
- lib/net/imap/stringprep/trace.rb
|
98
|
-
- lib/net/imap/uidplus_data.rb
|
99
98
|
- net-imap.gemspec
|
100
99
|
- rakelib/benchmarks.rake
|
101
100
|
- rakelib/rdoc.rake
|
@@ -111,6 +110,7 @@ metadata:
|
|
111
110
|
homepage_uri: https://github.com/ruby/net-imap
|
112
111
|
source_code_uri: https://github.com/ruby/net-imap
|
113
112
|
changelog_uri: https://github.com/ruby/net-imap/releases
|
113
|
+
post_install_message:
|
114
114
|
rdoc_options: []
|
115
115
|
require_paths:
|
116
116
|
- lib
|
@@ -118,14 +118,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
118
|
requirements:
|
119
119
|
- - ">="
|
120
120
|
- !ruby/object:Gem::Version
|
121
|
-
version:
|
121
|
+
version: 3.1.0
|
122
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
123
|
requirements:
|
124
124
|
- - ">="
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: '0'
|
127
127
|
requirements: []
|
128
|
-
rubygems_version: 3.
|
128
|
+
rubygems_version: 3.5.16
|
129
|
+
signing_key:
|
129
130
|
specification_version: 4
|
130
131
|
summary: Ruby client api for Internet Message Access Protocol
|
131
132
|
test_files: []
|
@@ -1,75 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Net
|
4
|
-
class IMAP
|
5
|
-
# See https://www.rfc-editor.org/rfc/rfc9051#section-2.2.2
|
6
|
-
class ResponseReader # :nodoc:
|
7
|
-
attr_reader :client
|
8
|
-
|
9
|
-
def initialize(client, sock)
|
10
|
-
@client, @sock = client, sock
|
11
|
-
end
|
12
|
-
|
13
|
-
def read_response_buffer
|
14
|
-
@buff = String.new
|
15
|
-
catch :eof do
|
16
|
-
while true
|
17
|
-
read_line
|
18
|
-
break unless (@literal_size = get_literal_size)
|
19
|
-
read_literal
|
20
|
-
end
|
21
|
-
end
|
22
|
-
buff
|
23
|
-
ensure
|
24
|
-
@buff = nil
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
attr_reader :buff, :literal_size
|
30
|
-
|
31
|
-
def bytes_read; buff.bytesize end
|
32
|
-
def empty?; buff.empty? end
|
33
|
-
def done?; line_done? && !get_literal_size end
|
34
|
-
def line_done?; buff.end_with?(CRLF) end
|
35
|
-
def get_literal_size; /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i end
|
36
|
-
|
37
|
-
def read_line
|
38
|
-
buff << (@sock.gets(CRLF, read_limit) or throw :eof)
|
39
|
-
max_response_remaining! unless line_done?
|
40
|
-
end
|
41
|
-
|
42
|
-
def read_literal
|
43
|
-
# check before allocating memory for literal
|
44
|
-
max_response_remaining!
|
45
|
-
literal = String.new(capacity: literal_size)
|
46
|
-
buff << (@sock.read(read_limit(literal_size), literal) or throw :eof)
|
47
|
-
ensure
|
48
|
-
@literal_size = nil
|
49
|
-
end
|
50
|
-
|
51
|
-
def read_limit(limit = nil)
|
52
|
-
[limit, max_response_remaining!].compact.min
|
53
|
-
end
|
54
|
-
|
55
|
-
def max_response_size; client.max_response_size end
|
56
|
-
def max_response_remaining; max_response_size &.- bytes_read end
|
57
|
-
def response_too_large?; max_response_size &.< min_response_size end
|
58
|
-
def min_response_size; bytes_read + min_response_remaining end
|
59
|
-
|
60
|
-
def min_response_remaining
|
61
|
-
empty? ? 3 : done? ? 0 : (literal_size || 0) + 2
|
62
|
-
end
|
63
|
-
|
64
|
-
def max_response_remaining!
|
65
|
-
return max_response_remaining unless response_too_large?
|
66
|
-
raise ResponseTooLargeError.new(
|
67
|
-
max_response_size: max_response_size,
|
68
|
-
bytes_read: bytes_read,
|
69
|
-
literal_size: literal_size,
|
70
|
-
)
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|