@@ -103,9 +103,9 @@ module Net
103103 #
104104 # == Capabilities
105105 #
106- # Net::IMAP does not _currently_ modify its behaviour according to the
107- # server's advertised #capabilities. Users of this class must check that the
108- # server is capable of extension commands or command arguments before
106+ # Most Net::IMAP methods do not _currently_ modify their behaviour according
107+ # to the server's advertised #capabilities. Users of this class must check
108+ # that the server is capable of extension commands or command arguments before
109109 # sending them. Special care should be taken to follow the #capabilities
110110 # requirements for #starttls, #login, and #authenticate.
111111 #
@@ -404,14 +404,14 @@ module Net
404404 #
405405 # Although IMAP4rev2[https://tools.ietf.org/html/rfc9051] is not supported
406406 # yet, Net::IMAP supports several extensions that have been folded into it:
407- # +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +UIDPLUS+, and +UNSELECT+. Commands
408- # for these extensions are listed with the
409- # {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
407+ # +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, + UIDPLUS+, and +UNSELECT+.
408+ # Commands for these extensions are listed with the {Core IMAP
409+ # commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
410410 #
411411 # >>>
412412 # <em>The following are folded into +IMAP4rev2+ but are currently
413413 # unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
414- # extensions, +ESEARCH+, +SEARCHRES+, +SASL-IR+, + LIST-EXTENDED+,
414+ # extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+,
415415 # +LIST-STATUS+, +LITERAL-+, +BINARY+ fetch, and +SPECIAL-USE+. The
416416 # following extensions are implicitly supported, but will be updated with
417417 # more direct support: RFC5530 response codes, <tt>STATUS=SIZE</tt>, and
@@ -457,6 +457,10 @@ module Net
457457 # - Updates #append with the +APPENDUID+ ResponseCode
458458 # - Updates #copy, #move with the +COPYUID+ ResponseCode
459459 #
460+ # ==== RFC4959: +SASL-IR+
461+ # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051].
462+ # - Updates #authenticate with the option to send an initial response.
463+ #
460464 # ==== RFC5161: +ENABLE+
461465 # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
462466 # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
@@ -983,19 +987,17 @@ def starttls(options = {}, verify = true)
983987 end
984988
985989 # :call-seq:
986- # authenticate(mechanism, ...) -> ok_resp
987- # authenticate(mech, *creds, **props) {|prop, auth| val } -> ok_resp
988- # authenticate(mechanism, authnid, credentials, authzid=nil) -> ok_resp
989- # authenticate(mechanism, **properties) -> ok_resp
990- # authenticate(mechanism) {|propname, authctx| prop_value } -> ok_resp
990+ # authenticate(mechanism, ...) -> ok_resp
991+ # authenticate(mech, *creds, sasl_ir: true, **attrs, &callback) -> ok_resp
991992 #
992993 # Sends an {AUTHENTICATE command [IMAP4rev1 Β§6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
993994 # to authenticate the client. If successful, the connection enters the
994995 # "_authenticated_" state.
995996 #
996997 # +mechanism+ is the name of the \SASL authentication mechanism to be used.
997- # All other arguments are forwarded to the authenticator for the requested
998- # mechanism. The listed call signatures are suggestions. <em>The
998+ # +sasl_ir+ allows or disallows sending an "initial response" (see the
999+ # +SASL-IR+ capability, below). All other arguments are forwarded to the
1000+ # registered SASL authenticator for the requested mechanism. <em>The
9991001 # documentation for each individual mechanism must be consulted for its
10001002 # specific parameters.</em>
10011003 #
@@ -1048,19 +1050,40 @@ def starttls(options = {}, verify = true)
10481050 # raise "No acceptable authentication mechanism is available"
10491051 # end
10501052 #
1051- # Server capabilities may change after #starttls, #login, and #authenticate.
1052- # Cached #capabilities will be cleared when this method completes.
1053- # If the TaggedResponse to #authenticate includes updated capabilities, they
1054- # will be cached.
1053+ # The SASL exchange provides a method for server challenges and client
1054+ # responses, but many mechanisms expect the client to "respond" first. When
1055+ # the server's capabilities include +SASL-IR+
1056+ # [RFC4959[https://tools.ietf.org/html/rfc4959]], this "initial response"
1057+ # may be sent as an argument to the +AUTHENTICATE+ command, saving a
1058+ # round-trip. The initial response will _only_ be sent when it is supported
1059+ # by both the mechanism and the server. Set +sasl_ir+ to +false+ to prevent
1060+ # sending an initial response, even when it is supported.
10551061 #
1056- def authenticate ( mechanism , ...)
1057- authenticator = self . class . authenticator ( mechanism , ...)
1058- send_command ( "AUTHENTICATE" , mechanism ) do |resp |
1062+ # Although servers _should_ advertise all supported auth mechanisms, it is
1063+ # possible to attempt to authenticate with a +mechanism+ that isn't listed.
1064+ # However the initial response will not be sent unless the appropriate
1065+ # <tt>"AUTH=#{mechanism}"</tt> capability is also present.
1066+ #
1067+ # Server capabilities may change after #starttls, #login, and #authenticate.
1068+ # Previously cached #capabilities will be cleared when this method
1069+ # completes. If the TaggedResponse to #authenticate includes updated
1070+ # capabilities, they will be cached.
1071+ def authenticate ( mechanism , *creds , sasl_ir : true , **props , &callback )
1072+ authenticator = self . class . authenticator ( mechanism ,
1073+ *creds ,
1074+ **props ,
1075+ &callback )
1076+ cmdargs = [ "AUTHENTICATE" , mechanism ]
1077+ if sasl_ir && capable? ( "SASL-IR" ) && auth_capable? ( mechanism ) &&
1078+ SASL . initial_response? ( authenticator )
1079+ cmdargs << [ authenticator . process ( nil ) ] . pack ( "m0" )
1080+ end
1081+ send_command ( *cmdargs ) do |resp |
10591082 if resp . instance_of? ( ContinuationRequest )
1060- data = authenticator . process ( resp . data . text . unpack ( "m" ) [ 0 ] )
1061- s = [ data ] . pack ( "m0" )
1062- send_string_data ( s )
1063- put_string ( CRLF )
1083+ challenge = resp . data . text . unpack1 ( "m" )
1084+ response = authenticator . process ( challenge )
1085+ response = [ response ] . pack ( "m0" )
1086+ put_string ( response + CRLF )
10641087 end
10651088 end
10661089 . tap { @capabilities = capabilities_from_resp_code _1 }
0 commit comments