diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2012-02-12 18:15:09 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2012-02-12 18:15:09 (GMT) |
commit | 425b8e85a99ee0c21a6f805e123d90e35f9f6a9b (patch) | |
tree | d74cc44c1f3f88cb65f60823c746b5ac2f7007a5 | |
parent | 2f234db3ebeb2417291bfe31f4a084f5fc280c66 (diff) | |
parent | 54411c1784bc404448f496e7a9bf657eaab14daf (diff) | |
download | cpython-425b8e85a99ee0c21a6f805e123d90e35f9f6a9b.zip cpython-425b8e85a99ee0c21a6f805e123d90e35f9f6a9b.tar.gz cpython-425b8e85a99ee0c21a6f805e123d90e35f9f6a9b.tar.bz2 |
Issue #10287: nntplib now queries the server's CAPABILITIES again after authenticating (since the result may change, according to RFC 4643).
Patch by Hynek Schlawack.
-rw-r--r-- | Lib/nntplib.py | 5 | ||||
-rw-r--r-- | Lib/test/test_nntplib.py | 62 | ||||
-rw-r--r-- | Misc/NEWS | 4 |
3 files changed, 65 insertions, 6 deletions
diff --git a/Lib/nntplib.py b/Lib/nntplib.py index 19a462d..7ebc621 100644 --- a/Lib/nntplib.py +++ b/Lib/nntplib.py @@ -378,7 +378,7 @@ class _NNTPBase: self.nntp_implementation = None try: resp, caps = self.capabilities() - except NNTPPermanentError: + except (NNTPPermanentError, NNTPTemporaryError): # Server doesn't support capabilities self._caps = {} else: @@ -955,6 +955,9 @@ class _NNTPBase: resp = self._shortcmd('authinfo pass ' + password) if not resp.startswith('281'): raise NNTPPermanentError(resp) + # Capabilities might have changed after login + self._caps = None + self.getcapabilities() # Attempt to send mode reader if it was requested after login. if self.readermode_afterauth: self._setreadermode() diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py index 3141389..9753251 100644 --- a/Lib/test/test_nntplib.py +++ b/Lib/test/test_nntplib.py @@ -395,6 +395,8 @@ class NNTPv1Handler: self.allow_posting = True self._readline = readline self._push_data = push_data + self._logged_in = False + self._user_sent = False # Our welcome self.handle_welcome() @@ -687,27 +689,56 @@ class NNTPv1Handler: self.push_lit(self.sample_body) self.push_lit(".") + def handle_AUTHINFO(self, cred_type, data): + if self._logged_in: + self.push_lit('502 Already Logged In') + elif cred_type == 'user': + if self._user_sent: + self.push_lit('482 User Credential Already Sent') + else: + self.push_lit('381 Password Required') + self._user_sent = True + elif cred_type == 'pass': + self.push_lit('281 Login Successful') + self._logged_in = True + else: + raise Exception('Unknown cred type {}'.format(cred_type)) + class NNTPv2Handler(NNTPv1Handler): """A handler for RFC 3977 (NNTP "v2")""" def handle_CAPABILITIES(self): - self.push_lit("""\ + fmt = """\ 101 Capability list: VERSION 2 3 - IMPLEMENTATION INN 2.5.1 - AUTHINFO USER + IMPLEMENTATION INN 2.5.1{} HDR LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT OVER POST READER - .""") + .""" + + if not self._logged_in: + self.push_lit(fmt.format('\n AUTHINFO USER')) + else: + self.push_lit(fmt.format('')) def handle_OVER(self, message_spec=None): return self.handle_XOVER(message_spec) +class CapsAfterLoginNNTPv2Handler(NNTPv2Handler): + """A handler that allows CAPABILITIES only after login""" + + def handle_CAPABILITIES(self): + if not self._logged_in: + self.push_lit('480 You must log in.') + else: + super().handle_CAPABILITIES() + + class NNTPv1v2TestsMixin: def setUp(self): @@ -716,6 +747,14 @@ class NNTPv1v2TestsMixin: def test_welcome(self): self.assertEqual(self.server.welcome, self.handler.welcome) + def test_authinfo(self): + if self.nntp_version == 2: + self.assertIn('AUTHINFO', self.server._caps) + self.server.login('testuser', 'testpw') + # if AUTHINFO is gone from _caps we also know that getcapabilities() + # has been called after login as it should + self.assertNotIn('AUTHINFO', self.server._caps) + def test_date(self): resp, date = self.server.date() self.assertEqual(resp, "111 20100914001155") @@ -1094,6 +1133,18 @@ class NNTPv2Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase): self.assertEqual(self.server.nntp_implementation, 'INN 2.5.1') +class CapsAfterLoginNNTPv2Tests(MockedNNTPTestsMixin, unittest.TestCase): + """Tests a probably NNTP v2 server with capabilities only after login.""" + + nntp_version = 2 + handler_class = CapsAfterLoginNNTPv2Handler + + def test_caps_only_after_login(self): + self.assertEqual(self.server._caps, {}) + self.server.login('testuser', 'testpw') + self.assertIn('VERSION', self.server._caps) + + class MiscTests(unittest.TestCase): def test_decode_header(self): @@ -1253,7 +1304,8 @@ class MiscTests(unittest.TestCase): def test_main(): - tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, NetworkedNNTPTests] + tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, CapsAfterLoginNNTPv2Tests, + NetworkedNNTPTests] if _have_ssl: tests.append(NetworkedNNTP_SSLTests) support.run_unittest(*tests) @@ -466,6 +466,10 @@ Core and Builtins Library ------- +- Issue #10287: nntplib now queries the server's CAPABILITIES again after + authenticating (since the result may change, according to RFC 4643). + Patch by Hynek Schlawack. + - Issue #13989: Document that GzipFile does not support text mode, and give a more helpful error message when opened with an invalid mode string. |