Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 54411c1

Browse files
committed
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.
1 parent 9ce366a commit 54411c1

3 files changed

Lines changed: 65 additions & 6 deletions

File tree

Lib/nntplib.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ def getcapabilities(self):
364364
self.nntp_implementation = None
365365
try:
366366
resp, caps = self.capabilities()
367-
except NNTPPermanentError:
367+
except (NNTPPermanentError, NNTPTemporaryError):
368368
# Server doesn't support capabilities
369369
self._caps = {}
370370
else:
@@ -941,6 +941,9 @@ def login(self, user=None, password=None, usenetrc=True):
941941
resp = self._shortcmd('authinfo pass ' + password)
942942
if not resp.startswith('281'):
943943
raise NNTPPermanentError(resp)
944+
# Capabilities might have changed after login
945+
self._caps = None
946+
self.getcapabilities()
944947
# Attempt to send mode reader if it was requested after login.
945948
if self.readermode_afterauth:
946949
self._setreadermode()

Lib/test/test_nntplib.py

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ def start(self, readline, push_data):
374374
self.allow_posting = True
375375
self._readline = readline
376376
self._push_data = push_data
377+
self._logged_in = False
378+
self._user_sent = False
377379
# Our welcome
378380
self.handle_welcome()
379381

@@ -666,27 +668,56 @@ def handle_BODY(self, message_spec=None):
666668
self.push_lit(self.sample_body)
667669
self.push_lit(".")
668670

671+
def handle_AUTHINFO(self, cred_type, data):
672+
if self._logged_in:
673+
self.push_lit('502 Already Logged In')
674+
elif cred_type == 'user':
675+
if self._user_sent:
676+
self.push_lit('482 User Credential Already Sent')
677+
else:
678+
self.push_lit('381 Password Required')
679+
self._user_sent = True
680+
elif cred_type == 'pass':
681+
self.push_lit('281 Login Successful')
682+
self._logged_in = True
683+
else:
684+
raise Exception('Unknown cred type {}'.format(cred_type))
685+
669686

670687
class NNTPv2Handler(NNTPv1Handler):
671688
"""A handler for RFC 3977 (NNTP "v2")"""
672689

673690
def handle_CAPABILITIES(self):
674-
self.push_lit("""\
691+
fmt = """\
675692
101 Capability list:
676693
VERSION 2 3
677-
IMPLEMENTATION INN 2.5.1
678-
AUTHINFO USER
694+
IMPLEMENTATION INN 2.5.1{}
679695
HDR
680696
LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT
681697
OVER
682698
POST
683699
READER
684-
.""")
700+
."""
701+
702+
if not self._logged_in:
703+
self.push_lit(fmt.format('\n AUTHINFO USER'))
704+
else:
705+
self.push_lit(fmt.format(''))
685706

686707
def handle_OVER(self, message_spec=None):
687708
return self.handle_XOVER(message_spec)
688709

689710

711+
class CapsAfterLoginNNTPv2Handler(NNTPv2Handler):
712+
"""A handler that allows CAPABILITIES only after login"""
713+
714+
def handle_CAPABILITIES(self):
715+
if not self._logged_in:
716+
self.push_lit('480 You must log in.')
717+
else:
718+
super().handle_CAPABILITIES()
719+
720+
690721
class NNTPv1v2TestsMixin:
691722

692723
def setUp(self):
@@ -695,6 +726,14 @@ def setUp(self):
695726
def test_welcome(self):
696727
self.assertEqual(self.server.welcome, self.handler.welcome)
697728

729+
def test_authinfo(self):
730+
if self.nntp_version == 2:
731+
self.assertIn('AUTHINFO', self.server._caps)
732+
self.server.login('testuser', 'testpw')
733+
# if AUTHINFO is gone from _caps we also know that getcapabilities()
734+
# has been called after login as it should
735+
self.assertNotIn('AUTHINFO', self.server._caps)
736+
698737
def test_date(self):
699738
resp, date = self.server.date()
700739
self.assertEqual(resp, "111 20100914001155")
@@ -1073,6 +1112,18 @@ def test_caps(self):
10731112
self.assertEqual(self.server.nntp_implementation, 'INN 2.5.1')
10741113

10751114

1115+
class CapsAfterLoginNNTPv2Tests(MockedNNTPTestsMixin, unittest.TestCase):
1116+
"""Tests a probably NNTP v2 server with capabilities only after login."""
1117+
1118+
nntp_version = 2
1119+
handler_class = CapsAfterLoginNNTPv2Handler
1120+
1121+
def test_caps_only_after_login(self):
1122+
self.assertEqual(self.server._caps, {})
1123+
self.server.login('testuser', 'testpw')
1124+
self.assertIn('VERSION', self.server._caps)
1125+
1126+
10761127
class MiscTests(unittest.TestCase):
10771128

10781129
def test_decode_header(self):
@@ -1232,7 +1283,8 @@ def gives(y, M, d, date_str, time_str):
12321283

12331284

12341285
def test_main():
1235-
tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, NetworkedNNTPTests]
1286+
tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, CapsAfterLoginNNTPv2Tests,
1287+
NetworkedNNTPTests]
12361288
if _have_ssl:
12371289
tests.append(NetworkedNNTP_SSLTests)
12381290
support.run_unittest(*tests)

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ Core and Builtins
113113
Library
114114
-------
115115

116+
- Issue #10287: nntplib now queries the server's CAPABILITIES again after
117+
authenticating (since the result may change, according to RFC 4643).
118+
Patch by Hynek Schlawack.
119+
116120
- Issue #13989: Document that GzipFile does not support text mode, and give a
117121
more helpful error message when opened with an invalid mode string.
118122

0 commit comments

Comments
 (0)