py-cvs-rel2_1 (Rev 1.2) merge
[python/dscho.git] / Lib / poplib.py
blobfb24a0f9c5b83c655d305ea31178426a87ede990
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.
11 # Example (see the test function at the end of this file)
13 # Imports
15 import re, socket
17 __all__ = ["POP3","error_proto"]
19 # Exception raised when an error or invalid response is received:
21 class error_proto(Exception): pass
23 # Standard Port
24 POP3_PORT = 110
26 # Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF)
27 CR = '\r'
28 LF = '\n'
29 CRLF = CR+LF
32 class POP3:
34 """This class supports both the minimal and optional command sets.
35 Arguments can be strings or integers (where appropriate)
36 (e.g.: retr(1) and retr('1') both work equally well.
38 Minimal Command Set:
39 USER name user(name)
40 PASS string pass_(string)
41 STAT stat()
42 LIST [msg] list(msg = None)
43 RETR msg retr(msg)
44 DELE msg dele(msg)
45 NOOP noop()
46 RSET rset()
47 QUIT quit()
49 Optional Commands (some servers support these):
50 RPOP name rpop(name)
51 APOP name digest apop(name, digest)
52 TOP msg n top(msg, n)
53 UIDL [msg] uidl(msg = None)
55 Raises one exception: 'error_proto'.
57 Instantiate with:
58 POP3(hostname, port=110)
60 NB: the POP protocol locks the mailbox from user
61 authorization until QUIT, so be sure to get in, suck
62 the messages, and quit, each time you access the
63 mailbox.
65 POP is a line-based protocol, which means large mail
66 messages consume lots of python cycles reading them
67 line-by-line.
69 If it's available on your mail server, use IMAP4
70 instead, it doesn't suffer from the two problems
71 above.
72 """
75 def __init__(self, host, port = POP3_PORT):
76 self.host = host
77 self.port = port
78 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
79 self.sock.connect((self.host, self.port))
80 self.file = self.sock.makefile('rb')
81 self._debugging = 0
82 self.welcome = self._getresp()
85 def _putline(self, line):
86 #if self._debugging > 1: print '*put*', `line`
87 self.sock.send('%s%s' % (line, CRLF))
90 # Internal: send one command to the server (through _putline())
92 def _putcmd(self, line):
93 #if self._debugging: print '*cmd*', `line`
94 self._putline(line)
97 # Internal: return one line from the server, stripping CRLF.
98 # This is where all the CPU time of this module is consumed.
99 # Raise error_proto('-ERR EOF') if the connection is closed.
101 def _getline(self):
102 line = self.file.readline()
103 #if self._debugging > 1: print '*get*', `line`
104 if not line: raise error_proto('-ERR EOF')
105 octets = len(line)
106 # server can send any combination of CR & LF
107 # however, 'readline()' returns lines ending in LF
108 # so only possibilities are ...LF, ...CRLF, CR...LF
109 if line[-2:] == CRLF:
110 return line[:-2], octets
111 if line[0] == CR:
112 return line[1:-1], octets
113 return line[:-1], octets
116 # Internal: get a response from the server.
117 # Raise 'error_proto' if the response doesn't start with '+'.
119 def _getresp(self):
120 resp, o = self._getline()
121 #if self._debugging > 1: print '*resp*', `resp`
122 c = resp[:1]
123 if c != '+':
124 raise error_proto(resp)
125 return resp
128 # Internal: get a response plus following text from the server.
130 def _getlongresp(self):
131 resp = self._getresp()
132 list = []; octets = 0
133 line, o = self._getline()
134 while line != '.':
135 if line[:2] == '..':
136 o = o-1
137 line = line[1:]
138 octets = octets + o
139 list.append(line)
140 line, o = self._getline()
141 return resp, list, octets
144 # Internal: send a command and get the response
146 def _shortcmd(self, line):
147 self._putcmd(line)
148 return self._getresp()
151 # Internal: send a command and get the response plus following text
153 def _longcmd(self, line):
154 self._putcmd(line)
155 return self._getlongresp()
158 # These can be useful:
160 def getwelcome(self):
161 return self.welcome
164 def set_debuglevel(self, level):
165 self._debugging = level
168 # Here are all the POP commands:
170 def user(self, user):
171 """Send user name, return response
173 (should indicate password required).
175 return self._shortcmd('USER %s' % user)
178 def pass_(self, pswd):
179 """Send password, return response
181 (response includes message count, mailbox size).
183 NB: mailbox is locked by server from here to 'quit()'
185 return self._shortcmd('PASS %s' % pswd)
188 def stat(self):
189 """Get mailbox status.
191 Result is tuple of 2 ints (message count, mailbox size)
193 retval = self._shortcmd('STAT')
194 rets = retval.split()
195 #if self._debugging: print '*stat*', `rets`
196 numMessages = int(rets[1])
197 sizeMessages = int(rets[2])
198 return (numMessages, sizeMessages)
201 def list(self, which=None):
202 """Request listing, return result.
204 Result without a message number argument is in form
205 ['response', ['mesg_num octets', ...]].
207 Result when a message number argument is given is a
208 single response: the "scan listing" for that message.
210 if which:
211 return self._shortcmd('LIST %s' % which)
212 return self._longcmd('LIST')
215 def retr(self, which):
216 """Retrieve whole message number 'which'.
218 Result is in form ['response', ['line', ...], octets].
220 return self._longcmd('RETR %s' % which)
223 def dele(self, which):
224 """Delete message number 'which'.
226 Result is 'response'.
228 return self._shortcmd('DELE %s' % which)
231 def noop(self):
232 """Does nothing.
234 One supposes the response indicates the server is alive.
236 return self._shortcmd('NOOP')
239 def rset(self):
240 """Not sure what this does."""
241 return self._shortcmd('RSET')
244 def quit(self):
245 """Signoff: commit changes on server, unlock mailbox, close connection."""
246 try:
247 resp = self._shortcmd('QUIT')
248 except error_proto, val:
249 resp = val
250 self.file.close()
251 self.sock.close()
252 del self.file, self.sock
253 return resp
255 #__del__ = quit
258 # optional commands:
260 def rpop(self, user):
261 """Not sure what this does."""
262 return self._shortcmd('RPOP %s' % user)
265 timestamp = re.compile(r'\+OK.*(<[^>]+>)')
267 def apop(self, user, secret):
268 """Authorisation
270 - only possible if server has supplied a timestamp in initial greeting.
272 Args:
273 user - mailbox user;
274 secret - secret shared between client and server.
276 NB: mailbox is locked by server from here to 'quit()'
278 m = self.timestamp.match(self.welcome)
279 if not m:
280 raise error_proto('-ERR APOP not supported by server')
281 import md5
282 digest = md5.new(m.group(1)+secret).digest()
283 digest = ''.join(map(lambda x:'%02x'%ord(x), digest))
284 return self._shortcmd('APOP %s %s' % (user, digest))
287 def top(self, which, howmuch):
288 """Retrieve message header of message number 'which'
289 and first 'howmuch' lines of message body.
291 Result is in form ['response', ['line', ...], octets].
293 return self._longcmd('TOP %s %s' % (which, howmuch))
296 def uidl(self, which=None):
297 """Return message digest (unique id) list.
299 If 'which', result contains unique id for that message
300 in the form 'response mesgnum uid', otherwise result is
301 the list ['response', ['mesgnum uid', ...], octets]
303 if which:
304 return self._shortcmd('UIDL %s' % which)
305 return self._longcmd('UIDL')
308 if __name__ == "__main__":
309 import sys
310 a = POP3(sys.argv[1])
311 print a.getwelcome()
312 a.user(sys.argv[2])
313 a.pass_(sys.argv[3])
314 a.list()
315 (numMsgs, totalSize) = a.stat()
316 for i in range(1, numMsgs + 1):
317 (header, msg, octets) = a.retr(i)
318 print "Message ", `i`, ':'
319 for line in msg:
320 print ' ' + line
321 print '-----------------------'
322 a.quit()