Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Lib/email/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,16 @@ def parseaddr(addr, *, strict=True):
def unquote(str):
"""Remove quotes from a string."""
if len(str) > 1:
if str.startswith('"') and str.endswith('"'):
return str[1:-1].replace('\\\\', '\\').replace('\\"', '"')
if str.startswith('"'):
pos = 1
while pos < len(str):
if str[pos] == '\\' and pos + 1 < len(str):
pos += 2
elif str[pos] == '"':
content = str[1:pos]
return re.sub(r'\\(.)', r'\1', content)
else:
pos += 1
if str.startswith('<') and str.endswith('>'):
return str[1:-1]
return str
Expand Down
30 changes: 30 additions & 0 deletions Lib/test/test_email/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,35 @@ def test_formatdate_with_localtime(self):
string = utils.formatdate(timeval, localtime=True)
self.assertEqual(string, 'Thu, 01 Dec 2011 18:00:00 +0300')

class UnquoteTests(unittest.TestCase):

def test_unquote_basic(self):
self.assertEqual(utils.unquote('"value"'), 'value')

def test_unquote_with_trailing_garbage(self):
self.assertEqual(utils.unquote('"bound"\n\tX-Priority: 3'), 'bound')

def test_unquote_with_escaped_quote(self):
self.assertEqual(utils.unquote(r'"val\"ue"'), 'val"ue')

def test_unquote_with_escaped_backslash(self):
self.assertEqual(utils.unquote(r'"val\\ue"'), r'val\ue')

def test_unquote_angle_brackets(self):
self.assertEqual(utils.unquote('<value>'), 'value')

def test_unquote_no_quotes(self):
self.assertEqual(utils.unquote('value'), 'value')

def test_unquote_single_char(self):
self.assertEqual(utils.unquote('v'), 'v')

def test_unquote_empty_quoted(self):
self.assertEqual(utils.unquote('""'), '')

def test_unquote_mixed_escapes(self):
self.assertEqual(utils.unquote(r'"a\\b\"c"'), r'a\b"c')


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`email.utils.unquote` to properly handle quoted strings with
trailing garbage by extracting only content between quotes and using
single-pass unescaping for RFC 2822 compliance. Patched by Shamil Abdulaev.
Loading