@@ -221,9 +221,11 @@ def _send_textline(self, message):
221221 def _send_tagged (self , tag , code , message ):
222222 self ._send_textline (' ' .join ((tag , code , message )))
223223
224+ welcome = '* OK IMAP4rev1'
225+
224226 def handle (self ):
225227 # Send a welcome message.
226- self ._send_textline ('* OK IMAP4rev1' )
228+ self ._send_textline (self . welcome )
227229 while 1 :
228230 # Gather up input until we receive a line terminator or we timeout.
229231 # Accumulate read(1) because it's simpler to handle the differences
@@ -659,6 +661,71 @@ def cmd_APPEND(self, tag, args):
659661 client .append ('INBOX' , None , None , message )
660662 self .assertEqual (server .response , b'a\r \n b\r \n c\r \n d' )
661663
664+ def test_login_capabilities (self ):
665+ # A server may advertise new capabilities after login (as an
666+ # untagged CAPABILITY response); imaplib must refresh its cached
667+ # capability list (gh-63121, gh-103451).
668+ class CapabilityLoginHandler (SimpleIMAPHandler ):
669+ def cmd_LOGIN (self , tag , args ):
670+ self .server .logged = args [0 ]
671+ self ._send_textline ('* CAPABILITY IMAP4rev1 ENABLE UTF8=ACCEPT' )
672+ self ._send_tagged (tag , 'OK' , 'LOGIN completed' )
673+ def cmd_ENABLE (self , tag , args ):
674+ self ._send_tagged (tag , 'OK' , 'ENABLE completed' )
675+
676+ client , _ = self ._setup (CapabilityLoginHandler )
677+ self .assertNotIn ('ENABLE' , client .capabilities )
678+ client .login ('user' , 'pass' )
679+ self .assertIn ('ENABLE' , client .capabilities )
680+ self .assertIn ('UTF8=ACCEPT' , client .capabilities )
681+ typ , _ = client .enable ('UTF8=ACCEPT' )
682+ self .assertEqual (typ , 'OK' )
683+
684+ def test_authenticate_capabilities (self ):
685+ # Capabilities are also refreshed after AUTHENTICATE, here from a
686+ # CAPABILITY response code in the tagged OK response.
687+ class CapabilityAuthHandler (SimpleIMAPHandler ):
688+ def cmd_AUTHENTICATE (self , tag , args ):
689+ self ._send_textline ('+' )
690+ self .server .response = yield
691+ self ._send_tagged (
692+ tag , 'OK' ,
693+ '[CAPABILITY IMAP4rev1 ENABLE] AUTHENTICATE completed' )
694+
695+ client , _ = self ._setup (CapabilityAuthHandler )
696+ self .assertNotIn ('ENABLE' , client .capabilities )
697+ client .authenticate ('MYAUTH' , lambda x : b'fake' )
698+ self .assertIn ('ENABLE' , client .capabilities )
699+
700+ def test_greeting_capabilities (self ):
701+ # Capabilities advertised in the greeting are used directly,
702+ # without sending a separate CAPABILITY command.
703+ class GreetingHandler (SimpleIMAPHandler ):
704+ welcome = '* OK [CAPABILITY IMAP4rev1 ENABLE] Server ready'
705+ def cmd_CAPABILITY (self , tag , args ):
706+ self .server .capability_queried = True
707+ super ().cmd_CAPABILITY (tag , args )
708+
709+ client , server = self ._setup (GreetingHandler )
710+ self .assertEqual (client .capabilities , ('IMAP4REV1' , 'ENABLE' ))
711+ self .assertFalse (getattr (server , 'capability_queried' , False ))
712+
713+ def test_login_requery_capabilities (self ):
714+ # If the server does not advertise capabilities after login,
715+ # imaplib re-queries them (as it does after STARTTLS), so a
716+ # capability that becomes available only after authentication is
717+ # still recognized (gh-63121).
718+ class RequeryHandler (SimpleIMAPHandler ):
719+ def cmd_CAPABILITY (self , tag , args ):
720+ caps = 'IMAP4rev1 ENABLE' if self .server .logged else 'IMAP4rev1'
721+ self ._send_textline ('* CAPABILITY ' + caps )
722+ self ._send_tagged (tag , 'OK' , 'CAPABILITY completed' )
723+
724+ client , _ = self ._setup (RequeryHandler )
725+ self .assertNotIn ('ENABLE' , client .capabilities )
726+ client .login ('user' , 'pass' )
727+ self .assertIn ('ENABLE' , client .capabilities )
728+
662729 def test_logout (self ):
663730 client , _ = self ._setup (SimpleIMAPHandler )
664731 typ , data = client .login ('user' , 'pass' )
0 commit comments