blob: 93ba61a1a99e5df571c27eff1aa0c5ee4660f331 [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
R David Murrayb079c072016-12-24 21:32:26 -050013import inspect
Martin v. Löwisea752fb2002-01-05 11:31:49 +000014
Antoine Pitroucac9e712014-07-31 18:35:45 -040015from test.support import (reap_threads, verbose, transient_internet,
Victor Stinner8a39af92017-06-14 22:43:01 +020016 run_with_tz, run_with_locale, cpython_only)
Christian Heimesf6cd9672008-03-26 13:45:42 +000017import unittest
R David Murrayb079c072016-12-24 21:32:26 -050018from unittest import mock
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040019from datetime import datetime, timezone, timedelta
R. David Murraye8dc2582009-12-10 02:08:06 +000020try:
21 import ssl
22except ImportError:
23 ssl = None
24
Antoine Pitroucac9e712014-07-31 18:35:45 -040025CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem")
26CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem")
R. David Murraye8dc2582009-12-10 02:08:06 +000027
Martin v. Löwisea752fb2002-01-05 11:31:49 +000028
Christian Heimesf6cd9672008-03-26 13:45:42 +000029class TestImaplib(unittest.TestCase):
R. David Murraye8dc2582009-12-10 02:08:06 +000030
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000031 def test_Internaldate2tuple(self):
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000032 t0 = calendar.timegm((2000, 1, 1, 0, 0, 0, -1, -1, -1))
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000033 tt = imaplib.Internaldate2tuple(
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000034 b'25 (INTERNALDATE "01-Jan-2000 00:00:00 +0000")')
35 self.assertEqual(time.mktime(tt), t0)
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000036 tt = imaplib.Internaldate2tuple(
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000037 b'25 (INTERNALDATE "01-Jan-2000 11:30:00 +1130")')
38 self.assertEqual(time.mktime(tt), t0)
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000039 tt = imaplib.Internaldate2tuple(
Alexander Belopolsky7dabf162011-01-29 19:49:40 +000040 b'25 (INTERNALDATE "31-Dec-1999 12:30:00 -1130")')
41 self.assertEqual(time.mktime(tt), t0)
Alexander Belopolsky19e0a9e2011-01-29 17:19:08 +000042
Alexander Belopolsky2420d832012-04-29 15:56:49 -040043 @run_with_tz('MST+07MDT,M4.1.0,M10.5.0')
44 def test_Internaldate2tuple_issue10941(self):
45 self.assertNotEqual(imaplib.Internaldate2tuple(
46 b'25 (INTERNALDATE "02-Apr-2000 02:30:00 +0000")'),
Antoine Pitroucac9e712014-07-31 18:35:45 -040047 imaplib.Internaldate2tuple(
48 b'25 (INTERNALDATE "02-Apr-2000 03:30:00 +0000")'))
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040049
50 def timevalues(self):
51 return [2000000000, 2000000000.0, time.localtime(2000000000),
52 (2033, 5, 18, 5, 33, 20, -1, -1, -1),
53 (2033, 5, 18, 5, 33, 20, -1, -1, 1),
Alexander Belopolsky64892132012-06-22 21:10:50 -040054 datetime.fromtimestamp(2000000000,
Antoine Pitroucac9e712014-07-31 18:35:45 -040055 timezone(timedelta(0, 2 * 60 * 60))),
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040056 '"18-May-2033 05:33:20 +0200"']
57
58 @run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
Martin Panter6e0889f2015-11-16 07:21:38 +000059 # DST rules included to work around quirk where the Gnu C library may not
60 # otherwise restore the previous time zone
61 @run_with_tz('STD-1DST,M3.2.0,M11.1.0')
Alexander Belopolsky8141cc72012-06-22 21:03:39 -040062 def test_Time2Internaldate(self):
63 expected = '"18-May-2033 05:33:20 +0200"'
64
65 for t in self.timevalues():
66 internal = imaplib.Time2Internaldate(t)
67 self.assertEqual(internal, expected)
68
69 def test_that_Time2Internaldate_returns_a_result(self):
70 # Without tzset, we can check only that it successfully
71 # produces a result, not the correctness of the result itself,
72 # since the result depends on the timezone the machine is in.
73 for t in self.timevalues():
Christian Heimesf6cd9672008-03-26 13:45:42 +000074 imaplib.Time2Internaldate(t)
75
76
R. David Murraye8dc2582009-12-10 02:08:06 +000077if ssl:
R. David Murraye8dc2582009-12-10 02:08:06 +000078 class SecureTCPServer(socketserver.TCPServer):
79
80 def get_request(self):
81 newsocket, fromaddr = self.socket.accept()
Christian Heimesd0486372016-09-10 23:23:33 +020082 context = ssl.SSLContext()
83 context.load_cert_chain(CERTFILE)
84 connstream = context.wrap_socket(newsocket, server_side=True)
R. David Murraye8dc2582009-12-10 02:08:06 +000085 return connstream, fromaddr
86
87 IMAP4_SSL = imaplib.IMAP4_SSL
88
89else:
90
91 class SecureTCPServer:
92 pass
93
94 IMAP4_SSL = None
95
96
97class SimpleIMAPHandler(socketserver.StreamRequestHandler):
R. David Murraye8dc2582009-12-10 02:08:06 +000098 timeout = 1
R David Murray774a39f2013-02-19 12:17:31 -050099 continuation = None
100 capabilities = ''
R. David Murraye8dc2582009-12-10 02:08:06 +0000101
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300102 def setup(self):
103 super().setup()
104 self.server.logged = None
105
R. David Murraye8dc2582009-12-10 02:08:06 +0000106 def _send(self, message):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400107 if verbose:
108 print("SENT: %r" % message.strip())
R. David Murraye8dc2582009-12-10 02:08:06 +0000109 self.wfile.write(message)
110
R David Murray774a39f2013-02-19 12:17:31 -0500111 def _send_line(self, message):
112 self._send(message + b'\r\n')
113
114 def _send_textline(self, message):
115 self._send_line(message.encode('ASCII'))
116
117 def _send_tagged(self, tag, code, message):
118 self._send_textline(' '.join((tag, code, message)))
119
R. David Murraye8dc2582009-12-10 02:08:06 +0000120 def handle(self):
121 # Send a welcome message.
R David Murray774a39f2013-02-19 12:17:31 -0500122 self._send_textline('* OK IMAP4rev1')
R. David Murraye8dc2582009-12-10 02:08:06 +0000123 while 1:
124 # Gather up input until we receive a line terminator or we timeout.
125 # Accumulate read(1) because it's simpler to handle the differences
126 # between naked sockets and SSL sockets.
127 line = b''
128 while 1:
129 try:
130 part = self.rfile.read(1)
131 if part == b'':
132 # Naked sockets return empty strings..
133 return
134 line += part
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200135 except OSError:
Andrew Svetlov737fb892012-12-18 21:14:22 +0200136 # ..but SSLSockets raise exceptions.
R. David Murraye8dc2582009-12-10 02:08:06 +0000137 return
138 if line.endswith(b'\r\n'):
139 break
140
Antoine Pitroucac9e712014-07-31 18:35:45 -0400141 if verbose:
142 print('GOT: %r' % line.strip())
R David Murray774a39f2013-02-19 12:17:31 -0500143 if self.continuation:
144 try:
145 self.continuation.send(line)
146 except StopIteration:
147 self.continuation = None
148 continue
149 splitline = line.decode('ASCII').split()
150 tag = splitline[0]
151 cmd = splitline[1]
R. David Murraye8dc2582009-12-10 02:08:06 +0000152 args = splitline[2:]
153
Antoine Pitroucac9e712014-07-31 18:35:45 -0400154 if hasattr(self, 'cmd_' + cmd):
155 continuation = getattr(self, 'cmd_' + cmd)(tag, args)
R David Murray774a39f2013-02-19 12:17:31 -0500156 if continuation:
157 self.continuation = continuation
158 next(continuation)
R. David Murraye8dc2582009-12-10 02:08:06 +0000159 else:
R David Murray774a39f2013-02-19 12:17:31 -0500160 self._send_tagged(tag, 'BAD', cmd + ' unknown')
R. David Murraye8dc2582009-12-10 02:08:06 +0000161
162 def cmd_CAPABILITY(self, tag, args):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400163 caps = ('IMAP4rev1 ' + self.capabilities
164 if self.capabilities
165 else 'IMAP4rev1')
R David Murray774a39f2013-02-19 12:17:31 -0500166 self._send_textline('* CAPABILITY ' + caps)
167 self._send_tagged(tag, 'OK', 'CAPABILITY completed')
168
169 def cmd_LOGOUT(self, tag, args):
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300170 self.server.logged = None
R David Murray774a39f2013-02-19 12:17:31 -0500171 self._send_textline('* BYE IMAP4ref1 Server logging out')
172 self._send_tagged(tag, 'OK', 'LOGOUT completed')
R. David Murraye8dc2582009-12-10 02:08:06 +0000173
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300174 def cmd_LOGIN(self, tag, args):
175 self.server.logged = args[0]
176 self._send_tagged(tag, 'OK', 'LOGIN completed')
177
R. David Murraye8dc2582009-12-10 02:08:06 +0000178
R David Murrayb079c072016-12-24 21:32:26 -0500179class NewIMAPTestsMixin():
180 client = None
181
182 def _setup(self, imap_handler, connect=True):
183 """
184 Sets up imap_handler for tests. imap_handler should inherit from either:
185 - SimpleIMAPHandler - for testing IMAP commands,
186 - socketserver.StreamRequestHandler - if raw access to stream is needed.
187 Returns (client, server).
188 """
189 class TestTCPServer(self.server_class):
190 def handle_error(self, request, client_address):
191 """
192 End request and raise the error if one occurs.
193 """
194 self.close_request(request)
195 self.server_close()
196 raise
197
198 self.addCleanup(self._cleanup)
199 self.server = self.server_class((support.HOST, 0), imap_handler)
200 self.thread = threading.Thread(
201 name=self._testMethodName+'-server',
202 target=self.server.serve_forever,
203 # Short poll interval to make the test finish quickly.
204 # Time between requests is short enough that we won't wake
205 # up spuriously too many times.
206 kwargs={'poll_interval': 0.01})
207 self.thread.daemon = True # In case this function raises.
208 self.thread.start()
209
210 if connect:
211 self.client = self.imap_class(*self.server.server_address)
212
213 return self.client, self.server
214
215 def _cleanup(self):
216 """
217 Cleans up the test server. This method should not be called manually,
218 it is added to the cleanup queue in the _setup method already.
219 """
220 # if logout was called already we'd raise an exception trying to
221 # shutdown the client once again
222 if self.client is not None and self.client.state != 'LOGOUT':
223 self.client.shutdown()
224 # cleanup the server
225 self.server.shutdown()
226 self.server.server_close()
227 self.thread.join(3.0)
228
229 def test_EOF_without_complete_welcome_message(self):
230 # http://bugs.python.org/issue5949
231 class EOFHandler(socketserver.StreamRequestHandler):
232 def handle(self):
233 self.wfile.write(b'* OK')
234 _, server = self._setup(EOFHandler, connect=False)
235 self.assertRaises(imaplib.IMAP4.abort, self.imap_class,
236 *server.server_address)
237
238 def test_line_termination(self):
239 class BadNewlineHandler(SimpleIMAPHandler):
240 def cmd_CAPABILITY(self, tag, args):
241 self._send(b'* CAPABILITY IMAP4rev1 AUTH\n')
242 self._send_tagged(tag, 'OK', 'CAPABILITY completed')
243 _, server = self._setup(BadNewlineHandler, connect=False)
244 self.assertRaises(imaplib.IMAP4.abort, self.imap_class,
245 *server.server_address)
246
247 def test_enable_raises_error_if_not_AUTH(self):
248 class EnableHandler(SimpleIMAPHandler):
249 capabilities = 'AUTH ENABLE UTF8=ACCEPT'
250 client, _ = self._setup(EnableHandler)
251 self.assertFalse(client.utf8_enabled)
252 with self.assertRaisesRegex(imaplib.IMAP4.error, 'ENABLE.*NONAUTH'):
253 client.enable('foo')
254 self.assertFalse(client.utf8_enabled)
255
256 def test_enable_raises_error_if_no_capability(self):
257 client, _ = self._setup(SimpleIMAPHandler)
258 with self.assertRaisesRegex(imaplib.IMAP4.error,
259 'does not support ENABLE'):
260 client.enable('foo')
261
262 def test_enable_UTF8_raises_error_if_not_supported(self):
263 client, _ = self._setup(SimpleIMAPHandler)
264 typ, data = client.login('user', 'pass')
265 self.assertEqual(typ, 'OK')
266 with self.assertRaisesRegex(imaplib.IMAP4.error,
267 'does not support ENABLE'):
268 client.enable('UTF8=ACCEPT')
269
270 def test_enable_UTF8_True_append(self):
271 class UTF8AppendServer(SimpleIMAPHandler):
272 capabilities = 'ENABLE UTF8=ACCEPT'
273 def cmd_ENABLE(self, tag, args):
274 self._send_tagged(tag, 'OK', 'ENABLE successful')
275 def cmd_AUTHENTICATE(self, tag, args):
276 self._send_textline('+')
277 self.server.response = yield
278 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
279 def cmd_APPEND(self, tag, args):
280 self._send_textline('+')
281 self.server.response = yield
282 self._send_tagged(tag, 'OK', 'okay')
283 client, server = self._setup(UTF8AppendServer)
284 self.assertEqual(client._encoding, 'ascii')
285 code, _ = client.authenticate('MYAUTH', lambda x: b'fake')
286 self.assertEqual(code, 'OK')
287 self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'
288 code, _ = client.enable('UTF8=ACCEPT')
289 self.assertEqual(code, 'OK')
290 self.assertEqual(client._encoding, 'utf-8')
291 msg_string = 'Subject: üñí©öðé'
292 typ, data = client.append(None, None, None, msg_string.encode('utf-8'))
293 self.assertEqual(typ, 'OK')
294 self.assertEqual(server.response,
295 ('UTF8 (%s)\r\n' % msg_string).encode('utf-8'))
296
297 def test_search_disallows_charset_in_utf8_mode(self):
298 class UTF8Server(SimpleIMAPHandler):
299 capabilities = 'AUTH ENABLE UTF8=ACCEPT'
300 def cmd_ENABLE(self, tag, args):
301 self._send_tagged(tag, 'OK', 'ENABLE successful')
302 def cmd_AUTHENTICATE(self, tag, args):
303 self._send_textline('+')
304 self.server.response = yield
305 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
306 client, _ = self._setup(UTF8Server)
307 typ, _ = client.authenticate('MYAUTH', lambda x: b'fake')
308 self.assertEqual(typ, 'OK')
309 typ, _ = client.enable('UTF8=ACCEPT')
310 self.assertEqual(typ, 'OK')
311 self.assertTrue(client.utf8_enabled)
312 with self.assertRaisesRegex(imaplib.IMAP4.error, 'charset.*UTF8'):
313 client.search('foo', 'bar')
314
315 def test_bad_auth_name(self):
316 class MyServer(SimpleIMAPHandler):
317 def cmd_AUTHENTICATE(self, tag, args):
318 self._send_tagged(tag, 'NO',
319 'unrecognized authentication type {}'.format(args[0]))
320 client, _ = self._setup(MyServer)
321 with self.assertRaisesRegex(imaplib.IMAP4.error,
322 'unrecognized authentication type METHOD'):
323 client.authenticate('METHOD', lambda: 1)
324
325 def test_invalid_authentication(self):
326 class MyServer(SimpleIMAPHandler):
327 def cmd_AUTHENTICATE(self, tag, args):
328 self._send_textline('+')
329 self.response = yield
330 self._send_tagged(tag, 'NO', '[AUTHENTICATIONFAILED] invalid')
331 client, _ = self._setup(MyServer)
332 with self.assertRaisesRegex(imaplib.IMAP4.error,
333 r'\[AUTHENTICATIONFAILED\] invalid'):
334 client.authenticate('MYAUTH', lambda x: b'fake')
335
336 def test_valid_authentication_bytes(self):
337 class MyServer(SimpleIMAPHandler):
338 def cmd_AUTHENTICATE(self, tag, args):
339 self._send_textline('+')
340 self.server.response = yield
341 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
342 client, server = self._setup(MyServer)
343 code, _ = client.authenticate('MYAUTH', lambda x: b'fake')
344 self.assertEqual(code, 'OK')
345 self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'
346
347 def test_valid_authentication_plain_text(self):
348 class MyServer(SimpleIMAPHandler):
349 def cmd_AUTHENTICATE(self, tag, args):
350 self._send_textline('+')
351 self.server.response = yield
352 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
353 client, server = self._setup(MyServer)
354 code, _ = client.authenticate('MYAUTH', lambda x: 'fake')
355 self.assertEqual(code, 'OK')
356 self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'
357
358 def test_login_cram_md5_bytes(self):
359 class AuthHandler(SimpleIMAPHandler):
360 capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
361 def cmd_AUTHENTICATE(self, tag, args):
362 self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
363 'VzdG9uLm1jaS5uZXQ=')
364 r = yield
365 if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
366 b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
367 self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
368 else:
369 self._send_tagged(tag, 'NO', 'No access')
370 client, _ = self._setup(AuthHandler)
371 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
372 ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf")
373 self.assertEqual(ret, "OK")
374
375 def test_login_cram_md5_plain_text(self):
376 class AuthHandler(SimpleIMAPHandler):
377 capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
378 def cmd_AUTHENTICATE(self, tag, args):
379 self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
380 'VzdG9uLm1jaS5uZXQ=')
381 r = yield
382 if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
383 b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
384 self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
385 else:
386 self._send_tagged(tag, 'NO', 'No access')
387 client, _ = self._setup(AuthHandler)
388 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
389 ret, _ = client.login_cram_md5("tim", "tanstaaftanstaaf")
390 self.assertEqual(ret, "OK")
391
392 def test_aborted_authentication(self):
393 class MyServer(SimpleIMAPHandler):
394 def cmd_AUTHENTICATE(self, tag, args):
395 self._send_textline('+')
396 self.response = yield
397 if self.response == b'*\r\n':
398 self._send_tagged(
399 tag,
400 'NO',
401 '[AUTHENTICATIONFAILED] aborted')
402 else:
403 self._send_tagged(tag, 'OK', 'MYAUTH successful')
404 client, _ = self._setup(MyServer)
405 with self.assertRaisesRegex(imaplib.IMAP4.error,
406 r'\[AUTHENTICATIONFAILED\] aborted'):
407 client.authenticate('MYAUTH', lambda x: None)
408
409 @mock.patch('imaplib._MAXLINE', 10)
410 def test_linetoolong(self):
411 class TooLongHandler(SimpleIMAPHandler):
412 def handle(self):
413 # send response line longer than the limit set in the next line
414 self.wfile.write(b'* OK ' + 11 * b'x' + b'\r\n')
415 _, server = self._setup(TooLongHandler, connect=False)
416 with self.assertRaisesRegex(imaplib.IMAP4.error,
417 'got more than 10 bytes'):
418 self.imap_class(*server.server_address)
419
420 def test_simple_with_statement(self):
421 _, server = self._setup(SimpleIMAPHandler, connect=False)
422 with self.imap_class(*server.server_address):
423 pass
424
425 def test_with_statement(self):
426 _, server = self._setup(SimpleIMAPHandler, connect=False)
427 with self.imap_class(*server.server_address) as imap:
428 imap.login('user', 'pass')
429 self.assertEqual(server.logged, 'user')
430 self.assertIsNone(server.logged)
431
432 def test_with_statement_logout(self):
433 # It is legal to log out explicitly inside the with block
434 _, server = self._setup(SimpleIMAPHandler, connect=False)
435 with self.imap_class(*server.server_address) as imap:
436 imap.login('user', 'pass')
437 self.assertEqual(server.logged, 'user')
438 imap.logout()
439 self.assertIsNone(server.logged)
440 self.assertIsNone(server.logged)
441
442 # command tests
443
444 def test_login(self):
445 client, _ = self._setup(SimpleIMAPHandler)
446 typ, data = client.login('user', 'pass')
447 self.assertEqual(typ, 'OK')
448 self.assertEqual(data[0], b'LOGIN completed')
449 self.assertEqual(client.state, 'AUTH')
450
451 def test_logout(self):
452 client, _ = self._setup(SimpleIMAPHandler)
453 typ, data = client.login('user', 'pass')
454 self.assertEqual(typ, 'OK')
455 self.assertEqual(data[0], b'LOGIN completed')
456 typ, data = client.logout()
457 self.assertEqual(typ, 'BYE')
458 self.assertEqual(data[0], b'IMAP4ref1 Server logging out')
459 self.assertEqual(client.state, 'LOGOUT')
460
461 def test_lsub(self):
462 class LsubCmd(SimpleIMAPHandler):
463 def cmd_LSUB(self, tag, args):
464 self._send_textline('* LSUB () "." directoryA')
465 return self._send_tagged(tag, 'OK', 'LSUB completed')
466 client, _ = self._setup(LsubCmd)
467 client.login('user', 'pass')
468 typ, data = client.lsub()
469 self.assertEqual(typ, 'OK')
470 self.assertEqual(data[0], b'() "." directoryA')
471
472
473class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase):
474 imap_class = imaplib.IMAP4
475 server_class = socketserver.TCPServer
476
477
478@unittest.skipUnless(ssl, "SSL not available")
479class NewIMAPSSLTests(NewIMAPTestsMixin, unittest.TestCase):
Victor Stinnerde383282017-01-12 11:51:31 +0100480 imap_class = IMAP4_SSL
R David Murrayb079c072016-12-24 21:32:26 -0500481 server_class = SecureTCPServer
482
483 def test_ssl_raises(self):
484 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
485 ssl_context.verify_mode = ssl.CERT_REQUIRED
486 ssl_context.check_hostname = True
487 ssl_context.load_verify_locations(CAFILE)
488
489 with self.assertRaisesRegex(ssl.CertificateError,
490 "hostname '127.0.0.1' doesn't match 'localhost'"):
491 _, server = self._setup(SimpleIMAPHandler)
492 client = self.imap_class(*server.server_address,
493 ssl_context=ssl_context)
494 client.shutdown()
495
496 def test_ssl_verified(self):
497 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
498 ssl_context.verify_mode = ssl.CERT_REQUIRED
499 ssl_context.check_hostname = True
500 ssl_context.load_verify_locations(CAFILE)
501
502 _, server = self._setup(SimpleIMAPHandler)
503 client = self.imap_class("localhost", server.server_address[1],
504 ssl_context=ssl_context)
505 client.shutdown()
506
Victor Stinner8a39af92017-06-14 22:43:01 +0200507 # Mock the private method _connect(), so mark the test as specific
508 # to CPython stdlib
509 @cpython_only
510 def test_certfile_arg_warn(self):
511 with support.check_warnings(('', DeprecationWarning)):
512 with mock.patch.object(self.imap_class, 'open'):
513 with mock.patch.object(self.imap_class, '_connect'):
514 self.imap_class('localhost', 143, certfile=CERTFILE)
515
Antoine Pitroucac9e712014-07-31 18:35:45 -0400516class ThreadedNetworkedTests(unittest.TestCase):
517 server_class = socketserver.TCPServer
518 imap_class = imaplib.IMAP4
R. David Murraye8dc2582009-12-10 02:08:06 +0000519
520 def make_server(self, addr, hdlr):
521
522 class MyServer(self.server_class):
523 def handle_error(self, request, client_address):
524 self.close_request(request)
525 self.server_close()
526 raise
527
Antoine Pitroucac9e712014-07-31 18:35:45 -0400528 if verbose:
529 print("creating server")
R. David Murraye8dc2582009-12-10 02:08:06 +0000530 server = MyServer(addr, hdlr)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000531 self.assertEqual(server.server_address, server.socket.getsockname())
R. David Murraye8dc2582009-12-10 02:08:06 +0000532
533 if verbose:
534 print("server created")
535 print("ADDR =", addr)
536 print("CLASS =", self.server_class)
537 print("HDLR =", server.RequestHandlerClass)
538
539 t = threading.Thread(
540 name='%s serving' % self.server_class,
541 target=server.serve_forever,
542 # Short poll interval to make the test finish quickly.
543 # Time between requests is short enough that we won't wake
544 # up spuriously too many times.
Antoine Pitroucac9e712014-07-31 18:35:45 -0400545 kwargs={'poll_interval': 0.01})
R. David Murraye8dc2582009-12-10 02:08:06 +0000546 t.daemon = True # In case this function raises.
547 t.start()
Antoine Pitroucac9e712014-07-31 18:35:45 -0400548 if verbose:
549 print("server running")
R. David Murraye8dc2582009-12-10 02:08:06 +0000550 return server, t
551
552 def reap_server(self, server, thread):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400553 if verbose:
554 print("waiting for server")
R. David Murraye8dc2582009-12-10 02:08:06 +0000555 server.shutdown()
Victor Stinner73efd622011-01-05 23:01:38 +0000556 server.server_close()
R. David Murraye8dc2582009-12-10 02:08:06 +0000557 thread.join()
Antoine Pitroucac9e712014-07-31 18:35:45 -0400558 if verbose:
559 print("done")
R. David Murraye8dc2582009-12-10 02:08:06 +0000560
561 @contextmanager
562 def reaped_server(self, hdlr):
563 server, thread = self.make_server((support.HOST, 0), hdlr)
564 try:
565 yield server
566 finally:
567 self.reap_server(server, thread)
568
R David Murray774a39f2013-02-19 12:17:31 -0500569 @contextmanager
570 def reaped_pair(self, hdlr):
Charles-François Natali9b116e82013-12-07 20:27:41 +0100571 with self.reaped_server(hdlr) as server:
572 client = self.imap_class(*server.server_address)
573 try:
574 yield server, client
575 finally:
576 client.logout()
R David Murray774a39f2013-02-19 12:17:31 -0500577
R. David Murraye8dc2582009-12-10 02:08:06 +0000578 @reap_threads
579 def test_connect(self):
580 with self.reaped_server(SimpleIMAPHandler) as server:
581 client = self.imap_class(*server.server_address)
582 client.shutdown()
583
584 @reap_threads
R David Murray317f64f2016-01-02 17:18:34 -0500585 def test_bracket_flags(self):
586
587 # This violates RFC 3501, which disallows ']' characters in tag names,
588 # but imaplib has allowed producing such tags forever, other programs
589 # also produce them (eg: OtherInbox's Organizer app as of 20140716),
590 # and Gmail, for example, accepts them and produces them. So we
591 # support them. See issue #21815.
592
593 class BracketFlagHandler(SimpleIMAPHandler):
594
595 def handle(self):
596 self.flags = ['Answered', 'Flagged', 'Deleted', 'Seen', 'Draft']
597 super().handle()
598
599 def cmd_AUTHENTICATE(self, tag, args):
600 self._send_textline('+')
601 self.server.response = yield
602 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
603
604 def cmd_SELECT(self, tag, args):
605 flag_msg = ' \\'.join(self.flags)
606 self._send_line(('* FLAGS (%s)' % flag_msg).encode('ascii'))
607 self._send_line(b'* 2 EXISTS')
608 self._send_line(b'* 0 RECENT')
609 msg = ('* OK [PERMANENTFLAGS %s \\*)] Flags permitted.'
610 % flag_msg)
611 self._send_line(msg.encode('ascii'))
612 self._send_tagged(tag, 'OK', '[READ-WRITE] SELECT completed.')
613
614 def cmd_STORE(self, tag, args):
615 new_flags = args[2].strip('(').strip(')').split()
616 self.flags.extend(new_flags)
617 flags_msg = '(FLAGS (%s))' % ' \\'.join(self.flags)
618 msg = '* %s FETCH %s' % (args[0], flags_msg)
619 self._send_line(msg.encode('ascii'))
620 self._send_tagged(tag, 'OK', 'STORE completed.')
621
622 with self.reaped_pair(BracketFlagHandler) as (server, client):
623 code, data = client.authenticate('MYAUTH', lambda x: b'fake')
624 self.assertEqual(code, 'OK')
625 self.assertEqual(server.response, b'ZmFrZQ==\r\n')
626 client.select('test')
627 typ, [data] = client.store(b'1', "+FLAGS", "[test]")
628 self.assertIn(b'[test]', data)
629 client.select('test')
630 typ, [data] = client.response('PERMANENTFLAGS')
631 self.assertIn(b'[test]', data)
632
633 @reap_threads
R. David Murraye8dc2582009-12-10 02:08:06 +0000634 def test_issue5949(self):
635
636 class EOFHandler(socketserver.StreamRequestHandler):
637 def handle(self):
638 # EOF without sending a complete welcome message.
639 self.wfile.write(b'* OK')
640
641 with self.reaped_server(EOFHandler) as server:
642 self.assertRaises(imaplib.IMAP4.abort,
643 self.imap_class, *server.server_address)
644
645 @reap_threads
646 def test_line_termination(self):
647
648 class BadNewlineHandler(SimpleIMAPHandler):
649
650 def cmd_CAPABILITY(self, tag, args):
651 self._send(b'* CAPABILITY IMAP4rev1 AUTH\n')
R David Murray774a39f2013-02-19 12:17:31 -0500652 self._send_tagged(tag, 'OK', 'CAPABILITY completed')
R. David Murraye8dc2582009-12-10 02:08:06 +0000653
654 with self.reaped_server(BadNewlineHandler) as server:
655 self.assertRaises(imaplib.IMAP4.abort,
656 self.imap_class, *server.server_address)
657
R David Murraya6429db2015-05-10 19:17:23 -0400658 class UTF8Server(SimpleIMAPHandler):
659 capabilities = 'AUTH ENABLE UTF8=ACCEPT'
660
661 def cmd_ENABLE(self, tag, args):
662 self._send_tagged(tag, 'OK', 'ENABLE successful')
663
664 def cmd_AUTHENTICATE(self, tag, args):
665 self._send_textline('+')
666 self.server.response = yield
667 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
668
669 @reap_threads
670 def test_enable_raises_error_if_not_AUTH(self):
671 with self.reaped_pair(self.UTF8Server) as (server, client):
672 self.assertFalse(client.utf8_enabled)
673 self.assertRaises(imaplib.IMAP4.error, client.enable, 'foo')
674 self.assertFalse(client.utf8_enabled)
675
676 # XXX Also need a test that enable after SELECT raises an error.
677
678 @reap_threads
679 def test_enable_raises_error_if_no_capability(self):
680 class NoEnableServer(self.UTF8Server):
681 capabilities = 'AUTH'
682 with self.reaped_pair(NoEnableServer) as (server, client):
683 self.assertRaises(imaplib.IMAP4.error, client.enable, 'foo')
684
685 @reap_threads
686 def test_enable_UTF8_raises_error_if_not_supported(self):
687 class NonUTF8Server(SimpleIMAPHandler):
688 pass
689 with self.assertRaises(imaplib.IMAP4.error):
690 with self.reaped_pair(NonUTF8Server) as (server, client):
691 typ, data = client.login('user', 'pass')
692 self.assertEqual(typ, 'OK')
693 client.enable('UTF8=ACCEPT')
694 pass
695
696 @reap_threads
697 def test_enable_UTF8_True_append(self):
698
699 class UTF8AppendServer(self.UTF8Server):
700 def cmd_APPEND(self, tag, args):
701 self._send_textline('+')
702 self.server.response = yield
703 self._send_tagged(tag, 'OK', 'okay')
704
705 with self.reaped_pair(UTF8AppendServer) as (server, client):
706 self.assertEqual(client._encoding, 'ascii')
707 code, _ = client.authenticate('MYAUTH', lambda x: b'fake')
708 self.assertEqual(code, 'OK')
709 self.assertEqual(server.response,
710 b'ZmFrZQ==\r\n') # b64 encoded 'fake'
711 code, _ = client.enable('UTF8=ACCEPT')
712 self.assertEqual(code, 'OK')
713 self.assertEqual(client._encoding, 'utf-8')
714 msg_string = 'Subject: üñí©öðé'
715 typ, data = client.append(
716 None, None, None, msg_string.encode('utf-8'))
717 self.assertEqual(typ, 'OK')
718 self.assertEqual(
719 server.response,
720 ('UTF8 (%s)\r\n' % msg_string).encode('utf-8')
721 )
722
723 # XXX also need a test that makes sure that the Literal and Untagged_status
724 # regexes uses unicode in UTF8 mode instead of the default ASCII.
725
726 @reap_threads
727 def test_search_disallows_charset_in_utf8_mode(self):
728 with self.reaped_pair(self.UTF8Server) as (server, client):
729 typ, _ = client.authenticate('MYAUTH', lambda x: b'fake')
730 self.assertEqual(typ, 'OK')
731 typ, _ = client.enable('UTF8=ACCEPT')
732 self.assertEqual(typ, 'OK')
733 self.assertTrue(client.utf8_enabled)
734 self.assertRaises(imaplib.IMAP4.error, client.search, 'foo', 'bar')
735
R David Murray774a39f2013-02-19 12:17:31 -0500736 @reap_threads
737 def test_bad_auth_name(self):
738
739 class MyServer(SimpleIMAPHandler):
740
741 def cmd_AUTHENTICATE(self, tag, args):
742 self._send_tagged(tag, 'NO', 'unrecognized authentication '
Antoine Pitroucac9e712014-07-31 18:35:45 -0400743 'type {}'.format(args[0]))
R David Murray774a39f2013-02-19 12:17:31 -0500744
745 with self.reaped_pair(MyServer) as (server, client):
746 with self.assertRaises(imaplib.IMAP4.error):
747 client.authenticate('METHOD', lambda: 1)
748
749 @reap_threads
750 def test_invalid_authentication(self):
751
752 class MyServer(SimpleIMAPHandler):
753
754 def cmd_AUTHENTICATE(self, tag, args):
755 self._send_textline('+')
756 self.response = yield
757 self._send_tagged(tag, 'NO', '[AUTHENTICATIONFAILED] invalid')
758
759 with self.reaped_pair(MyServer) as (server, client):
760 with self.assertRaises(imaplib.IMAP4.error):
761 code, data = client.authenticate('MYAUTH', lambda x: b'fake')
762
763 @reap_threads
764 def test_valid_authentication(self):
765
766 class MyServer(SimpleIMAPHandler):
767
768 def cmd_AUTHENTICATE(self, tag, args):
769 self._send_textline('+')
770 self.server.response = yield
771 self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
772
773 with self.reaped_pair(MyServer) as (server, client):
774 code, data = client.authenticate('MYAUTH', lambda x: b'fake')
775 self.assertEqual(code, 'OK')
776 self.assertEqual(server.response,
Antoine Pitroucac9e712014-07-31 18:35:45 -0400777 b'ZmFrZQ==\r\n') # b64 encoded 'fake'
R David Murray774a39f2013-02-19 12:17:31 -0500778
779 with self.reaped_pair(MyServer) as (server, client):
780 code, data = client.authenticate('MYAUTH', lambda x: 'fake')
781 self.assertEqual(code, 'OK')
782 self.assertEqual(server.response,
Antoine Pitroucac9e712014-07-31 18:35:45 -0400783 b'ZmFrZQ==\r\n') # b64 encoded 'fake'
R David Murray774a39f2013-02-19 12:17:31 -0500784
785 @reap_threads
786 def test_login_cram_md5(self):
787
788 class AuthHandler(SimpleIMAPHandler):
789
790 capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
791
792 def cmd_AUTHENTICATE(self, tag, args):
793 self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
Antoine Pitroucac9e712014-07-31 18:35:45 -0400794 'VzdG9uLm1jaS5uZXQ=')
R David Murray774a39f2013-02-19 12:17:31 -0500795 r = yield
Antoine Pitroucac9e712014-07-31 18:35:45 -0400796 if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
797 b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
R David Murray774a39f2013-02-19 12:17:31 -0500798 self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
799 else:
800 self._send_tagged(tag, 'NO', 'No access')
801
802 with self.reaped_pair(AuthHandler) as (server, client):
803 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
804 ret, data = client.login_cram_md5("tim", "tanstaaftanstaaf")
805 self.assertEqual(ret, "OK")
806
807 with self.reaped_pair(AuthHandler) as (server, client):
808 self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
809 ret, data = client.login_cram_md5("tim", b"tanstaaftanstaaf")
810 self.assertEqual(ret, "OK")
R. David Murraye8dc2582009-12-10 02:08:06 +0000811
812
Robert Collins5ccc18f2015-07-31 08:59:02 +1200813 @reap_threads
814 def test_aborted_authentication(self):
815
816 class MyServer(SimpleIMAPHandler):
817
818 def cmd_AUTHENTICATE(self, tag, args):
819 self._send_textline('+')
820 self.response = yield
821
822 if self.response == b'*\r\n':
823 self._send_tagged(tag, 'NO', '[AUTHENTICATIONFAILED] aborted')
824 else:
825 self._send_tagged(tag, 'OK', 'MYAUTH successful')
826
827 with self.reaped_pair(MyServer) as (server, client):
828 with self.assertRaises(imaplib.IMAP4.error):
829 code, data = client.authenticate('MYAUTH', lambda x: None)
830
Robert Collins78378e82015-07-31 09:01:38 +1200831
Georg Brandlca580f42013-10-27 06:52:14 +0100832 def test_linetoolong(self):
833 class TooLongHandler(SimpleIMAPHandler):
834 def handle(self):
835 # Send a very long response line
Antoine Pitroucac9e712014-07-31 18:35:45 -0400836 self.wfile.write(b'* OK ' + imaplib._MAXLINE * b'x' + b'\r\n')
Georg Brandlca580f42013-10-27 06:52:14 +0100837
838 with self.reaped_server(TooLongHandler) as server:
839 self.assertRaises(imaplib.IMAP4.error,
840 self.imap_class, *server.server_address)
841
Serhiy Storchaka38684c32014-09-09 19:07:49 +0300842 @reap_threads
843 def test_simple_with_statement(self):
844 # simplest call
845 with self.reaped_server(SimpleIMAPHandler) as server:
846 with self.imap_class(*server.server_address):
847 pass
848
849 @reap_threads
850 def test_with_statement(self):
851 with self.reaped_server(SimpleIMAPHandler) as server:
852 with self.imap_class(*server.server_address) as imap:
853 imap.login('user', 'pass')
854 self.assertEqual(server.logged, 'user')
855 self.assertIsNone(server.logged)
856
857 @reap_threads
858 def test_with_statement_logout(self):
859 # what happens if already logout in the block?
860 with self.reaped_server(SimpleIMAPHandler) as server:
861 with self.imap_class(*server.server_address) as imap:
862 imap.login('user', 'pass')
863 self.assertEqual(server.logged, 'user')
864 imap.logout()
865 self.assertIsNone(server.logged)
866 self.assertIsNone(server.logged)
867
Georg Brandlca580f42013-10-27 06:52:14 +0100868
R. David Murraye8dc2582009-12-10 02:08:06 +0000869@unittest.skipUnless(ssl, "SSL not available")
Antoine Pitroucac9e712014-07-31 18:35:45 -0400870class ThreadedNetworkedTestsSSL(ThreadedNetworkedTests):
R. David Murraye8dc2582009-12-10 02:08:06 +0000871 server_class = SecureTCPServer
872 imap_class = IMAP4_SSL
873
Christian Heimes48aae572013-12-02 20:01:29 +0100874 @reap_threads
875 def test_ssl_verified(self):
876 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
877 ssl_context.verify_mode = ssl.CERT_REQUIRED
878 ssl_context.check_hostname = True
879 ssl_context.load_verify_locations(CAFILE)
880
Antoine Pitroucac9e712014-07-31 18:35:45 -0400881 with self.assertRaisesRegex(
882 ssl.CertificateError,
883 "hostname '127.0.0.1' doesn't match 'localhost'"):
Christian Heimes48aae572013-12-02 20:01:29 +0100884 with self.reaped_server(SimpleIMAPHandler) as server:
885 client = self.imap_class(*server.server_address,
886 ssl_context=ssl_context)
887 client.shutdown()
888
889 with self.reaped_server(SimpleIMAPHandler) as server:
890 client = self.imap_class("localhost", server.server_address[1],
891 ssl_context=ssl_context)
892 client.shutdown()
893
R. David Murraye8dc2582009-12-10 02:08:06 +0000894
Antoine Pitroucac9e712014-07-31 18:35:45 -0400895@unittest.skipUnless(
896 support.is_resource_enabled('network'), 'network resource disabled')
Antoine Pitroub1436f12010-11-09 22:55:55 +0000897class RemoteIMAPTest(unittest.TestCase):
898 host = 'cyrus.andrew.cmu.edu'
899 port = 143
900 username = 'anonymous'
901 password = 'pass'
902 imap_class = imaplib.IMAP4
R. David Murraye8dc2582009-12-10 02:08:06 +0000903
Antoine Pitroub1436f12010-11-09 22:55:55 +0000904 def setUp(self):
905 with transient_internet(self.host):
906 self.server = self.imap_class(self.host, self.port)
907
908 def tearDown(self):
909 if self.server is not None:
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100910 with transient_internet(self.host):
911 self.server.logout()
Antoine Pitroub1436f12010-11-09 22:55:55 +0000912
913 def test_logincapa(self):
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100914 with transient_internet(self.host):
915 for cap in self.server.capabilities:
916 self.assertIsInstance(cap, str)
Nick Coghlane6ef4622012-06-17 21:10:21 +1000917 self.assertIn('LOGINDISABLED', self.server.capabilities)
918 self.assertIn('AUTH=ANONYMOUS', self.server.capabilities)
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100919 rs = self.server.login(self.username, self.password)
920 self.assertEqual(rs[0], 'OK')
Antoine Pitroub1436f12010-11-09 22:55:55 +0000921
922 def test_logout(self):
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100923 with transient_internet(self.host):
924 rs = self.server.logout()
925 self.server = None
926 self.assertEqual(rs[0], 'BYE')
Antoine Pitroub1436f12010-11-09 22:55:55 +0000927
928
929@unittest.skipUnless(ssl, "SSL not available")
Antoine Pitroucac9e712014-07-31 18:35:45 -0400930@unittest.skipUnless(
931 support.is_resource_enabled('network'), 'network resource disabled')
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000932class RemoteIMAP_STARTTLSTest(RemoteIMAPTest):
933
934 def setUp(self):
935 super().setUp()
Antoine Pitrou924cbea2011-03-23 03:10:14 +0100936 with transient_internet(self.host):
937 rs = self.server.starttls()
938 self.assertEqual(rs[0], 'OK')
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000939
940 def test_logincapa(self):
Antoine Pitroudbe75192010-11-16 17:55:26 +0000941 for cap in self.server.capabilities:
942 self.assertIsInstance(cap, str)
Nick Coghlane6ef4622012-06-17 21:10:21 +1000943 self.assertNotIn('LOGINDISABLED', self.server.capabilities)
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000944
945
946@unittest.skipUnless(ssl, "SSL not available")
Antoine Pitroub1436f12010-11-09 22:55:55 +0000947class RemoteIMAP_SSLTest(RemoteIMAPTest):
948 port = 993
949 imap_class = IMAP4_SSL
950
Antoine Pitrou08728162011-05-06 18:49:52 +0200951 def setUp(self):
952 pass
953
954 def tearDown(self):
955 pass
956
957 def create_ssl_context(self):
958 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
959 ssl_context.load_cert_chain(CERTFILE)
960 return ssl_context
961
962 def check_logincapa(self, server):
963 try:
964 for cap in server.capabilities:
965 self.assertIsInstance(cap, str)
Nick Coghlane51e25a2012-06-17 21:15:45 +1000966 self.assertNotIn('LOGINDISABLED', server.capabilities)
967 self.assertIn('AUTH=PLAIN', server.capabilities)
Antoine Pitrou08728162011-05-06 18:49:52 +0200968 rs = server.login(self.username, self.password)
969 self.assertEqual(rs[0], 'OK')
970 finally:
971 server.logout()
972
Antoine Pitroub1436f12010-11-09 22:55:55 +0000973 def test_logincapa(self):
Antoine Pitrou08728162011-05-06 18:49:52 +0200974 with transient_internet(self.host):
975 _server = self.imap_class(self.host, self.port)
976 self.check_logincapa(_server)
977
Antoine Pitrou08728162011-05-06 18:49:52 +0200978 def test_logout(self):
979 with transient_internet(self.host):
980 _server = self.imap_class(self.host, self.port)
981 rs = _server.logout()
982 self.assertEqual(rs[0], 'BYE')
983
984 def test_ssl_context_certfile_exclusive(self):
985 with transient_internet(self.host):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400986 self.assertRaises(
987 ValueError, self.imap_class, self.host, self.port,
988 certfile=CERTFILE, ssl_context=self.create_ssl_context())
Antoine Pitrou08728162011-05-06 18:49:52 +0200989
990 def test_ssl_context_keyfile_exclusive(self):
991 with transient_internet(self.host):
Antoine Pitroucac9e712014-07-31 18:35:45 -0400992 self.assertRaises(
993 ValueError, self.imap_class, self.host, self.port,
994 keyfile=CERTFILE, ssl_context=self.create_ssl_context())
Christian Heimesf6cd9672008-03-26 13:45:42 +0000995
996
997if __name__ == "__main__":
Ezio Melotti02bf7012013-03-02 14:25:56 +0200998 unittest.main()