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

Skip to content

Commit e8dc258

Browse files
committed
Merged revisions 76726-76727 via svnmerge from
svn+ssh://[email protected]/python/trunk The merge adds a test with an invalid rather than a missing line end, since the py3K code passed the original issue 5949 test. New test also by Scott Dial. ........ r76726 | r.david.murray | 2009-12-09 10:15:31 -0500 (Wed, 09 Dec 2009) | 6 lines Issue 5949: fixed IMAP4_SSL hang when the IMAP server response is missing proper end-of-line termination. Patch and tests by Scott Dial. The new tests include a test harness which will make it easier to add additional tests. ........ r76727 | r.david.murray | 2009-12-09 11:41:39 -0500 (Wed, 09 Dec 2009) | 2 lines Skip new imaplib SSL tests if ssl is not available. ........
1 parent e5fdedb commit e8dc258

3 files changed

Lines changed: 199 additions & 3 deletions

File tree

Lib/imaplib.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,8 @@ def _get_line(self):
10231023
raise self.abort('socket error: EOF')
10241024

10251025
# Protocol mandates all lines terminated by CRLF
1026+
if not line.endswith(b'\r\n'):
1027+
raise self.abort('socket error: unterminated line')
10261028

10271029
line = line[:-2]
10281030
if __debug__:

Lib/test/test_imaplib.py

Lines changed: 194 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
1+
from test import support
2+
# If we end up with a significant number of tests that don't require
3+
# threading, this test module should be split. Right now we skip
4+
# them all if we don't have threading.
5+
threading = support.import_module('threading')
6+
7+
from contextlib import contextmanager
18
import imaplib
9+
import os.path
10+
import select
11+
import socket
12+
import socketserver
13+
import sys
214
import time
315

4-
from test import support
16+
from test.support import reap_threads, verbose
517
import unittest
618

19+
try:
20+
import ssl
21+
except ImportError:
22+
ssl = None
23+
24+
CERTFILE = None
25+
726

827
class TestImaplib(unittest.TestCase):
28+
929
def test_that_Time2Internaldate_returns_a_result(self):
1030
# We can check only that it successfully produces a result,
1131
# not the correctness of the result itself, since the result
@@ -17,9 +37,180 @@ def test_that_Time2Internaldate_returns_a_result(self):
1737
imaplib.Time2Internaldate(t)
1838

1939

40+
if ssl:
41+
42+
class SecureTCPServer(socketserver.TCPServer):
43+
44+
def get_request(self):
45+
newsocket, fromaddr = self.socket.accept()
46+
connstream = ssl.wrap_socket(newsocket,
47+
server_side=True,
48+
certfile=CERTFILE)
49+
return connstream, fromaddr
50+
51+
IMAP4_SSL = imaplib.IMAP4_SSL
52+
53+
else:
54+
55+
class SecureTCPServer:
56+
pass
57+
58+
IMAP4_SSL = None
59+
60+
61+
class SimpleIMAPHandler(socketserver.StreamRequestHandler):
62+
63+
timeout = 1
64+
65+
def _send(self, message):
66+
if verbose: print("SENT:", message.strip())
67+
self.wfile.write(message)
68+
69+
def handle(self):
70+
# Send a welcome message.
71+
self._send(b'* OK IMAP4rev1\r\n')
72+
while 1:
73+
# Gather up input until we receive a line terminator or we timeout.
74+
# Accumulate read(1) because it's simpler to handle the differences
75+
# between naked sockets and SSL sockets.
76+
line = b''
77+
while 1:
78+
try:
79+
part = self.rfile.read(1)
80+
if part == b'':
81+
# Naked sockets return empty strings..
82+
return
83+
line += part
84+
except IOError:
85+
# ..but SSLSockets throw exceptions.
86+
return
87+
if line.endswith(b'\r\n'):
88+
break
89+
90+
if verbose: print('GOT:', line.strip())
91+
splitline = line.split()
92+
tag = splitline[0].decode('ASCII')
93+
cmd = splitline[1].decode('ASCII')
94+
args = splitline[2:]
95+
96+
if hasattr(self, 'cmd_'+cmd):
97+
getattr(self, 'cmd_'+cmd)(tag, args)
98+
else:
99+
self._send('{} BAD {} unknown\r\n'.format(tag, cmd).encode('ASCII'))
100+
101+
def cmd_CAPABILITY(self, tag, args):
102+
self._send(b'* CAPABILITY IMAP4rev1\r\n')
103+
self._send('{} OK CAPABILITY completed\r\n'.format(tag).encode('ASCII'))
104+
105+
106+
class BaseThreadedNetworkedTests(unittest.TestCase):
107+
108+
def make_server(self, addr, hdlr):
109+
110+
class MyServer(self.server_class):
111+
def handle_error(self, request, client_address):
112+
self.close_request(request)
113+
self.server_close()
114+
raise
115+
116+
if verbose: print("creating server")
117+
server = MyServer(addr, hdlr)
118+
self.assertEquals(server.server_address, server.socket.getsockname())
119+
120+
if verbose:
121+
print("server created")
122+
print("ADDR =", addr)
123+
print("CLASS =", self.server_class)
124+
print("HDLR =", server.RequestHandlerClass)
125+
126+
t = threading.Thread(
127+
name='%s serving' % self.server_class,
128+
target=server.serve_forever,
129+
# Short poll interval to make the test finish quickly.
130+
# Time between requests is short enough that we won't wake
131+
# up spuriously too many times.
132+
kwargs={'poll_interval':0.01})
133+
t.daemon = True # In case this function raises.
134+
t.start()
135+
if verbose: print("server running")
136+
return server, t
137+
138+
def reap_server(self, server, thread):
139+
if verbose: print("waiting for server")
140+
server.shutdown()
141+
thread.join()
142+
if verbose: print("done")
143+
144+
@contextmanager
145+
def reaped_server(self, hdlr):
146+
server, thread = self.make_server((support.HOST, 0), hdlr)
147+
try:
148+
yield server
149+
finally:
150+
self.reap_server(server, thread)
151+
152+
@reap_threads
153+
def test_connect(self):
154+
with self.reaped_server(SimpleIMAPHandler) as server:
155+
client = self.imap_class(*server.server_address)
156+
client.shutdown()
157+
158+
@reap_threads
159+
def test_issue5949(self):
160+
161+
class EOFHandler(socketserver.StreamRequestHandler):
162+
def handle(self):
163+
# EOF without sending a complete welcome message.
164+
self.wfile.write(b'* OK')
165+
166+
with self.reaped_server(EOFHandler) as server:
167+
self.assertRaises(imaplib.IMAP4.abort,
168+
self.imap_class, *server.server_address)
169+
170+
@reap_threads
171+
def test_line_termination(self):
172+
173+
class BadNewlineHandler(SimpleIMAPHandler):
174+
175+
def cmd_CAPABILITY(self, tag, args):
176+
self._send(b'* CAPABILITY IMAP4rev1 AUTH\n')
177+
self._send('{} OK CAPABILITY completed\r\n'.format(tag).encode('ASCII'))
178+
179+
with self.reaped_server(BadNewlineHandler) as server:
180+
self.assertRaises(imaplib.IMAP4.abort,
181+
self.imap_class, *server.server_address)
182+
183+
184+
185+
class ThreadedNetworkedTests(BaseThreadedNetworkedTests):
186+
187+
server_class = socketserver.TCPServer
188+
imap_class = imaplib.IMAP4
189+
190+
191+
@unittest.skipUnless(ssl, "SSL not available")
192+
class ThreadedNetworkedTestsSSL(BaseThreadedNetworkedTests):
193+
194+
server_class = SecureTCPServer
195+
imap_class = IMAP4_SSL
196+
197+
20198
def test_main():
21-
support.run_unittest(TestImaplib)
199+
200+
tests = [TestImaplib]
201+
202+
if support.is_resource_enabled('network'):
203+
if ssl:
204+
global CERTFILE
205+
CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
206+
"keycert.pem")
207+
if not os.path.exists(CERTFILE):
208+
raise support.TestFailed("Can't read certificate files!")
209+
tests.extend([ThreadedNetworkedTests, ThreadedNetworkedTestsSSL])
210+
211+
support.run_unittest(*tests)
22212

23213

24214
if __name__ == "__main__":
25-
unittest.main()
215+
support.use_resources = ['network']
216+
test_main()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ C-API
154154
Library
155155
-------
156156

157+
- Issue #5949: added check for correct lineends in input from IMAP server
158+
in imaplib.
159+
157160
- Add a reverse() method to collections.deque().
158161

159162
- Issue #6986: Fix crash in the JSON C accelerator when called with the

0 commit comments

Comments
 (0)