Commit 71135624 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #10287: nntplib now queries the server's CAPABILITIES first before...

Issue #10287: nntplib now queries the server's CAPABILITIES first before sending MODE READER, and only sends it if not already in READER mode.
Patch by Hynek Schlawack.
parent ffeee351
...@@ -324,25 +324,30 @@ class _NNTPBase: ...@@ -324,25 +324,30 @@ class _NNTPBase:
self.debugging = 0 self.debugging = 0
self.welcome = self._getresp() self.welcome = self._getresp()
# Inquire about capabilities (RFC 3977).
self._caps = None
self.getcapabilities()
# 'MODE READER' is sometimes necessary to enable 'reader' mode. # 'MODE READER' is sometimes necessary to enable 'reader' mode.
# However, the order in which 'MODE READER' and 'AUTHINFO' need to # However, the order in which 'MODE READER' and 'AUTHINFO' need to
# arrive differs between some NNTP servers. If _setreadermode() fails # arrive differs between some NNTP servers. If _setreadermode() fails
# with an authorization failed error, it will set this to True; # with an authorization failed error, it will set this to True;
# the login() routine will interpret that as a request to try again # the login() routine will interpret that as a request to try again
# after performing its normal function. # after performing its normal function.
# Enable only if we're not already in READER mode anyway.
self.readermode_afterauth = False self.readermode_afterauth = False
if readermode: if readermode and 'READER' not in self._caps:
self._setreadermode() self._setreadermode()
if not self.readermode_afterauth:
# Capabilities might have changed after MODE READER
self._caps = None
self.getcapabilities()
# RFC 4642 2.2.2: Both the client and the server MUST know if there is # RFC 4642 2.2.2: Both the client and the server MUST know if there is
# a TLS session active. A client MUST NOT attempt to start a TLS # a TLS session active. A client MUST NOT attempt to start a TLS
# session if a TLS session is already active. # session if a TLS session is already active.
self.tls_on = False self.tls_on = False
# Inquire about capabilities (RFC 3977).
self._caps = None
self.getcapabilities()
# Log in and encryption setup order is left to subclasses. # Log in and encryption setup order is left to subclasses.
self.authenticated = False self.authenticated = False
...@@ -945,8 +950,12 @@ class _NNTPBase: ...@@ -945,8 +950,12 @@ class _NNTPBase:
self._caps = None self._caps = None
self.getcapabilities() 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: # Only do so if we're not in reader mode already.
if self.readermode_afterauth and 'READER' not in self._caps:
self._setreadermode() self._setreadermode()
# Capabilities might have changed after MODE READER
self._caps = None
self.getcapabilities()
def _setreadermode(self): def _setreadermode(self):
try: try:
......
...@@ -364,6 +364,12 @@ class MockedNNTPTestsMixin: ...@@ -364,6 +364,12 @@ class MockedNNTPTestsMixin:
return self.server return self.server
class MockedNNTPWithReaderModeMixin(MockedNNTPTestsMixin):
def setUp(self):
super().setUp()
self.make_server(readermode=True)
class NNTPv1Handler: class NNTPv1Handler:
"""A handler for RFC 977""" """A handler for RFC 977"""
...@@ -704,6 +710,9 @@ class NNTPv2Handler(NNTPv1Handler): ...@@ -704,6 +710,9 @@ class NNTPv2Handler(NNTPv1Handler):
else: else:
self.push_lit(fmt.format('')) self.push_lit(fmt.format(''))
def handle_MODE(self, _):
raise Exception('MODE READER sent despite READER has been advertised')
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)
...@@ -718,6 +727,34 @@ class CapsAfterLoginNNTPv2Handler(NNTPv2Handler): ...@@ -718,6 +727,34 @@ class CapsAfterLoginNNTPv2Handler(NNTPv2Handler):
super().handle_CAPABILITIES() super().handle_CAPABILITIES()
class ModeSwitchingNNTPv2Handler(NNTPv2Handler):
"""A server that starts in transit mode"""
def __init__(self):
self._switched = False
def handle_CAPABILITIES(self):
fmt = """\
101 Capability list:
VERSION 2 3
IMPLEMENTATION INN 2.5.1
HDR
LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT
OVER
POST
{}READER
."""
if self._switched:
self.push_lit(fmt.format(''))
else:
self.push_lit(fmt.format('MODE-'))
def handle_MODE(self, what):
assert not self._switched and what == 'reader'
self._switched = True
self.push_lit('200 Posting allowed')
class NNTPv1v2TestsMixin: class NNTPv1v2TestsMixin:
def setUp(self): def setUp(self):
...@@ -1124,6 +1161,18 @@ class CapsAfterLoginNNTPv2Tests(MockedNNTPTestsMixin, unittest.TestCase): ...@@ -1124,6 +1161,18 @@ class CapsAfterLoginNNTPv2Tests(MockedNNTPTestsMixin, unittest.TestCase):
self.assertIn('VERSION', self.server._caps) self.assertIn('VERSION', self.server._caps)
class SendReaderNNTPv2Tests(MockedNNTPWithReaderModeMixin,
unittest.TestCase):
"""Same tests as for v2 but we tell NTTP to send MODE READER to a server
that isn't in READER mode by default."""
nntp_version = 2
handler_class = ModeSwitchingNNTPv2Handler
def test_we_are_in_reader_mode_after_connect(self):
self.assertIn('READER', self.server._caps)
class MiscTests(unittest.TestCase): class MiscTests(unittest.TestCase):
def test_decode_header(self): def test_decode_header(self):
...@@ -1284,7 +1333,7 @@ class MiscTests(unittest.TestCase): ...@@ -1284,7 +1333,7 @@ class MiscTests(unittest.TestCase):
def test_main(): def test_main():
tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, CapsAfterLoginNNTPv2Tests, tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, CapsAfterLoginNNTPv2Tests,
NetworkedNNTPTests] SendReaderNNTPv2Tests, 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 ...@@ -113,6 +113,10 @@ Core and Builtins
Library Library
------- -------
- Issue #10287: nntplib now queries the server's CAPABILITIES first before
sending MODE READER, and only sends it if not already in READER mode.
Patch by Hynek Schlawack.
- Issue #13979: A bug in ctypes.util.find_library that caused - Issue #13979: A bug in ctypes.util.find_library that caused
the wrong library name to be returned has been fixed. the wrong library name to be returned has been fixed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment