@@ -559,43 +559,44 @@ def encode_plain(user, password):
559559 if not self .has_extn ("auth" ):
560560 raise SMTPException ("SMTP AUTH extension not supported by server." )
561561
562- # Authentication methods the server supports:
563- authlist = self .esmtp_features ["auth" ].split ()
562+ # Authentication methods the server claims to support
563+ advertised_authlist = self .esmtp_features ["auth" ].split ()
564564
565565 # List of authentication methods we support: from preferred to
566566 # less preferred methods. Except for the purpose of testing the weaker
567567 # ones, we prefer stronger methods like CRAM-MD5:
568568 preferred_auths = [AUTH_CRAM_MD5 , AUTH_PLAIN , AUTH_LOGIN ]
569569
570- # Determine the authentication method we'll use
571- authmethod = None
572- for method in preferred_auths :
573- if method in authlist :
574- authmethod = method
575- break
576-
577- if authmethod == AUTH_CRAM_MD5 :
578- (code , resp ) = self .docmd ("AUTH" , AUTH_CRAM_MD5 )
579- if code == 503 :
580- # 503 == 'Error: already authenticated'
581- return (code , resp )
582- (code , resp ) = self .docmd (encode_cram_md5 (resp , user , password ))
583- elif authmethod == AUTH_PLAIN :
584- (code , resp ) = self .docmd ("AUTH" ,
585- AUTH_PLAIN + " " + encode_plain (user , password ))
586- elif authmethod == AUTH_LOGIN :
587- (code , resp ) = self .docmd ("AUTH" ,
588- "%s %s" % (AUTH_LOGIN , encode_base64 (user .encode ('ascii' ), eol = '' )))
589- if code != 334 :
590- raise SMTPAuthenticationError (code , resp )
591- (code , resp ) = self .docmd (encode_base64 (password .encode ('ascii' ), eol = '' ))
592- elif authmethod is None :
570+ # We try the authentication methods the server advertises, but only the
571+ # ones *we* support. And in our preferred order.
572+ authlist = [auth for auth in preferred_auths if auth in advertised_authlist ]
573+ if not authlist :
593574 raise SMTPException ("No suitable authentication method found." )
594- if code not in (235 , 503 ):
575+
576+ # Some servers advertise authentication methods they don't really
577+ # support, so if authentication fails, we continue until we've tried
578+ # all methods.
579+ for authmethod in authlist :
580+ if authmethod == AUTH_CRAM_MD5 :
581+ (code , resp ) = self .docmd ("AUTH" , AUTH_CRAM_MD5 )
582+ if code == 334 :
583+ (code , resp ) = self .docmd (encode_cram_md5 (resp , user , password ))
584+ elif authmethod == AUTH_PLAIN :
585+ (code , resp ) = self .docmd ("AUTH" ,
586+ AUTH_PLAIN + " " + encode_plain (user , password ))
587+ elif authmethod == AUTH_LOGIN :
588+ (code , resp ) = self .docmd ("AUTH" ,
589+ "%s %s" % (AUTH_LOGIN , encode_base64 (user .encode ('ascii' ), eol = '' )))
590+ if code == 334 :
591+ (code , resp ) = self .docmd (encode_base64 (password .encode ('ascii' ), eol = '' ))
592+
595593 # 235 == 'Authentication successful'
596594 # 503 == 'Error: already authenticated'
597- raise SMTPAuthenticationError (code , resp )
598- return (code , resp )
595+ if code in (235 , 503 ):
596+ return (code , resp )
597+
598+ # We could not login sucessfully. Return result of last attempt.
599+ raise SMTPAuthenticationError (code , resp )
599600
600601 def starttls (self , keyfile = None , certfile = None ):
601602 """Puts the connection to the SMTP server into TLS mode.
0 commit comments