Skip to content

Commit a7a6f58

Browse files
committed
Merge branch 'master-3.2' into dist/3.2/bookworm
2 parents f868315 + b93f231 commit a7a6f58

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1124
-483
lines changed
File renamed without changes.

.bundle/gems/net-imap-0.3.8/lib/net/imap.rb renamed to .bundle/gems/net-imap-0.3.9/lib/net/imap.rb

Lines changed: 118 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,16 @@ module Net
4545
# To work on the messages within a mailbox, the client must
4646
# first select that mailbox, using either #select or #examine
4747
# (for read-only access). Once the client has successfully
48-
# selected a mailbox, they enter the "_selected_" state, and that
48+
# selected a mailbox, they enter the +selected+ state, and that
4949
# mailbox becomes the _current_ mailbox, on which mail-item
5050
# related commands implicitly operate.
5151
#
52+
# === Connection state
53+
#
54+
# Once an IMAP connection is established, the connection is in one of four
55+
# states: <tt>not authenticated</tt>, +authenticated+, +selected+, and
56+
# +logout+. Most commands are valid only in certain states.
57+
#
5258
# === Sequence numbers and UIDs
5359
#
5460
# Messages have two sorts of identifiers: message sequence
@@ -126,6 +132,41 @@ module Net
126132
#
127133
# This script invokes the FETCH command and the SEARCH command concurrently.
128134
#
135+
# When running multiple commands, care must be taken to avoid ambiguity. For
136+
# example, SEARCH responses are ambiguous about which command they are
137+
# responding to, so search commands should not run simultaneously, unless the
138+
# server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
139+
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
140+
# §5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
141+
# other examples of command sequences which should not be pipelined.
142+
#
143+
# == Unbounded memory use
144+
#
145+
# Net::IMAP reads server responses in a separate receiver thread per client.
146+
# Unhandled response data is saved to #responses, and response_handlers run
147+
# inside the receiver thread. See the list of methods for {handling server
148+
# responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
149+
#
150+
# Because the receiver thread continuously reads and saves new responses, some
151+
# scenarios must be careful to avoid unbounded memory use:
152+
#
153+
# * Commands such as #list or #fetch can have an enormous number of responses.
154+
# * Commands such as #fetch can result in an enormous size per response.
155+
# * Long-lived connections will gradually accumulate unsolicited server
156+
# responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
157+
# * A buggy or untrusted server could send inappropriate responses, which
158+
# could be very numerous, very large, and very rapid.
159+
#
160+
# Use paginated or limited versions of commands whenever possible.
161+
#
162+
# Use #max_response_size to impose a limit on incoming server responses
163+
# as they are being read. <em>This is especially important for untrusted
164+
# servers.</em>
165+
#
166+
# Use #add_response_handler to handle responses after each one is received.
167+
# Use the +response_handlers+ argument to ::new to assign response handlers
168+
# before the receiver thread is started.
169+
#
129170
# == Errors
130171
#
131172
# An \IMAP server can send three different types of responses to indicate
@@ -187,7 +228,7 @@ module Net
187228
# - Net::IMAP.new: A new client connects immediately and waits for a
188229
# successful server greeting before returning the new client object.
189230
# - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
190-
# - #logout: Tells the server to end the session. Enters the "_logout_" state.
231+
# - #logout: Tells the server to end the session. Enters the +logout+ state.
191232
# - #disconnect: Disconnects the connection (without sending #logout first).
192233
# - #disconnected?: True if the connection has been closed.
193234
#
@@ -230,40 +271,39 @@ module Net
230271
# <em>Capabilities may change after</em> #starttls, #authenticate, or #login
231272
# <em>and cached capabilities must be reloaded.</em>
232273
# - #noop: Allows the server to send unsolicited untagged #responses.
233-
# - #logout: Tells the server to end the session. Enters the "_logout_" state.
274+
# - #logout: Tells the server to end the session. Enters the +logout+ state.
234275
#
235276
# ==== \IMAP commands for the "Not Authenticated" state
236277
#
237-
# In addition to the universal commands, the following commands are valid in
238-
# the "<em>not authenticated</em>" state:
278+
# In addition to the commands for any state, the following commands are valid
279+
# in the +not_authenticated+ state:
239280
#
240281
# - #starttls: Upgrades a clear-text connection to use TLS.
241282
#
242283
# <em>Requires the +STARTTLS+ capability.</em>
243-
# - #authenticate: Identifies the client to the server using a {SASL
244-
# mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml].
245-
# Enters the "_authenticated_" state.
284+
# - #authenticate: Identifies the client to the server using the given {SASL
285+
# mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
286+
# and credentials. Enters the +authenticated+ state.
246287
#
247288
# <em>Requires the <tt>AUTH=#{mechanism}</tt> capability for the chosen
248289
# mechanism.</em>
249290
# - #login: Identifies the client to the server using a plain text password.
250-
# Using #authenticate is generally preferred. Enters the "_authenticated_"
251-
# state.
291+
# Using #authenticate is preferred. Enters the +authenticated+ state.
252292
#
253293
# <em>The +LOGINDISABLED+ capability</em> <b>must NOT</b> <em>be listed.</em>
254294
#
255295
# ==== \IMAP commands for the "Authenticated" state
256296
#
257-
# In addition to the universal commands, the following commands are valid in
258-
# the "_authenticated_" state:
297+
# In addition to the commands for any state, the following commands are valid
298+
# in the +authenticated+ state:
259299
#
260300
#--
261301
# - #enable: <em>Not implemented by Net::IMAP, yet.</em>
262302
#
263303
# <em>Requires the +ENABLE+ capability.</em>
264304
#++
265-
# - #select: Open a mailbox and enter the "_selected_" state.
266-
# - #examine: Open a mailbox read-only, and enter the "_selected_" state.
305+
# - #select: Open a mailbox and enter the +selected+ state.
306+
# - #examine: Open a mailbox read-only, and enter the +selected+ state.
267307
# - #create: Creates a new mailbox.
268308
# - #delete: Permanently remove a mailbox.
269309
# - #rename: Change the name of a mailbox.
@@ -289,12 +329,12 @@ module Net
289329
#
290330
# ==== \IMAP commands for the "Selected" state
291331
#
292-
# In addition to the universal commands and the "authenticated" commands, the
293-
# following commands are valid in the "_selected_" state:
332+
# In addition to the commands for any state and the +authenticated+
333+
# commands, the following commands are valid in the +selected+ state:
294334
#
295-
# - #close: Closes the mailbox and returns to the "_authenticated_" state,
335+
# - #close: Closes the mailbox and returns to the +authenticated+ state,
296336
# expunging deleted messages, unless the mailbox was opened as read-only.
297-
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
337+
# - #unselect: Closes the mailbox and returns to the +authenticated+ state,
298338
# without expunging any messages.
299339
#
300340
# <em>Requires the +UNSELECT+ capability.</em>
@@ -384,7 +424,7 @@ module Net
384424
# ==== RFC3691: +UNSELECT+
385425
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051], so it is also
386426
# listed with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
387-
# - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
427+
# - #unselect: Closes the mailbox and returns to the +authenticated+ state,
388428
# without expunging any messages.
389429
#
390430
# ==== RFC4314: +ACL+
@@ -699,7 +739,9 @@ module Net
699739
# * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
700740
#
701741
class IMAP < Protocol
702-
VERSION = "0.3.8"
742+
VERSION = "0.3.9"
743+
744+
autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__)
703745

704746
include MonitorMixin
705747
if defined?(OpenSSL::SSL)
@@ -734,6 +776,40 @@ class IMAP < Protocol
734776
# Seconds to wait until an IDLE response is received.
735777
attr_reader :idle_response_timeout
736778

779+
# The maximum allowed server response size. When +nil+, there is no limit
780+
# on response size.
781+
#
782+
# The default value is _unlimited_ (after +v0.5.8+, the default is 512 MiB).
783+
# A _much_ lower value should be used with untrusted servers (for example,
784+
# when connecting to a user-provided hostname). When using a lower limit,
785+
# message bodies should be fetched in chunks rather than all at once.
786+
#
787+
# <em>Please Note:</em> this only limits the size per response. It does
788+
# not prevent a flood of individual responses and it does not limit how
789+
# many unhandled responses may be stored on the responses hash. See
790+
# Net::IMAP@Unbounded+memory+use.
791+
#
792+
# Socket reads are limited to the maximum remaining bytes for the current
793+
# response: max_response_size minus the bytes that have already been read.
794+
# When the limit is reached, or reading a +literal+ _would_ go over the
795+
# limit, ResponseTooLargeError is raised and the connection is closed.
796+
# See also #socket_read_limit.
797+
#
798+
# Note that changes will not take effect immediately, because the receiver
799+
# thread may already be waiting for the next response using the previous
800+
# value. Net::IMAP#noop can force a response and enforce the new setting
801+
# immediately.
802+
#
803+
# ==== Versioned Defaults
804+
#
805+
# Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
806+
# attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to a config
807+
# attribute.</em>
808+
#
809+
# * original: +nil+ <em>(no limit)</em>
810+
# * +0.5+: 512 MiB
811+
attr_accessor :max_response_size
812+
737813
attr_accessor :client_thread # :nodoc:
738814

739815
# Returns the debug mode.
@@ -1960,6 +2036,11 @@ def idle_done
19602036
# end
19612037
# }
19622038
#
2039+
# Response handlers can also be added when the client is created before the
2040+
# receiver thread is started, by the +response_handlers+ argument to ::new.
2041+
# This ensures every server response is handled, including the #greeting.
2042+
#
2043+
# Related: #remove_response_handler, #response_handlers
19632044
def add_response_handler(handler = nil, &block)
19642045
raise ArgumentError, "two Procs are passed" if handler && block
19652046
@response_handlers.push(block || handler)
@@ -1995,6 +2076,13 @@ def remove_response_handler(handler)
19952076
# OpenSSL::SSL::SSLContext#set_params as parameters.
19962077
# open_timeout:: Seconds to wait until a connection is opened
19972078
# idle_response_timeout:: Seconds to wait until an IDLE response is received
2079+
# response_handlers:: A list of response handlers to be added before the
2080+
# receiver thread is started. This ensures every server
2081+
# response is handled, including the #greeting. Note
2082+
# that the greeting is handled in the current thread,
2083+
# but all other responses are handled in the receiver
2084+
# thread.
2085+
# max_response_size:: See #max_response_size.
19982086
#
19992087
# The most common errors are:
20002088
#
@@ -2025,8 +2113,10 @@ def initialize(host, port_or_options = {},
20252113
@tagno = 0
20262114
@open_timeout = options[:open_timeout] || 30
20272115
@idle_response_timeout = options[:idle_response_timeout] || 5
2116+
@max_response_size = options[:max_response_size]
20282117
@parser = ResponseParser.new
20292118
@sock = tcp_socket(@host, @port)
2119+
@reader = ResponseReader.new(self, @sock)
20302120
begin
20312121
if options[:ssl]
20322122
start_tls_session(options[:ssl])
@@ -2037,6 +2127,7 @@ def initialize(host, port_or_options = {},
20372127
@responses = Hash.new([].freeze)
20382128
@tagged_responses = {}
20392129
@response_handlers = []
2130+
options[:response_handlers]&.each do |h| add_response_handler(h) end
20402131
@tagged_response_arrival = new_cond
20412132
@continued_command_tag = nil
20422133
@continuation_request_arrival = new_cond
@@ -2053,6 +2144,7 @@ def initialize(host, port_or_options = {},
20532144
if @greeting.name == "BYE"
20542145
raise ByeResponseError, @greeting
20552146
end
2147+
@response_handlers.each do |handler| handler.call(@greeting) end
20562148

20572149
@client_thread = Thread.current
20582150
@receiver_thread = Thread.start {
@@ -2176,25 +2268,14 @@ def get_tagged_response(tag, cmd, timeout = nil)
21762268
end
21772269

21782270
def get_response
2179-
buff = String.new
2180-
while true
2181-
s = @sock.gets(CRLF)
2182-
break unless s
2183-
buff.concat(s)
2184-
if /\{(\d+)\}\r\n/n =~ s
2185-
s = @sock.read($1.to_i)
2186-
buff.concat(s)
2187-
else
2188-
break
2189-
end
2190-
end
2271+
buff = @reader.read_response_buffer
21912272
return nil if buff.length == 0
2192-
if @@debug
2193-
$stderr.print(buff.gsub(/^/n, "S: "))
2194-
end
2195-
return @parser.parse(buff)
2273+
$stderr.print(buff.gsub(/^/n, "S: ")) if @@debug
2274+
@parser.parse(buff)
21962275
end
21972276

2277+
#############################
2278+
21982279
def record_response(name, data)
21992280
unless @responses.has_key?(name)
22002281
@responses[name] = []
@@ -2372,6 +2453,7 @@ def start_tls_session(params = {})
23722453
context.verify_callback = VerifyCallbackProc
23732454
end
23742455
@sock = SSLSocket.new(@sock, context)
2456+
@reader = ResponseReader.new(self, @sock)
23752457
@sock.sync_close = true
23762458
@sock.hostname = @host if @sock.respond_to? :hostname=
23772459
ssl_socket_connect(@sock, @open_timeout)

0 commit comments

Comments
 (0)