mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
9ce366a5a6
commit
54411c1784
|
@ -364,7 +364,7 @@ class _NNTPBase:
|
||||||
self.nntp_implementation = None
|
self.nntp_implementation = None
|
||||||
try:
|
try:
|
||||||
resp, caps = self.capabilities()
|
resp, caps = self.capabilities()
|
||||||
except NNTPPermanentError:
|
except (NNTPPermanentError, NNTPTemporaryError):
|
||||||
# Server doesn't support capabilities
|
# Server doesn't support capabilities
|
||||||
self._caps = {}
|
self._caps = {}
|
||||||
else:
|
else:
|
||||||
|
@ -941,6 +941,9 @@ class _NNTPBase:
|
||||||
resp = self._shortcmd('authinfo pass ' + password)
|
resp = self._shortcmd('authinfo pass ' + password)
|
||||||
if not resp.startswith('281'):
|
if not resp.startswith('281'):
|
||||||
raise NNTPPermanentError(resp)
|
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.
|
# Attempt to send mode reader if it was requested after login.
|
||||||
if self.readermode_afterauth:
|
if self.readermode_afterauth:
|
||||||
self._setreadermode()
|
self._setreadermode()
|
||||||
|
|
|
@ -374,6 +374,8 @@ class NNTPv1Handler:
|
||||||
self.allow_posting = True
|
self.allow_posting = True
|
||||||
self._readline = readline
|
self._readline = readline
|
||||||
self._push_data = push_data
|
self._push_data = push_data
|
||||||
|
self._logged_in = False
|
||||||
|
self._user_sent = False
|
||||||
# Our welcome
|
# Our welcome
|
||||||
self.handle_welcome()
|
self.handle_welcome()
|
||||||
|
|
||||||
|
@ -666,27 +668,56 @@ class NNTPv1Handler:
|
||||||
self.push_lit(self.sample_body)
|
self.push_lit(self.sample_body)
|
||||||
self.push_lit(".")
|
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):
|
class NNTPv2Handler(NNTPv1Handler):
|
||||||
"""A handler for RFC 3977 (NNTP "v2")"""
|
"""A handler for RFC 3977 (NNTP "v2")"""
|
||||||
|
|
||||||
def handle_CAPABILITIES(self):
|
def handle_CAPABILITIES(self):
|
||||||
self.push_lit("""\
|
fmt = """\
|
||||||
101 Capability list:
|
101 Capability list:
|
||||||
VERSION 2 3
|
VERSION 2 3
|
||||||
IMPLEMENTATION INN 2.5.1
|
IMPLEMENTATION INN 2.5.1{}
|
||||||
AUTHINFO USER
|
|
||||||
HDR
|
HDR
|
||||||
LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT
|
LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT
|
||||||
OVER
|
OVER
|
||||||
POST
|
POST
|
||||||
READER
|
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):
|
def handle_OVER(self, message_spec=None):
|
||||||
return self.handle_XOVER(message_spec)
|
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:
|
class NNTPv1v2TestsMixin:
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -695,6 +726,14 @@ class NNTPv1v2TestsMixin:
|
||||||
def test_welcome(self):
|
def test_welcome(self):
|
||||||
self.assertEqual(self.server.welcome, self.handler.welcome)
|
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):
|
def test_date(self):
|
||||||
resp, date = self.server.date()
|
resp, date = self.server.date()
|
||||||
self.assertEqual(resp, "111 20100914001155")
|
self.assertEqual(resp, "111 20100914001155")
|
||||||
|
@ -1073,6 +1112,18 @@ class NNTPv2Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase):
|
||||||
self.assertEqual(self.server.nntp_implementation, 'INN 2.5.1')
|
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):
|
class MiscTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_decode_header(self):
|
def test_decode_header(self):
|
||||||
|
@ -1232,7 +1283,8 @@ class MiscTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, NetworkedNNTPTests]
|
tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, CapsAfterLoginNNTPv2Tests,
|
||||||
|
NetworkedNNTPTests]
|
||||||
if _have_ssl:
|
if _have_ssl:
|
||||||
tests.append(NetworkedNNTP_SSLTests)
|
tests.append(NetworkedNNTP_SSLTests)
|
||||||
support.run_unittest(*tests)
|
support.run_unittest(*tests)
|
||||||
|
|
|
@ -113,6 +113,10 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #13989: Document that GzipFile does not support text mode, and give a
|
||||||
more helpful error message when opened with an invalid mode string.
|
more helpful error message when opened with an invalid mode string.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue