183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""A POP3 client class. 283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehBased on the J. Myers POP3 draft, Jan. 96 483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh""" 583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Author: David Ascher <david_ascher@brown.edu> 783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# [heavily stealing from nntplib.py] 883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Updated: Piers Lauder <piers@cs.su.oz.au> [Jul '97] 983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# String method conversion and test jig improvements by ESR, February 2001. 1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia <urtubia@mrbook.org> Aug 2003 1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Example (see the test function at the end of this file) 1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Imports 1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport re, socket 1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh__all__ = ["POP3","error_proto"] 1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Exception raised when an error or invalid response is received: 2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass error_proto(Exception): pass 2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Standard Port 2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehPOP3_PORT = 110 2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# POP SSL PORT 2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehPOP3_SSL_PORT = 995 2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF) 3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehCR = '\r' 3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehLF = '\n' 3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehCRLF = CR+LF 3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass POP3: 3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """This class supports both the minimal and optional command sets. 3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Arguments can be strings or integers (where appropriate) 4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh (e.g.: retr(1) and retr('1') both work equally well. 4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Minimal Command Set: 4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh USER name user(name) 4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh PASS string pass_(string) 4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh STAT stat() 4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh LIST [msg] list(msg = None) 4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh RETR msg retr(msg) 4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh DELE msg dele(msg) 4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh NOOP noop() 5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh RSET rset() 5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh QUIT quit() 5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Optional Commands (some servers support these): 5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh RPOP name rpop(name) 5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh APOP name digest apop(name, digest) 5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh TOP msg n top(msg, n) 5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh UIDL [msg] uidl(msg = None) 5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Raises one exception: 'error_proto'. 6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Instantiate with: 6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh POP3(hostname, port=110) 6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh NB: the POP protocol locks the mailbox from user 6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh authorization until QUIT, so be sure to get in, suck 6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh the messages, and quit, each time you access the 6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh mailbox. 6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh POP is a line-based protocol, which means large mail 7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh messages consume lots of python cycles reading them 7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line-by-line. 7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh If it's available on your mail server, use IMAP4 7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh instead, it doesn't suffer from the two problems 7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh above. 7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def __init__(self, host, port=POP3_PORT, 8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh timeout=socket._GLOBAL_DEFAULT_TIMEOUT): 8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.host = host 8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.port = port 8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock = socket.create_connection((host, port), timeout) 8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.file = self.sock.makefile('rb') 8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._debugging = 0 8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.welcome = self._getresp() 8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _putline(self, line): 9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self._debugging > 1: print '*put*', repr(line) 9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock.sendall('%s%s' % (line, CRLF)) 9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Internal: send one command to the server (through _putline()) 9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _putcmd(self, line): 9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self._debugging: print '*cmd*', repr(line) 9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._putline(line) 9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Internal: return one line from the server, stripping CRLF. 10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # This is where all the CPU time of this module is consumed. 10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Raise error_proto('-ERR EOF') if the connection is closed. 10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _getline(self): 10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line = self.file.readline() 10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self._debugging > 1: print '*get*', repr(line) 10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if not line: raise error_proto('-ERR EOF') 10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh octets = len(line) 11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # server can send any combination of CR & LF 11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # however, 'readline()' returns lines ending in LF 11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # so only possibilities are ...LF, ...CRLF, CR...LF 11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if line[-2:] == CRLF: 11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return line[:-2], octets 11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if line[0] == CR: 11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return line[1:-1], octets 11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return line[:-1], octets 11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Internal: get a response from the server. 12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Raise 'error_proto' if the response doesn't start with '+'. 12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _getresp(self): 12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh resp, o = self._getline() 12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self._debugging > 1: print '*resp*', repr(resp) 12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh c = resp[:1] 12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if c != '+': 12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh raise error_proto(resp) 12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return resp 13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Internal: get a response plus following text from the server. 13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _getlongresp(self): 13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh resp = self._getresp() 13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh list = []; octets = 0 13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line, o = self._getline() 13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh while line != '.': 13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if line[:2] == '..': 14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh o = o-1 14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line = line[1:] 14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh octets = octets + o 14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh list.append(line) 14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line, o = self._getline() 14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return resp, list, octets 14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Internal: send a command and get the response 14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 15083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _shortcmd(self, line): 15183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._putcmd(line) 15283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._getresp() 15383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 15483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 15583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Internal: send a command and get the response plus following text 15683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 15783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _longcmd(self, line): 15883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._putcmd(line) 15983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._getlongresp() 16083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 16183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 16283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # These can be useful: 16383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 16483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def getwelcome(self): 16583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self.welcome 16683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 16783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 16883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def set_debuglevel(self, level): 16983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._debugging = level 17083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 17183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 17283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # Here are all the POP commands: 17383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 17483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def user(self, user): 17583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Send user name, return response 17683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 17783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh (should indicate password required). 17883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 17983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('USER %s' % user) 18083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 18183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 18283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def pass_(self, pswd): 18383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Send password, return response 18483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 18583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh (response includes message count, mailbox size). 18683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 18783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh NB: mailbox is locked by server from here to 'quit()' 18883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 18983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('PASS %s' % pswd) 19083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 19183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 19283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def stat(self): 19383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Get mailbox status. 19483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 19583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Result is tuple of 2 ints (message count, mailbox size) 19683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 19783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh retval = self._shortcmd('STAT') 19883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh rets = retval.split() 19983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self._debugging: print '*stat*', repr(rets) 20083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh numMessages = int(rets[1]) 20183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh sizeMessages = int(rets[2]) 20283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return (numMessages, sizeMessages) 20383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 20483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 20583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def list(self, which=None): 20683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Request listing, return result. 20783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 20883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Result without a message number argument is in form 20983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh ['response', ['mesg_num octets', ...], octets]. 21083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 21183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Result when a message number argument is given is a 21283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh single response: the "scan listing" for that message. 21383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 21483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if which is not None: 21583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('LIST %s' % which) 21683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._longcmd('LIST') 21783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 21883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 21983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def retr(self, which): 22083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Retrieve whole message number 'which'. 22183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 22283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Result is in form ['response', ['line', ...], octets]. 22383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 22483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._longcmd('RETR %s' % which) 22583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 22683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 22783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def dele(self, which): 22883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Delete message number 'which'. 22983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 23083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Result is 'response'. 23183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 23283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('DELE %s' % which) 23383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 23483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 23583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def noop(self): 23683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Does nothing. 23783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 23883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh One supposes the response indicates the server is alive. 23983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 24083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('NOOP') 24183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 24283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 24383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def rset(self): 24483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Unmark all messages marked for deletion.""" 24583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('RSET') 24683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 24783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 24883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def quit(self): 24983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Signoff: commit changes on server, unlock mailbox, close connection.""" 25083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh try: 25183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh resp = self._shortcmd('QUIT') 25283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh except error_proto, val: 25383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh resp = val 25483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.file.close() 25583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock.close() 25683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh del self.file, self.sock 25783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return resp 25883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 25983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh #__del__ = quit 26083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 26183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 26283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # optional commands: 26383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 26483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def rpop(self, user): 26583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Not sure what this does.""" 26683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('RPOP %s' % user) 26783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 26883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 26983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh timestamp = re.compile(r'\+OK.*(<[^>]+>)') 27083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 27183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def apop(self, user, secret): 27283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Authorisation 27383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 27483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh - only possible if server has supplied a timestamp in initial greeting. 27583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 27683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Args: 27783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh user - mailbox user; 27883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh secret - secret shared between client and server. 27983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 28083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh NB: mailbox is locked by server from here to 'quit()' 28183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 28283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh m = self.timestamp.match(self.welcome) 28383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if not m: 28483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh raise error_proto('-ERR APOP not supported by server') 28583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh import hashlib 28683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh digest = hashlib.md5(m.group(1)+secret).digest() 28783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh digest = ''.join(map(lambda x:'%02x'%ord(x), digest)) 28883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('APOP %s %s' % (user, digest)) 28983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 29083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 29183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def top(self, which, howmuch): 29283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Retrieve message header of message number 'which' 29383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh and first 'howmuch' lines of message body. 29483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 29583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Result is in form ['response', ['line', ...], octets]. 29683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 29783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._longcmd('TOP %s %s' % (which, howmuch)) 29883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 29983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 30083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def uidl(self, which=None): 30183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Return message digest (unique id) list. 30283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 30383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh If 'which', result contains unique id for that message 30483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh in the form 'response mesgnum uid', otherwise result is 30583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh the list ['response', ['mesgnum uid', ...], octets] 30683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 30783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if which is not None: 30883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._shortcmd('UIDL %s' % which) 30983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return self._longcmd('UIDL') 31083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 31183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry: 31283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh import ssl 31383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError: 31483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh pass 31583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehelse: 31683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 31783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh class POP3_SSL(POP3): 31883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """POP3 client class over SSL connection 31983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 32083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None) 32183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 32283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh hostname - the hostname of the pop3 over ssl server 32383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh port - port number 32483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh keyfile - PEM formatted file that countains your private key 32583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh certfile - PEM formatted certificate chain file 32683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 32783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh See the methods of the parent class POP3 for more documentation. 32883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 32983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 33083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None): 33183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.host = host 33283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.port = port 33383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.keyfile = keyfile 33483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.certfile = certfile 33583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.buffer = "" 33683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh msg = "getaddrinfo returns an empty list" 33783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock = None 33883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): 33983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh af, socktype, proto, canonname, sa = res 34083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh try: 34183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock = socket.socket(af, socktype, proto) 34283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock.connect(sa) 34383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh except socket.error, msg: 34483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self.sock: 34583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock.close() 34683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock = None 34783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh continue 34883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh break 34983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if not self.sock: 35083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh raise socket.error, msg 35183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.file = self.sock.makefile('rb') 35283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile) 35383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._debugging = 0 35483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.welcome = self._getresp() 35583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 35683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _fillBuffer(self): 35783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh localbuf = self.sslobj.read() 35883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if len(localbuf) == 0: 35983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh raise error_proto('-ERR EOF') 36083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.buffer += localbuf 36183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 36283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _getline(self): 36383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line = "" 36483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh renewline = re.compile(r'.*?\n') 36583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh match = renewline.match(self.buffer) 36683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh while not match: 36783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._fillBuffer() 36883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh match = renewline.match(self.buffer) 36983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line = match.group(0) 37083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.buffer = renewline.sub('' ,self.buffer, 1) 37183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self._debugging > 1: print '*get*', repr(line) 37283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 37383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh octets = len(line) 37483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if line[-2:] == CRLF: 37583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return line[:-2], octets 37683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if line[0] == CR: 37783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return line[1:-1], octets 37883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return line[:-1], octets 37983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 38083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _putline(self, line): 38183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self._debugging > 1: print '*put*', repr(line) 38283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line += CRLF 38383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh bytes = len(line) 38483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh while bytes > 0: 38583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh sent = self.sslobj.write(line) 38683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if sent == bytes: 38783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh break # avoid copy 38883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line = line[sent:] 38983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh bytes = bytes - sent 39083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 39183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def quit(self): 39283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Signoff: commit changes on server, unlock mailbox, close connection.""" 39383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh try: 39483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh resp = self._shortcmd('QUIT') 39583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh except error_proto, val: 39683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh resp = val 39783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.sock.close() 39883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh del self.sslobj, self.sock 39983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return resp 40083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 40183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh __all__.append("POP3_SSL") 40283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 40383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif __name__ == "__main__": 40483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh import sys 40583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh a = POP3(sys.argv[1]) 40683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh print a.getwelcome() 40783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh a.user(sys.argv[2]) 40883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh a.pass_(sys.argv[3]) 40983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh a.list() 41083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh (numMsgs, totalSize) = a.stat() 41183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh for i in range(1, numMsgs + 1): 41283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh (header, msg, octets) = a.retr(i) 41383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh print "Message %d:" % i 41483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh for line in msg: 41583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh print ' ' + line 41683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh print '-----------------------' 41783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh a.quit() 418