From 16dafde9841ce85b0e7a17117d31832429fb91df Mon Sep 17 00:00:00 2001 From: nick evans Date: Sat, 22 Jul 2023 14:38:17 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9A=20More=20rdoc=20updates,=20all=20r?= =?UTF-8?q?elated=20to=20capabilities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap.rb | 186 ++++++++++++++++++++++++------------------------ 1 file changed, 94 insertions(+), 92 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index bf807643..72fb8b0d 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -101,7 +101,7 @@ module Net # end # imap.expunge # - # == Server capabilities and protocol extensions + # == Capabilities # # Net::IMAP does not _currently_ modify its behaviour according to the # server's advertised #capabilities. Users of this class must check that the @@ -144,12 +144,12 @@ module Net # # === Caching +CAPABILITY+ responses # - # Net::IMAP stores and discards capability - # data according to the requirements and recommendations in IMAP4rev2 - # {§6.1.1}[https://www.rfc-editor.org/rfc/rfc9051#section-6.1.1], + # Net::IMAP automatically stores and discards capability data according to the + # the requirements and recommendations in + # {IMAP4rev2 §6.1.1}[https://www.rfc-editor.org/rfc/rfc9051#section-6.1.1], # {§6.2}[https://www.rfc-editor.org/rfc/rfc9051#section-6.2], and # {§7.1}[https://www.rfc-editor.org/rfc/rfc9051#section-7.1]. - # Use #capable?, #auth_capable?, or #capabilities to this caching and avoid + # Use #capable?, #auth_capable?, or #capabilities to use this cache and avoid # sending the #capability command unnecessarily. # # The server may advertise its initial capabilities using the +CAPABILITY+ @@ -166,6 +166,10 @@ module Net # # === Using IMAP4rev1 extensions # + # See the {IANA IMAP4 capabilities + # registry}[http://www.iana.org/assignments/imap4-capabilities] for a list of + # all standard capabilities, and their reference RFCs. + # # IMAP4rev1 servers must not activate behavior that is incompatible with the # base specification until an explicit client action invokes a capability, # e.g. sending a command or command argument specific to that capability. @@ -179,7 +183,7 @@ module Net # BadResponseError, or cause other unexpected behavior. # # Some capabilities must be explicitly activated using the #enable command. - # See #enable for more details. + # See #enable for details. # # == Thread Safety # @@ -751,45 +755,35 @@ def disconnected? return @sock.closed? end - # Returns the server capabilities. When available, cached capabilities are - # used without sending a new #capability command to the server. - # - # To ensure case-insensitive capability comparison, use #capable? instead. - # - # Related: #capable?, #auth_capable?, #capability - def capabilities - @capabilities || capability - end - # Returns whether the server supports a given +capability+. When available, # cached #capabilities are used without sending a new #capability command to # the server. # - # See the {IANA IMAP4 capabilities - # registry}[http://www.iana.org/assignments/imap4-capabilities] for a list - # of all standard capabilities, and their reference RFCs. - # - # >>> - # *NOTE:* Net::IMAP does not _currently_ modify its behaviour - # according to the server's advertised capabilities. Users of this class - # must check that the server is #capable? of extension commands or command - # arguments before sending them. + # *NOTE:* Net::IMAP does not currently modify its behaviour + # according to the server's advertised capabilities. # - # Capability requirements—other than +IMAP4rev1+—are listed in the - # documentation for each command method. + # See Net::IMAP@Capabilities for more about \IMAP capabilities. # # Related: #auth_capable?, #capabilities, #capability, #enable # - # ===== Caching +CAPABILITY+ responses - # - # Servers may send their capability list unsolicited, using the +CAPABILITY+ - # response code or an untagged +CAPABILITY+ response. Cached capabilities - # are discarded after #starttls, #login, or #authenticate. Caching and - # cache invalidation are handled internally by Net::IMAP. - # def capable?(capability) capabilities.include? capability.to_s.upcase end alias capability? capable? + # Returns the server capabilities. When available, cached capabilities are + # used without sending a new #capability command to the server. + # + # To ensure a case-insensitive comparison, #capable? can be used instead. + # + # *NOTE:* Net::IMAP does not currently modify its behaviour + # according to the server's advertised capabilities. + # + # See Net::IMAP@Capabilities for more about \IMAP capabilities. + # + # Related: #capable?, #auth_capable?, #capability + def capabilities + @capabilities || capability + end + # Returns the #authenticate mechanisms that the server claims to support. # These are derived from the #capabilities with an AUTH= prefix. # @@ -797,20 +791,25 @@ def capable?(capability) capabilities.include? capability.to_s.upcase end # servers will drop all AUTH= mechanisms from #capabilities after # the connection has authenticated. # - # Related: #auth_capable?, #capabilities - # - # ===== Example - # # imap = Net::IMAP.new(hostname, ssl: false) # imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"] # imap.auth_mechanisms # => [] + # # imap.starttls # imap.capabilities # => ["IMAP4REV1", "AUTH=PLAIN", "AUTH=XOAUTH2", - # "AUTH=OAUTHBEARER", "AUTH=SCRAM-SHA-256"] - # imap.auth_mechanisms # => ["PLAIN", "XOAUTH2", "SCRAM-SHA-256"] - # imap.authenticate("OAUTHBEARER", username, oauth2_access_token) + # # "AUTH=OAUTHBEARER"] + # imap.auth_mechanisms # => ["PLAIN", "XOAUTH2", "OAUTHBEARER"] + # + # imap.authenticate("XOAUTH2", username, oauth2_access_token) # imap.auth_mechanisms # => [] # + # *NOTE:* Net::IMAP does not currently modify its behaviour + # according to the server's advertised capabilities. + # + # See Net::IMAP@Capabilities for more about \IMAP capabilities. + # + # Related: #auth_capable?, #capabilities + # def auth_mechanisms capabilities .grep(/\AAUTH=/i) @@ -823,12 +822,15 @@ def auth_mechanisms # available, cached capabilities are used without sending a new #capability # command to the server. # - # Per {[IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2], - # # imap.capable? "AUTH=PLAIN" # => true # imap.auth_capable? "PLAIN" # => true # imap.auth_capable? "blurdybloop" # => false # + # *NOTE:* Net::IMAP does not currently modify its behaviour + # according to the server's advertised capabilities. + # + # See Net::IMAP@Capabilities for more about \IMAP capabilities. + # # Related: #authenticate, #capable?, #capabilities def auth_capable?(mechanism) capable? "AUTH=#{mechanism}" @@ -837,16 +839,22 @@ def auth_capable?(mechanism) # Returns whether capabilities have been cached. When true, #capable? and # #capabilities don't require sending a #capability command to the server. # - - # Returns whether capabilities have been cached. When true, #capable? and - # #capabilities don't require sending a #capability command to the server. + # See Net::IMAP@Capabilities for more about \IMAP capabilities. + # + # Related: #capable?, #capability, #clear_cached_capabilities def capabilities_cached? !!@capabilities end - # Clears capabilities that are currently cached by the Net::IMAP client. - # This forces a #capability command to be sent the next time that #capable? - # or #capabilities? are called. + # Clears capabilities that have been remembered by the Net::IMAP client. + # This forces a #capability command to be sent the next time a #capabilities + # query method is called. + # + # Net::IMAP automatically discards its cached capabilities when they can + # change. Explicitly calling this _should_ be unnecessary for well-behaved + # servers. + # + # Related: #capable?, #capability, #capabilities_cached? def clear_cached_capabilities synchronize do clear_responses("CAPABILITY") @@ -856,23 +864,22 @@ def clear_cached_capabilities # Sends a {CAPABILITY command [IMAP4rev1 §6.1.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.1.1] # and returns an array of capabilities that are supported by the server. - # The result will be stored for use by #capable? and #capabilities. + # The result is stored for use by #capable? and #capabilities. # - # In general, #capable? or #capabilities should be used instead. They cache - # the capability result to avoid sending unnecessary commands. They also - # ensure cache invalidation is handled correctly. + # *NOTE:* Net::IMAP does not currently modify its behaviour + # according to the server's advertised capabilities. # - # >>> - # *NOTE:* Net::IMAP does not _currently_ modify its behaviour - # according to the server's advertised capabilities. Users of this class - # must check that the server is #capable? of extension commands or command - # arguments before sending them. + # Net::IMAP automatically stores and discards capability data according to + # the requirements and recommendations in + # {IMAP4rev2 §6.1.1}[https://www.rfc-editor.org/rfc/rfc9051#section-6.1.1], + # {§6.2}[https://www.rfc-editor.org/rfc/rfc9051#section-6.2], and + # {§7.1}[https://www.rfc-editor.org/rfc/rfc9051#section-7.1]. + # Use #capable?, #auth_capable?, or #capabilities to this cache and avoid + # sending the #capability command unnecessarily. # - # Capability requirements—other than +IMAP4rev1+—are listed in the - # documentation for each command method. + # See Net::IMAP@Capabilities for more about \IMAP capabilities. # # Related: #capable?, #auth_capable?, #capability, #enable - # def capability synchronize do send_command("CAPABILITY") @@ -994,7 +1001,7 @@ def starttls(options = {}, verify = true) # # An exception Net::IMAP::NoResponseError is raised if authentication fails. # - # Related: #login, #starttls + # Related: #login, #starttls, #auth_capable?, #auth_mechanisms # # ==== Supported SASL Mechanisms # @@ -1024,40 +1031,28 @@ def starttls(options = {}, verify = true) # for information on these and other SASL mechanisms. # # ===== Capabilities - # Clients should not call #authenticate with mechanisms that are included in the server #capabilities as "AUTH=#{mechanism}". + # + # The server should list "AUTH=#{mechanism}" capabilities for + # supported mechanisms. Check #auth_capable? or #auth_mechanisms before + # using a particular mechanism. + # + # if imap.auth_capable? "XOAUTH2" + # imap.authenticate "XOAUTH2", username, oauth2_access_token + # elsif imap.auth_capable? "PLAIN" + # imap.authenticate "PLAIN", username, password + # elsif imap.auth_capable? "DIGEST-MD5" + # imap.authenticate "DIGEST-MD5", username, password + # elsif !imap.capability? "LOGINDISABLED" + # imap.login username, password + # else + # raise "No acceptable authentication mechanism is available" + # end # # Server capabilities may change after #starttls, #login, and #authenticate. # Cached #capabilities will be cleared when this method completes. # If the TaggedResponse to #authenticate includes updated capabilities, they # will be cached. # - # ===== Example - # Use auth_capable? to discover which mechanisms are suuported by the - # server. For authenticators that ignore unhandled keyword arguments, the - # same config can be used for multiple mechanisms: - # - # password = nil # saved locally, so we don't ask more than once - # accesstok = nil # saved locally... - # creds = { - # authcid: username, - # password: proc { password ||= ui.prompt_for_password }, - # oauth2_token: proc { accesstok ||= kms.fresh_access_token }, - # } - # mechanism = %w[ - # OAUTHBEARER XOAUTH2 - # SCRAM-SHA-256 SCRAM-SHA-1 - # PLAIN - # ].find {|m| - # imap.auth_capable?(m) - # } - # if mechanism - # imap.authenticate mechanism, **creds - # elsif capable? "LOGINDISABLED" - # raise "the server has disabled login" - # else - # imap.login username, password - # end - # def authenticate(mechanism, ...) authenticator = self.class.authenticator(mechanism, ...) send_command("AUTHENTICATE", mechanism) do |resp| @@ -1085,10 +1080,17 @@ def authenticate(mechanism, ...) # # Related: #authenticate, #starttls # - # ==== Capabilities - # An IMAP client MUST NOT call #login unless the server advertises the + # ===== Capabilities + # + # An IMAP client MUST NOT call #login when the server advertises the # +LOGINDISABLED+ capability. # + # if imap.capability? "LOGINDISABLED" + # raise "Remote server has disabled the login command" + # else + # imap.login username, password + # end + # # Server capabilities may change after #starttls, #login, and #authenticate. # Cached capabilities _must_ be invalidated after this method completes. # The TaggedResponse to #login may include updated capabilities in its