@@ -812,8 +812,9 @@ class POPOAuth2ClientConnection(OAuth2ClientConnection):
812812
813813 class AUTH (enum .Enum ):
814814 PENDING = 1
815- AWAITING_PASS = 2
816- AWAITING_AUTH = 3
815+ PLAIN_AWAITING_CREDENTIALS = 2
816+ AWAITING_PASS = 3
817+ AWAITING_AUTH = 4
817818 CREDENTIALS_SENT = 5
818819
819820 def __init__ (self , connection , socket_map , connection_info , server_connection , proxy_parent , custom_configuration ):
@@ -825,21 +826,39 @@ def process_data(self, byte_data, censor_server_log=False):
825826 str_data = byte_data .decode ('utf-8' , 'replace' ).rstrip ('\r \n ' )
826827 str_data_lower = str_data .lower ()
827828
828- if self .authentication_state is self .AUTH .PENDING and str_data_lower .startswith ('user' ):
829+ if self .authentication_state is self .AUTH .PENDING and str_data_lower .startswith ('auth plain' ):
830+ if len (str_data ) > 11 : # 11 = len('AUTH PLAIN ') - this method can have the login details either inline...
831+ (self .server_connection .username , self .server_connection .password ) = OAuth2Helper .decode_credentials (
832+ str_data [11 :])
833+ self .send_authentication_request ()
834+ else : # ...or requested separately
835+ self .authentication_state = self .AUTH .PLAIN_AWAITING_CREDENTIALS
836+ self .censor_next_log = True
837+ self .send (b'+OK\r \n ' ) # request details (note: space after response code is mandatory)
838+
839+ elif self .authentication_state is self .AUTH .PLAIN_AWAITING_CREDENTIALS :
840+ (self .server_connection .username , self .server_connection .password ) = OAuth2Helper .decode_credentials (
841+ str_data )
842+ self .send_authentication_request ()
843+
844+ elif self .authentication_state is self .AUTH .PENDING and str_data_lower .startswith ('user' ):
829845 self .server_connection .username = str_data [5 :] # 5 = len('USER ')
830846 self .authentication_state = self .AUTH .AWAITING_PASS
831847 self .censor_next_log = True
832848 self .send (b'+OK\r \n ' ) # request password
833849
834850 elif self .authentication_state is self .AUTH .AWAITING_PASS and str_data_lower .startswith ('pass' ):
835851 self .server_connection .password = str_data [5 :] # 5 = len('PASS ')
836- self .authentication_state = self .AUTH .AWAITING_AUTH
837- super ().process_data (b'AUTH XOAUTH2\r \n ' )
852+ self .send_authentication_request ()
838853
839854 # some other command that we don't handle - pass directly to server
840855 else :
841856 super ().process_data (byte_data )
842857
858+ def send_authentication_request (self ):
859+ self .authentication_state = self .AUTH .AWAITING_AUTH
860+ super ().process_data (b'AUTH XOAUTH2\r \n ' )
861+
843862
844863class OAuth2ServerConnection (asyncore .dispatcher_with_send ):
845864 """The base server-side connection, setting up STARTTLS if requested, subclassed for IMAP/SMTP server interaction"""
@@ -1086,6 +1105,8 @@ def process_data(self, byte_data):
10861105class POPOAuth2ServerConnection (OAuth2ServerConnection ):
10871106 """The POP server side - submit credentials, then watch for +OK and ignore subsequent data"""
10881107
1108+ # POP3: https://tools.ietf.org/html/rfc1734
1109+ # POP3 SASL: https://tools.ietf.org/html/rfc5034
10891110 def __init__ (self , socket_map , server_address , connection_info , proxy_parent , custom_configuration ):
10901111 super ().__init__ ('POP' , socket_map , server_address , connection_info , proxy_parent , custom_configuration )
10911112 self .username = None
0 commit comments