14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""A POP3 client class. 24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmBased on the J. Myers POP3 draft, Jan. 96 44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm""" 54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Author: David Ascher <david_ascher@brown.edu> 74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# [heavily stealing from nntplib.py] 84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Updated: Piers Lauder <piers@cs.su.oz.au> [Jul '97] 94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# String method conversion and test jig improvements by ESR, February 2001. 104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia <urtubia@mrbook.org> Aug 2003 114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Example (see the test function at the end of this file) 134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Imports 154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport re, socket 174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm__all__ = ["POP3","error_proto"] 194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Exception raised when an error or invalid response is received: 214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_proto(Exception): pass 234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Standard Port 254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmPOP3_PORT = 110 264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# POP SSL PORT 284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmPOP3_SSL_PORT = 995 294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF) 314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCR = '\r' 324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmLF = '\n' 334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCRLF = CR+LF 344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass POP3: 374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """This class supports both the minimal and optional command sets. 394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Arguments can be strings or integers (where appropriate) 404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm (e.g.: retr(1) and retr('1') both work equally well. 414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Minimal Command Set: 434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm USER name user(name) 444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm PASS string pass_(string) 454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm STAT stat() 464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm LIST [msg] list(msg = None) 474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm RETR msg retr(msg) 484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm DELE msg dele(msg) 494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm NOOP noop() 504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm RSET rset() 514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm QUIT quit() 524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Optional Commands (some servers support these): 544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm RPOP name rpop(name) 554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm APOP name digest apop(name, digest) 564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm TOP msg n top(msg, n) 574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm UIDL [msg] uidl(msg = None) 584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Raises one exception: 'error_proto'. 604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Instantiate with: 624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm POP3(hostname, port=110) 634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm NB: the POP protocol locks the mailbox from user 654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm authorization until QUIT, so be sure to get in, suck 664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm the messages, and quit, each time you access the 674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm mailbox. 684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm POP is a line-based protocol, which means large mail 704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm messages consume lots of python cycles reading them 714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line-by-line. 724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm If it's available on your mail server, use IMAP4 744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm instead, it doesn't suffer from the two problems 754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm above. 764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, host, port=POP3_PORT, 804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm timeout=socket._GLOBAL_DEFAULT_TIMEOUT): 814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.host = host 824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.port = port 834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock = socket.create_connection((host, port), timeout) 844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.file = self.sock.makefile('rb') 854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._debugging = 0 864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.welcome = self._getresp() 874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _putline(self, line): 904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._debugging > 1: print '*put*', repr(line) 914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.sendall('%s%s' % (line, CRLF)) 924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: send one command to the server (through _putline()) 954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _putcmd(self, line): 974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._debugging: print '*cmd*', repr(line) 984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._putline(line) 994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: return one line from the server, stripping CRLF. 1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # This is where all the CPU time of this module is consumed. 1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Raise error_proto('-ERR EOF') if the connection is closed. 1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _getline(self): 1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = self.file.readline() 1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._debugging > 1: print '*get*', repr(line) 1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: raise error_proto('-ERR EOF') 1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm octets = len(line) 1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # server can send any combination of CR & LF 1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # however, 'readline()' returns lines ending in LF 1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # so only possibilities are ...LF, ...CRLF, CR...LF 1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[-2:] == CRLF: 1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line[:-2], octets 1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[0] == CR: 1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line[1:-1], octets 1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line[:-1], octets 1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: get a response from the server. 1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Raise 'error_proto' if the response doesn't start with '+'. 1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _getresp(self): 1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp, o = self._getline() 1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._debugging > 1: print '*resp*', repr(resp) 1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm c = resp[:1] 1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if c != '+': 1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto(resp) 1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: get a response plus following text from the server. 1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _getlongresp(self): 1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self._getresp() 1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm list = []; octets = 0 1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line, o = self._getline() 1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while line != '.': 1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[:2] == '..': 1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm o = o-1 1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[1:] 1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm octets = octets + o 1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm list.append(line) 1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line, o = self._getline() 1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp, list, octets 1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: send a command and get the response 1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _shortcmd(self, line): 1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._putcmd(line) 1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._getresp() 1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: send a command and get the response plus following text 1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _longcmd(self, line): 1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._putcmd(line) 1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._getlongresp() 1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # These can be useful: 1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getwelcome(self): 1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.welcome 1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def set_debuglevel(self, level): 1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._debugging = level 1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Here are all the POP commands: 1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def user(self, user): 1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Send user name, return response 1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm (should indicate password required). 1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('USER %s' % user) 1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def pass_(self, pswd): 1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Send password, return response 1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm (response includes message count, mailbox size). 1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm NB: mailbox is locked by server from here to 'quit()' 1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('PASS %s' % pswd) 1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def stat(self): 1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Get mailbox status. 1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Result is tuple of 2 ints (message count, mailbox size) 1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm retval = self._shortcmd('STAT') 1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm rets = retval.split() 1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._debugging: print '*stat*', repr(rets) 2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm numMessages = int(rets[1]) 2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sizeMessages = int(rets[2]) 2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return (numMessages, sizeMessages) 2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def list(self, which=None): 2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Request listing, return result. 2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Result without a message number argument is in form 2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ['response', ['mesg_num octets', ...], octets]. 2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Result when a message number argument is given is a 2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm single response: the "scan listing" for that message. 2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if which is not None: 2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('LIST %s' % which) 2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._longcmd('LIST') 2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def retr(self, which): 2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Retrieve whole message number 'which'. 2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Result is in form ['response', ['line', ...], octets]. 2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._longcmd('RETR %s' % which) 2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def dele(self, which): 2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Delete message number 'which'. 2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Result is 'response'. 2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('DELE %s' % which) 2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def noop(self): 2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Does nothing. 2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm One supposes the response indicates the server is alive. 2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('NOOP') 2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def rset(self): 2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Unmark all messages marked for deletion.""" 2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('RSET') 2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def quit(self): 2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Signoff: commit changes on server, unlock mailbox, close connection.""" 2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self._shortcmd('QUIT') 2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except error_proto, val: 2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = val 2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.file.close() 2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.close() 2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm del self.file, self.sock 2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm #__del__ = quit 2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # optional commands: 2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def rpop(self, user): 2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Not sure what this does.""" 2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('RPOP %s' % user) 2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm timestamp = re.compile(r'\+OK.*(<[^>]+>)') 2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def apop(self, user, secret): 2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Authorisation 2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm - only possible if server has supplied a timestamp in initial greeting. 2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Args: 2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm user - mailbox user; 2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm secret - secret shared between client and server. 2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm NB: mailbox is locked by server from here to 'quit()' 2814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm m = self.timestamp.match(self.welcome) 2834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not m: 2844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto('-ERR APOP not supported by server') 2854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import hashlib 2864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm digest = hashlib.md5(m.group(1)+secret).digest() 2874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm digest = ''.join(map(lambda x:'%02x'%ord(x), digest)) 2884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('APOP %s %s' % (user, digest)) 2894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def top(self, which, howmuch): 2924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Retrieve message header of message number 'which' 2934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm and first 'howmuch' lines of message body. 2944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Result is in form ['response', ['line', ...], octets]. 2964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._longcmd('TOP %s %s' % (which, howmuch)) 2984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def uidl(self, which=None): 3014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Return message digest (unique id) list. 3024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm If 'which', result contains unique id for that message 3044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm in the form 'response mesgnum uid', otherwise result is 3054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm the list ['response', ['mesgnum uid', ...], octets] 3064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 3074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if which is not None: 3084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._shortcmd('UIDL %s' % which) 3094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._longcmd('UIDL') 3104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtry: 3124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import ssl 3134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmexcept ImportError: 3144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm pass 3154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmelse: 3164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm class POP3_SSL(POP3): 3184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """POP3 client class over SSL connection 3194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None) 3214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm hostname - the hostname of the pop3 over ssl server 3234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port - port number 3244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm keyfile - PEM formatted file that countains your private key 3254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm certfile - PEM formatted certificate chain file 3264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm See the methods of the parent class POP3 for more documentation. 3284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 3294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None): 3314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.host = host 3324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.port = port 3334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.keyfile = keyfile 3344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.certfile = certfile 3354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.buffer = "" 3364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm msg = "getaddrinfo returns an empty list" 3374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock = None 3384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): 3394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm af, socktype, proto, canonname, sa = res 3404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 3414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock = socket.socket(af, socktype, proto) 3424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.connect(sa) 3434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except socket.error, msg: 3444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.sock: 3454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.close() 3464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock = None 3474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 3484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 3494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not self.sock: 3504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise socket.error, msg 3514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.file = self.sock.makefile('rb') 3524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile) 3534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._debugging = 0 3544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.welcome = self._getresp() 3554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _fillBuffer(self): 3574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm localbuf = self.sslobj.read() 3584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(localbuf) == 0: 3594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto('-ERR EOF') 3604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.buffer += localbuf 3614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _getline(self): 3634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = "" 3644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm renewline = re.compile(r'.*?\n') 3654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm match = renewline.match(self.buffer) 3664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while not match: 3674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._fillBuffer() 3684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm match = renewline.match(self.buffer) 3694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = match.group(0) 3704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.buffer = renewline.sub('' ,self.buffer, 1) 3714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._debugging > 1: print '*get*', repr(line) 3724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm octets = len(line) 3744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[-2:] == CRLF: 3754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line[:-2], octets 3764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[0] == CR: 3774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line[1:-1], octets 3784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line[:-1], octets 3794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _putline(self, line): 3814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._debugging > 1: print '*put*', repr(line) 3824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line += CRLF 3834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm bytes = len(line) 3844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while bytes > 0: 3854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sent = self.sslobj.write(line) 3864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sent == bytes: 3874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break # avoid copy 3884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[sent:] 3894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm bytes = bytes - sent 3904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def quit(self): 3924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Signoff: commit changes on server, unlock mailbox, close connection.""" 3934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 3944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self._shortcmd('QUIT') 3954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except error_proto, val: 3964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = val 3974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.close() 3984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm del self.sslobj, self.sock 3994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 4004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm __all__.append("POP3_SSL") 4024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmif __name__ == "__main__": 4044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import sys 4054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm a = POP3(sys.argv[1]) 4064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print a.getwelcome() 4074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm a.user(sys.argv[2]) 4084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm a.pass_(sys.argv[3]) 4094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm a.list() 4104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm (numMsgs, totalSize) = a.stat() 4114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for i in range(1, numMsgs + 1): 4124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm (header, msg, octets) = a.retr(i) 4134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print "Message %d:" % i 4144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for line in msg: 4154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print ' ' + line 4164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '-----------------------' 4174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm a.quit() 418