diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index c3cb89c0..ad475614 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -43,10 +43,16 @@ module Net
# To work on the messages within a mailbox, the client must
# first select that mailbox, using either #select or #examine
# (for read-only access). Once the client has successfully
- # selected a mailbox, they enter the "_selected_" state, and that
+ # selected a mailbox, they enter the +selected+ state, and that
# mailbox becomes the _current_ mailbox, on which mail-item
# related commands implicitly operate.
#
+ # === Connection state
+ #
+ # Once an IMAP connection is established, the connection is in one of four
+ # states: not authenticated, +authenticated+, +selected+, and
+ # +logout+. Most commands are valid only in certain states.
+ #
# === Sequence numbers and UIDs
#
# Messages have two sorts of identifiers: message sequence
@@ -199,6 +205,37 @@ module Net
#
# This script invokes the FETCH command and the SEARCH command concurrently.
#
+ # When running multiple commands, care must be taken to avoid ambiguity. For
+ # example, SEARCH responses are ambiguous about which command they are
+ # responding to, so search commands should not run simultaneously, unless the
+ # server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
+ # ยง5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
+ # other examples of command sequences which should not be pipelined.
+ #
+ # == Unbounded memory use
+ #
+ # Net::IMAP reads server responses in a separate receiver thread per client.
+ # Unhandled response data is saved to #responses, and response_handlers run
+ # inside the receiver thread. See the list of methods for {handling server
+ # responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
+ #
+ # Because the receiver thread continuously reads and saves new responses, some
+ # scenarios must be careful to avoid unbounded memory use:
+ #
+ # * Commands such as #list or #fetch can have an enormous number of responses.
+ # * Commands such as #fetch can result in an enormous size per response.
+ # * Long-lived connections will gradually accumulate unsolicited server
+ # responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
+ # * A buggy or untrusted server could send inappropriate responses, which
+ # could be very numerous, very large, and very rapid.
+ #
+ # Use paginated or limited versions of commands whenever possible.
+ #
+ # Use #add_response_handler to handle responses after each one is received.
+ # Use #extract_responses, #clear_responses, or #responses (with a block) to
+ # prune responses.
+ #
# == Errors
#
# An \IMAP server can send three different types of responses to indicate
@@ -260,8 +297,9 @@ module Net
#
# - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
# waits for a successful server greeting before the method returns.
+ # - #connection_state: Returns the connection state.
# - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
- # - #logout: Tells the server to end the session. Enters the "_logout_" state.
+ # - #logout: Tells the server to end the session. Enters the +logout+ state.
# - #disconnect: Disconnects the connection (without sending #logout first).
# - #disconnected?: True if the connection has been closed.
#
@@ -317,37 +355,36 @@ module Net
# In general, #capable? should be used rather than explicitly sending a
# +CAPABILITY+ command to the server.
# - #noop: Allows the server to send unsolicited untagged #responses.
- # - #logout: Tells the server to end the session. Enters the "_logout_" state.
+ # - #logout: Tells the server to end the session. Enters the +logout+ state.
#
# ==== Not Authenticated state
#
# In addition to the commands for any state, the following commands are valid
- # in the "not authenticated" state:
+ # in the +not_authenticated+ state:
#
# - #starttls: Upgrades a clear-text connection to use TLS.
#
# Requires the +STARTTLS+ capability.
# - #authenticate: Identifies the client to the server using the given
# {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
- # and credentials. Enters the "_authenticated_" state.
+ # and credentials. Enters the +authenticated+ state.
#
# The server should list "AUTH=#{mechanism}" capabilities for
# supported mechanisms.
# - #login: Identifies the client to the server using a plain text password.
- # Using #authenticate is generally preferred. Enters the "_authenticated_"
- # state.
+ # Using #authenticate is preferred. Enters the +authenticated+ state.
#
# The +LOGINDISABLED+ capability must NOT be listed.
#
# ==== Authenticated state
#
# In addition to the commands for any state, the following commands are valid
- # in the "_authenticated_" state:
+ # in the +authenticated+ state:
#
# - #enable: Enables backwards incompatible server extensions.
# Requires the +ENABLE+ or +IMAP4rev2+ capability.
- # - #select: Open a mailbox and enter the "_selected_" state.
- # - #examine: Open a mailbox read-only, and enter the "_selected_" state.
+ # - #select: Open a mailbox and enter the +selected+ state.
+ # - #examine: Open a mailbox read-only, and enter the +selected+ state.
# - #create: Creates a new mailbox.
# - #delete: Permanently remove a mailbox.
# - #rename: Change the name of a mailbox.
@@ -369,12 +406,12 @@ module Net
#
# ==== Selected state
#
- # In addition to the commands for any state and the "_authenticated_"
- # commands, the following commands are valid in the "_selected_" state:
+ # In addition to the commands for any state and the +authenticated+
+ # commands, the following commands are valid in the +selected+ state:
#
- # - #close: Closes the mailbox and returns to the "_authenticated_" state,
+ # - #close: Closes the mailbox and returns to the +authenticated+ state,
# expunging deleted messages, unless the mailbox was opened as read-only.
- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
+ # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
# without expunging any messages.
# Requires the +UNSELECT+ or +IMAP4rev2+ capability.
# - #expunge: Permanently removes messages which have the Deleted flag set.
@@ -395,7 +432,7 @@ module Net
#
# ==== Logout state
#
- # No \IMAP commands are valid in the "_logout_" state. If the socket is still
+ # No \IMAP commands are valid in the +logout+ state. If the socket is still
# open, Net::IMAP will close it after receiving server confirmation.
# Exceptions will be raised by \IMAP commands that have already started and
# are waiting for a response, as well as any that are called after logout.
@@ -449,7 +486,7 @@ module Net
# ==== RFC3691: +UNSELECT+
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state,
+ # - #unselect: Closes the mailbox and returns to the +authenticated+ state,
# without expunging any messages.
#
# ==== RFC4314: +ACL+
@@ -741,9 +778,11 @@ class IMAP < Protocol
def self.config; Config.global end
# Returns the global debug mode.
+ # Delegates to {Net::IMAP.config.debug}[rdoc-ref:Config#debug].
def self.debug; config.debug end
# Sets the global debug mode.
+ # Delegates to {Net::IMAP.config.debug=}[rdoc-ref:Config#debug=].
def self.debug=(val)
config.debug = val
end
@@ -764,7 +803,7 @@ class << self
alias default_ssl_port default_tls_port
end
- # Returns the initial greeting the server, an UntaggedResponse.
+ # Returns the initial greeting sent by the server, an UntaggedResponse.
attr_reader :greeting
# The client configuration. See Net::IMAP::Config.
@@ -773,13 +812,20 @@ class << self
# Net::IMAP.config.
attr_reader :config
- # Seconds to wait until a connection is opened.
- # If the IMAP object cannot open a connection within this time,
- # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
- def open_timeout; config.open_timeout end
+ ##
+ # :attr_reader: open_timeout
+ # Seconds to wait until a connection is opened. Also used by #starttls.
+ # Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout].
+ ##
+ # :attr_reader: idle_response_timeout
# Seconds to wait until an IDLE response is received.
- def idle_response_timeout; config.idle_response_timeout end
+ # Delegates to {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout].
+
+ # :stopdoc:
+ def open_timeout; config.open_timeout end
+ def idle_response_timeout; config.idle_response_timeout end
+ # :startdoc:
# The hostname this client connected to
attr_reader :host
@@ -1204,6 +1250,10 @@ def logout!
# both successful. Any error indicates that the connection has not been
# secured.
#
+ # After the server agrees to start a TLS connection, this method waits up to
+ # {config.open_timeout}[rdoc-ref:Config#open_timeout] before raising
+ # +Net::OpenTimeout+.
+ #
# *Note:*
# >>>
# Any #response_handlers added before STARTTLS should be aware that the
diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb
index aea07b89..96fa08b4 100644
--- a/lib/net/imap/config.rb
+++ b/lib/net/imap/config.rb
@@ -191,10 +191,13 @@ def self.[](config)
# Seconds to wait until a connection is opened.
#
+ # Applied separately for establishing TCP connection and starting a TLS
+ # connection.
+ #
# If the IMAP object cannot open a connection within this time,
# it raises a Net::OpenTimeout exception.
#
- # See Net::IMAP.new.
+ # See Net::IMAP.new and Net::IMAP#starttls.
#
# The default value is +30+ seconds.
attr_accessor :open_timeout, type: Integer
diff --git a/lib/net/imap/sequence_set.rb b/lib/net/imap/sequence_set.rb
index ba6a606e..cbdc2a74 100644
--- a/lib/net/imap/sequence_set.rb
+++ b/lib/net/imap/sequence_set.rb
@@ -178,7 +178,7 @@ class IMAP
#
# Set membership:
# - #include? (aliased as #member?):
- # Returns whether a given object (nz-number, range, or *) is
+ # Returns whether a given element (nz-number, range, or *) is
# contained by the set.
# - #include_star?: Returns whether the set contains *.
#
@@ -243,13 +243,13 @@ class IMAP
# These methods do not modify +self+.
#
# - #| (aliased as #union and #+): Returns a new set combining all members
- # from +self+ with all members from the other object.
+ # from +self+ with all members from the other set.
# - #& (aliased as #intersection): Returns a new set containing all members
- # common to +self+ and the other object.
+ # common to +self+ and the other set.
# - #- (aliased as #difference): Returns a copy of +self+ with all members
- # in the other object removed.
+ # in the other set removed.
# - #^ (aliased as #xor): Returns a new set containing all members from
- # +self+ and the other object except those common to both.
+ # +self+ and the other set except those common to both.
# - #~ (aliased as #complement): Returns a new set containing all members
# that are not in +self+
# - #limit: Returns a copy of +self+ which has replaced * with a
@@ -262,17 +262,17 @@ class IMAP
#
# These methods always update #string to be fully sorted and coalesced.
#
- # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
- # - #add?: If the given object is not an element in the set, adds it and
+ # - #add (aliased as #<<): Adds a given element to the set; returns +self+.
+ # - #add?: If the given element is not fully included the set, adds it and
# returns +self+; otherwise, returns +nil+.
- # - #merge: Merges multiple elements into the set; returns +self+.
+ # - #merge: Adds all members of the given sets into this set; returns +self+.
# - #complement!: Replaces the contents of the set with its own #complement.
#
# Order preserving:
#
# These methods _may_ cause #string to not be sorted or coalesced.
#
- # - #append: Adds a given object to the set, appending it to the existing
+ # - #append: Adds the given entry to the set, appending it to the existing
# string, and returns +self+.
# - #string=: Assigns a new #string value and replaces #elements to match.
# - #replace: Replaces the contents of the set with the contents
@@ -283,13 +283,14 @@ class IMAP
# sorted and coalesced.
#
# - #clear: Removes all elements in the set; returns +self+.
- # - #delete: Removes a given object from the set; returns +self+.
- # - #delete?: If the given object is an element in the set, removes it and
+ # - #delete: Removes a given element from the set; returns +self+.
+ # - #delete?: If the given element is included in the set, removes it and
# returns it; otherwise, returns +nil+.
# - #delete_at: Removes the number at a given offset.
# - #slice!: Removes the number or consecutive numbers at a given offset or
# range of offsets.
- # - #subtract: Removes each given object from the set; returns +self+.
+ # - #subtract: Removes all members of the given sets from this set; returns
+ # +self+.
# - #limit!: Replaces * with a given maximum value and removes all
# members over that maximum; returns +self+.
#
@@ -326,9 +327,12 @@ class SequenceSet
class << self
# :call-seq:
- # SequenceSet[*values] -> valid frozen sequence set
+ # SequenceSet[*inputs] -> valid frozen sequence set
#
- # Returns a frozen SequenceSet, constructed from +values+.
+ # Returns a frozen SequenceSet, constructed from +inputs+.
+ #
+ # When only a single valid frozen SequenceSet is given, that same set is
+ # returned.
#
# An empty SequenceSet is invalid and will raise a DataFormatError.
#
@@ -694,7 +698,7 @@ def ~; remain_frozen dup.complement! end
alias complement :~
# :call-seq:
- # add(object) -> self
+ # add(element) -> self
# self << other -> self
#
# Adds a range or number to the set and returns +self+.
@@ -702,8 +706,8 @@ def ~; remain_frozen dup.complement! end
# #string will be regenerated. Use #merge to add many elements at once.
#
# Related: #add?, #merge, #union
- def add(object)
- tuple_add input_to_tuple object
+ def add(element)
+ tuple_add input_to_tuple element
normalize!
end
alias << add
@@ -712,9 +716,9 @@ def add(object)
#
# Unlike #add, #merge, or #union, the new value is appended to #string.
# This may result in a #string which has duplicates or is out-of-order.
- def append(object)
+ def append(entry)
modifying!
- tuple = input_to_tuple object
+ tuple = input_to_tuple entry
entry = tuple_to_str tuple
string unless empty? # write @string before tuple_add
tuple_add tuple
@@ -722,19 +726,19 @@ def append(object)
self
end
- # :call-seq: add?(object) -> self or nil
+ # :call-seq: add?(element) -> self or nil
#
# Adds a range or number to the set and returns +self+. Returns +nil+
- # when the object is already included in the set.
+ # when the element is already included in the set.
#
# #string will be regenerated. Use #merge to add many elements at once.
#
# Related: #add, #merge, #union, #include?
- def add?(object)
- add object unless include? object
+ def add?(element)
+ add element unless include? element
end
- # :call-seq: delete(object) -> self
+ # :call-seq: delete(element) -> self
#
# Deletes the given range or number from the set and returns +self+.
#
@@ -742,8 +746,8 @@ def add?(object)
# many elements at once.
#
# Related: #delete?, #delete_at, #subtract, #difference
- def delete(object)
- tuple_subtract input_to_tuple object
+ def delete(element)
+ tuple_subtract input_to_tuple element
normalize!
end
@@ -779,8 +783,8 @@ def delete(object)
# #string will be regenerated after deletion.
#
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
- def delete?(object)
- tuple = input_to_tuple object
+ def delete?(element)
+ tuple = input_to_tuple element
if tuple.first == tuple.last
return unless include_tuple? tuple
tuple_subtract tuple
@@ -824,33 +828,31 @@ def slice!(index, length = nil)
deleted
end
- # Merges all of the elements that appear in any of the +inputs+ into the
+ # Merges all of the elements that appear in any of the +sets+ into the
# set, and returns +self+.
#
- # The +inputs+ may be any objects that would be accepted by ::new:
- # non-zero 32 bit unsigned integers, ranges, sequence-set
- # formatted strings, other sequence sets, or enumerables containing any of
- # these.
+ # The +sets+ may be any objects that would be accepted by ::new: non-zero
+ # 32 bit unsigned integers, ranges, sequence-set formatted
+ # strings, other sequence sets, or enumerables containing any of these.
#
- # #string will be regenerated after all inputs have been merged.
+ # #string will be regenerated after all sets have been merged.
#
# Related: #add, #add?, #union
- def merge(*inputs)
- tuples_add input_to_tuples inputs
+ def merge(*sets)
+ tuples_add input_to_tuples sets
normalize!
end
- # Removes all of the elements that appear in any of the given +objects+
- # from the set, and returns +self+.
+ # Removes all of the elements that appear in any of the given +sets+ from
+ # the set, and returns +self+.
#
- # The +objects+ may be any objects that would be accepted by ::new:
- # non-zero 32 bit unsigned integers, ranges, sequence-set
- # formatted strings, other sequence sets, or enumerables containing any of
- # these.
+ # The +sets+ may be any objects that would be accepted by ::new: non-zero
+ # 32 bit unsigned integers, ranges, sequence-set formatted
+ # strings, other sequence sets, or enumerables containing any of these.
#
# Related: #difference
- def subtract(*objects)
- tuples_subtract input_to_tuples objects
+ def subtract(*sets)
+ tuples_subtract input_to_tuples sets
normalize!
end
@@ -1390,29 +1392,29 @@ def initialize_dup(other)
super
end
- def input_to_tuple(obj)
- obj = input_try_convert obj
- case obj
- when *STARS, Integer then [int = to_tuple_int(obj), int]
- when Range then range_to_tuple(obj)
- when String then str_to_tuple(obj)
+ def input_to_tuple(entry)
+ entry = input_try_convert entry
+ case entry
+ when *STARS, Integer then [int = to_tuple_int(entry), int]
+ when Range then range_to_tuple(entry)
+ when String then str_to_tuple(entry)
else
- raise DataFormatError, "expected number or range, got %p" % [obj]
+ raise DataFormatError, "expected number or range, got %p" % [entry]
end
end
- def input_to_tuples(obj)
- obj = input_try_convert obj
- case obj
- when *STARS, Integer, Range then [input_to_tuple(obj)]
- when String then str_to_tuples obj
- when SequenceSet then obj.tuples
- when ENUMABLE then obj.flat_map { input_to_tuples _1 }
+ def input_to_tuples(set)
+ set = input_try_convert set
+ case set
+ when *STARS, Integer, Range then [input_to_tuple(set)]
+ when String then str_to_tuples set
+ when SequenceSet then set.tuples
+ when ENUMABLE then set.flat_map { input_to_tuples _1 }
when nil then []
else
raise DataFormatError,
"expected nz-number, range, string, or enumerable; " \
- "got %p" % [obj]
+ "got %p" % [set]
end
end