blob: 5ef9512c3f57c4ac7cefd996774816d33701dc9a [file] [log] [blame]
R. David Murraye8dc2582009-12-10 02:08:06 +00001from 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.
5threading = support.import_module('threading')
6
7from contextlib import contextmanager
Martin v. Löwisea752fb2002-01-05 11:31:49 +00008import imaplib
R. David Murraye8dc2582009-12-10 02:08:06 +00009import os.path
R. David Murraye8dc2582009-12-10 02:08:06 +000010import socketserver
Tim Peters108b7912002-07-31 16:42:33 +000011import time
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000012import calendar
Martin v. Löwisea752fb2002-01-05 11:31:49 +000013
Antoine Pitroucac9e712014-07-31 18:35:45 -040014from test.support import (reap_threads, verbose, transient_internet,
15 run_with_tz, run_with_locale)
Christian Heimesf6cd9672008-03-26 13:45:42 +000016import unittest
R David Murrayb079c072016-12-24 21:32:26 -050017from unittest import mock
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040018from datetime import datetime, timezone, timedelta
R. David Murraye8dc2582009-12-10 02:08:06 +000019try:
20 import ssl
21except ImportError:
22 ssl = None
23
Antoine Pitroucac9e712014-07-31 18:35:45 -040024CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem")
25CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem")
R. David Murraye8dc2582009-12-10 02:08:06 +000026
Martin v. Löwisea752fb2002-01-05 11:31:49 +000027
Christian Heimesf6cd9672008-03-26 13:45:42 +000028class TestImaplib(unittest.TestCase):
R. David Murraye8dc2582009-12-10 02:08:06 +000029
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000030 def test_Internaldate2tuple(self):
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000031 t0 = calendar.timegm((2000, 1, 1, 0, 0, 0, -1, -1, -1))
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000032 tt = imaplib.Internaldate2tuple(
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000033 b'25 (INTERNALDATE "01-Jan-2000 00:00:00 +0000")')
34 self.assertEqual(time.mktime(tt), t0)
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000035 tt = imaplib.Internaldate2tuple(
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000036 b'25 (INTERNALDATE "01-Jan-2000 11:30:00 +1130")')
37 self.assertEqual(time.mktime(tt), t0)
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000038 tt = imaplib.Internaldate2tuple(
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000039 b'25 (INTERNALDATE "31-Dec-1999 12:30:00 -1130")')
40 self.assertEqual(time.mktime(tt), t0)
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000041
Alexander Belopolsky2420d832012-04-29 15:56:49 -040042 @run_with_tz('MST+07MDT,M4.1.0,M10.5.0')
43 def test_Internaldate2tuple_issue10941(self):
44 self.assertNotEqual(imaplib.Internaldate2tuple(
45 b'25 (INTERNALDATE "02-Apr-2000 02:30:00 +0000")'),
Antoine Pitroucac9e712014-07-31 18:35:45 -040046 imaplib.Internaldate2tuple(
47 b'25 (INTERNALDATE "02-Apr-2000 03:30:00 +0000")'))
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040048
49 def timevalues(self):
50 return [2000000000, 2000000000.0, time.localtime(2000000000),
51 (2033, 5, 18, 5, 33, 20, -1, -1, -1),
52 (2033, 5, 18, 5, 33, 20, -1, -1, 1),
Alexander Belopolsky64892132012-06-22 21:10:50 -040053 datetime.fromtimestamp(2000000000,
Antoine Pitroucac9e712014-07-31 18:35:45 -040054 timezone(timedelta(0, 2 * 60 * 60))),
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040055 '"18-May-2033 05:33:20 +0200"']
56
57 @run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
Martin Panter6e0889f2015-11-16 07:21:38 +000058 # DST rules included to work around quirk where the Gnu C library may not
59 # otherwise restore the previous time zone
60 @run_with_tz('STD-1DST,M3.2.0,M11.1.0')
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040061 def test_Time2Internaldate(self):
62 expected = '"18-May-2033 05:33:20 +0200"'
63
64 for t in self.timevalues():
65 internal = imaplib.Time2Internaldate(t)
66 self.assertEqual(internal, expected)
67
68 def test_that_Time2Internaldate_returns_a_result(self):
69 # Without tzset, we can check only that it successfully
70 # produces a result, not the correctness of the result itself,
71 # since the result depends on the timezone the machine is in.
72 for t in self.timevalues():
Christian Heimesf6cd9672008-03-26 13:45:42 +000073 imaplib.Time2Internaldate(t)
74
75
R. David Murraye8dc2582009-12-10 02:08:06 +000076if ssl:
R. David Murraye8dc2582009-12-10 02:08:06 +000077 class SecureTCPServer(socketserver.TCPServer):
78
79 def get_request(self):
80 newsocket, fromaddr = self.socket.accept()
Christian Heimesd0486372016-09-10 23:23:33 +020081 context = ssl.SSLContext()
82 context.load_cert_chain(CERTFILE)
83 connstream = context.wrap_socket(newsocket, server_side=True)
R. David Murraye8dc2582009-12-10 02:08:06 +000084 return connstream, fromaddr
85
86 IMAP4_SSL = imaplib.IMAP4_SSL
87
88else:
89
90 class SecureTCPServer:
91 pass
92
93 IMAP4_SSL = None
94
95
96class SimpleIMAPHandler(socketserver.StreamRequestHandler):
R. David Murraye8dc2582009-12-10 02:08:06 +000097 timeout = 1
R David Murray774a39f2013-02-19 12:17:31 -050098 continuation = None
99 capabilities = ''
R. David Murraye8dc2582009-12-10 02:08:06 +0000100
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300101 def setup(self):
102 super().setup()
103 self.server.logged = None
104
R. David Murraye8dc2582009-12-10 02:08:06 +0000105 def _send(self, message):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400106 if verbose:
107 print("SENT: %r" % message.strip())
R. David Murraye8dc2582009-12-10 02:08:06 +0000108 self.wfile.write(message)
109
R David Murray774a39f2013-02-19 12:17:31 -0500110 def _send_line(self, message):
111 self._send(message + b'\r\n')
112
113 def _send_textline(self, message):
114 self._send_line(message.encode('ASCII'))
115
116 def _send_tagged(self, tag, code, message):
117 self._send_textline(' '.join((tag, code, message)))
118
R. David Murraye8dc2582009-12-10 02:08:06 +0000119 def handle(self):
120 # Send a welcome message.
R David Murray774a39f2013-02-19 12:17:31 -0500121 self._send_textline('* OK IMAP4rev1')
R. David Murraye8dc2582009-12-10 02:08:06 +0000122 while 1:
123 # Gather up input until we receive a line terminator or we timeout.
124 # Accumulate read(1) because it's simpler to handle the differences
125 # between naked sockets and SSL sockets.
126 line = b''
127 while 1:
128 try:
129 part = self.rfile.read(1)
130 if part == b'':
131 # Naked sockets return empty strings..
132 return
133 line += part
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200134 except OSError:
Andrew Svetlov737fb892012-12-18 21:14:22 +0200135 # ..but SSLSockets raise exceptions.
R. David Murraye8dc2582009-12-10 02:08:06 +0000136 return
137 if line.endswith(b'\r\n'):
138 break
139
Antoine Pitroucac9e712014-07-31 18:35:45 -0400140 if verbose:
141 print('GOT: %r' % line.strip())
R David Murray774a39f2013-02-19 12:17:31 -0500142 if self.continuation:
143 try:
144 self.continuation.send(line)
145 except StopIteration:
146 self.continuation = None
147 continue
148 splitline = line.decode('ASCII').split()
149 tag = splitline[0]
150 cmd = splitline[1]
R. David Murraye8dc2582009-12-10 02:08:06 +0000151 args = splitline[2:]
152
Antoine Pitroucac9e712014-07-31 18:35:45 -0400153 if hasattr(self, 'cmd_' + cmd):
154 continuation = getattr(self, 'cmd_' + cmd)(tag, args)
R David Murray774a39f2013-02-19 12:17:31 -0500155 if continuation:
156 self.continuation = continuation
157 next(continuation)
R. David Murraye8dc2582009-12-10 02:08:06 +0000158 else:
R David Murray774a39f2013-02-19 12:17:31 -0500159 self._send_tagged(tag, 'BAD', cmd + ' unknown')
R. David Murraye8dc2582009-12-10 02:08:06 +0000160
161 def cmd_CAPABILITY(self, tag, args):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400162 caps = ('IMAP4rev1 ' + self.capabilities
163 if self.capabilities
164 else 'IMAP4rev1')
R David Murray774a39f2013-02-19 12:17:31 -0500165 self._send_textline('* CAPABILITY ' + caps)
166 self._send_tagged(tag, 'OK', 'CAPABILITY completed')
167
168 def cmd_LOGOUT(self, tag, args):
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300169 self.server.logged = None
R David Murray774a39f2013-02-19 12:17:31 -0500170 self._send_textline('* BYE IMAP4ref1 Server logging out')
171 self._send_tagged(tag, 'OK', 'LOGOUT completed')
R. David Murraye8dc2582009-12-10 02:08:06 +0000172
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300173 def cmd_LOGIN(self, tag, args):
174 self.server.logged = args[0]
175 self._send_tagged(tag, 'OK', 'LOGIN completed')
176
R. David Murraye8dc2582009-12-10 02:08:06 +0000177
R David Murrayb079c072016-12-24 21:32:26 -0500178class NewIMAPTestsMixin():
179 client = None
180
181 def _setup(self, imap_handler, connect=True):
182 """
183 Sets up imap_handler for tests. imap_handler should inherit from either:
184 - SimpleIMAPHandler - for testing IMAP commands,
185 - socketserver.StreamRequestHandler - if raw access to stream is needed.
186 Returns (client, server).
187 """
188 class TestTCPServer(self.server_class):
189 def handle_error(self, request, client_address):
190 """
191 End request and raise the error if one occurs.
192 """
193 self.close_request(request)
194 self.server_close()
195 raise
196
197 self.addCleanup(self._cleanup)
198 self.server = self.server_class((support.HOST, 0), imap_handler)
199 self.thread = threading.Thread(
200 name=self._testMethodName+'-server',
201 target=self.server.serve_forever,
202 # Short poll interval to make the test finish quickly.
203 # Time between requests is short enough that we won't wake
204 # up spuriously too many times.
205 kwargs={'poll_interval': 0.01})
206 self.thread.daemon = True # In case this function raises.
207 self.thread.start()
208
209 if connect:
210 self.client = self.imap_class(*self.server.server_address)
211
212 return self.client, self.server
213
214 def _cleanup(self):
215 """
216 Cleans up the test server. This method should not be called manually,
217 it is added to the cleanup queue in the _setup method already.
218 """
219 # if logout was called already we'd raise an exception trying to
220 # shutdown the client once again
221 if self.client is not None and self.client.state != 'LOGOUT':
222 self.client.shutdown()
223 # cleanup the server
224 self.server.shutdown()
225 self.server.server_close()
226 self.thread.join(3.0)
227
228 def test_EOF_without_complete_welcome_message(self):
229 # http://bugs.python.org/issue5949
230 class EOFHandler(socketserver.StreamRequestHandler):
231 def handle(self):
232 self.wfile.write(b'* OK')
233 _, server = self._setup(EOFHandler, connect=False)
234 self.assertRaises(imaplib.IMAP4.abort, self.imap_class,
235 *server.server_address)
236
237 def test_line_termination(self):
238 class BadNewlineHandler(SimpleIMAPHandler):
239 def cmd_CAPABILITY(self, tag, args):
240 self._send(b'* CAPABILITY IMAP4rev1 AUTH\n')
241 self._send_tagged(tag, 'OK', 'CAPABILITY completed')
242 _, server = self._setup(BadNewlineHandler, connect=False)
243 self.assertRaises(imaplib.IMAP4.abort, self.imap_class,
244 *server.server_address)
245
246 def test_enable_raises_error_if_not_AUTH(self):
247 class EnableHandler(SimpleIMAPHandler):
248 capabilities = 'AUTH ENABLE UTF8=ACCEPT'
249 client, _ = self._setup(EnableHandler)
250 self.assertFalse(client.utf8_enabled)
251 with self.assertRaisesRegex(imaplib.IMAP4.error, 'ENABLE.*NONAUTH'):
252 client.enable('foo')
253 self.assertFalse(client.utf8_enabled)
254
255 def test_enable_raises_error_if_no_capability(self):
256 client, _ = self._setup(SimpleIMAPHandler)
257 with self.assertRaisesRegex(imaplib.IMAP4.error,
258 'does not support ENABLE'):
259 client.enable('foo')
260
261 def test_enable_UTF8_raises_error_if_not_supported(self):
262 client, _ = self._setup(SimpleIMAPHandler)
263 typ, data = client.login('user', 'pass')
264 self.assertEqual(typ, 'OK')
265 with self.assertRaisesRegex(imaplib.IMAP4.error,
266 'does not support ENABLE'):
267 client.enable('UTF8=ACCEPT')
268
269 def test_enable_UTF8_True_append(self):
270 class UTF8AppendServer(SimpleIMAPHandler):
271 capabilities = 'ENABLE UTF8=ACCEPT'
272 def cmd_ENABLE(self, tag, args):
273 self._send_tagged(tag, 'OK', 'ENABLE successful')
274 def cmd_AUTHENTICATE(self, tag, args):
275 self._send_textline('+')
276 self.server.response = yield
277 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
278 def cmd_APPEND(self, tag, args):
279 self._send_textline('+')
280 self.server.response = yield
281 self._send_tagged(tag, 'OK', 'okay')
282 client, server = self._setup(UTF8AppendServer)
283 self.assertEqual(client._encoding, 'ascii')
284 code, _ = client.authenticate('MYAUTH', lambda x: b'fake')
285 self.assertEqual(code, 'OK')
286 self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'
287 code, _ = client.enable('UTF8=ACCEPT')
288 self.assertEqual(code, 'OK')
289 self.assertEqual(client._encoding, 'utf-8')
290 msg_string = 'Subject: üñí©öðé'
291 typ, data = client.append(None, None, None, msg_string.encode('utf-8'))
292 self.assertEqual(typ, 'OK')
293 self.assertEqual(server.response,
294 ('UTF8 (%s)\r\n' % msg_string).encode('utf-8'))
295
296 def test_search_disallows_charset_in_utf8_mode(self):
297 class UTF8Server(SimpleIMAPHandler):
298 capabilities = 'AUTH ENABLE UTF8=ACCEPT'
299 def cmd_ENABLE(self, tag, args):
300 self._send_tagged(tag, 'OK', 'ENABLE successful')
301 def cmd_AUTHENTICATE(self, tag, args):
302 self._send_textline('+')
303 self.server.response = yield
304 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
305 client, _ = self._setup(UTF8Server)
306 typ, _ = client.authenticate('MYAUTH', lambda x: b'fake')
307 self.assertEqual(typ, 'OK')
308 typ, _ = client.enable('UTF8=ACCEPT')
309 self.assertEqual(typ, 'OK')
310 self.assertTrue(client.utf8_enabled)
311 with self.assertRaisesRegex(imaplib.IMAP4.error, 'charset.*UTF8'):
312 client.search('foo', 'bar')
313
314 def test_bad_auth_name(self):
315 class MyServer(SimpleIMAPHandler):
316 def cmd_AUTHENTICATE(self, tag, args):
317 self._send_tagged(tag, 'NO',
318 'unrecognized authentication type {}'.format(args[0]))
319 client, _ = self._setup(MyServer)
320 with self.assertRaisesRegex(imaplib.IMAP4.error,
321 'unrecognized authentication type METHOD'):
322 client.authenticate('METHOD', lambda: 1)
323
324 def test_invalid_authentication(self):
325 class MyServer(SimpleIMAPHandler):
326 def cmd_AUTHENTICATE(self, tag, args):
327 self._send_textline('+')
328 self.response = yield
329 self._send_tagged(tag, 'NO', '[AUTHENTICATIONFAILED] invalid')
330 client, _ = self._setup(MyServer)
331 with self.assertRaisesRegex(imaplib.IMAP4.error,
332 r'\[AUTHENTICATIONFAILED\] invalid'):
333 client.authenticate('MYAUTH', lambda x: b'fake')
334
335 def test_valid_authentication_bytes(self):
336 class MyServer(SimpleIMAPHandler):
337 def cmd_AUTHENTICATE(self, tag, args):
338 self._send_textline('+')
339 self.server.response = yield
340 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
341 client, server = self._setup(MyServer)
342 code, _ = client.authenticate('MYAUTH', lambda x: b'fake')
343 self.assertEqual(code, 'OK')
344 self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'
345
346 def test_valid_authentication_plain_text(self):
347 class MyServer(SimpleIMAPHandler):
348 def cmd_AUTHENTICATE(self, tag, args):
349 self._send_textline('+')
350 self.server.response = yield
351 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
352 client, server = self._setup(MyServer)
353 code, _ = client.authenticate('MYAUTH', lambda x: 'fake')
354 self.assertEqual(code, 'OK')
355 self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'
356
357 def test_login_cram_md5_bytes(self):
358 class AuthHandler(SimpleIMAPHandler):
359 capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
360 def cmd_AUTHENTICATE(self, tag, args):
361 self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
362 'VzdG9uLm1jaS5uZXQ=')
363 r = yield
364 if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
365 b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
366 self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
367 else:
368 self._send_tagged(tag, 'NO', 'No access')
369 client, _ = self._setup(AuthHandler)
370 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
371 ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf")
372 self.assertEqual(ret, "OK")
373
374 def test_login_cram_md5_plain_text(self):
375 class AuthHandler(SimpleIMAPHandler):
376 capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
377 def cmd_AUTHENTICATE(self, tag, args):
378 self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
379 'VzdG9uLm1jaS5uZXQ=')
380 r = yield
381 if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
382 b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
383 self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
384 else:
385 self._send_tagged(tag, 'NO', 'No access')
386 client, _ = self._setup(AuthHandler)
387 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
388 ret, _ = client.login_cram_md5("tim", "tanstaaftanstaaf")
389 self.assertEqual(ret, "OK")
390
391 def test_aborted_authentication(self):
392 class MyServer(SimpleIMAPHandler):
393 def cmd_AUTHENTICATE(self, tag, args):
394 self._send_textline('+')
395 self.response = yield
396 if self.response == b'*\r\n':
397 self._send_tagged(
398 tag,
399 'NO',
400 '[AUTHENTICATIONFAILED] aborted')
401 else:
402 self._send_tagged(tag, 'OK', 'MYAUTH successful')
403 client, _ = self._setup(MyServer)
404 with self.assertRaisesRegex(imaplib.IMAP4.error,
405 r'\[AUTHENTICATIONFAILED\] aborted'):
406 client.authenticate('MYAUTH', lambda x: None)
407
408 @mock.patch('imaplib._MAXLINE', 10)
409 def test_linetoolong(self):
410 class TooLongHandler(SimpleIMAPHandler):
411 def handle(self):
412 # send response line longer than the limit set in the next line
413 self.wfile.write(b'* OK ' + 11 * b'x' + b'\r\n')
414 _, server = self._setup(TooLongHandler, connect=False)
415 with self.assertRaisesRegex(imaplib.IMAP4.error,
416 'got more than 10 bytes'):
417 self.imap_class(*server.server_address)
418
419 def test_simple_with_statement(self):
420 _, server = self._setup(SimpleIMAPHandler, connect=False)
421 with self.imap_class(*server.server_address):
422 pass
423
424 def test_with_statement(self):
425 _, server = self._setup(SimpleIMAPHandler, connect=False)
426 with self.imap_class(*server.server_address) as imap:
427 imap.login('user', 'pass')
428 self.assertEqual(server.logged, 'user')
429 self.assertIsNone(server.logged)
430
431 def test_with_statement_logout(self):
432 # It is legal to log out explicitly inside the with block
433 _, server = self._setup(SimpleIMAPHandler, connect=False)
434 with self.imap_class(*server.server_address) as imap:
435 imap.login('user', 'pass')
436 self.assertEqual(server.logged, 'user')
437 imap.logout()
438 self.assertIsNone(server.logged)
439 self.assertIsNone(server.logged)
440
441 # command tests
442
443 def test_login(self):
444 client, _ = self._setup(SimpleIMAPHandler)
445 typ, data = client.login('user', 'pass')
446 self.assertEqual(typ, 'OK')
447 self.assertEqual(data[0], b'LOGIN completed')
448 self.assertEqual(client.state, 'AUTH')
449
450 def test_logout(self):
451 client, _ = self._setup(SimpleIMAPHandler)
452 typ, data = client.login('user', 'pass')
453 self.assertEqual(typ, 'OK')
454 self.assertEqual(data[0], b'LOGIN completed')
455 typ, data = client.logout()
456 self.assertEqual(typ, 'BYE')
457 self.assertEqual(data[0], b'IMAP4ref1 Server logging out')
458 self.assertEqual(client.state, 'LOGOUT')
459
460 def test_lsub(self):
461 class LsubCmd(SimpleIMAPHandler):
462 def cmd_LSUB(self, tag, args):
463 self._send_textline('* LSUB () "." directoryA')
464 return self._send_tagged(tag, 'OK', 'LSUB completed')
465 client, _ = self._setup(LsubCmd)
466 client.login('user', 'pass')
467 typ, data = client.lsub()
468 self.assertEqual(typ, 'OK')
469 self.assertEqual(data[0], b'() "." directoryA')
470
471
472class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase):
473 imap_class = imaplib.IMAP4
474 server_class = socketserver.TCPServer
475
476
477@unittest.skipUnless(ssl, "SSL not available")
478class NewIMAPSSLTests(NewIMAPTestsMixin, unittest.TestCase):
Victor Stinnerde383282017-01-12 11:51:31 +0100479 imap_class = IMAP4_SSL
R David Murrayb079c072016-12-24 21:32:26 -0500480 server_class = SecureTCPServer
481
482 def test_ssl_raises(self):
483 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
484 ssl_context.verify_mode = ssl.CERT_REQUIRED
485 ssl_context.check_hostname = True
486 ssl_context.load_verify_locations(CAFILE)
487
488 with self.assertRaisesRegex(ssl.CertificateError,
489 "hostname '127.0.0.1' doesn't match 'localhost'"):
490 _, server = self._setup(SimpleIMAPHandler)
491 client = self.imap_class(*server.server_address,
492 ssl_context=ssl_context)
493 client.shutdown()
494
495 def test_ssl_verified(self):
496 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
497 ssl_context.verify_mode = ssl.CERT_REQUIRED
498 ssl_context.check_hostname = True
499 ssl_context.load_verify_locations(CAFILE)
500
501 _, server = self._setup(SimpleIMAPHandler)
502 client = self.imap_class("localhost", server.server_address[1],
503 ssl_context=ssl_context)
504 client.shutdown()
505
Antoine Pitroucac9e712014-07-31 18:35:45 -0400506class ThreadedNetworkedTests(unittest.TestCase):
507 server_class = socketserver.TCPServer
508 imap_class = imaplib.IMAP4
R. David Murraye8dc2582009-12-10 02:08:06 +0000509
510 def make_server(self, addr, hdlr):
511
512 class MyServer(self.server_class):
513 def handle_error(self, request, client_address):
514 self.close_request(request)
515 self.server_close()
516 raise
517
Antoine Pitroucac9e712014-07-31 18:35:45 -0400518 if verbose:
519 print("creating server")
R. David Murraye8dc2582009-12-10 02:08:06 +0000520 server = MyServer(addr, hdlr)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000521 self.assertEqual(server.server_address, server.socket.getsockname())
R. David Murraye8dc2582009-12-10 02:08:06 +0000522
523 if verbose:
524 print("server created")
525 print("ADDR =", addr)
526 print("CLASS =", self.server_class)
527 print("HDLR =", server.RequestHandlerClass)
528
529 t = threading.Thread(
530 name='%s serving' % self.server_class,
531 target=server.serve_forever,
532 # Short poll interval to make the test finish quickly.
533 # Time between requests is short enough that we won't wake
534 # up spuriously too many times.
Antoine Pitroucac9e712014-07-31 18:35:45 -0400535 kwargs={'poll_interval': 0.01})
R. David Murraye8dc2582009-12-10 02:08:06 +0000536 t.daemon = True # In case this function raises.
537 t.start()
Antoine Pitroucac9e712014-07-31 18:35:45 -0400538 if verbose:
539 print("server running")
R. David Murraye8dc2582009-12-10 02:08:06 +0000540 return server, t
541
542 def reap_server(self, server, thread):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400543 if verbose:
544 print("waiting for server")
R. David Murraye8dc2582009-12-10 02:08:06 +0000545 server.shutdown()
Victor Stinner73efd622011-01-05 23:01:38 +0000546 server.server_close()
R. David Murraye8dc2582009-12-10 02:08:06 +0000547 thread.join()
Antoine Pitroucac9e712014-07-31 18:35:45 -0400548 if verbose:
549 print("done")
R. David Murraye8dc2582009-12-10 02:08:06 +0000550
551 @contextmanager
552 def reaped_server(self, hdlr):
553 server, thread = self.make_server((support.HOST, 0), hdlr)
554 try:
555 yield server
556 finally:
557 self.reap_server(server, thread)
558
R David Murray774a39f2013-02-19 12:17:31 -0500559 @contextmanager
560 def reaped_pair(self, hdlr):
Charles-François Natali9b116e82013-12-07 20:27:41 +0100561 with self.reaped_server(hdlr) as server:
562 client = self.imap_class(*server.server_address)
563 try:
564 yield server, client
565 finally:
566 client.logout()
R David Murray774a39f2013-02-19 12:17:31 -0500567
R. David Murraye8dc2582009-12-10 02:08:06 +0000568 @reap_threads
569 def test_connect(self):
570 with self.reaped_server(SimpleIMAPHandler) as server:
571 client = self.imap_class(*server.server_address)
572 client.shutdown()
573
574 @reap_threads
R David Murray317f64f2016-01-02 17:18:34 -0500575 def test_bracket_flags(self):
576
577 # This violates RFC 3501, which disallows ']' characters in tag names,
578 # but imaplib has allowed producing such tags forever, other programs
579 # also produce them (eg: OtherInbox's Organizer app as of 20140716),
580 # and Gmail, for example, accepts them and produces them. So we
581 # support them. See issue #21815.
582
583 class BracketFlagHandler(SimpleIMAPHandler):
584
585 def handle(self):
586 self.flags = ['Answered', 'Flagged', 'Deleted', 'Seen', 'Draft']
587 super().handle()
588
589 def cmd_AUTHENTICATE(self, tag, args):
590 self._send_textline('+')
591 self.server.response = yield
592 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
593
594 def cmd_SELECT(self, tag, args):
595 flag_msg = ' \\'.join(self.flags)
596 self._send_line(('* FLAGS (%s)' % flag_msg).encode('ascii'))
597 self._send_line(b'* 2 EXISTS')
598 self._send_line(b'* 0 RECENT')
599 msg = ('* OK [PERMANENTFLAGS %s \\*)] Flags permitted.'
600 % flag_msg)
601 self._send_line(msg.encode('ascii'))
602 self._send_tagged(tag, 'OK', '[READ-WRITE] SELECT completed.')
603
604 def cmd_STORE(self, tag, args):
605 new_flags = args[2].strip('(').strip(')').split()
606 self.flags.extend(new_flags)
607 flags_msg = '(FLAGS (%s))' % ' \\'.join(self.flags)
608 msg = '* %s FETCH %s' % (args[0], flags_msg)
609 self._send_line(msg.encode('ascii'))
610 self._send_tagged(tag, 'OK', 'STORE completed.')
611
612 with self.reaped_pair(BracketFlagHandler) as (server, client):
613 code, data = client.authenticate('MYAUTH', lambda x: b'fake')
614 self.assertEqual(code, 'OK')
615 self.assertEqual(server.response, b'ZmFrZQ==\r\n')
616 client.select('test')
617 typ, [data] = client.store(b'1', "+FLAGS", "[test]")
618 self.assertIn(b'[test]', data)
619 client.select('test')
620 typ, [data] = client.response('PERMANENTFLAGS')
621 self.assertIn(b'[test]', data)
622
623 @reap_threads
R. David Murraye8dc2582009-12-10 02:08:06 +0000624 def test_issue5949(self):
625
626 class EOFHandler(socketserver.StreamRequestHandler):
627 def handle(self):
628 # EOF without sending a complete welcome message.
629 self.wfile.write(b'* OK')
630
631 with self.reaped_server(EOFHandler) as server:
632 self.assertRaises(imaplib.IMAP4.abort,
633 self.imap_class, *server.server_address)
634
635 @reap_threads
636 def test_line_termination(self):
637
638 class BadNewlineHandler(SimpleIMAPHandler):
639
640 def cmd_CAPABILITY(self, tag, args):
641 self._send(b'* CAPABILITY IMAP4rev1 AUTH\n')
R David Murray774a39f2013-02-19 12:17:31 -0500642 self._send_tagged(tag, 'OK', 'CAPABILITY completed')
R. David Murraye8dc2582009-12-10 02:08:06 +0000643
644 with self.reaped_server(BadNewlineHandler) as server:
645 self.assertRaises(imaplib.IMAP4.abort,
646 self.imap_class, *server.server_address)
647
R David Murraya6429db2015-05-10 19:17:23 -0400648 class UTF8Server(SimpleIMAPHandler):
649 capabilities = 'AUTH ENABLE UTF8=ACCEPT'
650
651 def cmd_ENABLE(self, tag, args):
652 self._send_tagged(tag, 'OK', 'ENABLE successful')
653
654 def cmd_AUTHENTICATE(self, tag, args):
655 self._send_textline('+')
656 self.server.response = yield
657 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
658
659 @reap_threads
660 def test_enable_raises_error_if_not_AUTH(self):
661 with self.reaped_pair(self.UTF8Server) as (server, client):
662 self.assertFalse(client.utf8_enabled)
663 self.assertRaises(imaplib.IMAP4.error, client.enable, 'foo')
664 self.assertFalse(client.utf8_enabled)
665
666 # XXX Also need a test that enable after SELECT raises an error.
667
668 @reap_threads
669 def test_enable_raises_error_if_no_capability(self):
670 class NoEnableServer(self.UTF8Server):
671 capabilities = 'AUTH'
672 with self.reaped_pair(NoEnableServer) as (server, client):
673 self.assertRaises(imaplib.IMAP4.error, client.enable, 'foo')
674
675 @reap_threads
676 def test_enable_UTF8_raises_error_if_not_supported(self):
677 class NonUTF8Server(SimpleIMAPHandler):
678 pass
679 with self.assertRaises(imaplib.IMAP4.error):
680 with self.reaped_pair(NonUTF8Server) as (server, client):
681 typ, data = client.login('user', 'pass')
682 self.assertEqual(typ, 'OK')
683 client.enable('UTF8=ACCEPT')
684 pass
685
686 @reap_threads
687 def test_enable_UTF8_True_append(self):
688
689 class UTF8AppendServer(self.UTF8Server):
690 def cmd_APPEND(self, tag, args):
691 self._send_textline('+')
692 self.server.response = yield
693 self._send_tagged(tag, 'OK', 'okay')
694
695 with self.reaped_pair(UTF8AppendServer) as (server, client):
696 self.assertEqual(client._encoding, 'ascii')
697 code, _ = client.authenticate('MYAUTH', lambda x: b'fake')
698 self.assertEqual(code, 'OK')
699 self.assertEqual(server.response,
700 b'ZmFrZQ==\r\n') # b64 encoded 'fake'
701 code, _ = client.enable('UTF8=ACCEPT')
702 self.assertEqual(code, 'OK')
703 self.assertEqual(client._encoding, 'utf-8')
704 msg_string = 'Subject: üñí©öðé'
705 typ, data = client.append(
706 None, None, None, msg_string.encode('utf-8'))
707 self.assertEqual(typ, 'OK')
708 self.assertEqual(
709 server.response,
710 ('UTF8 (%s)\r\n' % msg_string).encode('utf-8')
711 )
712
713 # XXX also need a test that makes sure that the Literal and Untagged_status
714 # regexes uses unicode in UTF8 mode instead of the default ASCII.
715
716 @reap_threads
717 def test_search_disallows_charset_in_utf8_mode(self):
718 with self.reaped_pair(self.UTF8Server) as (server, client):
719 typ, _ = client.authenticate('MYAUTH', lambda x: b'fake')
720 self.assertEqual(typ, 'OK')
721 typ, _ = client.enable('UTF8=ACCEPT')
722 self.assertEqual(typ, 'OK')
723 self.assertTrue(client.utf8_enabled)
724 self.assertRaises(imaplib.IMAP4.error, client.search, 'foo', 'bar')
725
R David Murray774a39f2013-02-19 12:17:31 -0500726 @reap_threads
727 def test_bad_auth_name(self):
728
729 class MyServer(SimpleIMAPHandler):
730
731 def cmd_AUTHENTICATE(self, tag, args):
732 self._send_tagged(tag, 'NO', 'unrecognized authentication '
Antoine Pitroucac9e712014-07-31 18:35:45 -0400733 'type {}'.format(args[0]))
R David Murray774a39f2013-02-19 12:17:31 -0500734
735 with self.reaped_pair(MyServer) as (server, client):
736 with self.assertRaises(imaplib.IMAP4.error):
737 client.authenticate('METHOD', lambda: 1)
738
739 @reap_threads
740 def test_invalid_authentication(self):
741
742 class MyServer(SimpleIMAPHandler):
743
744 def cmd_AUTHENTICATE(self, tag, args):
745 self._send_textline('+')
746 self.response = yield
747 self._send_tagged(tag, 'NO', '[AUTHENTICATIONFAILED] invalid')
748
749 with self.reaped_pair(MyServer) as (server, client):
750 with self.assertRaises(imaplib.IMAP4.error):
751 code, data = client.authenticate('MYAUTH', lambda x: b'fake')
752
753 @reap_threads
754 def test_valid_authentication(self):
755
756 class MyServer(SimpleIMAPHandler):
757
758 def cmd_AUTHENTICATE(self, tag, args):
759 self._send_textline('+')
760 self.server.response = yield
761 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
762
763 with self.reaped_pair(MyServer) as (server, client):
764 code, data = client.authenticate('MYAUTH', lambda x: b'fake')
765 self.assertEqual(code, 'OK')
766 self.assertEqual(server.response,
Antoine Pitroucac9e712014-07-31 18:35:45 -0400767 b'ZmFrZQ==\r\n') # b64 encoded 'fake'
R David Murray774a39f2013-02-19 12:17:31 -0500768
769 with self.reaped_pair(MyServer) as (server, client):
770 code, data = client.authenticate('MYAUTH', lambda x: 'fake')
771 self.assertEqual(code, 'OK')
772 self.assertEqual(server.response,
Antoine Pitroucac9e712014-07-31 18:35:45 -0400773 b'ZmFrZQ==\r\n') # b64 encoded 'fake'
R David Murray774a39f2013-02-19 12:17:31 -0500774
775 @reap_threads
776 def test_login_cram_md5(self):
777
778 class AuthHandler(SimpleIMAPHandler):
779
780 capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
781
782 def cmd_AUTHENTICATE(self, tag, args):
783 self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
Antoine Pitroucac9e712014-07-31 18:35:45 -0400784 'VzdG9uLm1jaS5uZXQ=')
R David Murray774a39f2013-02-19 12:17:31 -0500785 r = yield
Antoine Pitroucac9e712014-07-31 18:35:45 -0400786 if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
787 b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
R David Murray774a39f2013-02-19 12:17:31 -0500788 self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
789 else:
790 self._send_tagged(tag, 'NO', 'No access')
791
792 with self.reaped_pair(AuthHandler) as (server, client):
793 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
794 ret, data = client.login_cram_md5("tim", "tanstaaftanstaaf")
795 self.assertEqual(ret, "OK")
796
797 with self.reaped_pair(AuthHandler) as (server, client):
798 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
799 ret, data = client.login_cram_md5("tim", b"tanstaaftanstaaf")
800 self.assertEqual(ret, "OK")
R. David Murraye8dc2582009-12-10 02:08:06 +0000801
802
Robert Collins5ccc18f2015-07-31 08:59:02 +1200803 @reap_threads
804 def test_aborted_authentication(self):
805
806 class MyServer(SimpleIMAPHandler):
807
808 def cmd_AUTHENTICATE(self, tag, args):
809 self._send_textline('+')
810 self.response = yield
811
812 if self.response == b'*\r\n':
813 self._send_tagged(tag, 'NO', '[AUTHENTICATIONFAILED] aborted')
814 else:
815 self._send_tagged(tag, 'OK', 'MYAUTH successful')
816
817 with self.reaped_pair(MyServer) as (server, client):
818 with self.assertRaises(imaplib.IMAP4.error):
819 code, data = client.authenticate('MYAUTH', lambda x: None)
820
Robert Collins78378e82015-07-31 09:01:38 +1200821
Georg Brandlca580f42013-10-27 06:52:14 +0100822 def test_linetoolong(self):
823 class TooLongHandler(SimpleIMAPHandler):
824 def handle(self):
825 # Send a very long response line
Antoine Pitroucac9e712014-07-31 18:35:45 -0400826 self.wfile.write(b'* OK ' + imaplib._MAXLINE * b'x' + b'\r\n')
Georg Brandlca580f42013-10-27 06:52:14 +0100827
828 with self.reaped_server(TooLongHandler) as server:
829 self.assertRaises(imaplib.IMAP4.error,
830 self.imap_class, *server.server_address)
831
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300832 @reap_threads
833 def test_simple_with_statement(self):
834 # simplest call
835 with self.reaped_server(SimpleIMAPHandler) as server:
836 with self.imap_class(*server.server_address):
837 pass
838
839 @reap_threads
840 def test_with_statement(self):
841 with self.reaped_server(SimpleIMAPHandler) as server:
842 with self.imap_class(*server.server_address) as imap:
843 imap.login('user', 'pass')
844 self.assertEqual(server.logged, 'user')
845 self.assertIsNone(server.logged)
846
847 @reap_threads
848 def test_with_statement_logout(self):
849 # what happens if already logout in the block?
850 with self.reaped_server(SimpleIMAPHandler) as server:
851 with self.imap_class(*server.server_address) as imap:
852 imap.login('user', 'pass')
853 self.assertEqual(server.logged, 'user')
854 imap.logout()
855 self.assertIsNone(server.logged)
856 self.assertIsNone(server.logged)
857
Georg Brandlca580f42013-10-27 06:52:14 +0100858
R. David Murraye8dc2582009-12-10 02:08:06 +0000859@unittest.skipUnless(ssl, "SSL not available")
Antoine Pitroucac9e712014-07-31 18:35:45 -0400860class ThreadedNetworkedTestsSSL(ThreadedNetworkedTests):
R. David Murraye8dc2582009-12-10 02:08:06 +0000861 server_class = SecureTCPServer
862 imap_class = IMAP4_SSL
863
Christian Heimes48aae572013-12-02 20:01:29 +0100864 @reap_threads
865 def test_ssl_verified(self):
866 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
867 ssl_context.verify_mode = ssl.CERT_REQUIRED
868 ssl_context.check_hostname = True
869 ssl_context.load_verify_locations(CAFILE)
870
Antoine Pitroucac9e712014-07-31 18:35:45 -0400871 with self.assertRaisesRegex(
872 ssl.CertificateError,
873 "hostname '127.0.0.1' doesn't match 'localhost'"):
Christian Heimes48aae572013-12-02 20:01:29 +0100874 with self.reaped_server(SimpleIMAPHandler) as server:
875 client = self.imap_class(*server.server_address,
876 ssl_context=ssl_context)
877 client.shutdown()
878
879 with self.reaped_server(SimpleIMAPHandler) as server:
880 client = self.imap_class("localhost", server.server_address[1],
881 ssl_context=ssl_context)
882 client.shutdown()
883
R. David Murraye8dc2582009-12-10 02:08:06 +0000884
Antoine Pitroucac9e712014-07-31 18:35:45 -0400885@unittest.skipUnless(
886 support.is_resource_enabled('network'), 'network resource disabled')
Antoine Pitroub1436f12010-11-09 22:55:55 +0000887class RemoteIMAPTest(unittest.TestCase):
888 host = 'cyrus.andrew.cmu.edu'
889 port = 143
890 username = 'anonymous'
891 password = 'pass'
892 imap_class = imaplib.IMAP4
R. David Murraye8dc2582009-12-10 02:08:06 +0000893
Antoine Pitroub1436f12010-11-09 22:55:55 +0000894 def setUp(self):
895 with transient_internet(self.host):
896 self.server = self.imap_class(self.host, self.port)
897
898 def tearDown(self):
899 if self.server is not None:
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100900 with transient_internet(self.host):
901 self.server.logout()
Antoine Pitroub1436f12010-11-09 22:55:55 +0000902
903 def test_logincapa(self):
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100904 with transient_internet(self.host):
905 for cap in self.server.capabilities:
906 self.assertIsInstance(cap, str)
Nick Coghlane6ef4622012-06-17 21:10:21 +1000907 self.assertIn('LOGINDISABLED', self.server.capabilities)
908 self.assertIn('AUTH=ANONYMOUS', self.server.capabilities)
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100909 rs = self.server.login(self.username, self.password)
910 self.assertEqual(rs[0], 'OK')
Antoine Pitroub1436f12010-11-09 22:55:55 +0000911
912 def test_logout(self):
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100913 with transient_internet(self.host):
914 rs = self.server.logout()
915 self.server = None
916 self.assertEqual(rs[0], 'BYE')
Antoine Pitroub1436f12010-11-09 22:55:55 +0000917
918
919@unittest.skipUnless(ssl, "SSL not available")
Antoine Pitroucac9e712014-07-31 18:35:45 -0400920@unittest.skipUnless(
921 support.is_resource_enabled('network'), 'network resource disabled')
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000922class RemoteIMAP_STARTTLSTest(RemoteIMAPTest):
923
924 def setUp(self):
925 super().setUp()
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100926 with transient_internet(self.host):
927 rs = self.server.starttls()
928 self.assertEqual(rs[0], 'OK')
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000929
930 def test_logincapa(self):
Antoine Pitroudbe75192010-11-16 17:55:26 +0000931 for cap in self.server.capabilities:
932 self.assertIsInstance(cap, str)
Nick Coghlane6ef4622012-06-17 21:10:21 +1000933 self.assertNotIn('LOGINDISABLED', self.server.capabilities)
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000934
935
936@unittest.skipUnless(ssl, "SSL not available")
Antoine Pitroub1436f12010-11-09 22:55:55 +0000937class RemoteIMAP_SSLTest(RemoteIMAPTest):
938 port = 993
939 imap_class = IMAP4_SSL
940
Antoine Pitrou08728162011-05-06 18:49:52 +0200941 def setUp(self):
942 pass
943
944 def tearDown(self):
945 pass
946
947 def create_ssl_context(self):
948 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
949 ssl_context.load_cert_chain(CERTFILE)
950 return ssl_context
951
952 def check_logincapa(self, server):
953 try:
954 for cap in server.capabilities:
955 self.assertIsInstance(cap, str)
Nick Coghlane51e25a2012-06-17 21:15:45 +1000956 self.assertNotIn('LOGINDISABLED', server.capabilities)
957 self.assertIn('AUTH=PLAIN', server.capabilities)
Antoine Pitrou08728162011-05-06 18:49:52 +0200958 rs = server.login(self.username, self.password)
959 self.assertEqual(rs[0], 'OK')
960 finally:
961 server.logout()
962
Antoine Pitroub1436f12010-11-09 22:55:55 +0000963 def test_logincapa(self):
Antoine Pitrou08728162011-05-06 18:49:52 +0200964 with transient_internet(self.host):
965 _server = self.imap_class(self.host, self.port)
966 self.check_logincapa(_server)
967
968 def test_logincapa_with_client_certfile(self):
969 with transient_internet(self.host):
Christian Heimes727cc932016-09-11 22:47:02 +0200970 with support.check_warnings(('', DeprecationWarning)):
971 _server = self.imap_class(self.host, self.port,
972 certfile=CERTFILE)
973 self.check_logincapa(_server)
Antoine Pitrou08728162011-05-06 18:49:52 +0200974
975 def test_logincapa_with_client_ssl_context(self):
976 with transient_internet(self.host):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400977 _server = self.imap_class(
978 self.host, self.port, ssl_context=self.create_ssl_context())
Antoine Pitrou08728162011-05-06 18:49:52 +0200979 self.check_logincapa(_server)
980
981 def test_logout(self):
982 with transient_internet(self.host):
983 _server = self.imap_class(self.host, self.port)
984 rs = _server.logout()
985 self.assertEqual(rs[0], 'BYE')
986
987 def test_ssl_context_certfile_exclusive(self):
988 with transient_internet(self.host):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400989 self.assertRaises(
990 ValueError, self.imap_class, self.host, self.port,
991 certfile=CERTFILE, ssl_context=self.create_ssl_context())
Antoine Pitrou08728162011-05-06 18:49:52 +0200992
993 def test_ssl_context_keyfile_exclusive(self):
994 with transient_internet(self.host):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400995 self.assertRaises(
996 ValueError, self.imap_class, self.host, self.port,
997 keyfile=CERTFILE, ssl_context=self.create_ssl_context())
Christian Heimesf6cd9672008-03-26 13:45:42 +0000998
999
1000if __name__ == "__main__":
Ezio Melotti02bf7012013-03-02 14:25:56 +02001001 unittest.main()