From 3dd28bb7eeb2bdbcf03146c0dee61e81fd85a0e5 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 7 Mar 2025 10:41:42 -0500 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=93=9A=20Document=20connection=20stat?= =?UTF-8?q?e=20more=20consistently?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because a `#connection_state` attribute will be added, I'd like to consistently name the connection states everywhere they are used. --- lib/net/imap.rb | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index c3cb89c0..c63b230e 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 @@ -260,8 +266,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 +324,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 +375,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 +401,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 +455,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+ From 573139e14d6993434aa327b449306dbc786fa4cd Mon Sep 17 00:00:00 2001 From: nick evans Date: Mon, 17 Feb 2025 20:40:46 -0500 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=93=9A=20Consistently=20use=20"elemen?= =?UTF-8?q?t"=20or=20"entry"=20vs=20"object"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/sequence_set.rb | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/net/imap/sequence_set.rb b/lib/net/imap/sequence_set.rb index ba6a606e..22d730e6 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 *. # @@ -262,8 +262,8 @@ 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+. # - #complement!: Replaces the contents of the set with its own #complement. @@ -272,7 +272,7 @@ class IMAP # # 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,8 +283,8 @@ 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 @@ -694,7 +694,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 +702,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 +712,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 +722,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 +742,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 +779,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 @@ -1390,14 +1390,14 @@ 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 From fc50c12cd16e940d7a2b6ec05b702c927fe4d696 Mon Sep 17 00:00:00 2001 From: nick evans Date: Mon, 17 Feb 2025 21:33:44 -0500 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=93=9A=20Consistently=20use=20"sets"?= =?UTF-8?q?=20or=20"other"=20vs=20"object"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/sequence_set.rb | 59 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/lib/net/imap/sequence_set.rb b/lib/net/imap/sequence_set.rb index 22d730e6..ebf0223d 100644 --- a/lib/net/imap/sequence_set.rb +++ b/lib/net/imap/sequence_set.rb @@ -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 @@ -265,7 +265,7 @@ class IMAP # - #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: @@ -289,7 +289,8 @@ class IMAP # - #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+. # @@ -824,33 +825,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 @@ -1401,18 +1400,18 @@ def input_to_tuple(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 From de8d242cf68a5c2dde6fc96e41d88fe1c47f4b73 Mon Sep 17 00:00:00 2001 From: nick evans Date: Mon, 17 Feb 2025 21:45:52 -0500 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=93=9A=20Improved=20SequenceSet[*inpu?= =?UTF-8?q?ts]=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/sequence_set.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/net/imap/sequence_set.rb b/lib/net/imap/sequence_set.rb index ebf0223d..cbdc2a74 100644 --- a/lib/net/imap/sequence_set.rb +++ b/lib/net/imap/sequence_set.rb @@ -327,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. # From b6cfd0167037e5c01ecbd528029c80282c27a8cf Mon Sep 17 00:00:00 2001 From: nick evans Date: Sat, 22 Mar 2025 14:31:32 -0400 Subject: [PATCH 5/8] =?UTF-8?q?=F0=9F=93=9A=20Add=20docs=20for=20receiver?= =?UTF-8?q?=20thread=20&=20server=20responses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most importantly, this documents the scenarios that need extra care to avoid memory leaks: * 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. --- lib/net/imap.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index c63b230e..9c8af878 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -205,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 From 1ba48cc8200330ad1e65db22d51270032eca7b9c Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 28 Mar 2025 21:41:13 -0400 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=93=9A=20Update=20rdoc=20for=20method?= =?UTF-8?q?s=20delegated=20to=20Config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 9c8af878..46d4548d 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -778,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 @@ -810,13 +812,20 @@ class << self # Net::IMAP.config. attr_reader :config + ## + # :attr_reader: open_timeout # 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 + # 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 From b7b70b7361e5c9ed0c740e3424c0fc7ddba80ba1 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 15 Apr 2025 21:40:34 -0400 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=93=9A=20Document=20that=20open=5Ftim?= =?UTF-8?q?eout=20is=20used=20for=20TLS=20too?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap.rb | 6 +++++- lib/net/imap/config.rb | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 46d4548d..b785116d 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -814,7 +814,7 @@ class << self ## # :attr_reader: open_timeout - # Seconds to wait until a connection is opened. + # Seconds to wait until a connection is opened. Also used by #starttls. # Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout]. ## @@ -1250,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 From a61b1fe3b3bf0c3757075033f6121044c5c31532 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 15 Apr 2025 22:43:06 -0400 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=93=9A=20Add=20a=20few=20missing=20wo?= =?UTF-8?q?rds=20to=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index b785116d..ad475614 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -803,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.