Added new optional credentials argument to SMTPHandler.__init__, and smtp.login(...
[python.git] / Lib / poplib.py
blobba4057215ab300490c107e0fa994e82ed00e07b2
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","POP3_SSL"]
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 class POP3_SSL(POP3):
311 """POP3 client class over SSL connection
313 Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
315 hostname - the hostname of the pop3 over ssl server
316 port - port number
317 keyfile - PEM formatted file that countains your private key
318 certfile - PEM formatted certificate chain file
320 See the methods of the parent class POP3 for more documentation.
323 def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None):
324 self.host = host
325 self.port = port
326 self.keyfile = keyfile
327 self.certfile = certfile
328 self.buffer = ""
329 msg = "getaddrinfo returns an empty list"
330 self.sock = None
331 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
332 af, socktype, proto, canonname, sa = res
333 try:
334 self.sock = socket.socket(af, socktype, proto)
335 self.sock.connect(sa)
336 except socket.error, msg:
337 if self.sock:
338 self.sock.close()
339 self.sock = None
340 continue
341 break
342 if not self.sock:
343 raise socket.error, msg
344 self.file = self.sock.makefile('rb')
345 self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
346 self._debugging = 0
347 self.welcome = self._getresp()
349 def _fillBuffer(self):
350 localbuf = self.sslobj.read()
351 if len(localbuf) == 0:
352 raise error_proto('-ERR EOF')
353 self.buffer += localbuf
355 def _getline(self):
356 line = ""
357 renewline = re.compile(r'.*?\n')
358 match = renewline.match(self.buffer)
359 while not match:
360 self._fillBuffer()
361 match = renewline.match(self.buffer)
362 line = match.group(0)
363 self.buffer = renewline.sub('' ,self.buffer, 1)
364 if self._debugging > 1: print '*get*', repr(line)
366 octets = len(line)
367 if line[-2:] == CRLF:
368 return line[:-2], octets
369 if line[0] == CR:
370 return line[1:-1], octets
371 return line[:-1], octets
373 def _putline(self, line):
374 if self._debugging > 1: print '*put*', repr(line)
375 line += CRLF
376 bytes = len(line)
377 while bytes > 0:
378 sent = self.sslobj.write(line)
379 if sent == bytes:
380 break # avoid copy
381 line = line[sent:]
382 bytes = bytes - sent
384 def quit(self):
385 """Signoff: commit changes on server, unlock mailbox, close connection."""
386 try:
387 resp = self._shortcmd('QUIT')
388 except error_proto, val:
389 resp = val
390 self.sock.close()
391 del self.sslobj, self.sock
392 return resp
395 if __name__ == "__main__":
396 import sys
397 a = POP3(sys.argv[1])
398 print a.getwelcome()
399 a.user(sys.argv[2])
400 a.pass_(sys.argv[3])
401 a.list()
402 (numMsgs, totalSize) = a.stat()
403 for i in range(1, numMsgs + 1):
404 (header, msg, octets) = a.retr(i)
405 print "Message %d:" % i
406 for line in msg:
407 print ' ' + line
408 print '-----------------------'
409 a.quit()