xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/poplib.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""A POP3 client class.
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerBased on the J. Myers POP3 draft, Jan. 96
4*cda5da8dSAndroid Build Coastguard Worker"""
5*cda5da8dSAndroid Build Coastguard Worker
6*cda5da8dSAndroid Build Coastguard Worker# Author: David Ascher <[email protected]>
7*cda5da8dSAndroid Build Coastguard Worker#         [heavily stealing from nntplib.py]
8*cda5da8dSAndroid Build Coastguard Worker# Updated: Piers Lauder <[email protected]> [Jul '97]
9*cda5da8dSAndroid Build Coastguard Worker# String method conversion and test jig improvements by ESR, February 2001.
10*cda5da8dSAndroid Build Coastguard Worker# Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia <[email protected]> Aug 2003
11*cda5da8dSAndroid Build Coastguard Worker
12*cda5da8dSAndroid Build Coastguard Worker# Example (see the test function at the end of this file)
13*cda5da8dSAndroid Build Coastguard Worker
14*cda5da8dSAndroid Build Coastguard Worker# Imports
15*cda5da8dSAndroid Build Coastguard Worker
16*cda5da8dSAndroid Build Coastguard Workerimport errno
17*cda5da8dSAndroid Build Coastguard Workerimport re
18*cda5da8dSAndroid Build Coastguard Workerimport socket
19*cda5da8dSAndroid Build Coastguard Workerimport sys
20*cda5da8dSAndroid Build Coastguard Worker
21*cda5da8dSAndroid Build Coastguard Workertry:
22*cda5da8dSAndroid Build Coastguard Worker    import ssl
23*cda5da8dSAndroid Build Coastguard Worker    HAVE_SSL = True
24*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
25*cda5da8dSAndroid Build Coastguard Worker    HAVE_SSL = False
26*cda5da8dSAndroid Build Coastguard Worker
27*cda5da8dSAndroid Build Coastguard Worker__all__ = ["POP3","error_proto"]
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Worker# Exception raised when an error or invalid response is received:
30*cda5da8dSAndroid Build Coastguard Worker
31*cda5da8dSAndroid Build Coastguard Workerclass error_proto(Exception): pass
32*cda5da8dSAndroid Build Coastguard Worker
33*cda5da8dSAndroid Build Coastguard Worker# Standard Port
34*cda5da8dSAndroid Build Coastguard WorkerPOP3_PORT = 110
35*cda5da8dSAndroid Build Coastguard Worker
36*cda5da8dSAndroid Build Coastguard Worker# POP SSL PORT
37*cda5da8dSAndroid Build Coastguard WorkerPOP3_SSL_PORT = 995
38*cda5da8dSAndroid Build Coastguard Worker
39*cda5da8dSAndroid Build Coastguard Worker# Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF)
40*cda5da8dSAndroid Build Coastguard WorkerCR = b'\r'
41*cda5da8dSAndroid Build Coastguard WorkerLF = b'\n'
42*cda5da8dSAndroid Build Coastguard WorkerCRLF = CR+LF
43*cda5da8dSAndroid Build Coastguard Worker
44*cda5da8dSAndroid Build Coastguard Worker# maximal line length when calling readline(). This is to prevent
45*cda5da8dSAndroid Build Coastguard Worker# reading arbitrary length lines. RFC 1939 limits POP3 line length to
46*cda5da8dSAndroid Build Coastguard Worker# 512 characters, including CRLF. We have selected 2048 just to be on
47*cda5da8dSAndroid Build Coastguard Worker# the safe side.
48*cda5da8dSAndroid Build Coastguard Worker_MAXLINE = 2048
49*cda5da8dSAndroid Build Coastguard Worker
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Workerclass POP3:
52*cda5da8dSAndroid Build Coastguard Worker
53*cda5da8dSAndroid Build Coastguard Worker    """This class supports both the minimal and optional command sets.
54*cda5da8dSAndroid Build Coastguard Worker    Arguments can be strings or integers (where appropriate)
55*cda5da8dSAndroid Build Coastguard Worker    (e.g.: retr(1) and retr('1') both work equally well.
56*cda5da8dSAndroid Build Coastguard Worker
57*cda5da8dSAndroid Build Coastguard Worker    Minimal Command Set:
58*cda5da8dSAndroid Build Coastguard Worker            USER name               user(name)
59*cda5da8dSAndroid Build Coastguard Worker            PASS string             pass_(string)
60*cda5da8dSAndroid Build Coastguard Worker            STAT                    stat()
61*cda5da8dSAndroid Build Coastguard Worker            LIST [msg]              list(msg = None)
62*cda5da8dSAndroid Build Coastguard Worker            RETR msg                retr(msg)
63*cda5da8dSAndroid Build Coastguard Worker            DELE msg                dele(msg)
64*cda5da8dSAndroid Build Coastguard Worker            NOOP                    noop()
65*cda5da8dSAndroid Build Coastguard Worker            RSET                    rset()
66*cda5da8dSAndroid Build Coastguard Worker            QUIT                    quit()
67*cda5da8dSAndroid Build Coastguard Worker
68*cda5da8dSAndroid Build Coastguard Worker    Optional Commands (some servers support these):
69*cda5da8dSAndroid Build Coastguard Worker            RPOP name               rpop(name)
70*cda5da8dSAndroid Build Coastguard Worker            APOP name digest        apop(name, digest)
71*cda5da8dSAndroid Build Coastguard Worker            TOP msg n               top(msg, n)
72*cda5da8dSAndroid Build Coastguard Worker            UIDL [msg]              uidl(msg = None)
73*cda5da8dSAndroid Build Coastguard Worker            CAPA                    capa()
74*cda5da8dSAndroid Build Coastguard Worker            STLS                    stls()
75*cda5da8dSAndroid Build Coastguard Worker            UTF8                    utf8()
76*cda5da8dSAndroid Build Coastguard Worker
77*cda5da8dSAndroid Build Coastguard Worker    Raises one exception: 'error_proto'.
78*cda5da8dSAndroid Build Coastguard Worker
79*cda5da8dSAndroid Build Coastguard Worker    Instantiate with:
80*cda5da8dSAndroid Build Coastguard Worker            POP3(hostname, port=110)
81*cda5da8dSAndroid Build Coastguard Worker
82*cda5da8dSAndroid Build Coastguard Worker    NB:     the POP protocol locks the mailbox from user
83*cda5da8dSAndroid Build Coastguard Worker            authorization until QUIT, so be sure to get in, suck
84*cda5da8dSAndroid Build Coastguard Worker            the messages, and quit, each time you access the
85*cda5da8dSAndroid Build Coastguard Worker            mailbox.
86*cda5da8dSAndroid Build Coastguard Worker
87*cda5da8dSAndroid Build Coastguard Worker            POP is a line-based protocol, which means large mail
88*cda5da8dSAndroid Build Coastguard Worker            messages consume lots of python cycles reading them
89*cda5da8dSAndroid Build Coastguard Worker            line-by-line.
90*cda5da8dSAndroid Build Coastguard Worker
91*cda5da8dSAndroid Build Coastguard Worker            If it's available on your mail server, use IMAP4
92*cda5da8dSAndroid Build Coastguard Worker            instead, it doesn't suffer from the two problems
93*cda5da8dSAndroid Build Coastguard Worker            above.
94*cda5da8dSAndroid Build Coastguard Worker    """
95*cda5da8dSAndroid Build Coastguard Worker
96*cda5da8dSAndroid Build Coastguard Worker    encoding = 'UTF-8'
97*cda5da8dSAndroid Build Coastguard Worker
98*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, host, port=POP3_PORT,
99*cda5da8dSAndroid Build Coastguard Worker                 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
100*cda5da8dSAndroid Build Coastguard Worker        self.host = host
101*cda5da8dSAndroid Build Coastguard Worker        self.port = port
102*cda5da8dSAndroid Build Coastguard Worker        self._tls_established = False
103*cda5da8dSAndroid Build Coastguard Worker        sys.audit("poplib.connect", self, host, port)
104*cda5da8dSAndroid Build Coastguard Worker        self.sock = self._create_socket(timeout)
105*cda5da8dSAndroid Build Coastguard Worker        self.file = self.sock.makefile('rb')
106*cda5da8dSAndroid Build Coastguard Worker        self._debugging = 0
107*cda5da8dSAndroid Build Coastguard Worker        self.welcome = self._getresp()
108*cda5da8dSAndroid Build Coastguard Worker
109*cda5da8dSAndroid Build Coastguard Worker    def _create_socket(self, timeout):
110*cda5da8dSAndroid Build Coastguard Worker        if timeout is not None and not timeout:
111*cda5da8dSAndroid Build Coastguard Worker            raise ValueError('Non-blocking socket (timeout=0) is not supported')
112*cda5da8dSAndroid Build Coastguard Worker        return socket.create_connection((self.host, self.port), timeout)
113*cda5da8dSAndroid Build Coastguard Worker
114*cda5da8dSAndroid Build Coastguard Worker    def _putline(self, line):
115*cda5da8dSAndroid Build Coastguard Worker        if self._debugging > 1: print('*put*', repr(line))
116*cda5da8dSAndroid Build Coastguard Worker        sys.audit("poplib.putline", self, line)
117*cda5da8dSAndroid Build Coastguard Worker        self.sock.sendall(line + CRLF)
118*cda5da8dSAndroid Build Coastguard Worker
119*cda5da8dSAndroid Build Coastguard Worker
120*cda5da8dSAndroid Build Coastguard Worker    # Internal: send one command to the server (through _putline())
121*cda5da8dSAndroid Build Coastguard Worker
122*cda5da8dSAndroid Build Coastguard Worker    def _putcmd(self, line):
123*cda5da8dSAndroid Build Coastguard Worker        if self._debugging: print('*cmd*', repr(line))
124*cda5da8dSAndroid Build Coastguard Worker        line = bytes(line, self.encoding)
125*cda5da8dSAndroid Build Coastguard Worker        self._putline(line)
126*cda5da8dSAndroid Build Coastguard Worker
127*cda5da8dSAndroid Build Coastguard Worker
128*cda5da8dSAndroid Build Coastguard Worker    # Internal: return one line from the server, stripping CRLF.
129*cda5da8dSAndroid Build Coastguard Worker    # This is where all the CPU time of this module is consumed.
130*cda5da8dSAndroid Build Coastguard Worker    # Raise error_proto('-ERR EOF') if the connection is closed.
131*cda5da8dSAndroid Build Coastguard Worker
132*cda5da8dSAndroid Build Coastguard Worker    def _getline(self):
133*cda5da8dSAndroid Build Coastguard Worker        line = self.file.readline(_MAXLINE + 1)
134*cda5da8dSAndroid Build Coastguard Worker        if len(line) > _MAXLINE:
135*cda5da8dSAndroid Build Coastguard Worker            raise error_proto('line too long')
136*cda5da8dSAndroid Build Coastguard Worker
137*cda5da8dSAndroid Build Coastguard Worker        if self._debugging > 1: print('*get*', repr(line))
138*cda5da8dSAndroid Build Coastguard Worker        if not line: raise error_proto('-ERR EOF')
139*cda5da8dSAndroid Build Coastguard Worker        octets = len(line)
140*cda5da8dSAndroid Build Coastguard Worker        # server can send any combination of CR & LF
141*cda5da8dSAndroid Build Coastguard Worker        # however, 'readline()' returns lines ending in LF
142*cda5da8dSAndroid Build Coastguard Worker        # so only possibilities are ...LF, ...CRLF, CR...LF
143*cda5da8dSAndroid Build Coastguard Worker        if line[-2:] == CRLF:
144*cda5da8dSAndroid Build Coastguard Worker            return line[:-2], octets
145*cda5da8dSAndroid Build Coastguard Worker        if line[:1] == CR:
146*cda5da8dSAndroid Build Coastguard Worker            return line[1:-1], octets
147*cda5da8dSAndroid Build Coastguard Worker        return line[:-1], octets
148*cda5da8dSAndroid Build Coastguard Worker
149*cda5da8dSAndroid Build Coastguard Worker
150*cda5da8dSAndroid Build Coastguard Worker    # Internal: get a response from the server.
151*cda5da8dSAndroid Build Coastguard Worker    # Raise 'error_proto' if the response doesn't start with '+'.
152*cda5da8dSAndroid Build Coastguard Worker
153*cda5da8dSAndroid Build Coastguard Worker    def _getresp(self):
154*cda5da8dSAndroid Build Coastguard Worker        resp, o = self._getline()
155*cda5da8dSAndroid Build Coastguard Worker        if self._debugging > 1: print('*resp*', repr(resp))
156*cda5da8dSAndroid Build Coastguard Worker        if not resp.startswith(b'+'):
157*cda5da8dSAndroid Build Coastguard Worker            raise error_proto(resp)
158*cda5da8dSAndroid Build Coastguard Worker        return resp
159*cda5da8dSAndroid Build Coastguard Worker
160*cda5da8dSAndroid Build Coastguard Worker
161*cda5da8dSAndroid Build Coastguard Worker    # Internal: get a response plus following text from the server.
162*cda5da8dSAndroid Build Coastguard Worker
163*cda5da8dSAndroid Build Coastguard Worker    def _getlongresp(self):
164*cda5da8dSAndroid Build Coastguard Worker        resp = self._getresp()
165*cda5da8dSAndroid Build Coastguard Worker        list = []; octets = 0
166*cda5da8dSAndroid Build Coastguard Worker        line, o = self._getline()
167*cda5da8dSAndroid Build Coastguard Worker        while line != b'.':
168*cda5da8dSAndroid Build Coastguard Worker            if line.startswith(b'..'):
169*cda5da8dSAndroid Build Coastguard Worker                o = o-1
170*cda5da8dSAndroid Build Coastguard Worker                line = line[1:]
171*cda5da8dSAndroid Build Coastguard Worker            octets = octets + o
172*cda5da8dSAndroid Build Coastguard Worker            list.append(line)
173*cda5da8dSAndroid Build Coastguard Worker            line, o = self._getline()
174*cda5da8dSAndroid Build Coastguard Worker        return resp, list, octets
175*cda5da8dSAndroid Build Coastguard Worker
176*cda5da8dSAndroid Build Coastguard Worker
177*cda5da8dSAndroid Build Coastguard Worker    # Internal: send a command and get the response
178*cda5da8dSAndroid Build Coastguard Worker
179*cda5da8dSAndroid Build Coastguard Worker    def _shortcmd(self, line):
180*cda5da8dSAndroid Build Coastguard Worker        self._putcmd(line)
181*cda5da8dSAndroid Build Coastguard Worker        return self._getresp()
182*cda5da8dSAndroid Build Coastguard Worker
183*cda5da8dSAndroid Build Coastguard Worker
184*cda5da8dSAndroid Build Coastguard Worker    # Internal: send a command and get the response plus following text
185*cda5da8dSAndroid Build Coastguard Worker
186*cda5da8dSAndroid Build Coastguard Worker    def _longcmd(self, line):
187*cda5da8dSAndroid Build Coastguard Worker        self._putcmd(line)
188*cda5da8dSAndroid Build Coastguard Worker        return self._getlongresp()
189*cda5da8dSAndroid Build Coastguard Worker
190*cda5da8dSAndroid Build Coastguard Worker
191*cda5da8dSAndroid Build Coastguard Worker    # These can be useful:
192*cda5da8dSAndroid Build Coastguard Worker
193*cda5da8dSAndroid Build Coastguard Worker    def getwelcome(self):
194*cda5da8dSAndroid Build Coastguard Worker        return self.welcome
195*cda5da8dSAndroid Build Coastguard Worker
196*cda5da8dSAndroid Build Coastguard Worker
197*cda5da8dSAndroid Build Coastguard Worker    def set_debuglevel(self, level):
198*cda5da8dSAndroid Build Coastguard Worker        self._debugging = level
199*cda5da8dSAndroid Build Coastguard Worker
200*cda5da8dSAndroid Build Coastguard Worker
201*cda5da8dSAndroid Build Coastguard Worker    # Here are all the POP commands:
202*cda5da8dSAndroid Build Coastguard Worker
203*cda5da8dSAndroid Build Coastguard Worker    def user(self, user):
204*cda5da8dSAndroid Build Coastguard Worker        """Send user name, return response
205*cda5da8dSAndroid Build Coastguard Worker
206*cda5da8dSAndroid Build Coastguard Worker        (should indicate password required).
207*cda5da8dSAndroid Build Coastguard Worker        """
208*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('USER %s' % user)
209*cda5da8dSAndroid Build Coastguard Worker
210*cda5da8dSAndroid Build Coastguard Worker
211*cda5da8dSAndroid Build Coastguard Worker    def pass_(self, pswd):
212*cda5da8dSAndroid Build Coastguard Worker        """Send password, return response
213*cda5da8dSAndroid Build Coastguard Worker
214*cda5da8dSAndroid Build Coastguard Worker        (response includes message count, mailbox size).
215*cda5da8dSAndroid Build Coastguard Worker
216*cda5da8dSAndroid Build Coastguard Worker        NB: mailbox is locked by server from here to 'quit()'
217*cda5da8dSAndroid Build Coastguard Worker        """
218*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('PASS %s' % pswd)
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Worker
221*cda5da8dSAndroid Build Coastguard Worker    def stat(self):
222*cda5da8dSAndroid Build Coastguard Worker        """Get mailbox status.
223*cda5da8dSAndroid Build Coastguard Worker
224*cda5da8dSAndroid Build Coastguard Worker        Result is tuple of 2 ints (message count, mailbox size)
225*cda5da8dSAndroid Build Coastguard Worker        """
226*cda5da8dSAndroid Build Coastguard Worker        retval = self._shortcmd('STAT')
227*cda5da8dSAndroid Build Coastguard Worker        rets = retval.split()
228*cda5da8dSAndroid Build Coastguard Worker        if self._debugging: print('*stat*', repr(rets))
229*cda5da8dSAndroid Build Coastguard Worker        numMessages = int(rets[1])
230*cda5da8dSAndroid Build Coastguard Worker        sizeMessages = int(rets[2])
231*cda5da8dSAndroid Build Coastguard Worker        return (numMessages, sizeMessages)
232*cda5da8dSAndroid Build Coastguard Worker
233*cda5da8dSAndroid Build Coastguard Worker
234*cda5da8dSAndroid Build Coastguard Worker    def list(self, which=None):
235*cda5da8dSAndroid Build Coastguard Worker        """Request listing, return result.
236*cda5da8dSAndroid Build Coastguard Worker
237*cda5da8dSAndroid Build Coastguard Worker        Result without a message number argument is in form
238*cda5da8dSAndroid Build Coastguard Worker        ['response', ['mesg_num octets', ...], octets].
239*cda5da8dSAndroid Build Coastguard Worker
240*cda5da8dSAndroid Build Coastguard Worker        Result when a message number argument is given is a
241*cda5da8dSAndroid Build Coastguard Worker        single response: the "scan listing" for that message.
242*cda5da8dSAndroid Build Coastguard Worker        """
243*cda5da8dSAndroid Build Coastguard Worker        if which is not None:
244*cda5da8dSAndroid Build Coastguard Worker            return self._shortcmd('LIST %s' % which)
245*cda5da8dSAndroid Build Coastguard Worker        return self._longcmd('LIST')
246*cda5da8dSAndroid Build Coastguard Worker
247*cda5da8dSAndroid Build Coastguard Worker
248*cda5da8dSAndroid Build Coastguard Worker    def retr(self, which):
249*cda5da8dSAndroid Build Coastguard Worker        """Retrieve whole message number 'which'.
250*cda5da8dSAndroid Build Coastguard Worker
251*cda5da8dSAndroid Build Coastguard Worker        Result is in form ['response', ['line', ...], octets].
252*cda5da8dSAndroid Build Coastguard Worker        """
253*cda5da8dSAndroid Build Coastguard Worker        return self._longcmd('RETR %s' % which)
254*cda5da8dSAndroid Build Coastguard Worker
255*cda5da8dSAndroid Build Coastguard Worker
256*cda5da8dSAndroid Build Coastguard Worker    def dele(self, which):
257*cda5da8dSAndroid Build Coastguard Worker        """Delete message number 'which'.
258*cda5da8dSAndroid Build Coastguard Worker
259*cda5da8dSAndroid Build Coastguard Worker        Result is 'response'.
260*cda5da8dSAndroid Build Coastguard Worker        """
261*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('DELE %s' % which)
262*cda5da8dSAndroid Build Coastguard Worker
263*cda5da8dSAndroid Build Coastguard Worker
264*cda5da8dSAndroid Build Coastguard Worker    def noop(self):
265*cda5da8dSAndroid Build Coastguard Worker        """Does nothing.
266*cda5da8dSAndroid Build Coastguard Worker
267*cda5da8dSAndroid Build Coastguard Worker        One supposes the response indicates the server is alive.
268*cda5da8dSAndroid Build Coastguard Worker        """
269*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('NOOP')
270*cda5da8dSAndroid Build Coastguard Worker
271*cda5da8dSAndroid Build Coastguard Worker
272*cda5da8dSAndroid Build Coastguard Worker    def rset(self):
273*cda5da8dSAndroid Build Coastguard Worker        """Unmark all messages marked for deletion."""
274*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('RSET')
275*cda5da8dSAndroid Build Coastguard Worker
276*cda5da8dSAndroid Build Coastguard Worker
277*cda5da8dSAndroid Build Coastguard Worker    def quit(self):
278*cda5da8dSAndroid Build Coastguard Worker        """Signoff: commit changes on server, unlock mailbox, close connection."""
279*cda5da8dSAndroid Build Coastguard Worker        resp = self._shortcmd('QUIT')
280*cda5da8dSAndroid Build Coastguard Worker        self.close()
281*cda5da8dSAndroid Build Coastguard Worker        return resp
282*cda5da8dSAndroid Build Coastguard Worker
283*cda5da8dSAndroid Build Coastguard Worker    def close(self):
284*cda5da8dSAndroid Build Coastguard Worker        """Close the connection without assuming anything about it."""
285*cda5da8dSAndroid Build Coastguard Worker        try:
286*cda5da8dSAndroid Build Coastguard Worker            file = self.file
287*cda5da8dSAndroid Build Coastguard Worker            self.file = None
288*cda5da8dSAndroid Build Coastguard Worker            if file is not None:
289*cda5da8dSAndroid Build Coastguard Worker                file.close()
290*cda5da8dSAndroid Build Coastguard Worker        finally:
291*cda5da8dSAndroid Build Coastguard Worker            sock = self.sock
292*cda5da8dSAndroid Build Coastguard Worker            self.sock = None
293*cda5da8dSAndroid Build Coastguard Worker            if sock is not None:
294*cda5da8dSAndroid Build Coastguard Worker                try:
295*cda5da8dSAndroid Build Coastguard Worker                    sock.shutdown(socket.SHUT_RDWR)
296*cda5da8dSAndroid Build Coastguard Worker                except OSError as exc:
297*cda5da8dSAndroid Build Coastguard Worker                    # The server might already have closed the connection.
298*cda5da8dSAndroid Build Coastguard Worker                    # On Windows, this may result in WSAEINVAL (error 10022):
299*cda5da8dSAndroid Build Coastguard Worker                    # An invalid operation was attempted.
300*cda5da8dSAndroid Build Coastguard Worker                    if (exc.errno != errno.ENOTCONN
301*cda5da8dSAndroid Build Coastguard Worker                       and getattr(exc, 'winerror', 0) != 10022):
302*cda5da8dSAndroid Build Coastguard Worker                        raise
303*cda5da8dSAndroid Build Coastguard Worker                finally:
304*cda5da8dSAndroid Build Coastguard Worker                    sock.close()
305*cda5da8dSAndroid Build Coastguard Worker
306*cda5da8dSAndroid Build Coastguard Worker    #__del__ = quit
307*cda5da8dSAndroid Build Coastguard Worker
308*cda5da8dSAndroid Build Coastguard Worker
309*cda5da8dSAndroid Build Coastguard Worker    # optional commands:
310*cda5da8dSAndroid Build Coastguard Worker
311*cda5da8dSAndroid Build Coastguard Worker    def rpop(self, user):
312*cda5da8dSAndroid Build Coastguard Worker        """Not sure what this does."""
313*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('RPOP %s' % user)
314*cda5da8dSAndroid Build Coastguard Worker
315*cda5da8dSAndroid Build Coastguard Worker
316*cda5da8dSAndroid Build Coastguard Worker    timestamp = re.compile(br'\+OK.[^<]*(<.*>)')
317*cda5da8dSAndroid Build Coastguard Worker
318*cda5da8dSAndroid Build Coastguard Worker    def apop(self, user, password):
319*cda5da8dSAndroid Build Coastguard Worker        """Authorisation
320*cda5da8dSAndroid Build Coastguard Worker
321*cda5da8dSAndroid Build Coastguard Worker        - only possible if server has supplied a timestamp in initial greeting.
322*cda5da8dSAndroid Build Coastguard Worker
323*cda5da8dSAndroid Build Coastguard Worker        Args:
324*cda5da8dSAndroid Build Coastguard Worker                user     - mailbox user;
325*cda5da8dSAndroid Build Coastguard Worker                password - mailbox password.
326*cda5da8dSAndroid Build Coastguard Worker
327*cda5da8dSAndroid Build Coastguard Worker        NB: mailbox is locked by server from here to 'quit()'
328*cda5da8dSAndroid Build Coastguard Worker        """
329*cda5da8dSAndroid Build Coastguard Worker        secret = bytes(password, self.encoding)
330*cda5da8dSAndroid Build Coastguard Worker        m = self.timestamp.match(self.welcome)
331*cda5da8dSAndroid Build Coastguard Worker        if not m:
332*cda5da8dSAndroid Build Coastguard Worker            raise error_proto('-ERR APOP not supported by server')
333*cda5da8dSAndroid Build Coastguard Worker        import hashlib
334*cda5da8dSAndroid Build Coastguard Worker        digest = m.group(1)+secret
335*cda5da8dSAndroid Build Coastguard Worker        digest = hashlib.md5(digest).hexdigest()
336*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('APOP %s %s' % (user, digest))
337*cda5da8dSAndroid Build Coastguard Worker
338*cda5da8dSAndroid Build Coastguard Worker
339*cda5da8dSAndroid Build Coastguard Worker    def top(self, which, howmuch):
340*cda5da8dSAndroid Build Coastguard Worker        """Retrieve message header of message number 'which'
341*cda5da8dSAndroid Build Coastguard Worker        and first 'howmuch' lines of message body.
342*cda5da8dSAndroid Build Coastguard Worker
343*cda5da8dSAndroid Build Coastguard Worker        Result is in form ['response', ['line', ...], octets].
344*cda5da8dSAndroid Build Coastguard Worker        """
345*cda5da8dSAndroid Build Coastguard Worker        return self._longcmd('TOP %s %s' % (which, howmuch))
346*cda5da8dSAndroid Build Coastguard Worker
347*cda5da8dSAndroid Build Coastguard Worker
348*cda5da8dSAndroid Build Coastguard Worker    def uidl(self, which=None):
349*cda5da8dSAndroid Build Coastguard Worker        """Return message digest (unique id) list.
350*cda5da8dSAndroid Build Coastguard Worker
351*cda5da8dSAndroid Build Coastguard Worker        If 'which', result contains unique id for that message
352*cda5da8dSAndroid Build Coastguard Worker        in the form 'response mesgnum uid', otherwise result is
353*cda5da8dSAndroid Build Coastguard Worker        the list ['response', ['mesgnum uid', ...], octets]
354*cda5da8dSAndroid Build Coastguard Worker        """
355*cda5da8dSAndroid Build Coastguard Worker        if which is not None:
356*cda5da8dSAndroid Build Coastguard Worker            return self._shortcmd('UIDL %s' % which)
357*cda5da8dSAndroid Build Coastguard Worker        return self._longcmd('UIDL')
358*cda5da8dSAndroid Build Coastguard Worker
359*cda5da8dSAndroid Build Coastguard Worker
360*cda5da8dSAndroid Build Coastguard Worker    def utf8(self):
361*cda5da8dSAndroid Build Coastguard Worker        """Try to enter UTF-8 mode (see RFC 6856). Returns server response.
362*cda5da8dSAndroid Build Coastguard Worker        """
363*cda5da8dSAndroid Build Coastguard Worker        return self._shortcmd('UTF8')
364*cda5da8dSAndroid Build Coastguard Worker
365*cda5da8dSAndroid Build Coastguard Worker
366*cda5da8dSAndroid Build Coastguard Worker    def capa(self):
367*cda5da8dSAndroid Build Coastguard Worker        """Return server capabilities (RFC 2449) as a dictionary
368*cda5da8dSAndroid Build Coastguard Worker        >>> c=poplib.POP3('localhost')
369*cda5da8dSAndroid Build Coastguard Worker        >>> c.capa()
370*cda5da8dSAndroid Build Coastguard Worker        {'IMPLEMENTATION': ['Cyrus', 'POP3', 'server', 'v2.2.12'],
371*cda5da8dSAndroid Build Coastguard Worker         'TOP': [], 'LOGIN-DELAY': ['0'], 'AUTH-RESP-CODE': [],
372*cda5da8dSAndroid Build Coastguard Worker         'EXPIRE': ['NEVER'], 'USER': [], 'STLS': [], 'PIPELINING': [],
373*cda5da8dSAndroid Build Coastguard Worker         'UIDL': [], 'RESP-CODES': []}
374*cda5da8dSAndroid Build Coastguard Worker        >>>
375*cda5da8dSAndroid Build Coastguard Worker
376*cda5da8dSAndroid Build Coastguard Worker        Really, according to RFC 2449, the cyrus folks should avoid
377*cda5da8dSAndroid Build Coastguard Worker        having the implementation split into multiple arguments...
378*cda5da8dSAndroid Build Coastguard Worker        """
379*cda5da8dSAndroid Build Coastguard Worker        def _parsecap(line):
380*cda5da8dSAndroid Build Coastguard Worker            lst = line.decode('ascii').split()
381*cda5da8dSAndroid Build Coastguard Worker            return lst[0], lst[1:]
382*cda5da8dSAndroid Build Coastguard Worker
383*cda5da8dSAndroid Build Coastguard Worker        caps = {}
384*cda5da8dSAndroid Build Coastguard Worker        try:
385*cda5da8dSAndroid Build Coastguard Worker            resp = self._longcmd('CAPA')
386*cda5da8dSAndroid Build Coastguard Worker            rawcaps = resp[1]
387*cda5da8dSAndroid Build Coastguard Worker            for capline in rawcaps:
388*cda5da8dSAndroid Build Coastguard Worker                capnm, capargs = _parsecap(capline)
389*cda5da8dSAndroid Build Coastguard Worker                caps[capnm] = capargs
390*cda5da8dSAndroid Build Coastguard Worker        except error_proto:
391*cda5da8dSAndroid Build Coastguard Worker            raise error_proto('-ERR CAPA not supported by server')
392*cda5da8dSAndroid Build Coastguard Worker        return caps
393*cda5da8dSAndroid Build Coastguard Worker
394*cda5da8dSAndroid Build Coastguard Worker
395*cda5da8dSAndroid Build Coastguard Worker    def stls(self, context=None):
396*cda5da8dSAndroid Build Coastguard Worker        """Start a TLS session on the active connection as specified in RFC 2595.
397*cda5da8dSAndroid Build Coastguard Worker
398*cda5da8dSAndroid Build Coastguard Worker                context - a ssl.SSLContext
399*cda5da8dSAndroid Build Coastguard Worker        """
400*cda5da8dSAndroid Build Coastguard Worker        if not HAVE_SSL:
401*cda5da8dSAndroid Build Coastguard Worker            raise error_proto('-ERR TLS support missing')
402*cda5da8dSAndroid Build Coastguard Worker        if self._tls_established:
403*cda5da8dSAndroid Build Coastguard Worker            raise error_proto('-ERR TLS session already established')
404*cda5da8dSAndroid Build Coastguard Worker        caps = self.capa()
405*cda5da8dSAndroid Build Coastguard Worker        if not 'STLS' in caps:
406*cda5da8dSAndroid Build Coastguard Worker            raise error_proto('-ERR STLS not supported by server')
407*cda5da8dSAndroid Build Coastguard Worker        if context is None:
408*cda5da8dSAndroid Build Coastguard Worker            context = ssl._create_stdlib_context()
409*cda5da8dSAndroid Build Coastguard Worker        resp = self._shortcmd('STLS')
410*cda5da8dSAndroid Build Coastguard Worker        self.sock = context.wrap_socket(self.sock,
411*cda5da8dSAndroid Build Coastguard Worker                                        server_hostname=self.host)
412*cda5da8dSAndroid Build Coastguard Worker        self.file = self.sock.makefile('rb')
413*cda5da8dSAndroid Build Coastguard Worker        self._tls_established = True
414*cda5da8dSAndroid Build Coastguard Worker        return resp
415*cda5da8dSAndroid Build Coastguard Worker
416*cda5da8dSAndroid Build Coastguard Worker
417*cda5da8dSAndroid Build Coastguard Workerif HAVE_SSL:
418*cda5da8dSAndroid Build Coastguard Worker
419*cda5da8dSAndroid Build Coastguard Worker    class POP3_SSL(POP3):
420*cda5da8dSAndroid Build Coastguard Worker        """POP3 client class over SSL connection
421*cda5da8dSAndroid Build Coastguard Worker
422*cda5da8dSAndroid Build Coastguard Worker        Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None,
423*cda5da8dSAndroid Build Coastguard Worker                                   context=None)
424*cda5da8dSAndroid Build Coastguard Worker
425*cda5da8dSAndroid Build Coastguard Worker               hostname - the hostname of the pop3 over ssl server
426*cda5da8dSAndroid Build Coastguard Worker               port - port number
427*cda5da8dSAndroid Build Coastguard Worker               keyfile - PEM formatted file that contains your private key
428*cda5da8dSAndroid Build Coastguard Worker               certfile - PEM formatted certificate chain file
429*cda5da8dSAndroid Build Coastguard Worker               context - a ssl.SSLContext
430*cda5da8dSAndroid Build Coastguard Worker
431*cda5da8dSAndroid Build Coastguard Worker        See the methods of the parent class POP3 for more documentation.
432*cda5da8dSAndroid Build Coastguard Worker        """
433*cda5da8dSAndroid Build Coastguard Worker
434*cda5da8dSAndroid Build Coastguard Worker        def __init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None,
435*cda5da8dSAndroid Build Coastguard Worker                     timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None):
436*cda5da8dSAndroid Build Coastguard Worker            if context is not None and keyfile is not None:
437*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("context and keyfile arguments are mutually "
438*cda5da8dSAndroid Build Coastguard Worker                                 "exclusive")
439*cda5da8dSAndroid Build Coastguard Worker            if context is not None and certfile is not None:
440*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("context and certfile arguments are mutually "
441*cda5da8dSAndroid Build Coastguard Worker                                 "exclusive")
442*cda5da8dSAndroid Build Coastguard Worker            if keyfile is not None or certfile is not None:
443*cda5da8dSAndroid Build Coastguard Worker                import warnings
444*cda5da8dSAndroid Build Coastguard Worker                warnings.warn("keyfile and certfile are deprecated, use a "
445*cda5da8dSAndroid Build Coastguard Worker                              "custom context instead", DeprecationWarning, 2)
446*cda5da8dSAndroid Build Coastguard Worker            self.keyfile = keyfile
447*cda5da8dSAndroid Build Coastguard Worker            self.certfile = certfile
448*cda5da8dSAndroid Build Coastguard Worker            if context is None:
449*cda5da8dSAndroid Build Coastguard Worker                context = ssl._create_stdlib_context(certfile=certfile,
450*cda5da8dSAndroid Build Coastguard Worker                                                     keyfile=keyfile)
451*cda5da8dSAndroid Build Coastguard Worker            self.context = context
452*cda5da8dSAndroid Build Coastguard Worker            POP3.__init__(self, host, port, timeout)
453*cda5da8dSAndroid Build Coastguard Worker
454*cda5da8dSAndroid Build Coastguard Worker        def _create_socket(self, timeout):
455*cda5da8dSAndroid Build Coastguard Worker            sock = POP3._create_socket(self, timeout)
456*cda5da8dSAndroid Build Coastguard Worker            sock = self.context.wrap_socket(sock,
457*cda5da8dSAndroid Build Coastguard Worker                                            server_hostname=self.host)
458*cda5da8dSAndroid Build Coastguard Worker            return sock
459*cda5da8dSAndroid Build Coastguard Worker
460*cda5da8dSAndroid Build Coastguard Worker        def stls(self, keyfile=None, certfile=None, context=None):
461*cda5da8dSAndroid Build Coastguard Worker            """The method unconditionally raises an exception since the
462*cda5da8dSAndroid Build Coastguard Worker            STLS command doesn't make any sense on an already established
463*cda5da8dSAndroid Build Coastguard Worker            SSL/TLS session.
464*cda5da8dSAndroid Build Coastguard Worker            """
465*cda5da8dSAndroid Build Coastguard Worker            raise error_proto('-ERR TLS session already established')
466*cda5da8dSAndroid Build Coastguard Worker
467*cda5da8dSAndroid Build Coastguard Worker    __all__.append("POP3_SSL")
468*cda5da8dSAndroid Build Coastguard Worker
469*cda5da8dSAndroid Build Coastguard Workerif __name__ == "__main__":
470*cda5da8dSAndroid Build Coastguard Worker    import sys
471*cda5da8dSAndroid Build Coastguard Worker    a = POP3(sys.argv[1])
472*cda5da8dSAndroid Build Coastguard Worker    print(a.getwelcome())
473*cda5da8dSAndroid Build Coastguard Worker    a.user(sys.argv[2])
474*cda5da8dSAndroid Build Coastguard Worker    a.pass_(sys.argv[3])
475*cda5da8dSAndroid Build Coastguard Worker    a.list()
476*cda5da8dSAndroid Build Coastguard Worker    (numMsgs, totalSize) = a.stat()
477*cda5da8dSAndroid Build Coastguard Worker    for i in range(1, numMsgs + 1):
478*cda5da8dSAndroid Build Coastguard Worker        (header, msg, octets) = a.retr(i)
479*cda5da8dSAndroid Build Coastguard Worker        print("Message %d:" % i)
480*cda5da8dSAndroid Build Coastguard Worker        for line in msg:
481*cda5da8dSAndroid Build Coastguard Worker            print('   ' + line)
482*cda5da8dSAndroid Build Coastguard Worker        print('-----------------------')
483*cda5da8dSAndroid Build Coastguard Worker    a.quit()
484