--- Lib.orig/imaplib.py 2008-11-30 17:04:44.000000000 +0100 +++ Lib/imaplib.py 2008-11-30 16:46:30.000000000 +0100 @@ -24,6 +24,12 @@ import binascii, os, random, re, socket, 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 @@ 'SETANNOTATION':('AUTH', 'SELECTED'), 'SETQUOTA': ('AUTH', 'SELECTED'), 'SORT': ('SELECTED',), + 'STARTTLS': ('NONAUTH',), 'STATUS': ('AUTH', 'SELECTED'), 'STORE': ('SELECTED',), 'SUBSCRIBE': ('AUTH', 'SELECTED'), @@ -157,6 +164,7 @@ 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. @@ -248,8 +256,14 @@ def shutdown(self): """Close I/O established in "open".""" - self.file.close() - self.sock.close() + if hasattr(self,'file') and self.file: + self.file.close() + if hasattr(self,'sock') and self.sock: + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + if hasattr(self,'_sock') and self._sock: + self._sock.shutdown(socket.SHUT_RDWR) + self._sock.close() def socket(self): @@ -692,6 +706,24 @@ typ, dat = self._simple_command(name, sort_criteria, charset, *search_criteria) return self._untagged_response(typ, dat, name) + def starttls(self, keyfile = None, certfile = None): + name = 'STARTTLS' + 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') + typ, dat = self._simple_command(name) + if typ == 'OK': + self._sock=self.sock + self.sock=ssl.wrap_socket(self._sock, keyfile, certfile) + self.file = self.sock.makefile('rb') + typ, dat = self.capability() + if dat == [None]: + raise self.error('no CAPABILITY response from server') + self.capabilities = tuple(dat[-1].upper().split()) + 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. @@ -1111,11 +1143,7 @@ -try: - import ssl -except ImportError: - pass -else: +if HAVE_SSL: class IMAP4_SSL(IMAP4): """IMAP4 client class over SSL connection @@ -1145,47 +1173,10 @@ """ self.host = host self.port = port - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((host, port)) - self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile) - - - def read(self, size): - """Read 'size' bytes from remote.""" - # sslobj.read() sometimes returns < size bytes - chunks = [] - read = 0 - while read < size: - data = self.sslobj.read(min(size-read, 16384)) - read += len(data) - chunks.append(data) - - return ''.join(chunks) - - - def readline(self): - """Read line from remote.""" - line = [] - while 1: - char = self.sslobj.read(1) - line.append(char) - if char == "\n": return ''.join(line) - - - def send(self, data): - """Send data to remote.""" - bytes = len(data) - while bytes > 0: - sent = self.sslobj.write(data) - if sent == bytes: - break # avoid copy - data = data[sent:] - bytes = bytes - sent - - - def shutdown(self): - """Close I/O established in "open".""" - self.sock.close() + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._sock.connect((host, port)) + self.sock = ssl.wrap_socket(self._sock, self.keyfile, self.certfile) + self.file=self.sock.makefile('rb') def socket(self): @@ -1193,7 +1184,7 @@ socket = .socket() """ - return self.sock + return self._sock def ssl(self): @@ -1201,7 +1192,7 @@ ssl = ssl.wrap_socket(.socket) """ - return self.sslobj + return self.sock __all__.append("IMAP4_SSL")