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