Minor documentation change - hyperlink tidied up.
[python.git] / Lib / poplib.py
blob149675a4aeef741eaf3bf5558629d975433bc359
1 """A POP3 client class.
3 Based on the J. Myers POP3 draft, Jan. 96
4 """
6 # Author: David Ascher <david_ascher@brown.edu>
7 # [heavily stealing from nntplib.py]
8 # Updated: Piers Lauder <piers@cs.su.oz.au> [Jul '97]
9 # String method conversion and test jig improvements by ESR, February 2001.
10 # Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia <urtubia@mrbook.org> Aug 2003
12 # Example (see the test function at the end of this file)
14 # Imports
16 import re, socket
18 __all__ = ["POP3","error_proto"]
20 # Exception raised when an error or invalid response is received:
22 class error_proto(Exception): pass
24 # Standard Port
25 POP3_PORT = 110
27 # POP SSL PORT
28 POP3_SSL_PORT = 995
30 # Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF)
31 CR = '\r'
32 LF = '\n'
33 CRLF = CR+LF
36 class POP3:
38 """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.
42 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()
53 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)
59 Raises one exception: 'error_proto'.
61 Instantiate with:
62 POP3(hostname, port=110)
64 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.
69 POP is a line-based protocol, which means large mail
70 messages consume lots of python cycles reading them
71 line-by-line.
73 If it's available on your mail server, use IMAP4
74 instead, it doesn't suffer from the two problems
75 above.
76 """
79 def __init__(self, host, port=POP3_PORT, timeout=None):
80 self.host = host
81 self.port = port
82 self.sock = socket.create_connection((host, port), timeout)
83 self.file = self.sock.makefile('rb')
84 self._debugging = 0
85 self.welcome = self._getresp()
88 def _putline(self, line):
89 if self._debugging > 1: print '*put*', repr(line)
90 self.sock.sendall('%s%s' % (line, CRLF))
93 # Internal: send one command to the server (through _putline())
95 def _putcmd(self, line):
96 if self._debugging: print '*cmd*', repr(line)
97 self._putline(line)
100 # Internal: return one line from the server, stripping CRLF.
101 # This is where all the CPU time of this module is consumed.
102 # Raise error_proto('-ERR EOF') if the connection is closed.
104 def _getline(self):
105 line = self.file.readline()
106 if self._debugging > 1: print '*get*', repr(line)
107 if not line: raise error_proto('-ERR EOF')
108 octets = len(line)
109 # server can send any combination of CR & LF
110 # however, 'readline()' returns lines ending in LF
111 # so only possibilities are ...LF, ...CRLF, CR...LF
112 if line[-2:] == CRLF:
113 return line[:-2], octets
114 if line[0] == CR:
115 return line[1:-1], octets
116 return line[:-1], octets
119 # Internal: get a response from the server.
120 # Raise 'error_proto' if the response doesn't start with '+'.
122 def _getresp(self):
123 resp, o = self._getline()
124 if self._debugging > 1: print '*resp*', repr(resp)
125 c = resp[:1]
126 if c != '+':
127 raise error_proto(resp)
128 return resp
131 # Internal: get a response plus following text from the server.
133 def _getlongresp(self):
134 resp = self._getresp()
135 list = []; octets = 0
136 line, o = self._getline()
137 while line != '.':
138 if line[:2] == '..':
139 o = o-1
140 line = line[1:]
141 octets = octets + o
142 list.append(line)
143 line, o = self._getline()
144 return resp, list, octets
147 # Internal: send a command and get the response
149 def _shortcmd(self, line):
150 self._putcmd(line)
151 return self._getresp()
154 # Internal: send a command and get the response plus following text
156 def _longcmd(self, line):
157 self._putcmd(line)
158 return self._getlongresp()
161 # These can be useful:
163 def getwelcome(self):
164 return self.welcome
167 def set_debuglevel(self, level):
168 self._debugging = level
171 # Here are all the POP commands:
173 def user(self, user):
174 """Send user name, return response
176 (should indicate password required).
178 return self._shortcmd('USER %s' % user)
181 def pass_(self, pswd):
182 """Send password, return response
184 (response includes message count, mailbox size).
186 NB: mailbox is locked by server from here to 'quit()'
188 return self._shortcmd('PASS %s' % pswd)
191 def stat(self):
192 """Get mailbox status.
194 Result is tuple of 2 ints (message count, mailbox size)
196 retval = self._shortcmd('STAT')
197 rets = retval.split()
198 if self._debugging: print '*stat*', repr(rets)
199 numMessages = int(rets[1])
200 sizeMessages = int(rets[2])
201 return (numMessages, sizeMessages)
204 def list(self, which=None):
205 """Request listing, return result.
207 Result without a message number argument is in form
208 ['response', ['mesg_num octets', ...], octets].
210 Result when a message number argument is given is a
211 single response: the "scan listing" for that message.
213 if which is not None:
214 return self._shortcmd('LIST %s' % which)
215 return self._longcmd('LIST')
218 def retr(self, which):
219 """Retrieve whole message number 'which'.
221 Result is in form ['response', ['line', ...], octets].
223 return self._longcmd('RETR %s' % which)
226 def dele(self, which):
227 """Delete message number 'which'.
229 Result is 'response'.
231 return self._shortcmd('DELE %s' % which)
234 def noop(self):
235 """Does nothing.
237 One supposes the response indicates the server is alive.
239 return self._shortcmd('NOOP')
242 def rset(self):
243 """Not sure what this does."""
244 return self._shortcmd('RSET')
247 def quit(self):
248 """Signoff: commit changes on server, unlock mailbox, close connection."""
249 try:
250 resp = self._shortcmd('QUIT')
251 except error_proto, val:
252 resp = val
253 self.file.close()
254 self.sock.close()
255 del self.file, self.sock
256 return resp
258 #__del__ = quit
261 # optional commands:
263 def rpop(self, user):
264 """Not sure what this does."""
265 return self._shortcmd('RPOP %s' % user)
268 timestamp = re.compile(r'\+OK.*(<[^>]+>)')
270 def apop(self, user, secret):
271 """Authorisation
273 - only possible if server has supplied a timestamp in initial greeting.
275 Args:
276 user - mailbox user;
277 secret - secret shared between client and server.
279 NB: mailbox is locked by server from here to 'quit()'
281 m = self.timestamp.match(self.welcome)
282 if not m:
283 raise error_proto('-ERR APOP not supported by server')
284 import hashlib
285 digest = hashlib.md5(m.group(1)+secret).digest()
286 digest = ''.join(map(lambda x:'%02x'%ord(x), digest))
287 return self._shortcmd('APOP %s %s' % (user, digest))
290 def top(self, which, howmuch):
291 """Retrieve message header of message number 'which'
292 and first 'howmuch' lines of message body.
294 Result is in form ['response', ['line', ...], octets].
296 return self._longcmd('TOP %s %s' % (which, howmuch))
299 def uidl(self, which=None):
300 """Return message digest (unique id) list.
302 If 'which', result contains unique id for that message
303 in the form 'response mesgnum uid', otherwise result is
304 the list ['response', ['mesgnum uid', ...], octets]
306 if which is not None:
307 return self._shortcmd('UIDL %s' % which)
308 return self._longcmd('UIDL')
310 try:
311 import ssl
312 except ImportError:
313 pass
314 else:
316 class POP3_SSL(POP3):
317 """POP3 client class over SSL connection
319 Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
321 hostname - the hostname of the pop3 over ssl server
322 port - port number
323 keyfile - PEM formatted file that countains your private key
324 certfile - PEM formatted certificate chain file
326 See the methods of the parent class POP3 for more documentation.
329 def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None):
330 self.host = host
331 self.port = port
332 self.keyfile = keyfile
333 self.certfile = certfile
334 self.buffer = ""
335 msg = "getaddrinfo returns an empty list"
336 self.sock = None
337 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
338 af, socktype, proto, canonname, sa = res
339 try:
340 self.sock = socket.socket(af, socktype, proto)
341 self.sock.connect(sa)
342 except socket.error, msg:
343 if self.sock:
344 self.sock.close()
345 self.sock = None
346 continue
347 break
348 if not self.sock:
349 raise socket.error, msg
350 self.file = self.sock.makefile('rb')
351 self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
352 self._debugging = 0
353 self.welcome = self._getresp()
355 def _fillBuffer(self):
356 localbuf = self.sslobj.read()
357 if len(localbuf) == 0:
358 raise error_proto('-ERR EOF')
359 self.buffer += localbuf
361 def _getline(self):
362 line = ""
363 renewline = re.compile(r'.*?\n')
364 match = renewline.match(self.buffer)
365 while not match:
366 self._fillBuffer()
367 match = renewline.match(self.buffer)
368 line = match.group(0)
369 self.buffer = renewline.sub('' ,self.buffer, 1)
370 if self._debugging > 1: print '*get*', repr(line)
372 octets = len(line)
373 if line[-2:] == CRLF:
374 return line[:-2], octets
375 if line[0] == CR:
376 return line[1:-1], octets
377 return line[:-1], octets
379 def _putline(self, line):
380 if self._debugging > 1: print '*put*', repr(line)
381 line += CRLF
382 bytes = len(line)
383 while bytes > 0:
384 sent = self.sslobj.write(line)
385 if sent == bytes:
386 break # avoid copy
387 line = line[sent:]
388 bytes = bytes - sent
390 def quit(self):
391 """Signoff: commit changes on server, unlock mailbox, close connection."""
392 try:
393 resp = self._shortcmd('QUIT')
394 except error_proto, val:
395 resp = val
396 self.sock.close()
397 del self.sslobj, self.sock
398 return resp
400 __all__.append("POP3_SSL")
402 if __name__ == "__main__":
403 import sys
404 a = POP3(sys.argv[1])
405 print a.getwelcome()
406 a.user(sys.argv[2])
407 a.pass_(sys.argv[3])
408 a.list()
409 (numMsgs, totalSize) = a.stat()
410 for i in range(1, numMsgs + 1):
411 (header, msg, octets) = a.retr(i)
412 print "Message %d:" % i
413 for line in msg:
414 print ' ' + line
415 print '-----------------------'
416 a.quit()