Commit 46586822 authored by Guido van Rossum's avatar Guido van Rossum

Subject: bug fixes for imaplib.py

From: Piers Lauder <piers@staff.cs.usyd.edu.au>
To: Python List <python-list@cwi.nl>
Date: Mon, 18 May 1998 09:51:53 +1000

Following is a context diff for imaplib.py in the Python1.5.1 distribution.
It fixes 2 bugs. One to do with argument quoting, and the other to do with
caching of un-tagged responses. Apologies for its size.
parent 7beaad4e
...@@ -172,8 +172,7 @@ class IMAP4: ...@@ -172,8 +172,7 @@ class IMAP4:
date_time = Time2Internaldate(date_time) date_time = Time2Internaldate(date_time)
else: else:
date_time = None date_time = None
tag = self._command(name, mailbox, flags, date_time, message) return self._simple_command(name, mailbox, flags, date_time, message)
return self._command_complete(name, tag)
def authenticate(self, func): def authenticate(self, func):
...@@ -314,6 +313,8 @@ class IMAP4: ...@@ -314,6 +313,8 @@ class IMAP4:
def recent(self): def recent(self):
"""Prompt server for an update. """Prompt server for an update.
Flush all untagged responses.
(typ, [data]) = <instance>.recent() (typ, [data]) = <instance>.recent()
'data' is None if no new messages, 'data' is None if no new messages,
...@@ -323,6 +324,7 @@ class IMAP4: ...@@ -323,6 +324,7 @@ class IMAP4:
typ, dat = self._untagged_response('OK', name) typ, dat = self._untagged_response('OK', name)
if dat[-1]: if dat[-1]:
return typ, dat return typ, dat
self.untagged_responses = {}
typ, dat = self._simple_command('NOOP') typ, dat = self._simple_command('NOOP')
return self._untagged_response(typ, name) return self._untagged_response(typ, name)
...@@ -338,9 +340,11 @@ class IMAP4: ...@@ -338,9 +340,11 @@ class IMAP4:
def response(self, code): def response(self, code):
"""Return data for response 'code' if received, or None. """Return data for response 'code' if received, or None.
Old value for response 'code' is cleared.
(code, [data]) = <instance>.response(code) (code, [data]) = <instance>.response(code)
""" """
return code, self.untagged_responses.get(code, [None]) return self._untagged_response(code, code)
def search(self, charset, criteria): def search(self, charset, criteria):
...@@ -360,15 +364,14 @@ class IMAP4: ...@@ -360,15 +364,14 @@ class IMAP4:
def select(self, mailbox='INBOX', readonly=None): def select(self, mailbox='INBOX', readonly=None):
"""Select a mailbox. """Select a mailbox.
Flush all untagged responses.
(typ, [data]) = <instance>.select(mailbox='INBOX', readonly=None) (typ, [data]) = <instance>.select(mailbox='INBOX', readonly=None)
'data' is count of messages in mailbox ('EXISTS' response). 'data' is count of messages in mailbox ('EXISTS' response).
""" """
# Mandated responses are ('FLAGS', 'EXISTS', 'RECENT', 'UIDVALIDITY') # Mandated responses are ('FLAGS', 'EXISTS', 'RECENT', 'UIDVALIDITY')
# Remove immediately interesting responses self.untagged_responses = {} # Flush old responses.
for r in ('EXISTS', 'READ-WRITE'):
if self.untagged_responses.has_key(r):
del self.untagged_responses[r]
if readonly: if readonly:
name = 'EXAMINE' name = 'EXAMINE'
else: else:
...@@ -400,8 +403,7 @@ class IMAP4: ...@@ -400,8 +403,7 @@ class IMAP4:
(typ, [data]) = <instance>.store(message_set, command, flag_list) (typ, [data]) = <instance>.store(message_set, command, flag_list)
""" """
command = '%s %s' % (command, flag_list) typ, dat = self._simple_command('STORE', message_set, command, flag_list)
typ, dat = self._simple_command('STORE', message_set, command)
return self._untagged_response(typ, 'FETCH') return self._untagged_response(typ, 'FETCH')
...@@ -413,16 +415,16 @@ class IMAP4: ...@@ -413,16 +415,16 @@ class IMAP4:
return self._simple_command('SUBSCRIBE', mailbox) return self._simple_command('SUBSCRIBE', mailbox)
def uid(self, command, args): def uid(self, command, *args):
"""Execute "command args" with messages identified by UID, """Execute "command arg ..." with messages identified by UID,
rather than message number. rather than message number.
(typ, [data]) = <instance>.uid(command, args) (typ, [data]) = <instance>.uid(command, arg1, arg2, ...)
Returns response appropriate to 'command'. Returns response appropriate to 'command'.
""" """
name = 'UID' name = 'UID'
typ, dat = self._simple_command('UID', command, args) typ, dat = apply(self._simple_command, ('UID', command) + args)
if command == 'SEARCH': if command == 'SEARCH':
name = 'SEARCH' name = 'SEARCH'
else: else:
...@@ -440,15 +442,15 @@ class IMAP4: ...@@ -440,15 +442,15 @@ class IMAP4:
return self._simple_command('UNSUBSCRIBE', mailbox) return self._simple_command('UNSUBSCRIBE', mailbox)
def xatom(self, name, arg1=None, arg2=None): def xatom(self, name, *args):
"""Allow simple extension commands """Allow simple extension commands
notified by server in CAPABILITY response. notified by server in CAPABILITY response.
(typ, [data]) = <instance>.xatom(name, arg1=None, arg2=None) (typ, [data]) = <instance>.xatom(name, arg, ...)
""" """
if name[0] != 'X' or not name in self.capabilities: if name[0] != 'X' or not name in self.capabilities:
raise self.error('unknown extension command: %s' % name) raise self.error('unknown extension command: %s' % name)
return self._simple_command(name, arg1, arg2) return apply(self._simple_command, (name,) + args)
...@@ -475,7 +477,15 @@ class IMAP4: ...@@ -475,7 +477,15 @@ class IMAP4:
tag = self._new_tag() tag = self._new_tag()
data = '%s %s' % (tag, name) data = '%s %s' % (tag, name)
for d in (dat1, dat2, dat3): for d in (dat1, dat2, dat3):
if d is not None: data = '%s %s' % (data, d) if d is None: continue
if type(d) is type(''):
l = len(string.split(d))
else:
l = 1
if l == 0 or l > 1 and (d[0],d[-1]) not in (('(',')'),('"','"')):
data = '%s "%s"' % (data, d)
else:
data = '%s %s' % (data, d)
if literal is not None: if literal is not None:
data = '%s {%s}' % (data, len(literal)) data = '%s {%s}' % (data, len(literal))
...@@ -529,11 +539,9 @@ class IMAP4: ...@@ -529,11 +539,9 @@ class IMAP4:
# Read response and store. # Read response and store.
# #
# Returns None for continuation responses, # Returns None for continuation responses,
# otherwise first response line received # otherwise first response line received.
# Protocol mandates all lines terminated by CRLF. resp = self._get_line()
resp = self._get_line()[:-2]
# Command completion response? # Command completion response?
...@@ -584,7 +592,7 @@ class IMAP4: ...@@ -584,7 +592,7 @@ class IMAP4:
# Read trailer - possibly containing another literal # Read trailer - possibly containing another literal
dat = self._get_line()[:-2] dat = self._get_line()
self._append_untagged(typ, dat) self._append_untagged(typ, dat)
...@@ -614,8 +622,9 @@ class IMAP4: ...@@ -614,8 +622,9 @@ class IMAP4:
# Protocol mandates all lines terminated by CRLF # Protocol mandates all lines terminated by CRLF
line = line[:-2]
if __debug__ and self.debug >= 4: if __debug__ and self.debug >= 4:
print '\t< %s' % line[:-2] print '\t< %s' % line
return line return line
...@@ -638,9 +647,9 @@ class IMAP4: ...@@ -638,9 +647,9 @@ class IMAP4:
return tag return tag
def _simple_command(self, name, dat1=None, dat2=None): def _simple_command(self, name, *args):
return self._command_complete(name, self._command(name, dat1, dat2)) return self._command_complete(name, apply(self._command, (name,) + args))
def _untagged_response(self, typ, name): def _untagged_response(self, typ, name):
...@@ -648,6 +657,8 @@ class IMAP4: ...@@ -648,6 +657,8 @@ class IMAP4:
if not self.untagged_responses.has_key(name): if not self.untagged_responses.has_key(name):
return typ, [None] return typ, [None]
data = self.untagged_responses[name] data = self.untagged_responses[name]
if __debug__ and self.debug >= 5:
print '\tuntagged_responses[%s] => %.20s..' % (name, `data`)
del self.untagged_responses[name] del self.untagged_responses[name]
return typ, data return typ, data
...@@ -755,16 +766,16 @@ if __debug__ and __name__ == '__main__': ...@@ -755,16 +766,16 @@ if __debug__ and __name__ == '__main__':
test_seq1 = ( test_seq1 = (
('login', (USER, PASSWD)), ('login', (USER, PASSWD)),
('create', ('/tmp/xxx',)), ('create', ('/tmp/xxx 1',)),
('rename', ('/tmp/xxx', '/tmp/yyy')), ('rename', ('/tmp/xxx 1', '/tmp/yyy')),
('CREATE', ('/tmp/yyz',)), ('CREATE', ('/tmp/yyz 2',)),
('append', ('/tmp/yyz', None, None, 'From: anon@x.y.z\n\ndata...')), ('append', ('/tmp/yyz 2', None, None, 'From: anon@x.y.z\n\ndata...')),
('select', ('/tmp/yyz',)), ('select', ('/tmp/yyz 2',)),
('recent', ()),
('uid', ('SEARCH', 'ALL')), ('uid', ('SEARCH', 'ALL')),
('fetch', ('1', '(INTERNALDATE RFC822)')), ('fetch', ('1', '(INTERNALDATE RFC822)')),
('store', ('1', 'FLAGS', '(\Deleted)')), ('store', ('1', 'FLAGS', '(\Deleted)')),
('expunge', ()), ('expunge', ()),
('recent', ()),
('close', ()), ('close', ()),
) )
...@@ -772,8 +783,8 @@ if __debug__ and __name__ == '__main__': ...@@ -772,8 +783,8 @@ if __debug__ and __name__ == '__main__':
('select', ()), ('select', ()),
('response',('UIDVALIDITY',)), ('response',('UIDVALIDITY',)),
('uid', ('SEARCH', 'ALL')), ('uid', ('SEARCH', 'ALL')),
('recent', ()),
('response', ('EXISTS',)), ('response', ('EXISTS',)),
('recent', ()),
('logout', ()), ('logout', ()),
) )
...@@ -790,7 +801,9 @@ if __debug__ and __name__ == '__main__': ...@@ -790,7 +801,9 @@ if __debug__ and __name__ == '__main__':
run(cmd, args) run(cmd, args)
for ml in run('list', ('/tmp/', 'yy%')): for ml in run('list', ('/tmp/', 'yy%')):
path = string.split(ml)[-1] mo = re.match(r'.*"([^"]+)"$', ml)
if mo: path = mo.group(1)
else: path = string.split(ml)[-1]
run('delete', (path,)) run('delete', (path,))
for cmd,args in test_seq2: for cmd,args in test_seq2:
...@@ -800,5 +813,5 @@ if __debug__ and __name__ == '__main__': ...@@ -800,5 +813,5 @@ if __debug__ and __name__ == '__main__':
continue continue
uid = string.split(dat[0])[-1] uid = string.split(dat[0])[-1]
run('uid', ('FETCH', run('uid', ('FETCH', '%s' % uid,
'%s (FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822)' % uid)) '(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822)'))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment