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