blob: 421cb97139b07d5bc96a67844b65a8f28066e5af [file] [log] [blame]
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001"""IMAP4 client.
2
3Based on RFC 2060.
4
Tim Peters07e99cb2001-01-14 23:47:14 +00005Public class: IMAP4
6Public variable: Debug
7Public functions: Internaldate2tuple
8 Int2AP
9 ParseFlags
10 Time2Internaldate
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000011"""
Guido van Rossumb1f08121998-06-25 02:22:16 +000012
Guido van Rossum98d9fd32000-02-28 15:12:25 +000013# Author: Piers Lauder <piers@cs.su.oz.au> December 1997.
Tim Peters07e99cb2001-01-14 23:47:14 +000014#
Guido van Rossum98d9fd32000-02-28 15:12:25 +000015# Authentication code contributed by Donn Cave <donn@u.washington.edu> June 1998.
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +000016# String method conversion by ESR, February 2001.
Piers Lauder15e5d532001-07-20 10:52:06 +000017# GET/SETACL contributed by Anthony Baxter <anthony@interlink.com.au> April 2001.
Piers Laudera4f83132002-03-08 01:53:24 +000018# IMAP4_SSL contributed by Tino Lange <Tino.Lange@isg.de> March 2002.
Piers Lauder3fca2912002-06-17 07:07:20 +000019# GET/SETQUOTA contributed by Andreas Zeidler <az@kreativkombinat.de> June 2002.
Piers Laudere0273de2002-11-22 05:53:04 +000020# PROXYAUTH contributed by Rick Holbert <holbert.13@osu.edu> November 2002.
Piers Lauderd80ef022005-06-01 23:50:52 +000021# GET/SETANNOTATION contributed by Tomas Lindroos <skitta@abo.fi> June 2005.
Guido van Rossum98d9fd32000-02-28 15:12:25 +000022
Piers Lauderbe5615e2005-08-31 10:50:03 +000023__version__ = "2.58"
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000024
Antoine Pitrou81c87c52010-11-10 08:59:25 +000025import binascii, errno, random, re, socket, subprocess, sys, time
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000026
Antoine Pitrouf3b001f2010-11-12 18:49:16 +000027try:
28 import ssl
29 HAVE_SSL = True
30except ImportError:
31 HAVE_SSL = False
32
Thomas Wouters47b49bf2007-08-30 22:15:33 +000033__all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple",
Barry Warsawf4493912001-01-24 04:16:09 +000034 "Int2AP", "ParseFlags", "Time2Internaldate"]
Skip Montanaro2dd42762001-01-23 15:35:05 +000035
Tim Peters07e99cb2001-01-14 23:47:14 +000036# Globals
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000037
Christian Heimesfb5faf02008-11-05 19:39:50 +000038CRLF = b'\r\n'
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000039Debug = 0
40IMAP4_PORT = 143
Piers Lauder95f84952002-03-08 09:05:12 +000041IMAP4_SSL_PORT = 993
Tim Peters07e99cb2001-01-14 23:47:14 +000042AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000043
Tim Peters07e99cb2001-01-14 23:47:14 +000044# Commands
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000045
46Commands = {
Tim Peters07e99cb2001-01-14 23:47:14 +000047 # name valid states
48 'APPEND': ('AUTH', 'SELECTED'),
49 'AUTHENTICATE': ('NONAUTH',),
50 'CAPABILITY': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
51 'CHECK': ('SELECTED',),
52 'CLOSE': ('SELECTED',),
53 'COPY': ('SELECTED',),
54 'CREATE': ('AUTH', 'SELECTED'),
55 'DELETE': ('AUTH', 'SELECTED'),
Martin v. Löwis7b9190b2004-07-27 05:07:19 +000056 'DELETEACL': ('AUTH', 'SELECTED'),
Tim Peters07e99cb2001-01-14 23:47:14 +000057 'EXAMINE': ('AUTH', 'SELECTED'),
58 'EXPUNGE': ('SELECTED',),
59 'FETCH': ('SELECTED',),
Piers Lauder15e5d532001-07-20 10:52:06 +000060 'GETACL': ('AUTH', 'SELECTED'),
Piers Lauderd80ef022005-06-01 23:50:52 +000061 'GETANNOTATION':('AUTH', 'SELECTED'),
Piers Lauder3fca2912002-06-17 07:07:20 +000062 'GETQUOTA': ('AUTH', 'SELECTED'),
63 'GETQUOTAROOT': ('AUTH', 'SELECTED'),
Martin v. Löwis7b9190b2004-07-27 05:07:19 +000064 'MYRIGHTS': ('AUTH', 'SELECTED'),
Tim Peters07e99cb2001-01-14 23:47:14 +000065 'LIST': ('AUTH', 'SELECTED'),
66 'LOGIN': ('NONAUTH',),
67 'LOGOUT': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
68 'LSUB': ('AUTH', 'SELECTED'),
Piers Lauder15e5d532001-07-20 10:52:06 +000069 'NAMESPACE': ('AUTH', 'SELECTED'),
Tim Peters07e99cb2001-01-14 23:47:14 +000070 'NOOP': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
Piers Lauderf2d7d152002-02-22 01:15:17 +000071 'PARTIAL': ('SELECTED',), # NB: obsolete
Piers Laudere0273de2002-11-22 05:53:04 +000072 'PROXYAUTH': ('AUTH',),
Tim Peters07e99cb2001-01-14 23:47:14 +000073 'RENAME': ('AUTH', 'SELECTED'),
74 'SEARCH': ('SELECTED',),
75 'SELECT': ('AUTH', 'SELECTED'),
Piers Lauder15e5d532001-07-20 10:52:06 +000076 'SETACL': ('AUTH', 'SELECTED'),
Piers Lauderd80ef022005-06-01 23:50:52 +000077 'SETANNOTATION':('AUTH', 'SELECTED'),
Piers Lauder3fca2912002-06-17 07:07:20 +000078 'SETQUOTA': ('AUTH', 'SELECTED'),
Piers Lauder15e5d532001-07-20 10:52:06 +000079 'SORT': ('SELECTED',),
Antoine Pitrouf3b001f2010-11-12 18:49:16 +000080 'STARTTLS': ('NONAUTH',),
Tim Peters07e99cb2001-01-14 23:47:14 +000081 'STATUS': ('AUTH', 'SELECTED'),
82 'STORE': ('SELECTED',),
83 'SUBSCRIBE': ('AUTH', 'SELECTED'),
Martin v. Löwisd8921372003-11-10 06:44:44 +000084 'THREAD': ('SELECTED',),
Tim Peters07e99cb2001-01-14 23:47:14 +000085 'UID': ('SELECTED',),
86 'UNSUBSCRIBE': ('AUTH', 'SELECTED'),
87 }
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000088
Tim Peters07e99cb2001-01-14 23:47:14 +000089# Patterns to match server responses
Guido van Rossumc2c07fa1998-04-09 13:51:46 +000090
Christian Heimesfb5faf02008-11-05 19:39:50 +000091Continuation = re.compile(br'\+( (?P<data>.*))?')
92Flags = re.compile(br'.*FLAGS \((?P<flags>[^\)]*)\)')
93InternalDate = re.compile(br'.*INTERNALDATE "'
94 br'(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9])'
95 br' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
96 br' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
97 br'"')
98Literal = re.compile(br'.*{(?P<size>\d+)}$', re.ASCII)
99MapCRLF = re.compile(br'\r\n|\r|\n')
100Response_code = re.compile(br'\[(?P<type>[A-Z-]+)( (?P<data>[^\]]*))?\]')
101Untagged_response = re.compile(br'\* (?P<type>[A-Z-]+)( (?P<data>.*))?')
Antoine Pitroufd036452008-08-19 17:56:33 +0000102Untagged_status = re.compile(
Christian Heimesfb5faf02008-11-05 19:39:50 +0000103 br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?', re.ASCII)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000104
105
106
107class IMAP4:
108
Tim Peters07e99cb2001-01-14 23:47:14 +0000109 """IMAP4 client class.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000110
Tim Peters07e99cb2001-01-14 23:47:14 +0000111 Instantiate with: IMAP4([host[, port]])
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000112
Tim Peters07e99cb2001-01-14 23:47:14 +0000113 host - host's name (default: localhost);
114 port - port number (default: standard IMAP4 port).
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000115
Tim Peters07e99cb2001-01-14 23:47:14 +0000116 All IMAP4rev1 commands are supported by methods of the same
117 name (in lower-case).
Guido van Rossum6884af71998-05-29 13:34:03 +0000118
Tim Peters07e99cb2001-01-14 23:47:14 +0000119 All arguments to commands are converted to strings, except for
120 AUTHENTICATE, and the last argument to APPEND which is passed as
121 an IMAP4 literal. If necessary (the string contains any
122 non-printing characters or white-space and isn't enclosed with
123 either parentheses or double quotes) each string is quoted.
124 However, the 'password' argument to the LOGIN command is always
125 quoted. If you want to avoid having an argument string quoted
126 (eg: the 'flags' argument to STORE) then enclose the string in
127 parentheses (eg: "(\Deleted)").
Guido van Rossum6884af71998-05-29 13:34:03 +0000128
Tim Peters07e99cb2001-01-14 23:47:14 +0000129 Each command returns a tuple: (type, [data, ...]) where 'type'
130 is usually 'OK' or 'NO', and 'data' is either the text from the
Piers Laudere0273de2002-11-22 05:53:04 +0000131 tagged response, or untagged results from command. Each 'data'
132 is either a string, or a tuple. If a tuple, then the first part
133 is the header of the response, and the second part contains
134 the data (ie: 'literal' value).
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000135
Tim Peters07e99cb2001-01-14 23:47:14 +0000136 Errors raise the exception class <instance>.error("<reason>").
137 IMAP4 server errors raise <instance>.abort("<reason>"),
138 which is a sub-class of 'error'. Mailbox status changes
139 from READ-WRITE to READ-ONLY raise the exception class
140 <instance>.readonly("<reason>"), which is a sub-class of 'abort'.
Guido van Rossumeda960a1998-06-18 14:24:28 +0000141
Tim Peters07e99cb2001-01-14 23:47:14 +0000142 "error" exceptions imply a program error.
143 "abort" exceptions imply the connection should be reset, and
144 the command re-tried.
145 "readonly" exceptions imply the command should be re-tried.
Guido van Rossum8c062211999-12-13 23:27:45 +0000146
Piers Lauderd80ef022005-06-01 23:50:52 +0000147 Note: to use this module, you must read the RFCs pertaining to the
148 IMAP4 protocol, as the semantics of the arguments to each IMAP4
149 command are left to the invoker, not to mention the results. Also,
150 most IMAP servers implement a sub-set of the commands available here.
Tim Peters07e99cb2001-01-14 23:47:14 +0000151 """
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000152
Tim Peters07e99cb2001-01-14 23:47:14 +0000153 class error(Exception): pass # Logical errors - debug required
154 class abort(error): pass # Service errors - close and retry
155 class readonly(abort): pass # Mailbox status changed to READ-ONLY
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000156
Tim Peters07e99cb2001-01-14 23:47:14 +0000157 def __init__(self, host = '', port = IMAP4_PORT):
Tim Peters07e99cb2001-01-14 23:47:14 +0000158 self.debug = Debug
159 self.state = 'LOGOUT'
160 self.literal = None # A literal argument to a command
161 self.tagged_commands = {} # Tagged commands awaiting response
162 self.untagged_responses = {} # {typ: [data, ...], ...}
163 self.continuation_response = '' # Last continuation response
Piers Lauder14f39402005-08-31 10:46:29 +0000164 self.is_readonly = False # READ-ONLY desired state
Tim Peters07e99cb2001-01-14 23:47:14 +0000165 self.tagnum = 0
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000166 self._tls_established = False
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000167
Tim Peters07e99cb2001-01-14 23:47:14 +0000168 # Open socket to server.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000169
Tim Peters07e99cb2001-01-14 23:47:14 +0000170 self.open(host, port)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000171
Tim Peters07e99cb2001-01-14 23:47:14 +0000172 # Create unique tag for this session,
173 # and compile tagged response matcher.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000174
Piers Lauder2dfc1682005-07-05 04:20:07 +0000175 self.tagpre = Int2AP(random.randint(4096, 65535))
Christian Heimesfb5faf02008-11-05 19:39:50 +0000176 self.tagre = re.compile(br'(?P<tag>'
Tim Peters07e99cb2001-01-14 23:47:14 +0000177 + self.tagpre
Christian Heimesfb5faf02008-11-05 19:39:50 +0000178 + br'\d+) (?P<type>[A-Z]+) (?P<data>.*)', re.ASCII)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000179
Tim Peters07e99cb2001-01-14 23:47:14 +0000180 # Get server welcome message,
181 # request and store CAPABILITY response.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000182
Tim Peters07e99cb2001-01-14 23:47:14 +0000183 if __debug__:
Piers Lauderf2d7d152002-02-22 01:15:17 +0000184 self._cmd_log_len = 10
185 self._cmd_log_idx = 0
186 self._cmd_log = {} # Last `_cmd_log_len' interactions
Tim Peters07e99cb2001-01-14 23:47:14 +0000187 if self.debug >= 1:
Piers Lauderf2d7d152002-02-22 01:15:17 +0000188 self._mesg('imaplib version %s' % __version__)
189 self._mesg('new IMAP4 connection, tag=%s' % self.tagpre)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000190
Tim Peters07e99cb2001-01-14 23:47:14 +0000191 self.welcome = self._get_response()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000192 if 'PREAUTH' in self.untagged_responses:
Tim Peters07e99cb2001-01-14 23:47:14 +0000193 self.state = 'AUTH'
Raymond Hettinger54f02222002-06-01 14:18:47 +0000194 elif 'OK' in self.untagged_responses:
Tim Peters07e99cb2001-01-14 23:47:14 +0000195 self.state = 'NONAUTH'
196 else:
197 raise self.error(self.welcome)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000198
Piers Lauderd80ef022005-06-01 23:50:52 +0000199 typ, dat = self.capability()
200 if dat == [None]:
Tim Peters07e99cb2001-01-14 23:47:14 +0000201 raise self.error('no CAPABILITY response from server')
Christian Heimesfb5faf02008-11-05 19:39:50 +0000202 dat = str(dat[-1], "ASCII")
203 dat = dat.upper()
204 self.capabilities = tuple(dat.split())
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000205
Tim Peters07e99cb2001-01-14 23:47:14 +0000206 if __debug__:
207 if self.debug >= 3:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000208 self._mesg('CAPABILITIES: %r' % (self.capabilities,))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000209
Tim Peters07e99cb2001-01-14 23:47:14 +0000210 for version in AllowedVersions:
211 if not version in self.capabilities:
212 continue
213 self.PROTOCOL_VERSION = version
214 return
Guido van Rossumb1f08121998-06-25 02:22:16 +0000215
Tim Peters07e99cb2001-01-14 23:47:14 +0000216 raise self.error('server not IMAP4 compliant')
Guido van Rossum38d8f4e1998-04-11 01:22:34 +0000217
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000218
Tim Peters07e99cb2001-01-14 23:47:14 +0000219 def __getattr__(self, attr):
220 # Allow UPPERCASE variants of IMAP4 command methods.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000221 if attr in Commands:
Piers Lauder15e5d532001-07-20 10:52:06 +0000222 return getattr(self, attr.lower())
Tim Peters07e99cb2001-01-14 23:47:14 +0000223 raise AttributeError("Unknown IMAP4 command: '%s'" % attr)
Guido van Rossum26367a01998-09-28 15:34:46 +0000224
225
226
Piers Lauder15e5d532001-07-20 10:52:06 +0000227 # Overridable methods
Guido van Rossum26367a01998-09-28 15:34:46 +0000228
229
Christian Heimesfb5faf02008-11-05 19:39:50 +0000230 def _create_socket(self):
Antoine Pitrouc1d09362009-05-15 12:15:51 +0000231 return socket.create_connection((self.host, self.port))
Christian Heimesfb5faf02008-11-05 19:39:50 +0000232
Piers Lauderf97b2d72002-06-05 22:31:57 +0000233 def open(self, host = '', port = IMAP4_PORT):
234 """Setup connection to remote server on "host:port"
235 (default: localhost:standard IMAP4 port).
Piers Lauder15e5d532001-07-20 10:52:06 +0000236 This connection will be used by the routines:
237 read, readline, send, shutdown.
238 """
Piers Lauderf97b2d72002-06-05 22:31:57 +0000239 self.host = host
240 self.port = port
Christian Heimesfb5faf02008-11-05 19:39:50 +0000241 self.sock = self._create_socket()
Guido van Rossumc0f1bfe2001-10-15 13:47:08 +0000242 self.file = self.sock.makefile('rb')
Guido van Rossumeda960a1998-06-18 14:24:28 +0000243
244
Piers Lauder15e5d532001-07-20 10:52:06 +0000245 def read(self, size):
246 """Read 'size' bytes from remote."""
Christian Heimesfb5faf02008-11-05 19:39:50 +0000247 chunks = []
248 read = 0
249 while read < size:
250 data = self.file.read(min(size-read, 4096))
251 if not data:
252 break
253 read += len(data)
254 chunks.append(data)
255 return b''.join(chunks)
Piers Lauder15e5d532001-07-20 10:52:06 +0000256
257
258 def readline(self):
259 """Read line from remote."""
260 return self.file.readline()
261
262
263 def send(self, data):
264 """Send data to remote."""
Martin v. Löwise12454f2002-02-16 23:06:19 +0000265 self.sock.sendall(data)
Piers Lauder15e5d532001-07-20 10:52:06 +0000266
Piers Lauderf2d7d152002-02-22 01:15:17 +0000267
Piers Lauder15e5d532001-07-20 10:52:06 +0000268 def shutdown(self):
269 """Close I/O established in "open"."""
270 self.file.close()
Antoine Pitrou81c87c52010-11-10 08:59:25 +0000271 try:
272 self.sock.shutdown(socket.SHUT_RDWR)
273 except socket.error as e:
274 # The server might already have closed the connection
275 if e.errno != errno.ENOTCONN:
276 raise
277 finally:
278 self.sock.close()
Piers Lauder15e5d532001-07-20 10:52:06 +0000279
280
281 def socket(self):
282 """Return socket instance used to connect to IMAP4 server.
283
284 socket = <instance>.socket()
285 """
286 return self.sock
287
288
289
290 # Utility methods
291
292
Tim Peters07e99cb2001-01-14 23:47:14 +0000293 def recent(self):
294 """Return most recent 'RECENT' responses if any exist,
295 else prompt server for an update using the 'NOOP' command.
Guido van Rossum26367a01998-09-28 15:34:46 +0000296
Tim Peters07e99cb2001-01-14 23:47:14 +0000297 (typ, [data]) = <instance>.recent()
Guido van Rossum26367a01998-09-28 15:34:46 +0000298
Tim Peters07e99cb2001-01-14 23:47:14 +0000299 'data' is None if no new messages,
300 else list of RECENT responses, most recent last.
301 """
302 name = 'RECENT'
303 typ, dat = self._untagged_response('OK', [None], name)
304 if dat[-1]:
305 return typ, dat
306 typ, dat = self.noop() # Prod server for response
307 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000308
309
Tim Peters07e99cb2001-01-14 23:47:14 +0000310 def response(self, code):
311 """Return data for response 'code' if received, or None.
Guido van Rossum26367a01998-09-28 15:34:46 +0000312
Tim Peters07e99cb2001-01-14 23:47:14 +0000313 Old value for response 'code' is cleared.
Guido van Rossum26367a01998-09-28 15:34:46 +0000314
Tim Peters07e99cb2001-01-14 23:47:14 +0000315 (code, [data]) = <instance>.response(code)
316 """
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +0000317 return self._untagged_response(code, [None], code.upper())
Guido van Rossum26367a01998-09-28 15:34:46 +0000318
319
Guido van Rossum26367a01998-09-28 15:34:46 +0000320
Tim Peters07e99cb2001-01-14 23:47:14 +0000321 # IMAP4 commands
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000322
323
Tim Peters07e99cb2001-01-14 23:47:14 +0000324 def append(self, mailbox, flags, date_time, message):
325 """Append message to named mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000326
Tim Peters07e99cb2001-01-14 23:47:14 +0000327 (typ, [data]) = <instance>.append(mailbox, flags, date_time, message)
Guido van Rossum8c062211999-12-13 23:27:45 +0000328
Tim Peters07e99cb2001-01-14 23:47:14 +0000329 All args except `message' can be None.
330 """
331 name = 'APPEND'
332 if not mailbox:
333 mailbox = 'INBOX'
334 if flags:
335 if (flags[0],flags[-1]) != ('(',')'):
336 flags = '(%s)' % flags
337 else:
338 flags = None
339 if date_time:
340 date_time = Time2Internaldate(date_time)
341 else:
342 date_time = None
Piers Lauder47404ff2003-04-29 23:40:59 +0000343 self.literal = MapCRLF.sub(CRLF, message)
Tim Peters07e99cb2001-01-14 23:47:14 +0000344 return self._simple_command(name, mailbox, flags, date_time)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000345
346
Tim Peters07e99cb2001-01-14 23:47:14 +0000347 def authenticate(self, mechanism, authobject):
348 """Authenticate command - requires response processing.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000349
Tim Peters07e99cb2001-01-14 23:47:14 +0000350 'mechanism' specifies which authentication mechanism is to
351 be used - it must appear in <instance>.capabilities in the
352 form AUTH=<mechanism>.
Guido van Rossumeda960a1998-06-18 14:24:28 +0000353
Tim Peters07e99cb2001-01-14 23:47:14 +0000354 'authobject' must be a callable object:
Guido van Rossumeda960a1998-06-18 14:24:28 +0000355
Tim Peters07e99cb2001-01-14 23:47:14 +0000356 data = authobject(response)
Guido van Rossumeda960a1998-06-18 14:24:28 +0000357
Tim Peters07e99cb2001-01-14 23:47:14 +0000358 It will be called to process server continuation responses.
359 It should return data that will be encoded and sent to server.
360 It should return None if the client abort response '*' should
361 be sent instead.
362 """
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +0000363 mech = mechanism.upper()
Neal Norwitzb2071702003-06-29 04:21:43 +0000364 # XXX: shouldn't this code be removed, not commented out?
365 #cap = 'AUTH=%s' % mech
Tim Peters77c06fb2002-11-24 02:35:35 +0000366 #if not cap in self.capabilities: # Let the server decide!
Piers Laudere0273de2002-11-22 05:53:04 +0000367 # raise self.error("Server doesn't allow %s authentication." % mech)
Tim Peters07e99cb2001-01-14 23:47:14 +0000368 self.literal = _Authenticator(authobject).process
369 typ, dat = self._simple_command('AUTHENTICATE', mech)
370 if typ != 'OK':
371 raise self.error(dat[-1])
372 self.state = 'AUTH'
373 return typ, dat
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000374
375
Piers Lauderd80ef022005-06-01 23:50:52 +0000376 def capability(self):
377 """(typ, [data]) = <instance>.capability()
378 Fetch capabilities list from server."""
379
380 name = 'CAPABILITY'
381 typ, dat = self._simple_command(name)
382 return self._untagged_response(typ, dat, name)
383
384
Tim Peters07e99cb2001-01-14 23:47:14 +0000385 def check(self):
386 """Checkpoint mailbox on server.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000387
Tim Peters07e99cb2001-01-14 23:47:14 +0000388 (typ, [data]) = <instance>.check()
389 """
390 return self._simple_command('CHECK')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000391
392
Tim Peters07e99cb2001-01-14 23:47:14 +0000393 def close(self):
394 """Close currently selected mailbox.
Guido van Rossumeeec0af1998-04-09 14:20:31 +0000395
Tim Peters07e99cb2001-01-14 23:47:14 +0000396 Deleted messages are removed from writable mailbox.
397 This is the recommended command before 'LOGOUT'.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000398
Tim Peters07e99cb2001-01-14 23:47:14 +0000399 (typ, [data]) = <instance>.close()
400 """
401 try:
402 typ, dat = self._simple_command('CLOSE')
403 finally:
404 self.state = 'AUTH'
405 return typ, dat
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000406
407
Tim Peters07e99cb2001-01-14 23:47:14 +0000408 def copy(self, message_set, new_mailbox):
409 """Copy 'message_set' messages onto end of 'new_mailbox'.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000410
Tim Peters07e99cb2001-01-14 23:47:14 +0000411 (typ, [data]) = <instance>.copy(message_set, new_mailbox)
412 """
413 return self._simple_command('COPY', message_set, new_mailbox)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000414
415
Tim Peters07e99cb2001-01-14 23:47:14 +0000416 def create(self, mailbox):
417 """Create new mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000418
Tim Peters07e99cb2001-01-14 23:47:14 +0000419 (typ, [data]) = <instance>.create(mailbox)
420 """
421 return self._simple_command('CREATE', mailbox)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000422
423
Tim Peters07e99cb2001-01-14 23:47:14 +0000424 def delete(self, mailbox):
425 """Delete old mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000426
Tim Peters07e99cb2001-01-14 23:47:14 +0000427 (typ, [data]) = <instance>.delete(mailbox)
428 """
429 return self._simple_command('DELETE', mailbox)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000430
Martin v. Löwis7b9190b2004-07-27 05:07:19 +0000431 def deleteacl(self, mailbox, who):
432 """Delete the ACLs (remove any rights) set for who on mailbox.
433
434 (typ, [data]) = <instance>.deleteacl(mailbox, who)
435 """
436 return self._simple_command('DELETEACL', mailbox, who)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000437
Tim Peters07e99cb2001-01-14 23:47:14 +0000438 def expunge(self):
439 """Permanently remove deleted items from selected mailbox.
Guido van Rossumeeec0af1998-04-09 14:20:31 +0000440
Tim Peters07e99cb2001-01-14 23:47:14 +0000441 Generates 'EXPUNGE' response for each deleted message.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000442
Tim Peters07e99cb2001-01-14 23:47:14 +0000443 (typ, [data]) = <instance>.expunge()
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000444
Tim Peters07e99cb2001-01-14 23:47:14 +0000445 'data' is list of 'EXPUNGE'd message numbers in order received.
446 """
447 name = 'EXPUNGE'
448 typ, dat = self._simple_command(name)
449 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000450
451
Tim Peters07e99cb2001-01-14 23:47:14 +0000452 def fetch(self, message_set, message_parts):
453 """Fetch (parts of) messages.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000454
Tim Peters07e99cb2001-01-14 23:47:14 +0000455 (typ, [data, ...]) = <instance>.fetch(message_set, message_parts)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000456
Tim Peters07e99cb2001-01-14 23:47:14 +0000457 'message_parts' should be a string of selected parts
458 enclosed in parentheses, eg: "(UID BODY[TEXT])".
Fred Drakefd267d92000-05-25 03:25:26 +0000459
Tim Peters07e99cb2001-01-14 23:47:14 +0000460 'data' are tuples of message part envelope and data.
461 """
462 name = 'FETCH'
463 typ, dat = self._simple_command(name, message_set, message_parts)
464 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000465
466
Piers Lauder15e5d532001-07-20 10:52:06 +0000467 def getacl(self, mailbox):
468 """Get the ACLs for a mailbox.
469
470 (typ, [data]) = <instance>.getacl(mailbox)
471 """
472 typ, dat = self._simple_command('GETACL', mailbox)
473 return self._untagged_response(typ, dat, 'ACL')
474
475
Piers Lauderd80ef022005-06-01 23:50:52 +0000476 def getannotation(self, mailbox, entry, attribute):
477 """(typ, [data]) = <instance>.getannotation(mailbox, entry, attribute)
478 Retrieve ANNOTATIONs."""
479
480 typ, dat = self._simple_command('GETANNOTATION', mailbox, entry, attribute)
481 return self._untagged_response(typ, dat, 'ANNOTATION')
482
483
Piers Lauder3fca2912002-06-17 07:07:20 +0000484 def getquota(self, root):
485 """Get the quota root's resource usage and limits.
486
Neal Norwitz1ed564a2002-06-17 12:43:20 +0000487 Part of the IMAP4 QUOTA extension defined in rfc2087.
Piers Lauder3fca2912002-06-17 07:07:20 +0000488
489 (typ, [data]) = <instance>.getquota(root)
Neal Norwitz1ed564a2002-06-17 12:43:20 +0000490 """
Piers Lauder3fca2912002-06-17 07:07:20 +0000491 typ, dat = self._simple_command('GETQUOTA', root)
492 return self._untagged_response(typ, dat, 'QUOTA')
493
494
495 def getquotaroot(self, mailbox):
496 """Get the list of quota roots for the named mailbox.
497
498 (typ, [[QUOTAROOT responses...], [QUOTA responses]]) = <instance>.getquotaroot(mailbox)
Neal Norwitz1ed564a2002-06-17 12:43:20 +0000499 """
Piers Lauder6a4e6352004-08-10 01:24:54 +0000500 typ, dat = self._simple_command('GETQUOTAROOT', mailbox)
Piers Lauder3fca2912002-06-17 07:07:20 +0000501 typ, quota = self._untagged_response(typ, dat, 'QUOTA')
502 typ, quotaroot = self._untagged_response(typ, dat, 'QUOTAROOT')
Neal Norwitz1ed564a2002-06-17 12:43:20 +0000503 return typ, [quotaroot, quota]
Piers Lauder3fca2912002-06-17 07:07:20 +0000504
505
Tim Peters07e99cb2001-01-14 23:47:14 +0000506 def list(self, directory='""', pattern='*'):
507 """List mailbox names in directory matching pattern.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000508
Tim Peters07e99cb2001-01-14 23:47:14 +0000509 (typ, [data]) = <instance>.list(directory='""', pattern='*')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000510
Tim Peters07e99cb2001-01-14 23:47:14 +0000511 'data' is list of LIST responses.
512 """
513 name = 'LIST'
514 typ, dat = self._simple_command(name, directory, pattern)
515 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000516
517
Tim Peters07e99cb2001-01-14 23:47:14 +0000518 def login(self, user, password):
519 """Identify client using plaintext password.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000520
Tim Peters07e99cb2001-01-14 23:47:14 +0000521 (typ, [data]) = <instance>.login(user, password)
Guido van Rossum8c062211999-12-13 23:27:45 +0000522
Tim Peters07e99cb2001-01-14 23:47:14 +0000523 NB: 'password' will be quoted.
524 """
Tim Peters07e99cb2001-01-14 23:47:14 +0000525 typ, dat = self._simple_command('LOGIN', user, self._quote(password))
526 if typ != 'OK':
527 raise self.error(dat[-1])
528 self.state = 'AUTH'
529 return typ, dat
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000530
531
Piers Laudere0273de2002-11-22 05:53:04 +0000532 def login_cram_md5(self, user, password):
533 """ Force use of CRAM-MD5 authentication.
534
535 (typ, [data]) = <instance>.login_cram_md5(user, password)
536 """
537 self.user, self.password = user, password
538 return self.authenticate('CRAM-MD5', self._CRAM_MD5_AUTH)
539
540
541 def _CRAM_MD5_AUTH(self, challenge):
542 """ Authobject to use with CRAM-MD5 authentication. """
543 import hmac
544 return self.user + " " + hmac.HMAC(self.password, challenge).hexdigest()
545
546
Tim Peters07e99cb2001-01-14 23:47:14 +0000547 def logout(self):
548 """Shutdown connection to server.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000549
Tim Peters07e99cb2001-01-14 23:47:14 +0000550 (typ, [data]) = <instance>.logout()
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000551
Tim Peters07e99cb2001-01-14 23:47:14 +0000552 Returns server 'BYE' response.
553 """
554 self.state = 'LOGOUT'
555 try: typ, dat = self._simple_command('LOGOUT')
556 except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]]
Piers Lauder15e5d532001-07-20 10:52:06 +0000557 self.shutdown()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000558 if 'BYE' in self.untagged_responses:
Tim Peters07e99cb2001-01-14 23:47:14 +0000559 return 'BYE', self.untagged_responses['BYE']
560 return typ, dat
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000561
562
Tim Peters07e99cb2001-01-14 23:47:14 +0000563 def lsub(self, directory='""', pattern='*'):
564 """List 'subscribed' mailbox names in directory matching pattern.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000565
Tim Peters07e99cb2001-01-14 23:47:14 +0000566 (typ, [data, ...]) = <instance>.lsub(directory='""', pattern='*')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000567
Tim Peters07e99cb2001-01-14 23:47:14 +0000568 'data' are tuples of message part envelope and data.
569 """
570 name = 'LSUB'
571 typ, dat = self._simple_command(name, directory, pattern)
572 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000573
Martin v. Löwis7b9190b2004-07-27 05:07:19 +0000574 def myrights(self, mailbox):
575 """Show my ACLs for a mailbox (i.e. the rights that I have on mailbox).
576
577 (typ, [data]) = <instance>.myrights(mailbox)
578 """
579 typ,dat = self._simple_command('MYRIGHTS', mailbox)
580 return self._untagged_response(typ, dat, 'MYRIGHTS')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000581
Piers Lauder15e5d532001-07-20 10:52:06 +0000582 def namespace(self):
583 """ Returns IMAP namespaces ala rfc2342
584
585 (typ, [data, ...]) = <instance>.namespace()
586 """
587 name = 'NAMESPACE'
588 typ, dat = self._simple_command(name)
589 return self._untagged_response(typ, dat, name)
590
591
Tim Peters07e99cb2001-01-14 23:47:14 +0000592 def noop(self):
593 """Send NOOP command.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000594
Piers Laudere0273de2002-11-22 05:53:04 +0000595 (typ, [data]) = <instance>.noop()
Tim Peters07e99cb2001-01-14 23:47:14 +0000596 """
597 if __debug__:
598 if self.debug >= 3:
Piers Lauderf2d7d152002-02-22 01:15:17 +0000599 self._dump_ur(self.untagged_responses)
Tim Peters07e99cb2001-01-14 23:47:14 +0000600 return self._simple_command('NOOP')
Guido van Rossum6884af71998-05-29 13:34:03 +0000601
602
Tim Peters07e99cb2001-01-14 23:47:14 +0000603 def partial(self, message_num, message_part, start, length):
604 """Fetch truncated part of a message.
Guido van Rossumeda960a1998-06-18 14:24:28 +0000605
Tim Peters07e99cb2001-01-14 23:47:14 +0000606 (typ, [data, ...]) = <instance>.partial(message_num, message_part, start, length)
Guido van Rossumeda960a1998-06-18 14:24:28 +0000607
Tim Peters07e99cb2001-01-14 23:47:14 +0000608 'data' is tuple of message part envelope and data.
609 """
610 name = 'PARTIAL'
611 typ, dat = self._simple_command(name, message_num, message_part, start, length)
612 return self._untagged_response(typ, dat, 'FETCH')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000613
614
Piers Laudere0273de2002-11-22 05:53:04 +0000615 def proxyauth(self, user):
616 """Assume authentication as "user".
617
618 Allows an authorised administrator to proxy into any user's
619 mailbox.
620
621 (typ, [data]) = <instance>.proxyauth(user)
622 """
623
624 name = 'PROXYAUTH'
625 return self._simple_command('PROXYAUTH', user)
626
627
Tim Peters07e99cb2001-01-14 23:47:14 +0000628 def rename(self, oldmailbox, newmailbox):
629 """Rename old mailbox name to new.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000630
Piers Laudere0273de2002-11-22 05:53:04 +0000631 (typ, [data]) = <instance>.rename(oldmailbox, newmailbox)
Tim Peters07e99cb2001-01-14 23:47:14 +0000632 """
633 return self._simple_command('RENAME', oldmailbox, newmailbox)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000634
635
Tim Peters07e99cb2001-01-14 23:47:14 +0000636 def search(self, charset, *criteria):
637 """Search mailbox for matching messages.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000638
Martin v. Löwis3ae0f7a2003-03-27 16:59:38 +0000639 (typ, [data]) = <instance>.search(charset, criterion, ...)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000640
Tim Peters07e99cb2001-01-14 23:47:14 +0000641 'data' is space separated list of matching message numbers.
642 """
643 name = 'SEARCH'
644 if charset:
Guido van Rossum68468eb2003-02-27 20:14:51 +0000645 typ, dat = self._simple_command(name, 'CHARSET', charset, *criteria)
Piers Lauder15e5d532001-07-20 10:52:06 +0000646 else:
Guido van Rossum68468eb2003-02-27 20:14:51 +0000647 typ, dat = self._simple_command(name, *criteria)
Tim Peters07e99cb2001-01-14 23:47:14 +0000648 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000649
650
Piers Lauder14f39402005-08-31 10:46:29 +0000651 def select(self, mailbox='INBOX', readonly=False):
Tim Peters07e99cb2001-01-14 23:47:14 +0000652 """Select a mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000653
Tim Peters07e99cb2001-01-14 23:47:14 +0000654 Flush all untagged responses.
Guido van Rossum46586821998-05-18 14:39:42 +0000655
Piers Lauder14f39402005-08-31 10:46:29 +0000656 (typ, [data]) = <instance>.select(mailbox='INBOX', readonly=False)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000657
Tim Peters07e99cb2001-01-14 23:47:14 +0000658 'data' is count of messages in mailbox ('EXISTS' response).
Piers Lauder6a4e6352004-08-10 01:24:54 +0000659
660 Mandated responses are ('FLAGS', 'EXISTS', 'RECENT', 'UIDVALIDITY'), so
661 other responses should be obtained via <instance>.response('FLAGS') etc.
Tim Peters07e99cb2001-01-14 23:47:14 +0000662 """
Tim Peters07e99cb2001-01-14 23:47:14 +0000663 self.untagged_responses = {} # Flush old responses.
664 self.is_readonly = readonly
Piers Lauder14f39402005-08-31 10:46:29 +0000665 if readonly:
Tim Peters07e99cb2001-01-14 23:47:14 +0000666 name = 'EXAMINE'
667 else:
668 name = 'SELECT'
669 typ, dat = self._simple_command(name, mailbox)
670 if typ != 'OK':
671 self.state = 'AUTH' # Might have been 'SELECTED'
672 return typ, dat
673 self.state = 'SELECTED'
Raymond Hettinger54f02222002-06-01 14:18:47 +0000674 if 'READ-ONLY' in self.untagged_responses \
Tim Peters07e99cb2001-01-14 23:47:14 +0000675 and not readonly:
676 if __debug__:
677 if self.debug >= 1:
Piers Lauderf2d7d152002-02-22 01:15:17 +0000678 self._dump_ur(self.untagged_responses)
Tim Peters07e99cb2001-01-14 23:47:14 +0000679 raise self.readonly('%s is not writable' % mailbox)
680 return typ, self.untagged_responses.get('EXISTS', [None])
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000681
682
Piers Lauder15e5d532001-07-20 10:52:06 +0000683 def setacl(self, mailbox, who, what):
684 """Set a mailbox acl.
685
Piers Lauderf167dc32004-03-25 00:12:21 +0000686 (typ, [data]) = <instance>.setacl(mailbox, who, what)
Piers Lauder15e5d532001-07-20 10:52:06 +0000687 """
688 return self._simple_command('SETACL', mailbox, who, what)
689
690
Piers Lauderd80ef022005-06-01 23:50:52 +0000691 def setannotation(self, *args):
692 """(typ, [data]) = <instance>.setannotation(mailbox[, entry, attribute]+)
693 Set ANNOTATIONs."""
694
695 typ, dat = self._simple_command('SETANNOTATION', *args)
696 return self._untagged_response(typ, dat, 'ANNOTATION')
697
698
Piers Lauder3fca2912002-06-17 07:07:20 +0000699 def setquota(self, root, limits):
700 """Set the quota root's resource limits.
701
702 (typ, [data]) = <instance>.setquota(root, limits)
Neal Norwitz1ed564a2002-06-17 12:43:20 +0000703 """
Piers Lauder3fca2912002-06-17 07:07:20 +0000704 typ, dat = self._simple_command('SETQUOTA', root, limits)
705 return self._untagged_response(typ, dat, 'QUOTA')
706
707
Piers Lauder15e5d532001-07-20 10:52:06 +0000708 def sort(self, sort_criteria, charset, *search_criteria):
709 """IMAP4rev1 extension SORT command.
710
711 (typ, [data]) = <instance>.sort(sort_criteria, charset, search_criteria, ...)
712 """
713 name = 'SORT'
Tim Peters87cc0c32001-07-21 01:41:30 +0000714 #if not name in self.capabilities: # Let the server decide!
715 # raise self.error('unimplemented extension command: %s' % name)
Piers Lauder15e5d532001-07-20 10:52:06 +0000716 if (sort_criteria[0],sort_criteria[-1]) != ('(',')'):
Tim Peters87cc0c32001-07-21 01:41:30 +0000717 sort_criteria = '(%s)' % sort_criteria
Guido van Rossum68468eb2003-02-27 20:14:51 +0000718 typ, dat = self._simple_command(name, sort_criteria, charset, *search_criteria)
Piers Lauder15e5d532001-07-20 10:52:06 +0000719 return self._untagged_response(typ, dat, name)
720
721
Antoine Pitrouf3b001f2010-11-12 18:49:16 +0000722 def starttls(self, ssl_context=None):
723 name = 'STARTTLS'
724 if not HAVE_SSL:
725 raise self.error('SSL support missing')
726 if self._tls_established:
727 raise self.abort('TLS session already established')
728 if name not in self.capabilities:
729 raise self.abort('TLS not supported by server')
730 # Generate a default SSL context if none was passed.
731 if ssl_context is None:
732 ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
733 # SSLv2 considered harmful.
734 ssl_context.options |= ssl.OP_NO_SSLv2
735 typ, dat = self._simple_command(name)
736 if typ == 'OK':
737 self.sock = ssl_context.wrap_socket(self.sock)
738 self.file = self.sock.makefile('rb')
739 self._tls_established = True
740 typ, dat = self.capability()
741 if dat == [None]:
742 raise self.error('no CAPABILITY response from server')
743 self.capabilities = tuple(dat[-1].upper().split())
744 else:
745 raise self.error("Couldn't establish TLS session")
746 return self._untagged_response(typ, dat, name)
747
748
Tim Peters07e99cb2001-01-14 23:47:14 +0000749 def status(self, mailbox, names):
750 """Request named status conditions for mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000751
Tim Peters07e99cb2001-01-14 23:47:14 +0000752 (typ, [data]) = <instance>.status(mailbox, names)
753 """
754 name = 'STATUS'
Tim Peters87cc0c32001-07-21 01:41:30 +0000755 #if self.PROTOCOL_VERSION == 'IMAP4': # Let the server decide!
Piers Lauder15e5d532001-07-20 10:52:06 +0000756 # raise self.error('%s unimplemented in IMAP4 (obtain IMAP4rev1 server, or re-code)' % name)
Tim Peters07e99cb2001-01-14 23:47:14 +0000757 typ, dat = self._simple_command(name, mailbox, names)
758 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000759
760
Tim Peters07e99cb2001-01-14 23:47:14 +0000761 def store(self, message_set, command, flags):
762 """Alters flag dispositions for messages in mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000763
Tim Peters07e99cb2001-01-14 23:47:14 +0000764 (typ, [data]) = <instance>.store(message_set, command, flags)
765 """
766 if (flags[0],flags[-1]) != ('(',')'):
767 flags = '(%s)' % flags # Avoid quoting the flags
768 typ, dat = self._simple_command('STORE', message_set, command, flags)
769 return self._untagged_response(typ, dat, 'FETCH')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000770
771
Tim Peters07e99cb2001-01-14 23:47:14 +0000772 def subscribe(self, mailbox):
773 """Subscribe to new mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000774
Tim Peters07e99cb2001-01-14 23:47:14 +0000775 (typ, [data]) = <instance>.subscribe(mailbox)
776 """
777 return self._simple_command('SUBSCRIBE', mailbox)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000778
779
Martin v. Löwisd8921372003-11-10 06:44:44 +0000780 def thread(self, threading_algorithm, charset, *search_criteria):
781 """IMAPrev1 extension THREAD command.
782
Mark Dickinson8ae535b2009-12-24 16:12:49 +0000783 (type, [data]) = <instance>.thread(threading_algorithm, charset, search_criteria, ...)
Martin v. Löwisd8921372003-11-10 06:44:44 +0000784 """
785 name = 'THREAD'
786 typ, dat = self._simple_command(name, threading_algorithm, charset, *search_criteria)
787 return self._untagged_response(typ, dat, name)
788
789
Tim Peters07e99cb2001-01-14 23:47:14 +0000790 def uid(self, command, *args):
791 """Execute "command arg ..." with messages identified by UID,
792 rather than message number.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000793
Tim Peters07e99cb2001-01-14 23:47:14 +0000794 (typ, [data]) = <instance>.uid(command, arg1, arg2, ...)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000795
Tim Peters07e99cb2001-01-14 23:47:14 +0000796 Returns response appropriate to 'command'.
797 """
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +0000798 command = command.upper()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000799 if not command in Commands:
Tim Peters07e99cb2001-01-14 23:47:14 +0000800 raise self.error("Unknown IMAP4 UID command: %s" % command)
801 if self.state not in Commands[command]:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000802 raise self.error("command %s illegal in state %s, "
803 "only allowed in states %s" %
804 (command, self.state,
805 ', '.join(Commands[command])))
Tim Peters07e99cb2001-01-14 23:47:14 +0000806 name = 'UID'
Guido van Rossum68468eb2003-02-27 20:14:51 +0000807 typ, dat = self._simple_command(name, command, *args)
Georg Brandl05245f72010-07-31 22:32:52 +0000808 if command in ('SEARCH', 'SORT', 'THREAD'):
Piers Lauder15e5d532001-07-20 10:52:06 +0000809 name = command
Tim Peters07e99cb2001-01-14 23:47:14 +0000810 else:
811 name = 'FETCH'
812 return self._untagged_response(typ, dat, name)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000813
814
Tim Peters07e99cb2001-01-14 23:47:14 +0000815 def unsubscribe(self, mailbox):
816 """Unsubscribe from old mailbox.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000817
Tim Peters07e99cb2001-01-14 23:47:14 +0000818 (typ, [data]) = <instance>.unsubscribe(mailbox)
819 """
820 return self._simple_command('UNSUBSCRIBE', mailbox)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000821
822
Tim Peters07e99cb2001-01-14 23:47:14 +0000823 def xatom(self, name, *args):
824 """Allow simple extension commands
825 notified by server in CAPABILITY response.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000826
Piers Lauder15e5d532001-07-20 10:52:06 +0000827 Assumes command is legal in current state.
828
Tim Peters07e99cb2001-01-14 23:47:14 +0000829 (typ, [data]) = <instance>.xatom(name, arg, ...)
Piers Lauder15e5d532001-07-20 10:52:06 +0000830
831 Returns response appropriate to extension command `name'.
Tim Peters07e99cb2001-01-14 23:47:14 +0000832 """
Piers Lauder15e5d532001-07-20 10:52:06 +0000833 name = name.upper()
Tim Peters87cc0c32001-07-21 01:41:30 +0000834 #if not name in self.capabilities: # Let the server decide!
Piers Lauder15e5d532001-07-20 10:52:06 +0000835 # raise self.error('unknown extension command: %s' % name)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000836 if not name in Commands:
Piers Lauder15e5d532001-07-20 10:52:06 +0000837 Commands[name] = (self.state,)
Guido van Rossum68468eb2003-02-27 20:14:51 +0000838 return self._simple_command(name, *args)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000839
840
841
Tim Peters07e99cb2001-01-14 23:47:14 +0000842 # Private methods
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000843
844
Tim Peters07e99cb2001-01-14 23:47:14 +0000845 def _append_untagged(self, typ, dat):
Christian Heimesfb5faf02008-11-05 19:39:50 +0000846 if dat is None:
847 dat = b''
Tim Peters07e99cb2001-01-14 23:47:14 +0000848 ur = self.untagged_responses
849 if __debug__:
850 if self.debug >= 5:
Christian Heimesfb5faf02008-11-05 19:39:50 +0000851 self._mesg('untagged_responses[%s] %s += ["%r"]' %
Tim Peters07e99cb2001-01-14 23:47:14 +0000852 (typ, len(ur.get(typ,'')), dat))
Raymond Hettinger54f02222002-06-01 14:18:47 +0000853 if typ in ur:
Tim Peters07e99cb2001-01-14 23:47:14 +0000854 ur[typ].append(dat)
855 else:
856 ur[typ] = [dat]
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000857
858
Tim Peters07e99cb2001-01-14 23:47:14 +0000859 def _check_bye(self):
860 bye = self.untagged_responses.get('BYE')
861 if bye:
Antoine Pitroudac47912010-11-10 00:18:40 +0000862 raise self.abort(bye[-1].decode('ascii', 'replace'))
Guido van Rossum8c062211999-12-13 23:27:45 +0000863
864
Tim Peters07e99cb2001-01-14 23:47:14 +0000865 def _command(self, name, *args):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000866
Tim Peters07e99cb2001-01-14 23:47:14 +0000867 if self.state not in Commands[name]:
868 self.literal = None
Guido van Rossumd8faa362007-04-27 19:54:29 +0000869 raise self.error("command %s illegal in state %s, "
870 "only allowed in states %s" %
871 (name, self.state,
872 ', '.join(Commands[name])))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000873
Tim Peters07e99cb2001-01-14 23:47:14 +0000874 for typ in ('OK', 'NO', 'BAD'):
Raymond Hettinger54f02222002-06-01 14:18:47 +0000875 if typ in self.untagged_responses:
Tim Peters07e99cb2001-01-14 23:47:14 +0000876 del self.untagged_responses[typ]
Guido van Rossum26367a01998-09-28 15:34:46 +0000877
Raymond Hettinger54f02222002-06-01 14:18:47 +0000878 if 'READ-ONLY' in self.untagged_responses \
Tim Peters07e99cb2001-01-14 23:47:14 +0000879 and not self.is_readonly:
880 raise self.readonly('mailbox status changed to READ-ONLY')
Guido van Rossumeda960a1998-06-18 14:24:28 +0000881
Tim Peters07e99cb2001-01-14 23:47:14 +0000882 tag = self._new_tag()
Christian Heimesfb5faf02008-11-05 19:39:50 +0000883 name = bytes(name, 'ASCII')
884 data = tag + b' ' + name
Tim Peters07e99cb2001-01-14 23:47:14 +0000885 for arg in args:
886 if arg is None: continue
Christian Heimesfb5faf02008-11-05 19:39:50 +0000887 if isinstance(arg, str):
888 arg = bytes(arg, "ASCII")
Christian Heimesfb5faf02008-11-05 19:39:50 +0000889 data = data + b' ' + arg
Guido van Rossum6884af71998-05-29 13:34:03 +0000890
Tim Peters07e99cb2001-01-14 23:47:14 +0000891 literal = self.literal
892 if literal is not None:
893 self.literal = None
894 if type(literal) is type(self._command):
895 literator = literal
896 else:
897 literator = None
Christian Heimesfb5faf02008-11-05 19:39:50 +0000898 data = data + bytes(' {%s}' % len(literal), 'ASCII')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000899
Tim Peters07e99cb2001-01-14 23:47:14 +0000900 if __debug__:
901 if self.debug >= 4:
Christian Heimesfb5faf02008-11-05 19:39:50 +0000902 self._mesg('> %r' % data)
Tim Peters07e99cb2001-01-14 23:47:14 +0000903 else:
Christian Heimesfb5faf02008-11-05 19:39:50 +0000904 self._log('> %r' % data)
Guido van Rossum8c062211999-12-13 23:27:45 +0000905
Tim Peters07e99cb2001-01-14 23:47:14 +0000906 try:
Christian Heimesfb5faf02008-11-05 19:39:50 +0000907 self.send(data + CRLF)
Guido van Rossumb940e112007-01-10 16:19:56 +0000908 except (socket.error, OSError) as val:
Tim Peters07e99cb2001-01-14 23:47:14 +0000909 raise self.abort('socket error: %s' % val)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000910
Tim Peters07e99cb2001-01-14 23:47:14 +0000911 if literal is None:
912 return tag
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000913
Tim Peters07e99cb2001-01-14 23:47:14 +0000914 while 1:
915 # Wait for continuation response
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000916
Tim Peters07e99cb2001-01-14 23:47:14 +0000917 while self._get_response():
918 if self.tagged_commands[tag]: # BAD/NO?
919 return tag
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000920
Tim Peters07e99cb2001-01-14 23:47:14 +0000921 # Send literal
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000922
Tim Peters07e99cb2001-01-14 23:47:14 +0000923 if literator:
924 literal = literator(self.continuation_response)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000925
Tim Peters07e99cb2001-01-14 23:47:14 +0000926 if __debug__:
927 if self.debug >= 4:
Piers Lauderf2d7d152002-02-22 01:15:17 +0000928 self._mesg('write literal size %s' % len(literal))
Guido van Rossumeda960a1998-06-18 14:24:28 +0000929
Tim Peters07e99cb2001-01-14 23:47:14 +0000930 try:
Piers Lauder15e5d532001-07-20 10:52:06 +0000931 self.send(literal)
932 self.send(CRLF)
Guido van Rossumb940e112007-01-10 16:19:56 +0000933 except (socket.error, OSError) as val:
Tim Peters07e99cb2001-01-14 23:47:14 +0000934 raise self.abort('socket error: %s' % val)
Guido van Rossumeda960a1998-06-18 14:24:28 +0000935
Tim Peters07e99cb2001-01-14 23:47:14 +0000936 if not literator:
937 break
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000938
Tim Peters07e99cb2001-01-14 23:47:14 +0000939 return tag
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000940
941
Tim Peters07e99cb2001-01-14 23:47:14 +0000942 def _command_complete(self, name, tag):
Antoine Pitroudac47912010-11-10 00:18:40 +0000943 # BYE is expected after LOGOUT
944 if name != 'LOGOUT':
945 self._check_bye()
Tim Peters07e99cb2001-01-14 23:47:14 +0000946 try:
947 typ, data = self._get_tagged_response(tag)
Guido van Rossumb940e112007-01-10 16:19:56 +0000948 except self.abort as val:
Tim Peters07e99cb2001-01-14 23:47:14 +0000949 raise self.abort('command: %s => %s' % (name, val))
Guido van Rossumb940e112007-01-10 16:19:56 +0000950 except self.error as val:
Tim Peters07e99cb2001-01-14 23:47:14 +0000951 raise self.error('command: %s => %s' % (name, val))
Antoine Pitroudac47912010-11-10 00:18:40 +0000952 if name != 'LOGOUT':
953 self._check_bye()
Tim Peters07e99cb2001-01-14 23:47:14 +0000954 if typ == 'BAD':
955 raise self.error('%s command error: %s %s' % (name, typ, data))
956 return typ, data
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000957
958
Tim Peters07e99cb2001-01-14 23:47:14 +0000959 def _get_response(self):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000960
Tim Peters07e99cb2001-01-14 23:47:14 +0000961 # Read response and store.
962 #
963 # Returns None for continuation responses,
964 # otherwise first response line received.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000965
Tim Peters07e99cb2001-01-14 23:47:14 +0000966 resp = self._get_line()
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000967
Tim Peters07e99cb2001-01-14 23:47:14 +0000968 # Command completion response?
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000969
Tim Peters07e99cb2001-01-14 23:47:14 +0000970 if self._match(self.tagre, resp):
971 tag = self.mo.group('tag')
Raymond Hettinger54f02222002-06-01 14:18:47 +0000972 if not tag in self.tagged_commands:
Tim Peters07e99cb2001-01-14 23:47:14 +0000973 raise self.abort('unexpected tagged response: %s' % resp)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000974
Tim Peters07e99cb2001-01-14 23:47:14 +0000975 typ = self.mo.group('type')
Christian Heimesfb5faf02008-11-05 19:39:50 +0000976 typ = str(typ, 'ASCII')
Tim Peters07e99cb2001-01-14 23:47:14 +0000977 dat = self.mo.group('data')
978 self.tagged_commands[tag] = (typ, [dat])
979 else:
980 dat2 = None
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000981
Tim Peters07e99cb2001-01-14 23:47:14 +0000982 # '*' (untagged) responses?
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000983
Tim Peters07e99cb2001-01-14 23:47:14 +0000984 if not self._match(Untagged_response, resp):
985 if self._match(Untagged_status, resp):
986 dat2 = self.mo.group('data2')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000987
Tim Peters07e99cb2001-01-14 23:47:14 +0000988 if self.mo is None:
989 # Only other possibility is '+' (continuation) response...
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000990
Tim Peters07e99cb2001-01-14 23:47:14 +0000991 if self._match(Continuation, resp):
992 self.continuation_response = self.mo.group('data')
993 return None # NB: indicates continuation
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000994
Tim Peters07e99cb2001-01-14 23:47:14 +0000995 raise self.abort("unexpected response: '%s'" % resp)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +0000996
Tim Peters07e99cb2001-01-14 23:47:14 +0000997 typ = self.mo.group('type')
Christian Heimesfb5faf02008-11-05 19:39:50 +0000998 typ = str(typ, 'ascii')
Tim Peters07e99cb2001-01-14 23:47:14 +0000999 dat = self.mo.group('data')
Christian Heimesfb5faf02008-11-05 19:39:50 +00001000 if dat is None: dat = b'' # Null untagged response
1001 if dat2: dat = dat + b' ' + dat2
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001002
Tim Peters07e99cb2001-01-14 23:47:14 +00001003 # Is there a literal to come?
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001004
Tim Peters07e99cb2001-01-14 23:47:14 +00001005 while self._match(Literal, dat):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001006
Tim Peters07e99cb2001-01-14 23:47:14 +00001007 # Read literal direct from connection.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001008
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +00001009 size = int(self.mo.group('size'))
Tim Peters07e99cb2001-01-14 23:47:14 +00001010 if __debug__:
1011 if self.debug >= 4:
Piers Lauderf2d7d152002-02-22 01:15:17 +00001012 self._mesg('read literal size %s' % size)
Piers Lauder15e5d532001-07-20 10:52:06 +00001013 data = self.read(size)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001014
Tim Peters07e99cb2001-01-14 23:47:14 +00001015 # Store response with literal as tuple
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001016
Tim Peters07e99cb2001-01-14 23:47:14 +00001017 self._append_untagged(typ, (dat, data))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001018
Tim Peters07e99cb2001-01-14 23:47:14 +00001019 # Read trailer - possibly containing another literal
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001020
Tim Peters07e99cb2001-01-14 23:47:14 +00001021 dat = self._get_line()
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001022
Tim Peters07e99cb2001-01-14 23:47:14 +00001023 self._append_untagged(typ, dat)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001024
Tim Peters07e99cb2001-01-14 23:47:14 +00001025 # Bracketed response information?
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001026
Tim Peters07e99cb2001-01-14 23:47:14 +00001027 if typ in ('OK', 'NO', 'BAD') and self._match(Response_code, dat):
Christian Heimesfb5faf02008-11-05 19:39:50 +00001028 typ = self.mo.group('type')
1029 typ = str(typ, "ASCII")
1030 self._append_untagged(typ, self.mo.group('data'))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001031
Tim Peters07e99cb2001-01-14 23:47:14 +00001032 if __debug__:
1033 if self.debug >= 1 and typ in ('NO', 'BAD', 'BYE'):
Christian Heimesfb5faf02008-11-05 19:39:50 +00001034 self._mesg('%s response: %r' % (typ, dat))
Guido van Rossum26367a01998-09-28 15:34:46 +00001035
Tim Peters07e99cb2001-01-14 23:47:14 +00001036 return resp
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001037
1038
Tim Peters07e99cb2001-01-14 23:47:14 +00001039 def _get_tagged_response(self, tag):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001040
Tim Peters07e99cb2001-01-14 23:47:14 +00001041 while 1:
1042 result = self.tagged_commands[tag]
1043 if result is not None:
1044 del self.tagged_commands[tag]
1045 return result
Guido van Rossumf36b1822000-02-17 17:12:39 +00001046
Tim Peters07e99cb2001-01-14 23:47:14 +00001047 # Some have reported "unexpected response" exceptions.
1048 # Note that ignoring them here causes loops.
1049 # Instead, send me details of the unexpected response and
1050 # I'll update the code in `_get_response()'.
Guido van Rossumf36b1822000-02-17 17:12:39 +00001051
Tim Peters07e99cb2001-01-14 23:47:14 +00001052 try:
1053 self._get_response()
Guido van Rossumb940e112007-01-10 16:19:56 +00001054 except self.abort as val:
Tim Peters07e99cb2001-01-14 23:47:14 +00001055 if __debug__:
1056 if self.debug >= 1:
Piers Lauderf2d7d152002-02-22 01:15:17 +00001057 self.print_log()
Tim Peters07e99cb2001-01-14 23:47:14 +00001058 raise
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001059
1060
Tim Peters07e99cb2001-01-14 23:47:14 +00001061 def _get_line(self):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001062
Piers Lauder15e5d532001-07-20 10:52:06 +00001063 line = self.readline()
Tim Peters07e99cb2001-01-14 23:47:14 +00001064 if not line:
1065 raise self.abort('socket error: EOF')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001066
Tim Peters07e99cb2001-01-14 23:47:14 +00001067 # Protocol mandates all lines terminated by CRLF
R. David Murraye8dc2582009-12-10 02:08:06 +00001068 if not line.endswith(b'\r\n'):
1069 raise self.abort('socket error: unterminated line')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001070
Tim Peters07e99cb2001-01-14 23:47:14 +00001071 line = line[:-2]
1072 if __debug__:
1073 if self.debug >= 4:
Christian Heimesfb5faf02008-11-05 19:39:50 +00001074 self._mesg('< %r' % line)
Tim Peters07e99cb2001-01-14 23:47:14 +00001075 else:
Christian Heimesfb5faf02008-11-05 19:39:50 +00001076 self._log('< %r' % line)
Tim Peters07e99cb2001-01-14 23:47:14 +00001077 return line
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001078
1079
Tim Peters07e99cb2001-01-14 23:47:14 +00001080 def _match(self, cre, s):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001081
Tim Peters07e99cb2001-01-14 23:47:14 +00001082 # Run compiled regular expression match method on 's'.
1083 # Save result, return success.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001084
Tim Peters07e99cb2001-01-14 23:47:14 +00001085 self.mo = cre.match(s)
1086 if __debug__:
1087 if self.mo is not None and self.debug >= 5:
Christian Heimesfb5faf02008-11-05 19:39:50 +00001088 self._mesg("\tmatched r'%r' => %r" % (cre.pattern, self.mo.groups()))
Tim Peters07e99cb2001-01-14 23:47:14 +00001089 return self.mo is not None
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001090
1091
Tim Peters07e99cb2001-01-14 23:47:14 +00001092 def _new_tag(self):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001093
Christian Heimesfb5faf02008-11-05 19:39:50 +00001094 tag = self.tagpre + bytes(str(self.tagnum), 'ASCII')
Tim Peters07e99cb2001-01-14 23:47:14 +00001095 self.tagnum = self.tagnum + 1
1096 self.tagged_commands[tag] = None
1097 return tag
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001098
1099
Tim Peters07e99cb2001-01-14 23:47:14 +00001100 def _quote(self, arg):
Guido van Rossum8c062211999-12-13 23:27:45 +00001101
Antoine Pitroub1436f12010-11-09 22:55:55 +00001102 arg = arg.replace('\\', '\\\\')
1103 arg = arg.replace('"', '\\"')
Guido van Rossum8c062211999-12-13 23:27:45 +00001104
Antoine Pitroub1436f12010-11-09 22:55:55 +00001105 return '"' + arg + '"'
Guido van Rossum8c062211999-12-13 23:27:45 +00001106
1107
Tim Peters07e99cb2001-01-14 23:47:14 +00001108 def _simple_command(self, name, *args):
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001109
Guido van Rossum68468eb2003-02-27 20:14:51 +00001110 return self._command_complete(name, self._command(name, *args))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001111
1112
Tim Peters07e99cb2001-01-14 23:47:14 +00001113 def _untagged_response(self, typ, dat, name):
Tim Peters07e99cb2001-01-14 23:47:14 +00001114 if typ == 'NO':
1115 return typ, dat
Raymond Hettinger54f02222002-06-01 14:18:47 +00001116 if not name in self.untagged_responses:
Tim Peters07e99cb2001-01-14 23:47:14 +00001117 return typ, [None]
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +00001118 data = self.untagged_responses.pop(name)
Tim Peters07e99cb2001-01-14 23:47:14 +00001119 if __debug__:
1120 if self.debug >= 5:
Piers Lauderf2d7d152002-02-22 01:15:17 +00001121 self._mesg('untagged_responses[%s] => %s' % (name, data))
Tim Peters07e99cb2001-01-14 23:47:14 +00001122 return typ, data
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001123
1124
Piers Lauderf2d7d152002-02-22 01:15:17 +00001125 if __debug__:
1126
1127 def _mesg(self, s, secs=None):
1128 if secs is None:
1129 secs = time.time()
1130 tm = time.strftime('%M:%S', time.localtime(secs))
1131 sys.stderr.write(' %s.%02d %s\n' % (tm, (secs*100)%100, s))
1132 sys.stderr.flush()
1133
1134 def _dump_ur(self, dict):
1135 # Dump untagged responses (in `dict').
1136 l = dict.items()
1137 if not l: return
1138 t = '\n\t\t'
1139 l = map(lambda x:'%s: "%s"' % (x[0], x[1][0] and '" "'.join(x[1]) or ''), l)
1140 self._mesg('untagged responses dump:%s%s' % (t, t.join(l)))
1141
1142 def _log(self, line):
1143 # Keep log of last `_cmd_log_len' interactions for debugging.
1144 self._cmd_log[self._cmd_log_idx] = (line, time.time())
1145 self._cmd_log_idx += 1
1146 if self._cmd_log_idx >= self._cmd_log_len:
1147 self._cmd_log_idx = 0
1148
1149 def print_log(self):
1150 self._mesg('last %d IMAP4 interactions:' % len(self._cmd_log))
1151 i, n = self._cmd_log_idx, self._cmd_log_len
1152 while n:
1153 try:
Guido van Rossum68468eb2003-02-27 20:14:51 +00001154 self._mesg(*self._cmd_log[i])
Piers Lauderf2d7d152002-02-22 01:15:17 +00001155 except:
1156 pass
1157 i += 1
1158 if i >= self._cmd_log_len:
1159 i = 0
1160 n -= 1
1161
1162
Antoine Pitrouf3b001f2010-11-12 18:49:16 +00001163if HAVE_SSL:
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001164
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001165 class IMAP4_SSL(IMAP4):
Piers Laudera4f83132002-03-08 01:53:24 +00001166
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001167 """IMAP4 client class over SSL connection
Piers Laudera4f83132002-03-08 01:53:24 +00001168
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001169 Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile]]]])
Piers Laudera4f83132002-03-08 01:53:24 +00001170
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001171 host - host's name (default: localhost);
1172 port - port number (default: standard IMAP4 SSL port).
1173 keyfile - PEM formatted file that contains your private key (default: None);
1174 certfile - PEM formatted certificate chain file (default: None);
Piers Laudera4f83132002-03-08 01:53:24 +00001175
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001176 for more documentation see the docstring of the parent class IMAP4.
Piers Laudera4f83132002-03-08 01:53:24 +00001177 """
Piers Laudera4f83132002-03-08 01:53:24 +00001178
1179
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001180 def __init__(self, host = '', port = IMAP4_SSL_PORT, keyfile = None, certfile = None):
1181 self.keyfile = keyfile
1182 self.certfile = certfile
1183 IMAP4.__init__(self, host, port)
Piers Laudera4f83132002-03-08 01:53:24 +00001184
Christian Heimesfb5faf02008-11-05 19:39:50 +00001185 def _create_socket(self):
1186 sock = IMAP4._create_socket(self)
1187 return ssl.wrap_socket(sock, self.keyfile, self.certfile)
Piers Laudera4f83132002-03-08 01:53:24 +00001188
Christian Heimesfb5faf02008-11-05 19:39:50 +00001189 def open(self, host='', port=IMAP4_SSL_PORT):
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001190 """Setup connection to remote server on "host:port".
1191 (default: localhost:standard IMAP4 SSL port).
1192 This connection will be used by the routines:
1193 read, readline, send, shutdown.
1194 """
Christian Heimesfb5faf02008-11-05 19:39:50 +00001195 IMAP4.open(self, host, port)
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001196
1197 __all__.append("IMAP4_SSL")
Piers Laudera4f83132002-03-08 01:53:24 +00001198
1199
Piers Laudere0273de2002-11-22 05:53:04 +00001200class IMAP4_stream(IMAP4):
1201
1202 """IMAP4 client class over a stream
1203
1204 Instantiate with: IMAP4_stream(command)
1205
Christian Heimesfb5faf02008-11-05 19:39:50 +00001206 where "command" is a string that can be passed to subprocess.Popen()
Piers Laudere0273de2002-11-22 05:53:04 +00001207
1208 for more documentation see the docstring of the parent class IMAP4.
1209 """
1210
1211
1212 def __init__(self, command):
1213 self.command = command
1214 IMAP4.__init__(self)
1215
1216
1217 def open(self, host = None, port = None):
1218 """Setup a stream connection.
1219 This connection will be used by the routines:
1220 read, readline, send, shutdown.
1221 """
1222 self.host = None # For compatibility with parent class
1223 self.port = None
1224 self.sock = None
1225 self.file = None
Christian Heimesfb5faf02008-11-05 19:39:50 +00001226 self.process = subprocess.Popen(self.command,
1227 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1228 shell=True, close_fds=True)
1229 self.writefile = self.process.stdin
1230 self.readfile = self.process.stdout
Piers Laudere0273de2002-11-22 05:53:04 +00001231
1232 def read(self, size):
1233 """Read 'size' bytes from remote."""
1234 return self.readfile.read(size)
1235
1236
1237 def readline(self):
1238 """Read line from remote."""
1239 return self.readfile.readline()
1240
1241
1242 def send(self, data):
1243 """Send data to remote."""
1244 self.writefile.write(data)
1245 self.writefile.flush()
1246
1247
1248 def shutdown(self):
1249 """Close I/O established in "open"."""
1250 self.readfile.close()
1251 self.writefile.close()
Christian Heimesfb5faf02008-11-05 19:39:50 +00001252 self.process.wait()
Piers Laudere0273de2002-11-22 05:53:04 +00001253
1254
1255
Guido van Rossumeda960a1998-06-18 14:24:28 +00001256class _Authenticator:
1257
Tim Peters07e99cb2001-01-14 23:47:14 +00001258 """Private class to provide en/decoding
1259 for base64-based authentication conversation.
1260 """
Guido van Rossumeda960a1998-06-18 14:24:28 +00001261
Tim Peters07e99cb2001-01-14 23:47:14 +00001262 def __init__(self, mechinst):
1263 self.mech = mechinst # Callable object to provide/process data
Guido van Rossumeda960a1998-06-18 14:24:28 +00001264
Tim Peters07e99cb2001-01-14 23:47:14 +00001265 def process(self, data):
1266 ret = self.mech(self.decode(data))
1267 if ret is None:
1268 return '*' # Abort conversation
1269 return self.encode(ret)
Guido van Rossumeda960a1998-06-18 14:24:28 +00001270
Tim Peters07e99cb2001-01-14 23:47:14 +00001271 def encode(self, inp):
1272 #
1273 # Invoke binascii.b2a_base64 iteratively with
1274 # short even length buffers, strip the trailing
1275 # line feed from the result and append. "Even"
1276 # means a number that factors to both 6 and 8,
1277 # so when it gets to the end of the 8-bit input
1278 # there's no partial 6-bit output.
1279 #
1280 oup = ''
1281 while inp:
1282 if len(inp) > 48:
1283 t = inp[:48]
1284 inp = inp[48:]
1285 else:
1286 t = inp
1287 inp = ''
1288 e = binascii.b2a_base64(t)
1289 if e:
1290 oup = oup + e[:-1]
1291 return oup
1292
1293 def decode(self, inp):
1294 if not inp:
1295 return ''
1296 return binascii.a2b_base64(inp)
1297
Guido van Rossumeda960a1998-06-18 14:24:28 +00001298
1299
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001300Mon2num = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
Tim Peters07e99cb2001-01-14 23:47:14 +00001301 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001302
1303def Internaldate2tuple(resp):
Tim Peters07e99cb2001-01-14 23:47:14 +00001304 """Convert IMAP4 INTERNALDATE to UT.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001305
Tim Peters07e99cb2001-01-14 23:47:14 +00001306 Returns Python time module tuple.
1307 """
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001308
Tim Peters07e99cb2001-01-14 23:47:14 +00001309 mo = InternalDate.match(resp)
1310 if not mo:
1311 return None
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001312
Tim Peters07e99cb2001-01-14 23:47:14 +00001313 mon = Mon2num[mo.group('mon')]
1314 zonen = mo.group('zonen')
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001315
Jeremy Hyltonf5d3ea02001-02-22 13:24:27 +00001316 day = int(mo.group('day'))
1317 year = int(mo.group('year'))
1318 hour = int(mo.group('hour'))
1319 min = int(mo.group('min'))
1320 sec = int(mo.group('sec'))
1321 zoneh = int(mo.group('zoneh'))
1322 zonem = int(mo.group('zonem'))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001323
Tim Peters07e99cb2001-01-14 23:47:14 +00001324 # INTERNALDATE timezone must be subtracted to get UT
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001325
Tim Peters07e99cb2001-01-14 23:47:14 +00001326 zone = (zoneh*60 + zonem)*60
1327 if zonen == '-':
1328 zone = -zone
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001329
Tim Peters07e99cb2001-01-14 23:47:14 +00001330 tt = (year, mon, day, hour, min, sec, -1, -1, -1)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001331
Tim Peters07e99cb2001-01-14 23:47:14 +00001332 utc = time.mktime(tt)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001333
Tim Peters07e99cb2001-01-14 23:47:14 +00001334 # Following is necessary because the time module has no 'mkgmtime'.
1335 # 'mktime' assumes arg in local timezone, so adds timezone/altzone.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001336
Tim Peters07e99cb2001-01-14 23:47:14 +00001337 lt = time.localtime(utc)
1338 if time.daylight and lt[-1]:
1339 zone = zone + time.altzone
1340 else:
1341 zone = zone + time.timezone
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001342
Tim Peters07e99cb2001-01-14 23:47:14 +00001343 return time.localtime(utc - zone)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001344
1345
1346
1347def Int2AP(num):
1348
Tim Peters07e99cb2001-01-14 23:47:14 +00001349 """Convert integer to A-P string representation."""
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001350
Christian Heimesfb5faf02008-11-05 19:39:50 +00001351 val = b''; AP = b'ABCDEFGHIJKLMNOP'
Tim Peters07e99cb2001-01-14 23:47:14 +00001352 num = int(abs(num))
1353 while num:
1354 num, mod = divmod(num, 16)
Christian Heimesfb5faf02008-11-05 19:39:50 +00001355 val = AP[mod:mod+1] + val
Tim Peters07e99cb2001-01-14 23:47:14 +00001356 return val
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001357
1358
1359
1360def ParseFlags(resp):
1361
Tim Peters07e99cb2001-01-14 23:47:14 +00001362 """Convert IMAP4 flags response to python tuple."""
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001363
Tim Peters07e99cb2001-01-14 23:47:14 +00001364 mo = Flags.match(resp)
1365 if not mo:
1366 return ()
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001367
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +00001368 return tuple(mo.group('flags').split())
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001369
1370
1371def Time2Internaldate(date_time):
1372
Tim Peters07e99cb2001-01-14 23:47:14 +00001373 """Convert 'date_time' to IMAP4 INTERNALDATE representation.
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001374
Tim Peters07e99cb2001-01-14 23:47:14 +00001375 Return string in form: '"DD-Mmm-YYYY HH:MM:SS +HHMM"'
1376 """
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001377
Fred Drakedb519202002-01-05 17:17:09 +00001378 if isinstance(date_time, (int, float)):
Tim Peters07e99cb2001-01-14 23:47:14 +00001379 tt = time.localtime(date_time)
Fred Drakedb519202002-01-05 17:17:09 +00001380 elif isinstance(date_time, (tuple, time.struct_time)):
Tim Peters07e99cb2001-01-14 23:47:14 +00001381 tt = date_time
Piers Lauder3fca2912002-06-17 07:07:20 +00001382 elif isinstance(date_time, str) and (date_time[0],date_time[-1]) == ('"','"'):
Tim Peters07e99cb2001-01-14 23:47:14 +00001383 return date_time # Assume in correct format
Fred Drakedb519202002-01-05 17:17:09 +00001384 else:
1385 raise ValueError("date_time not of a known type")
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001386
Tim Peters07e99cb2001-01-14 23:47:14 +00001387 dt = time.strftime("%d-%b-%Y %H:%M:%S", tt)
1388 if dt[0] == '0':
1389 dt = ' ' + dt[1:]
1390 if time.daylight and tt[-1]:
1391 zone = -time.altzone
1392 else:
1393 zone = -time.timezone
Raymond Hettingerffdb8bb2004-09-27 15:29:05 +00001394 return '"' + dt + " %+03d%02d" % divmod(zone//60, 60) + '"'
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001395
1396
1397
Guido van Rossum8c062211999-12-13 23:27:45 +00001398if __name__ == '__main__':
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001399
Piers Laudere0273de2002-11-22 05:53:04 +00001400 # To test: invoke either as 'python imaplib.py [IMAP4_server_hostname]'
1401 # or 'python imaplib.py -s "rsh IMAP4_server_hostname exec /etc/rimapd"'
1402 # to test the IMAP4_stream class
1403
Guido van Rossuma79bbac2001-08-13 15:34:41 +00001404 import getopt, getpass
Guido van Rossumd6596931998-05-29 18:08:48 +00001405
Tim Peters07e99cb2001-01-14 23:47:14 +00001406 try:
Piers Laudere0273de2002-11-22 05:53:04 +00001407 optlist, args = getopt.getopt(sys.argv[1:], 'd:s:')
Guido van Rossumb940e112007-01-10 16:19:56 +00001408 except getopt.error as val:
Piers Laudere0273de2002-11-22 05:53:04 +00001409 optlist, args = (), ()
Guido van Rossum66d45132000-03-28 20:20:53 +00001410
Piers Laudere0273de2002-11-22 05:53:04 +00001411 stream_command = None
Tim Peters07e99cb2001-01-14 23:47:14 +00001412 for opt,val in optlist:
1413 if opt == '-d':
1414 Debug = int(val)
Piers Laudere0273de2002-11-22 05:53:04 +00001415 elif opt == '-s':
1416 stream_command = val
1417 if not args: args = (stream_command,)
Guido van Rossum66d45132000-03-28 20:20:53 +00001418
Tim Peters07e99cb2001-01-14 23:47:14 +00001419 if not args: args = ('',)
Guido van Rossum66d45132000-03-28 20:20:53 +00001420
Tim Peters07e99cb2001-01-14 23:47:14 +00001421 host = args[0]
Guido van Rossumb1f08121998-06-25 02:22:16 +00001422
Tim Peters07e99cb2001-01-14 23:47:14 +00001423 USER = getpass.getuser()
Piers Lauder15e5d532001-07-20 10:52:06 +00001424 PASSWD = getpass.getpass("IMAP password for %s on %s: " % (USER, host or "localhost"))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001425
Piers Lauder47404ff2003-04-29 23:40:59 +00001426 test_mesg = 'From: %(user)s@localhost%(lf)sSubject: IMAP4 test%(lf)s%(lf)sdata...%(lf)s' % {'user':USER, 'lf':'\n'}
Tim Peters07e99cb2001-01-14 23:47:14 +00001427 test_seq1 = (
1428 ('login', (USER, PASSWD)),
1429 ('create', ('/tmp/xxx 1',)),
1430 ('rename', ('/tmp/xxx 1', '/tmp/yyy')),
1431 ('CREATE', ('/tmp/yyz 2',)),
1432 ('append', ('/tmp/yyz 2', None, None, test_mesg)),
1433 ('list', ('/tmp', 'yy*')),
1434 ('select', ('/tmp/yyz 2',)),
1435 ('search', (None, 'SUBJECT', 'test')),
Piers Lauderf2d7d152002-02-22 01:15:17 +00001436 ('fetch', ('1', '(FLAGS INTERNALDATE RFC822)')),
Tim Peters07e99cb2001-01-14 23:47:14 +00001437 ('store', ('1', 'FLAGS', '(\Deleted)')),
Piers Lauder15e5d532001-07-20 10:52:06 +00001438 ('namespace', ()),
Tim Peters07e99cb2001-01-14 23:47:14 +00001439 ('expunge', ()),
1440 ('recent', ()),
1441 ('close', ()),
1442 )
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001443
Tim Peters07e99cb2001-01-14 23:47:14 +00001444 test_seq2 = (
1445 ('select', ()),
1446 ('response',('UIDVALIDITY',)),
1447 ('uid', ('SEARCH', 'ALL')),
1448 ('response', ('EXISTS',)),
1449 ('append', (None, None, None, test_mesg)),
1450 ('recent', ()),
1451 ('logout', ()),
1452 )
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001453
Tim Peters07e99cb2001-01-14 23:47:14 +00001454 def run(cmd, args):
Piers Lauderf2d7d152002-02-22 01:15:17 +00001455 M._mesg('%s %s' % (cmd, args))
Guido van Rossum68468eb2003-02-27 20:14:51 +00001456 typ, dat = getattr(M, cmd)(*args)
Piers Lauderf2d7d152002-02-22 01:15:17 +00001457 M._mesg('%s => %s %s' % (cmd, typ, dat))
Piers Laudere0273de2002-11-22 05:53:04 +00001458 if typ == 'NO': raise dat[0]
Tim Peters07e99cb2001-01-14 23:47:14 +00001459 return dat
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001460
Tim Peters07e99cb2001-01-14 23:47:14 +00001461 try:
Piers Laudere0273de2002-11-22 05:53:04 +00001462 if stream_command:
1463 M = IMAP4_stream(stream_command)
1464 else:
1465 M = IMAP4(host)
1466 if M.state == 'AUTH':
Tim Peters77c06fb2002-11-24 02:35:35 +00001467 test_seq1 = test_seq1[1:] # Login not needed
Piers Lauderf2d7d152002-02-22 01:15:17 +00001468 M._mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
Walter Dörwald70a6b492004-02-12 17:35:32 +00001469 M._mesg('CAPABILITIES = %r' % (M.capabilities,))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001470
Tim Peters07e99cb2001-01-14 23:47:14 +00001471 for cmd,args in test_seq1:
1472 run(cmd, args)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001473
Tim Peters07e99cb2001-01-14 23:47:14 +00001474 for ml in run('list', ('/tmp/', 'yy%')):
1475 mo = re.match(r'.*"([^"]+)"$', ml)
1476 if mo: path = mo.group(1)
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +00001477 else: path = ml.split()[-1]
Tim Peters07e99cb2001-01-14 23:47:14 +00001478 run('delete', (path,))
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001479
Tim Peters07e99cb2001-01-14 23:47:14 +00001480 for cmd,args in test_seq2:
1481 dat = run(cmd, args)
Guido van Rossumc2c07fa1998-04-09 13:51:46 +00001482
Tim Peters07e99cb2001-01-14 23:47:14 +00001483 if (cmd,args) != ('uid', ('SEARCH', 'ALL')):
1484 continue
Guido van Rossum38d8f4e1998-04-11 01:22:34 +00001485
Eric S. Raymond25a0cbc2001-02-09 06:50:21 +00001486 uid = dat[-1].split()
Tim Peters07e99cb2001-01-14 23:47:14 +00001487 if not uid: continue
1488 run('uid', ('FETCH', '%s' % uid[-1],
1489 '(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822.TEXT)'))
Guido van Rossum66d45132000-03-28 20:20:53 +00001490
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001491 print('\nAll tests OK.')
Guido van Rossum66d45132000-03-28 20:20:53 +00001492
Tim Peters07e99cb2001-01-14 23:47:14 +00001493 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001494 print('\nTests failed.')
Guido van Rossum66d45132000-03-28 20:20:53 +00001495
Tim Peters07e99cb2001-01-14 23:47:14 +00001496 if not Debug:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001497 print('''
Guido van Rossum66d45132000-03-28 20:20:53 +00001498If you would like to see debugging output,
1499try: %s -d5
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001500''' % sys.argv[0])
Guido van Rossum66d45132000-03-28 20:20:53 +00001501
Tim Peters07e99cb2001-01-14 23:47:14 +00001502 raise