blob: 1475bdce875d403d1afbab59b445ce541edbf7c8 [file] [log] [blame]
Guido van Rossum03774bb1998-04-09 13:50:55 +00001"""A POP3 client class.
Guido van Rossum484772d1998-04-06 18:27:27 +00002
Guido van Rossum03774bb1998-04-09 13:50:55 +00003Based on the J. Myers POP3 draft, Jan. 96
Guido van Rossum484772d1998-04-06 18:27:27 +00004"""
5
Guido van Rossum98d9fd32000-02-28 15:12:25 +00006# Author: David Ascher <david_ascher@brown.edu>
7# [heavily stealing from nntplib.py]
8# Updated: Piers Lauder <piers@cs.su.oz.au> [Jul '97]
Eric S. Raymond341f9292001-02-09 06:56:56 +00009# String method conversion and test jig improvements by ESR, February 2001.
Martin v. Löwis48440b72003-10-31 12:52:35 +000010# Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia <urtubia@mrbook.org> Aug 2003
Guido van Rossum98d9fd32000-02-28 15:12:25 +000011
Guido van Rossum484772d1998-04-06 18:27:27 +000012# Example (see the test function at the end of this file)
13
Guido van Rossum484772d1998-04-06 18:27:27 +000014# Imports
15
Eric S. Raymond341f9292001-02-09 06:56:56 +000016import re, socket
Guido van Rossum484772d1998-04-06 18:27:27 +000017
Martin v. Löwis48440b72003-10-31 12:52:35 +000018__all__ = ["POP3","error_proto","POP3_SSL"]
Skip Montanaroc62c81e2001-02-12 02:00:42 +000019
Guido van Rossum484772d1998-04-06 18:27:27 +000020# Exception raised when an error or invalid response is received:
Guido van Rossum03774bb1998-04-09 13:50:55 +000021
22class error_proto(Exception): pass
Guido van Rossum484772d1998-04-06 18:27:27 +000023
24# Standard Port
25POP3_PORT = 110
26
Martin v. Löwis48440b72003-10-31 12:52:35 +000027# POP SSL PORT
28POP3_SSL_PORT = 995
29
Guido van Rossum03774bb1998-04-09 13:50:55 +000030# Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF)
31CR = '\r'
32LF = '\n'
33CRLF = CR+LF
Guido van Rossum484772d1998-04-06 18:27:27 +000034
35
36class POP3:
Guido van Rossum03774bb1998-04-09 13:50:55 +000037
Tim Peters2344fae2001-01-15 00:50:52 +000038 """This class supports both the minimal and optional command sets.
39 Arguments can be strings or integers (where appropriate)
40 (e.g.: retr(1) and retr('1') both work equally well.
Guido van Rossum03774bb1998-04-09 13:50:55 +000041
Tim Peters2344fae2001-01-15 00:50:52 +000042 Minimal Command Set:
43 USER name user(name)
44 PASS string pass_(string)
45 STAT stat()
46 LIST [msg] list(msg = None)
47 RETR msg retr(msg)
48 DELE msg dele(msg)
49 NOOP noop()
50 RSET rset()
51 QUIT quit()
Guido van Rossum03774bb1998-04-09 13:50:55 +000052
Tim Peters2344fae2001-01-15 00:50:52 +000053 Optional Commands (some servers support these):
54 RPOP name rpop(name)
55 APOP name digest apop(name, digest)
56 TOP msg n top(msg, n)
57 UIDL [msg] uidl(msg = None)
Guido van Rossum03774bb1998-04-09 13:50:55 +000058
Tim Peters2344fae2001-01-15 00:50:52 +000059 Raises one exception: 'error_proto'.
Guido van Rossum03774bb1998-04-09 13:50:55 +000060
Tim Peters2344fae2001-01-15 00:50:52 +000061 Instantiate with:
62 POP3(hostname, port=110)
Guido van Rossum03774bb1998-04-09 13:50:55 +000063
Tim Peters2344fae2001-01-15 00:50:52 +000064 NB: the POP protocol locks the mailbox from user
65 authorization until QUIT, so be sure to get in, suck
66 the messages, and quit, each time you access the
67 mailbox.
Guido van Rossum03774bb1998-04-09 13:50:55 +000068
Tim Peters2344fae2001-01-15 00:50:52 +000069 POP is a line-based protocol, which means large mail
70 messages consume lots of python cycles reading them
71 line-by-line.
Guido van Rossum03774bb1998-04-09 13:50:55 +000072
Tim Peters2344fae2001-01-15 00:50:52 +000073 If it's available on your mail server, use IMAP4
74 instead, it doesn't suffer from the two problems
75 above.
76 """
Guido van Rossum03774bb1998-04-09 13:50:55 +000077
78
Tim Peters2344fae2001-01-15 00:50:52 +000079 def __init__(self, host, port = POP3_PORT):
Martin v. Löwis4eb59402001-07-26 13:37:33 +000080 self.host = host
81 self.port = port
Martin v. Löwis2ad25692001-07-31 08:40:21 +000082 msg = "getaddrinfo returns an empty list"
Martin v. Löwis322c0d12001-10-07 08:53:32 +000083 self.sock = None
Martin v. Löwis4eb59402001-07-26 13:37:33 +000084 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
85 af, socktype, proto, canonname, sa = res
86 try:
87 self.sock = socket.socket(af, socktype, proto)
88 self.sock.connect(sa)
89 except socket.error, msg:
Martin v. Löwis322c0d12001-10-07 08:53:32 +000090 if self.sock:
91 self.sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +000092 self.sock = None
93 continue
94 break
95 if not self.sock:
96 raise socket.error, msg
97 self.file = self.sock.makefile('rb')
98 self._debugging = 0
99 self.welcome = self._getresp()
Guido van Rossum484772d1998-04-06 18:27:27 +0000100
Guido van Rossum03774bb1998-04-09 13:50:55 +0000101
Tim Peters2344fae2001-01-15 00:50:52 +0000102 def _putline(self, line):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000103 if self._debugging > 1: print '*put*', repr(line)
Martin v. Löwise12454f2002-02-16 23:06:19 +0000104 self.sock.sendall('%s%s' % (line, CRLF))
Guido van Rossum03774bb1998-04-09 13:50:55 +0000105
Guido van Rossum484772d1998-04-06 18:27:27 +0000106
Tim Peters2344fae2001-01-15 00:50:52 +0000107 # Internal: send one command to the server (through _putline())
Guido van Rossum03774bb1998-04-09 13:50:55 +0000108
Tim Peters2344fae2001-01-15 00:50:52 +0000109 def _putcmd(self, line):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000110 if self._debugging: print '*cmd*', repr(line)
Tim Peters2344fae2001-01-15 00:50:52 +0000111 self._putline(line)
Guido van Rossum484772d1998-04-06 18:27:27 +0000112
Guido van Rossum03774bb1998-04-09 13:50:55 +0000113
Tim Peters2344fae2001-01-15 00:50:52 +0000114 # Internal: return one line from the server, stripping CRLF.
115 # This is where all the CPU time of this module is consumed.
116 # Raise error_proto('-ERR EOF') if the connection is closed.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000117
Tim Peters2344fae2001-01-15 00:50:52 +0000118 def _getline(self):
119 line = self.file.readline()
Walter Dörwald70a6b492004-02-12 17:35:32 +0000120 if self._debugging > 1: print '*get*', repr(line)
Tim Peters2344fae2001-01-15 00:50:52 +0000121 if not line: raise error_proto('-ERR EOF')
122 octets = len(line)
123 # server can send any combination of CR & LF
124 # however, 'readline()' returns lines ending in LF
125 # so only possibilities are ...LF, ...CRLF, CR...LF
126 if line[-2:] == CRLF:
127 return line[:-2], octets
128 if line[0] == CR:
129 return line[1:-1], octets
130 return line[:-1], octets
Guido van Rossum03774bb1998-04-09 13:50:55 +0000131
Guido van Rossum484772d1998-04-06 18:27:27 +0000132
Tim Peters2344fae2001-01-15 00:50:52 +0000133 # Internal: get a response from the server.
134 # Raise 'error_proto' if the response doesn't start with '+'.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000135
Tim Peters2344fae2001-01-15 00:50:52 +0000136 def _getresp(self):
137 resp, o = self._getline()
Walter Dörwald70a6b492004-02-12 17:35:32 +0000138 if self._debugging > 1: print '*resp*', repr(resp)
Tim Peters2344fae2001-01-15 00:50:52 +0000139 c = resp[:1]
140 if c != '+':
141 raise error_proto(resp)
142 return resp
Guido van Rossum484772d1998-04-06 18:27:27 +0000143
Guido van Rossum03774bb1998-04-09 13:50:55 +0000144
Tim Peters2344fae2001-01-15 00:50:52 +0000145 # Internal: get a response plus following text from the server.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000146
Tim Peters2344fae2001-01-15 00:50:52 +0000147 def _getlongresp(self):
148 resp = self._getresp()
149 list = []; octets = 0
150 line, o = self._getline()
151 while line != '.':
152 if line[:2] == '..':
153 o = o-1
154 line = line[1:]
155 octets = octets + o
156 list.append(line)
157 line, o = self._getline()
158 return resp, list, octets
Guido van Rossum03774bb1998-04-09 13:50:55 +0000159
Guido van Rossum484772d1998-04-06 18:27:27 +0000160
Tim Peters2344fae2001-01-15 00:50:52 +0000161 # Internal: send a command and get the response
Guido van Rossum03774bb1998-04-09 13:50:55 +0000162
Tim Peters2344fae2001-01-15 00:50:52 +0000163 def _shortcmd(self, line):
164 self._putcmd(line)
165 return self._getresp()
Guido van Rossum484772d1998-04-06 18:27:27 +0000166
Guido van Rossum03774bb1998-04-09 13:50:55 +0000167
Tim Peters2344fae2001-01-15 00:50:52 +0000168 # Internal: send a command and get the response plus following text
Guido van Rossum03774bb1998-04-09 13:50:55 +0000169
Tim Peters2344fae2001-01-15 00:50:52 +0000170 def _longcmd(self, line):
171 self._putcmd(line)
172 return self._getlongresp()
Guido van Rossum484772d1998-04-06 18:27:27 +0000173
Guido van Rossum03774bb1998-04-09 13:50:55 +0000174
Tim Peters2344fae2001-01-15 00:50:52 +0000175 # These can be useful:
176
177 def getwelcome(self):
178 return self.welcome
179
180
181 def set_debuglevel(self, level):
182 self._debugging = level
183
184
185 # Here are all the POP commands:
186
187 def user(self, user):
188 """Send user name, return response
Guido van Rossum484772d1998-04-06 18:27:27 +0000189
Tim Peters2344fae2001-01-15 00:50:52 +0000190 (should indicate password required).
191 """
192 return self._shortcmd('USER %s' % user)
Guido van Rossum484772d1998-04-06 18:27:27 +0000193
Guido van Rossum03774bb1998-04-09 13:50:55 +0000194
Tim Peters2344fae2001-01-15 00:50:52 +0000195 def pass_(self, pswd):
196 """Send password, return response
Guido van Rossum484772d1998-04-06 18:27:27 +0000197
Tim Peters2344fae2001-01-15 00:50:52 +0000198 (response includes message count, mailbox size).
Guido van Rossum03774bb1998-04-09 13:50:55 +0000199
Tim Peters2344fae2001-01-15 00:50:52 +0000200 NB: mailbox is locked by server from here to 'quit()'
201 """
202 return self._shortcmd('PASS %s' % pswd)
Guido van Rossum484772d1998-04-06 18:27:27 +0000203
Guido van Rossum03774bb1998-04-09 13:50:55 +0000204
Tim Peters2344fae2001-01-15 00:50:52 +0000205 def stat(self):
206 """Get mailbox status.
Guido van Rossum484772d1998-04-06 18:27:27 +0000207
Tim Peters2344fae2001-01-15 00:50:52 +0000208 Result is tuple of 2 ints (message count, mailbox size)
209 """
210 retval = self._shortcmd('STAT')
Eric S. Raymond341f9292001-02-09 06:56:56 +0000211 rets = retval.split()
Walter Dörwald70a6b492004-02-12 17:35:32 +0000212 if self._debugging: print '*stat*', repr(rets)
Eric S. Raymond341f9292001-02-09 06:56:56 +0000213 numMessages = int(rets[1])
214 sizeMessages = int(rets[2])
Tim Peters2344fae2001-01-15 00:50:52 +0000215 return (numMessages, sizeMessages)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000216
Guido van Rossum03774bb1998-04-09 13:50:55 +0000217
Tim Peters2344fae2001-01-15 00:50:52 +0000218 def list(self, which=None):
219 """Request listing, return result.
Guido van Rossum484772d1998-04-06 18:27:27 +0000220
Tim Peters2344fae2001-01-15 00:50:52 +0000221 Result without a message number argument is in form
222 ['response', ['mesg_num octets', ...]].
Guido van Rossum484772d1998-04-06 18:27:27 +0000223
Tim Peters2344fae2001-01-15 00:50:52 +0000224 Result when a message number argument is given is a
225 single response: the "scan listing" for that message.
226 """
Raymond Hettinger16e3c422002-06-01 16:07:16 +0000227 if which is not None:
Tim Peters2344fae2001-01-15 00:50:52 +0000228 return self._shortcmd('LIST %s' % which)
229 return self._longcmd('LIST')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000230
Guido van Rossum03774bb1998-04-09 13:50:55 +0000231
Tim Peters2344fae2001-01-15 00:50:52 +0000232 def retr(self, which):
233 """Retrieve whole message number 'which'.
Guido van Rossumf6ae7431998-09-02 14:42:02 +0000234
Tim Peters2344fae2001-01-15 00:50:52 +0000235 Result is in form ['response', ['line', ...], octets].
236 """
237 return self._longcmd('RETR %s' % which)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000238
Guido van Rossum484772d1998-04-06 18:27:27 +0000239
Tim Peters2344fae2001-01-15 00:50:52 +0000240 def dele(self, which):
241 """Delete message number 'which'.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000242
Tim Peters2344fae2001-01-15 00:50:52 +0000243 Result is 'response'.
244 """
245 return self._shortcmd('DELE %s' % which)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000246
Guido van Rossum484772d1998-04-06 18:27:27 +0000247
Tim Peters2344fae2001-01-15 00:50:52 +0000248 def noop(self):
249 """Does nothing.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000250
Tim Peters2344fae2001-01-15 00:50:52 +0000251 One supposes the response indicates the server is alive.
252 """
253 return self._shortcmd('NOOP')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000254
Guido van Rossum484772d1998-04-06 18:27:27 +0000255
Tim Peters2344fae2001-01-15 00:50:52 +0000256 def rset(self):
257 """Not sure what this does."""
258 return self._shortcmd('RSET')
Guido van Rossum484772d1998-04-06 18:27:27 +0000259
Guido van Rossum03774bb1998-04-09 13:50:55 +0000260
Tim Peters2344fae2001-01-15 00:50:52 +0000261 def quit(self):
262 """Signoff: commit changes on server, unlock mailbox, close connection."""
263 try:
264 resp = self._shortcmd('QUIT')
265 except error_proto, val:
266 resp = val
267 self.file.close()
268 self.sock.close()
269 del self.file, self.sock
270 return resp
Guido van Rossum484772d1998-04-06 18:27:27 +0000271
Tim Peters2344fae2001-01-15 00:50:52 +0000272 #__del__ = quit
Guido van Rossum484772d1998-04-06 18:27:27 +0000273
Guido van Rossum484772d1998-04-06 18:27:27 +0000274
Tim Peters2344fae2001-01-15 00:50:52 +0000275 # optional commands:
Guido van Rossum03774bb1998-04-09 13:50:55 +0000276
Tim Peters2344fae2001-01-15 00:50:52 +0000277 def rpop(self, user):
278 """Not sure what this does."""
279 return self._shortcmd('RPOP %s' % user)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000280
Guido van Rossum03774bb1998-04-09 13:50:55 +0000281
Moshe Zadkaccc2e3d2001-01-19 19:56:27 +0000282 timestamp = re.compile(r'\+OK.*(<[^>]+>)')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000283
Tim Peters2344fae2001-01-15 00:50:52 +0000284 def apop(self, user, secret):
285 """Authorisation
Guido van Rossum03774bb1998-04-09 13:50:55 +0000286
Tim Peters2344fae2001-01-15 00:50:52 +0000287 - only possible if server has supplied a timestamp in initial greeting.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000288
Tim Peters2344fae2001-01-15 00:50:52 +0000289 Args:
290 user - mailbox user;
291 secret - secret shared between client and server.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000292
Tim Peters2344fae2001-01-15 00:50:52 +0000293 NB: mailbox is locked by server from here to 'quit()'
294 """
Moshe Zadkaccc2e3d2001-01-19 19:56:27 +0000295 m = self.timestamp.match(self.welcome)
296 if not m:
Tim Peters2344fae2001-01-15 00:50:52 +0000297 raise error_proto('-ERR APOP not supported by server')
298 import md5
Moshe Zadkaccc2e3d2001-01-19 19:56:27 +0000299 digest = md5.new(m.group(1)+secret).digest()
Eric S. Raymond341f9292001-02-09 06:56:56 +0000300 digest = ''.join(map(lambda x:'%02x'%ord(x), digest))
Tim Peters2344fae2001-01-15 00:50:52 +0000301 return self._shortcmd('APOP %s %s' % (user, digest))
Guido van Rossum03774bb1998-04-09 13:50:55 +0000302
Guido van Rossum03774bb1998-04-09 13:50:55 +0000303
Tim Peters2344fae2001-01-15 00:50:52 +0000304 def top(self, which, howmuch):
305 """Retrieve message header of message number 'which'
306 and first 'howmuch' lines of message body.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000307
Tim Peters2344fae2001-01-15 00:50:52 +0000308 Result is in form ['response', ['line', ...], octets].
309 """
310 return self._longcmd('TOP %s %s' % (which, howmuch))
Guido van Rossum03774bb1998-04-09 13:50:55 +0000311
Guido van Rossum03774bb1998-04-09 13:50:55 +0000312
Tim Peters2344fae2001-01-15 00:50:52 +0000313 def uidl(self, which=None):
314 """Return message digest (unique id) list.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000315
Tim Peters2344fae2001-01-15 00:50:52 +0000316 If 'which', result contains unique id for that message
317 in the form 'response mesgnum uid', otherwise result is
318 the list ['response', ['mesgnum uid', ...], octets]
319 """
Raymond Hettinger16e3c422002-06-01 16:07:16 +0000320 if which is not None:
Tim Peters2344fae2001-01-15 00:50:52 +0000321 return self._shortcmd('UIDL %s' % which)
322 return self._longcmd('UIDL')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000323
Martin v. Löwis48440b72003-10-31 12:52:35 +0000324class POP3_SSL(POP3):
325 """POP3 client class over SSL connection
326
327 Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
328
329 hostname - the hostname of the pop3 over ssl server
330 port - port number
331 keyfile - PEM formatted file that countains your private key
332 certfile - PEM formatted certificate chain file
333
334 See the methods of the parent class POP3 for more documentation.
335 """
336
337 def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None):
338 self.host = host
339 self.port = port
340 self.keyfile = keyfile
341 self.certfile = certfile
342 self.buffer = ""
343 msg = "getaddrinfo returns an empty list"
344 self.sock = None
345 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
346 af, socktype, proto, canonname, sa = res
347 try:
348 self.sock = socket.socket(af, socktype, proto)
349 self.sock.connect(sa)
350 except socket.error, msg:
351 if self.sock:
352 self.sock.close()
353 self.sock = None
354 continue
355 break
356 if not self.sock:
357 raise socket.error, msg
358 self.file = self.sock.makefile('rb')
359 self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
360 self._debugging = 0
361 self.welcome = self._getresp()
362
363 def _fillBuffer(self):
364 localbuf = self.sslobj.read()
365 if len(localbuf) == 0:
366 raise error_proto('-ERR EOF')
367 self.buffer += localbuf
368
369 def _getline(self):
370 line = ""
371 renewline = re.compile(r'.*?\n')
372 match = renewline.match(self.buffer)
373 while not match:
374 self._fillBuffer()
375 match = renewline.match(self.buffer)
376 line = match.group(0)
377 self.buffer = renewline.sub('' ,self.buffer, 1)
Walter Dörwald70a6b492004-02-12 17:35:32 +0000378 if self._debugging > 1: print '*get*', repr(line)
Martin v. Löwis48440b72003-10-31 12:52:35 +0000379
380 octets = len(line)
381 if line[-2:] == CRLF:
382 return line[:-2], octets
383 if line[0] == CR:
384 return line[1:-1], octets
385 return line[:-1], octets
386
387 def _putline(self, line):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000388 if self._debugging > 1: print '*put*', repr(line)
Martin v. Löwis48440b72003-10-31 12:52:35 +0000389 line += CRLF
390 bytes = len(line)
391 while bytes > 0:
392 sent = self.sslobj.write(line)
393 if sent == bytes:
394 break # avoid copy
395 line = line[sent:]
396 bytes = bytes - sent
397
398 def quit(self):
399 """Signoff: commit changes on server, unlock mailbox, close connection."""
400 try:
401 resp = self._shortcmd('QUIT')
402 except error_proto, val:
403 resp = val
404 self.sock.close()
405 del self.sslobj, self.sock
406 return resp
407
Guido van Rossum03774bb1998-04-09 13:50:55 +0000408
Guido van Rossum484772d1998-04-06 18:27:27 +0000409if __name__ == "__main__":
Eric S. Raymond341f9292001-02-09 06:56:56 +0000410 import sys
411 a = POP3(sys.argv[1])
Tim Peters2344fae2001-01-15 00:50:52 +0000412 print a.getwelcome()
Eric S. Raymond341f9292001-02-09 06:56:56 +0000413 a.user(sys.argv[2])
414 a.pass_(sys.argv[3])
Tim Peters2344fae2001-01-15 00:50:52 +0000415 a.list()
416 (numMsgs, totalSize) = a.stat()
417 for i in range(1, numMsgs + 1):
418 (header, msg, octets) = a.retr(i)
Walter Dörwald70a6b492004-02-12 17:35:32 +0000419 print "Message %d:" % i
Tim Peters2344fae2001-01-15 00:50:52 +0000420 for line in msg:
421 print ' ' + line
422 print '-----------------------'
423 a.quit()