aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/imaplib.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/imaplib.py')
-rw-r--r--Lib/imaplib.py80
1 files changed, 64 insertions, 16 deletions
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
index fc1e20a27dc..1022e77c4f6 100644
--- a/Lib/imaplib.py
+++ b/Lib/imaplib.py
@@ -24,6 +24,12 @@ __version__ = "2.58"
import binascii, errno, random, re, socket, subprocess, sys, time
+try:
+ import ssl
+ HAVE_SSL = True
+except ImportError:
+ HAVE_SSL = False
+
__all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple",
"Int2AP", "ParseFlags", "Time2Internaldate"]
@@ -71,6 +77,7 @@ Commands = {
'SETANNOTATION':('AUTH', 'SELECTED'),
'SETQUOTA': ('AUTH', 'SELECTED'),
'SORT': ('SELECTED',),
+ 'STARTTLS': ('NONAUTH',),
'STATUS': ('AUTH', 'SELECTED'),
'STORE': ('SELECTED',),
'SUBSCRIBE': ('AUTH', 'SELECTED'),
@@ -156,11 +163,23 @@ class IMAP4:
self.continuation_response = '' # Last continuation response
self.is_readonly = False # READ-ONLY desired state
self.tagnum = 0
+ self._tls_established = False
# Open socket to server.
self.open(host, port)
+ try:
+ self._connect()
+ except Exception:
+ try:
+ self.shutdown()
+ except socket.error:
+ pass
+ raise
+
+
+ def _connect(self):
# Create unique tag for this session,
# and compile tagged response matcher.
@@ -188,13 +207,7 @@ class IMAP4:
else:
raise self.error(self.welcome)
- typ, dat = self.capability()
- if dat == [None]:
- raise self.error('no CAPABILITY response from server')
- dat = str(dat[-1], "ASCII")
- dat = dat.upper()
- self.capabilities = tuple(dat.split())
-
+ self._get_capabilities()
if __debug__:
if self.debug >= 3:
self._mesg('CAPABILITIES: %r' % (self.capabilities,))
@@ -711,6 +724,30 @@ class IMAP4:
return self._untagged_response(typ, dat, name)
+ def starttls(self, ssl_context=None):
+ name = 'STARTTLS'
+ if not HAVE_SSL:
+ raise self.error('SSL support missing')
+ if self._tls_established:
+ raise self.abort('TLS session already established')
+ if name not in self.capabilities:
+ raise self.abort('TLS not supported by server')
+ # Generate a default SSL context if none was passed.
+ if ssl_context is None:
+ ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ # SSLv2 considered harmful.
+ ssl_context.options |= ssl.OP_NO_SSLv2
+ typ, dat = self._simple_command(name)
+ if typ == 'OK':
+ self.sock = ssl_context.wrap_socket(self.sock)
+ self.file = self.sock.makefile('rb')
+ self._tls_established = True
+ self._get_capabilities()
+ else:
+ raise self.error("Couldn't establish TLS session")
+ return self._untagged_response(typ, dat, name)
+
+
def status(self, mailbox, names):
"""Request named status conditions for mailbox.
@@ -921,6 +958,15 @@ class IMAP4:
return typ, data
+ def _get_capabilities(self):
+ typ, dat = self.capability()
+ if dat == [None]:
+ raise self.error('no CAPABILITY response from server')
+ dat = str(dat[-1], "ASCII")
+ dat = dat.upper()
+ self.capabilities = tuple(dat.split())
+
+
def _get_response(self):
# Read response and store.
@@ -1125,12 +1171,8 @@ class IMAP4:
n -= 1
+if HAVE_SSL:
-try:
- import ssl
-except ImportError:
- pass
-else:
class IMAP4_SSL(IMAP4):
"""IMAP4 client class over SSL connection
@@ -1270,9 +1312,10 @@ Mon2num = {b'Jan': 1, b'Feb': 2, b'Mar': 3, b'Apr': 4, b'May': 5, b'Jun': 6,
b'Jul': 7, b'Aug': 8, b'Sep': 9, b'Oct': 10, b'Nov': 11, b'Dec': 12}
def Internaldate2tuple(resp):
- """Convert IMAP4 INTERNALDATE to UT.
+ """Parse an IMAP4 INTERNALDATE string.
- Returns Python time module tuple.
+ Return corresponding local time. The return value is a
+ time.struct_time tuple or None if the string has wrong format.
"""
mo = InternalDate.match(resp)
@@ -1339,9 +1382,14 @@ def ParseFlags(resp):
def Time2Internaldate(date_time):
- """Convert 'date_time' to IMAP4 INTERNALDATE representation.
+ """Convert date_time to IMAP4 INTERNALDATE representation.
- Return string in form: '"DD-Mmm-YYYY HH:MM:SS +HHMM"'
+ Return string in form: '"DD-Mmm-YYYY HH:MM:SS +HHMM"'. The
+ date_time argument can be a number (int or float) represening
+ seconds since epoch (as returned by time.time()), a 9-tuple
+ representing local time (as returned by time.localtime()), or a
+ double-quoted string. In the last case, it is assumed to already
+ be in the correct format.
"""
if isinstance(date_time, (int, float)):