Issue #5788: `datetime.timedelta` objects get a new `total_seconds()` method returning
[python.git] / Lib / poplib.py
blobe2b33ef10f499fd75ab1d709b1a3c9d7b4ca4437
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,
80 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
81 self.host = host
82 self.port = port
83 self.sock = socket.create_connection((host, port), timeout)
84 self.file = self.sock.makefile('rb')
85 self._debugging = 0
86 self.welcome = self._getresp()
89 def _putline(self, line):
90 if self._debugging > 1: print '*put*', repr(line)
91 self.sock.sendall('%s%s' % (line, CRLF))
94 # Internal: send one command to the server (through _putline())
96 def _putcmd(self, line):
97 if self._debugging: print '*cmd*', repr(line)
98 self._putline(line)
101 # Internal: return one line from the server, stripping CRLF.
102 # This is where all the CPU time of this module is consumed.
103 # Raise error_proto('-ERR EOF') if the connection is closed.
105 def _getline(self):
106 line = self.file.readline()
107 if self._debugging > 1: print '*get*', repr(line)
108 if not line: raise error_proto('-ERR EOF')
109 octets = len(line)
110 # server can send any combination of CR & LF
111 # however, 'readline()' returns lines ending in LF
112 # so only possibilities are ...LF, ...CRLF, CR...LF
113 if line[-2:] == CRLF:
114 return line[:-2], octets
115 if line[0] == CR:
116 return line[1:-1], octets
117 return line[:-1], octets
120 # Internal: get a response from the server.
121 # Raise 'error_proto' if the response doesn't start with '+'.
123 def _getresp(self):
124 resp, o = self._getline()
125 if self._debugging > 1: print '*resp*', repr(resp)
126 c = resp[:1]
127 if c != '+':
128 raise error_proto(resp)
129 return resp
132 # Internal: get a response plus following text from the server.
134 def _getlongresp(self):
135 resp = self._getresp()
136 list = []; octets = 0
137 line, o = self._getline()
138 while line != '.':
139 if line[:2] == '..':
140 o = o-1
141 line = line[1:]
142 octets = octets + o
143 list.append(line)
144 line, o = self._getline()
145 return resp, list, octets
148 # Internal: send a command and get the response
150 def _shortcmd(self, line):
151 self._putcmd(line)
152 return self._getresp()
155 # Internal: send a command and get the response plus following text
157 def _longcmd(self, line):
158 self._putcmd(line)
159 return self._getlongresp()
162 # These can be useful:
164 def getwelcome(self):
165 return self.welcome
168 def set_debuglevel(self, level):
169 self._debugging = level
172 # Here are all the POP commands:
174 def user(self, user):
175 """Send user name, return response
177 (should indicate password required).
179 return self._shortcmd('USER %s' % user)
182 def pass_(self, pswd):
183 """Send password, return response
185 (response includes message count, mailbox size).
187 NB: mailbox is locked by server from here to 'quit()'
189 return self._shortcmd('PASS %s' % pswd)
192 def stat(self):
193 """Get mailbox status.
195 Result is tuple of 2 ints (message count, mailbox size)
197 retval = self._shortcmd('STAT')
198 rets = retval.split()
199 if self._debugging: print '*stat*', repr(rets)
200 numMessages = int(rets[1])
201 sizeMessages = int(rets[2])
202 return (numMessages, sizeMessages)
205 def list(self, which=None):
206 """Request listing, return result.
208 Result without a message number argument is in form
209 ['response', ['mesg_num octets', ...], octets].
211 Result when a message number argument is given is a
212 single response: the "scan listing" for that message.
214 if which is not None:
215 return self._shortcmd('LIST %s' % which)
216 return self._longcmd('LIST')
219 def retr(self, which):
220 """Retrieve whole message number 'which'.
222 Result is in form ['response', ['line', ...], octets].
224 return self._longcmd('RETR %s' % which)
227 def dele(self, which):
228 """Delete message number 'which'.
230 Result is 'response'.
232 return self._shortcmd('DELE %s' % which)
235 def noop(self):
236 """Does nothing.
238 One supposes the response indicates the server is alive.
240 return self._shortcmd('NOOP')
243 def rset(self):
244 """Unmark all messages marked for deletion."""
245 return self._shortcmd('RSET')
248 def quit(self):
249 """Signoff: commit changes on server, unlock mailbox, close connection."""
250 try:
251 resp = self._shortcmd('QUIT')
252 except error_proto, val:
253 resp = val
254 self.file.close()
255 self.sock.close()
256 del self.file, self.sock
257 return resp
259 #__del__ = quit
262 # optional commands:
264 def rpop(self, user):
265 """Not sure what this does."""
266 return self._shortcmd('RPOP %s' % user)
269 timestamp = re.compile(r'\+OK.*(<[^>]+>)')
271 def apop(self, user, secret):
272 """Authorisation
274 - only possible if server has supplied a timestamp in initial greeting.
276 Args:
277 user - mailbox user;
278 secret - secret shared between client and server.
280 NB: mailbox is locked by server from here to 'quit()'
282 m = self.timestamp.match(self.welcome)
283 if not m:
284 raise error_proto('-ERR APOP not supported by server')
285 import hashlib
286 digest = hashlib.md5(m.group(1)+secret).digest()
287 digest = ''.join(map(lambda x:'%02x'%ord(x), digest))
288 return self._shortcmd('APOP %s %s' % (user, digest))
291 def top(self, which, howmuch):
292 """Retrieve message header of message number 'which'
293 and first 'howmuch' lines of message body.
295 Result is in form ['response', ['line', ...], octets].
297 return self._longcmd('TOP %s %s' % (which, howmuch))
300 def uidl(self, which=None):
301 """Return message digest (unique id) list.
303 If 'which', result contains unique id for that message
304 in the form 'response mesgnum uid', otherwise result is
305 the list ['response', ['mesgnum uid', ...], octets]
307 if which is not None:
308 return self._shortcmd('UIDL %s' % which)
309 return self._longcmd('UIDL')
311 try:
312 import ssl
313 except ImportError:
314 pass
315 else:
317 class POP3_SSL(POP3):
318 """POP3 client class over SSL connection
320 Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
322 hostname - the hostname of the pop3 over ssl server
323 port - port number
324 keyfile - PEM formatted file that countains your private key
325 certfile - PEM formatted certificate chain file
327 See the methods of the parent class POP3 for more documentation.
330 def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None):
331 self.host = host
332 self.port = port
333 self.keyfile = keyfile
334 self.certfile = certfile
335 self.buffer = ""
336 msg = "getaddrinfo returns an empty list"
337 self.sock = None
338 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
339 af, socktype, proto, canonname, sa = res
340 try:
341 self.sock = socket.socket(af, socktype, proto)
342 self.sock.connect(sa)
343 except socket.error, msg:
344 if self.sock:
345 self.sock.close()
346 self.sock = None
347 continue
348 break
349 if not self.sock:
350 raise socket.error, msg
351 self.file = self.sock.makefile('rb')
352 self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
353 self._debugging = 0
354 self.welcome = self._getresp()
356 def _fillBuffer(self):
357 localbuf = self.sslobj.read()
358 if len(localbuf) == 0:
359 raise error_proto('-ERR EOF')
360 self.buffer += localbuf
362 def _getline(self):
363 line = ""
364 renewline = re.compile(r'.*?\n')
365 match = renewline.match(self.buffer)
366 while not match:
367 self._fillBuffer()
368 match = renewline.match(self.buffer)
369 line = match.group(0)
370 self.buffer = renewline.sub('' ,self.buffer, 1)
371 if self._debugging > 1: print '*get*', repr(line)
373 octets = len(line)
374 if line[-2:] == CRLF:
375 return line[:-2], octets
376 if line[0] == CR:
377 return line[1:-1], octets
378 return line[:-1], octets
380 def _putline(self, line):
381 if self._debugging > 1: print '*put*', repr(line)
382 line += CRLF
383 bytes = len(line)
384 while bytes > 0:
385 sent = self.sslobj.write(line)
386 if sent == bytes:
387 break # avoid copy
388 line = line[sent:]
389 bytes = bytes - sent
391 def quit(self):
392 """Signoff: commit changes on server, unlock mailbox, close connection."""
393 try:
394 resp = self._shortcmd('QUIT')
395 except error_proto, val:
396 resp = val
397 self.sock.close()
398 del self.sslobj, self.sock
399 return resp
401 __all__.append("POP3_SSL")
403 if __name__ == "__main__":
404 import sys
405 a = POP3(sys.argv[1])
406 print a.getwelcome()
407 a.user(sys.argv[2])
408 a.pass_(sys.argv[3])
409 a.list()
410 (numMsgs, totalSize) = a.stat()
411 for i in range(1, numMsgs + 1):
412 (header, msg, octets) = a.retr(i)
413 print "Message %d:" % i
414 for line in msg:
415 print ' ' + line
416 print '-----------------------'
417 a.quit()