Make folders containing quotes work

imaputil.imapsplit did not cope with strings that contained encoded
quotation marks, e.g. a folder name '"Make" Magazine' would fail and
crash OfflineImap. Make it work by adapting the regex that we use to
extract the first quote to also work with encoded \" quotes. (We do no
sanity checks that there is an even number of such marks within a string
though)

This commit makes such folders work. This was reported and analyzed by
Mark Eichin.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>

Conflicts:

	Changelog.draft.rst
This commit is contained in:
Sebastian Spaeth 2012-01-20 11:01:27 +01:00
parent bf31accb78
commit eb04036da5
2 changed files with 18 additions and 7 deletions

View File

@ -16,6 +16,9 @@ New Features
* Beginning of a test suite. So far there is only one test. Configure * Beginning of a test suite. So far there is only one test. Configure
test/credentials.conf and invoke with "python setup.py test" test/credentials.conf and invoke with "python setup.py test"
* Make folders containing quotes work rather than crashing
(reported by Mark Eichin)
Changes Changes
------- -------

View File

@ -25,7 +25,12 @@ try: # python 2.6 has set() built in
except NameError: except NameError:
from sets import Set as set from sets import Set as set
quotere = re.compile('^("(?:[^"]|\\\\")*")') # find the first quote in a string
quotere = re.compile(
r"""(?P<quote>"(?:\\"|[^"])*") # Quote, possibly containing encoded
# quotation mark
\s*(?P<rest>.*)$ # Whitespace & remainder of string""",
re.VERBOSE)
def debug(*args): def debug(*args):
msg = [] msg = []
@ -71,7 +76,7 @@ def options2hash(list):
def flags2hash(flags): def flags2hash(flags):
"""Converts IMAP response string from eg IMAP4.fetch() to a hash. """Converts IMAP response string from eg IMAP4.fetch() to a hash.
E.g. '(FLAGS (\\Seen Old) UID 4807)' leads to E.g. '(FLAGS (\\Seen Old) UID 4807)' leads to
{'FLAGS': '(\\Seen Old)', 'UID': '4807'}""" {'FLAGS': '(\\Seen Old)', 'UID': '4807'}"""
return options2hash(flagsplit(flags)) return options2hash(flagsplit(flags))
@ -124,10 +129,11 @@ def imapsplit(imapstring):
retval.extend(imapsplit(arg)) retval.extend(imapsplit(arg))
debug("imapsplit() non-string: returning %s" % str(retval)) debug("imapsplit() non-string: returning %s" % str(retval))
return retval return retval
workstr = imapstring.strip() workstr = imapstring.strip()
retval = [] retval = []
while len(workstr): while len(workstr):
# handle parenthized fragments (...()...)
if workstr[0] == '(': if workstr[0] == '(':
rparenc = 1 # count of right parenthesis to match rparenc = 1 # count of right parenthesis to match
rpareni = 1 # position to examine rpareni = 1 # position to examine
@ -141,9 +147,10 @@ def imapsplit(imapstring):
workstr = workstr[rpareni:].lstrip() workstr = workstr[rpareni:].lstrip()
retval.append(parenlist) retval.append(parenlist)
elif workstr[0] == '"': elif workstr[0] == '"':
quotelist = quotere.search(workstr).group(1) # quoted fragments '"...\"..."'
workstr = workstr[len(quotelist):].lstrip() m = quotere.match(workstr)
retval.append(quotelist) retval.append(m.group('quote'))
workstr = m.group('rest')
else: else:
splits = string.split(workstr, maxsplit = 1) splits = string.split(workstr, maxsplit = 1)
splitslen = len(splits) splitslen = len(splits)
@ -161,8 +168,9 @@ def imapsplit(imapstring):
elif splitslen == 0: elif splitslen == 0:
# There was not even an unquoted word. # There was not even an unquoted word.
break break
getglobalui().warn("%s->%s" % (imapstring, retval))
return retval return retval
flagmap = [('\\Seen', 'S'), flagmap = [('\\Seen', 'S'),
('\\Answered', 'R'), ('\\Answered', 'R'),
('\\Flagged', 'F'), ('\\Flagged', 'F'),