blob: d42d9dd32024ac97c841cd03a87852c7c2ba4a5e [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
Thomas Wouters47b49bf2007-08-30 22:15:33 +000018__all__ = ["POP3","error_proto"]
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)
Jeremy Hylton88d06a72007-08-29 19:08:30 +000031CR = b'\r'
32LF = b'\n'
Guido van Rossum03774bb1998-04-09 13:50:55 +000033CRLF = 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
Christian Heimesd3956292008-11-05 19:48:27 +000078 encoding = 'UTF-8'
Guido van Rossum03774bb1998-04-09 13:50:55 +000079
Georg Brandlf78e02b2008-06-10 17:40:04 +000080 def __init__(self, host, port=POP3_PORT,
81 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Martin v. Löwis4eb59402001-07-26 13:37:33 +000082 self.host = host
83 self.port = port
Christian Heimesd3956292008-11-05 19:48:27 +000084 self.sock = self._create_socket(timeout)
Martin v. Löwis4eb59402001-07-26 13:37:33 +000085 self.file = self.sock.makefile('rb')
86 self._debugging = 0
87 self.welcome = self._getresp()
Guido van Rossum484772d1998-04-06 18:27:27 +000088
Christian Heimesd3956292008-11-05 19:48:27 +000089 def _create_socket(self, timeout):
90 return socket.create_connection((self.host, self.port), timeout)
Guido van Rossum03774bb1998-04-09 13:50:55 +000091
Tim Peters2344fae2001-01-15 00:50:52 +000092 def _putline(self, line):
Guido van Rossumbe19ed72007-02-09 05:37:30 +000093 if self._debugging > 1: print('*put*', repr(line))
Christian Heimesd3956292008-11-05 19:48:27 +000094 self.sock.sendall(line + CRLF)
Guido van Rossum03774bb1998-04-09 13:50:55 +000095
Guido van Rossum484772d1998-04-06 18:27:27 +000096
Tim Peters2344fae2001-01-15 00:50:52 +000097 # Internal: send one command to the server (through _putline())
Guido van Rossum03774bb1998-04-09 13:50:55 +000098
Tim Peters2344fae2001-01-15 00:50:52 +000099 def _putcmd(self, line):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000100 if self._debugging: print('*cmd*', repr(line))
Christian Heimesd3956292008-11-05 19:48:27 +0000101 line = bytes(line, self.encoding)
Tim Peters2344fae2001-01-15 00:50:52 +0000102 self._putline(line)
Guido van Rossum484772d1998-04-06 18:27:27 +0000103
Guido van Rossum03774bb1998-04-09 13:50:55 +0000104
Tim Peters2344fae2001-01-15 00:50:52 +0000105 # Internal: return one line from the server, stripping CRLF.
106 # This is where all the CPU time of this module is consumed.
107 # Raise error_proto('-ERR EOF') if the connection is closed.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000108
Tim Peters2344fae2001-01-15 00:50:52 +0000109 def _getline(self):
110 line = self.file.readline()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000111 if self._debugging > 1: print('*get*', repr(line))
Tim Peters2344fae2001-01-15 00:50:52 +0000112 if not line: raise error_proto('-ERR EOF')
113 octets = len(line)
114 # server can send any combination of CR & LF
115 # however, 'readline()' returns lines ending in LF
116 # so only possibilities are ...LF, ...CRLF, CR...LF
117 if line[-2:] == CRLF:
118 return line[:-2], octets
119 if line[0] == CR:
120 return line[1:-1], octets
121 return line[:-1], octets
Guido van Rossum03774bb1998-04-09 13:50:55 +0000122
Guido van Rossum484772d1998-04-06 18:27:27 +0000123
Tim Peters2344fae2001-01-15 00:50:52 +0000124 # Internal: get a response from the server.
125 # Raise 'error_proto' if the response doesn't start with '+'.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000126
Tim Peters2344fae2001-01-15 00:50:52 +0000127 def _getresp(self):
128 resp, o = self._getline()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000129 if self._debugging > 1: print('*resp*', repr(resp))
Christian Heimesd3956292008-11-05 19:48:27 +0000130 if not resp.startswith(b'+'):
Tim Peters2344fae2001-01-15 00:50:52 +0000131 raise error_proto(resp)
132 return resp
Guido van Rossum484772d1998-04-06 18:27:27 +0000133
Guido van Rossum03774bb1998-04-09 13:50:55 +0000134
Tim Peters2344fae2001-01-15 00:50:52 +0000135 # Internal: get a response plus following text from the server.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000136
Tim Peters2344fae2001-01-15 00:50:52 +0000137 def _getlongresp(self):
138 resp = self._getresp()
139 list = []; octets = 0
140 line, o = self._getline()
Guido van Rossum0ec34772007-09-10 00:27:13 +0000141 while line != b'.':
Christian Heimesd3956292008-11-05 19:48:27 +0000142 if line.startswith(b'..'):
Tim Peters2344fae2001-01-15 00:50:52 +0000143 o = o-1
144 line = line[1:]
145 octets = octets + o
146 list.append(line)
147 line, o = self._getline()
148 return resp, list, octets
Guido van Rossum03774bb1998-04-09 13:50:55 +0000149
Guido van Rossum484772d1998-04-06 18:27:27 +0000150
Tim Peters2344fae2001-01-15 00:50:52 +0000151 # Internal: send a command and get the response
Guido van Rossum03774bb1998-04-09 13:50:55 +0000152
Tim Peters2344fae2001-01-15 00:50:52 +0000153 def _shortcmd(self, line):
154 self._putcmd(line)
155 return self._getresp()
Guido van Rossum484772d1998-04-06 18:27:27 +0000156
Guido van Rossum03774bb1998-04-09 13:50:55 +0000157
Tim Peters2344fae2001-01-15 00:50:52 +0000158 # Internal: send a command and get the response plus following text
Guido van Rossum03774bb1998-04-09 13:50:55 +0000159
Tim Peters2344fae2001-01-15 00:50:52 +0000160 def _longcmd(self, line):
161 self._putcmd(line)
162 return self._getlongresp()
Guido van Rossum484772d1998-04-06 18:27:27 +0000163
Guido van Rossum03774bb1998-04-09 13:50:55 +0000164
Tim Peters2344fae2001-01-15 00:50:52 +0000165 # These can be useful:
166
167 def getwelcome(self):
168 return self.welcome
169
170
171 def set_debuglevel(self, level):
172 self._debugging = level
173
174
175 # Here are all the POP commands:
176
177 def user(self, user):
178 """Send user name, return response
Guido van Rossum484772d1998-04-06 18:27:27 +0000179
Tim Peters2344fae2001-01-15 00:50:52 +0000180 (should indicate password required).
181 """
182 return self._shortcmd('USER %s' % user)
Guido van Rossum484772d1998-04-06 18:27:27 +0000183
Guido van Rossum03774bb1998-04-09 13:50:55 +0000184
Tim Peters2344fae2001-01-15 00:50:52 +0000185 def pass_(self, pswd):
186 """Send password, return response
Guido van Rossum484772d1998-04-06 18:27:27 +0000187
Tim Peters2344fae2001-01-15 00:50:52 +0000188 (response includes message count, mailbox size).
Guido van Rossum03774bb1998-04-09 13:50:55 +0000189
Tim Peters2344fae2001-01-15 00:50:52 +0000190 NB: mailbox is locked by server from here to 'quit()'
191 """
192 return self._shortcmd('PASS %s' % pswd)
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 stat(self):
196 """Get mailbox status.
Guido van Rossum484772d1998-04-06 18:27:27 +0000197
Tim Peters2344fae2001-01-15 00:50:52 +0000198 Result is tuple of 2 ints (message count, mailbox size)
199 """
200 retval = self._shortcmd('STAT')
Eric S. Raymond341f9292001-02-09 06:56:56 +0000201 rets = retval.split()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000202 if self._debugging: print('*stat*', repr(rets))
Eric S. Raymond341f9292001-02-09 06:56:56 +0000203 numMessages = int(rets[1])
204 sizeMessages = int(rets[2])
Tim Peters2344fae2001-01-15 00:50:52 +0000205 return (numMessages, sizeMessages)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000206
Guido van Rossum03774bb1998-04-09 13:50:55 +0000207
Tim Peters2344fae2001-01-15 00:50:52 +0000208 def list(self, which=None):
209 """Request listing, return result.
Guido van Rossum484772d1998-04-06 18:27:27 +0000210
Tim Peters2344fae2001-01-15 00:50:52 +0000211 Result without a message number argument is in form
Georg Brandl2772c672005-08-05 21:01:58 +0000212 ['response', ['mesg_num octets', ...], octets].
Guido van Rossum484772d1998-04-06 18:27:27 +0000213
Tim Peters2344fae2001-01-15 00:50:52 +0000214 Result when a message number argument is given is a
215 single response: the "scan listing" for that message.
216 """
Raymond Hettinger16e3c422002-06-01 16:07:16 +0000217 if which is not None:
Tim Peters2344fae2001-01-15 00:50:52 +0000218 return self._shortcmd('LIST %s' % which)
219 return self._longcmd('LIST')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000220
Guido van Rossum03774bb1998-04-09 13:50:55 +0000221
Tim Peters2344fae2001-01-15 00:50:52 +0000222 def retr(self, which):
223 """Retrieve whole message number 'which'.
Guido van Rossumf6ae7431998-09-02 14:42:02 +0000224
Tim Peters2344fae2001-01-15 00:50:52 +0000225 Result is in form ['response', ['line', ...], octets].
226 """
227 return self._longcmd('RETR %s' % which)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000228
Guido van Rossum484772d1998-04-06 18:27:27 +0000229
Tim Peters2344fae2001-01-15 00:50:52 +0000230 def dele(self, which):
231 """Delete message number 'which'.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000232
Tim Peters2344fae2001-01-15 00:50:52 +0000233 Result is 'response'.
234 """
235 return self._shortcmd('DELE %s' % which)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000236
Guido van Rossum484772d1998-04-06 18:27:27 +0000237
Tim Peters2344fae2001-01-15 00:50:52 +0000238 def noop(self):
239 """Does nothing.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000240
Tim Peters2344fae2001-01-15 00:50:52 +0000241 One supposes the response indicates the server is alive.
242 """
243 return self._shortcmd('NOOP')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000244
Guido van Rossum484772d1998-04-06 18:27:27 +0000245
Tim Peters2344fae2001-01-15 00:50:52 +0000246 def rset(self):
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000247 """Unmark all messages marked for deletion."""
Tim Peters2344fae2001-01-15 00:50:52 +0000248 return self._shortcmd('RSET')
Guido van Rossum484772d1998-04-06 18:27:27 +0000249
Guido van Rossum03774bb1998-04-09 13:50:55 +0000250
Tim Peters2344fae2001-01-15 00:50:52 +0000251 def quit(self):
252 """Signoff: commit changes on server, unlock mailbox, close connection."""
Giampaolo Rodolà95bcb932011-02-25 22:28:24 +0000253 resp = self._shortcmd('QUIT')
254 self.close()
Tim Peters2344fae2001-01-15 00:50:52 +0000255 return resp
Guido van Rossum484772d1998-04-06 18:27:27 +0000256
Giampaolo Rodolà95bcb932011-02-25 22:28:24 +0000257 def close(self):
258 """Close the connection without assuming anything about it."""
259 if self.file is not None:
260 self.file.close()
261 if self.sock is not None:
262 self.sock.close()
263 self.file = self.sock = None
264
Tim Peters2344fae2001-01-15 00:50:52 +0000265 #__del__ = quit
Guido van Rossum484772d1998-04-06 18:27:27 +0000266
Guido van Rossum484772d1998-04-06 18:27:27 +0000267
Tim Peters2344fae2001-01-15 00:50:52 +0000268 # optional commands:
Guido van Rossum03774bb1998-04-09 13:50:55 +0000269
Tim Peters2344fae2001-01-15 00:50:52 +0000270 def rpop(self, user):
271 """Not sure what this does."""
272 return self._shortcmd('RPOP %s' % user)
Guido van Rossum03774bb1998-04-09 13:50:55 +0000273
Guido van Rossum03774bb1998-04-09 13:50:55 +0000274
Christian Heimesd3956292008-11-05 19:48:27 +0000275 timestamp = re.compile(br'\+OK.*(<[^>]+>)')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000276
Christian Heimesd3956292008-11-05 19:48:27 +0000277 def apop(self, user, password):
Tim Peters2344fae2001-01-15 00:50:52 +0000278 """Authorisation
Guido van Rossum03774bb1998-04-09 13:50:55 +0000279
Tim Peters2344fae2001-01-15 00:50:52 +0000280 - only possible if server has supplied a timestamp in initial greeting.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000281
Tim Peters2344fae2001-01-15 00:50:52 +0000282 Args:
Christian Heimesd3956292008-11-05 19:48:27 +0000283 user - mailbox user;
284 password - mailbox password.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000285
Tim Peters2344fae2001-01-15 00:50:52 +0000286 NB: mailbox is locked by server from here to 'quit()'
287 """
Mark Dickinsonea1158f2009-08-06 16:06:25 +0000288 secret = bytes(password, self.encoding)
Moshe Zadkaccc2e3d2001-01-19 19:56:27 +0000289 m = self.timestamp.match(self.welcome)
290 if not m:
Tim Peters2344fae2001-01-15 00:50:52 +0000291 raise error_proto('-ERR APOP not supported by server')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000292 import hashlib
Christian Heimesd3956292008-11-05 19:48:27 +0000293 digest = m.group(1)+secret
294 digest = hashlib.md5(digest).hexdigest()
Tim Peters2344fae2001-01-15 00:50:52 +0000295 return self._shortcmd('APOP %s %s' % (user, digest))
Guido van Rossum03774bb1998-04-09 13:50:55 +0000296
Guido van Rossum03774bb1998-04-09 13:50:55 +0000297
Tim Peters2344fae2001-01-15 00:50:52 +0000298 def top(self, which, howmuch):
299 """Retrieve message header of message number 'which'
300 and first 'howmuch' lines of message body.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000301
Tim Peters2344fae2001-01-15 00:50:52 +0000302 Result is in form ['response', ['line', ...], octets].
303 """
304 return self._longcmd('TOP %s %s' % (which, howmuch))
Guido van Rossum03774bb1998-04-09 13:50:55 +0000305
Guido van Rossum03774bb1998-04-09 13:50:55 +0000306
Tim Peters2344fae2001-01-15 00:50:52 +0000307 def uidl(self, which=None):
308 """Return message digest (unique id) list.
Guido van Rossum03774bb1998-04-09 13:50:55 +0000309
Tim Peters2344fae2001-01-15 00:50:52 +0000310 If 'which', result contains unique id for that message
311 in the form 'response mesgnum uid', otherwise result is
312 the list ['response', ['mesgnum uid', ...], octets]
313 """
Raymond Hettinger16e3c422002-06-01 16:07:16 +0000314 if which is not None:
Tim Peters2344fae2001-01-15 00:50:52 +0000315 return self._shortcmd('UIDL %s' % which)
316 return self._longcmd('UIDL')
Guido van Rossum03774bb1998-04-09 13:50:55 +0000317
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000318try:
319 import ssl
320except ImportError:
321 pass
322else:
Martin v. Löwis48440b72003-10-31 12:52:35 +0000323
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000324 class POP3_SSL(POP3):
325 """POP3 client class over SSL connection
Martin v. Löwis48440b72003-10-31 12:52:35 +0000326
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000327 Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
Martin v. Löwis48440b72003-10-31 12:52:35 +0000328
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000329 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
Martin v. Löwis48440b72003-10-31 12:52:35 +0000333
Christian Heimesd3956292008-11-05 19:48:27 +0000334 See the methods of the parent class POP3 for more documentation.
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000335 """
Martin v. Löwis48440b72003-10-31 12:52:35 +0000336
Giampaolo Rodolà42382fe2010-08-17 16:09:53 +0000337 def __init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None,
338 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None):
339 if context is not None and keyfile is not None:
340 raise ValueError("context and keyfile arguments are mutually "
341 "exclusive")
342 if context is not None and certfile is not None:
343 raise ValueError("context and certfile arguments are mutually "
344 "exclusive")
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000345 self.keyfile = keyfile
346 self.certfile = certfile
Giampaolo Rodolà42382fe2010-08-17 16:09:53 +0000347 self.context = context
Christian Heimesd3956292008-11-05 19:48:27 +0000348 POP3.__init__(self, host, port, timeout)
Martin v. Löwis48440b72003-10-31 12:52:35 +0000349
Christian Heimesd3956292008-11-05 19:48:27 +0000350 def _create_socket(self, timeout):
351 sock = POP3._create_socket(self, timeout)
Giampaolo Rodolà42382fe2010-08-17 16:09:53 +0000352 if self.context is not None:
353 sock = self.context.wrap_socket(sock)
354 else:
355 sock = ssl.wrap_socket(sock, self.keyfile, self.certfile)
356 return sock
Martin v. Löwis48440b72003-10-31 12:52:35 +0000357
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000358 __all__.append("POP3_SSL")
Guido van Rossum03774bb1998-04-09 13:50:55 +0000359
Guido van Rossum484772d1998-04-06 18:27:27 +0000360if __name__ == "__main__":
Eric S. Raymond341f9292001-02-09 06:56:56 +0000361 import sys
362 a = POP3(sys.argv[1])
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000363 print(a.getwelcome())
Eric S. Raymond341f9292001-02-09 06:56:56 +0000364 a.user(sys.argv[2])
365 a.pass_(sys.argv[3])
Tim Peters2344fae2001-01-15 00:50:52 +0000366 a.list()
367 (numMsgs, totalSize) = a.stat()
368 for i in range(1, numMsgs + 1):
369 (header, msg, octets) = a.retr(i)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000370 print("Message %d:" % i)
Tim Peters2344fae2001-01-15 00:50:52 +0000371 for line in msg:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000372 print(' ' + line)
373 print('-----------------------')
Tim Peters2344fae2001-01-15 00:50:52 +0000374 a.quit()