From 98c72db3b13ac9b2705d071e8c59d42fac5b4f33 Mon Sep 17 00:00:00 2001 From: William Grant Date: Wed, 9 Mar 2011 22:46:17 +1100 Subject: [PATCH] Add a (hopefully correct) Tag=Value list parser, with tests. --- dkim/tests/test_dkim.py | 32 ++++++++++++++++++++++++++++ dkim/util.py | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 dkim/util.py diff --git a/dkim/tests/test_dkim.py b/dkim/tests/test_dkim.py index 1baff54..c442a96 100644 --- a/dkim/tests/test_dkim.py +++ b/dkim/tests/test_dkim.py @@ -2,6 +2,10 @@ import os.path import unittest import dkim +from dkim.util import ( + InvalidTagValueList, + parse_tag_value, + ) def read_test_data(filename): @@ -50,5 +54,33 @@ class TestSignAndVerify(unittest.TestCase): self.assertFalse(res) +class TestParseTagValue(unittest.TestCase): + """Tag=Value parsing tests.""" + + def test_single(self): + self.assertEqual( + {'foo': 'bar'}, + parse_tag_value('foo=bar')) + + def test_trailing_separator_ignored(self): + self.assertEqual( + {'foo': 'bar'}, + parse_tag_value('foo=bar;')) + + def test_multiple(self): + self.assertEqual( + {'foo': 'bar', 'baz': 'foo'}, + parse_tag_value('foo=bar;baz=foo')) + + def test_value_with_equals(self): + self.assertEqual( + {'foo': 'bar', 'baz': 'foo=bar'}, + parse_tag_value('foo=bar;baz=foo=bar')) + + def test_no_value(self): + self.assertRaises( + InvalidTagValueList, + parse_tag_value, 'foo=bar;baz') + if __name__ == '__main__': unittest.main() diff --git a/dkim/util.py b/dkim/util.py new file mode 100644 index 0000000..1b73102 --- /dev/null +++ b/dkim/util.py @@ -0,0 +1,46 @@ +# This software is provided 'as-is', without any express or implied +# warranty. In no event will the author be held liable for any damages +# arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software +# in a product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# 3. This notice may not be removed or altered from any source distribution. +# +# Copyright (c) 2011 William Grant + +__all__ = [ + 'InvalidTagValueList', + 'parse_tag_value', + ] + + +class InvalidTagValueList(Exception): + pass + + +def parse_tag_value(tag_list): + """Parse a DKIM Tag=Value list. + + Interprets the syntax specified by RFC4871 section 3.2. + Assumes that folding whitespace is already unfolded. + """ + tags = {} + tag_specs = tag_list.split(';') + # Trailing semicolons are valid. + if not tag_specs[-1]: + tag_specs.pop() + for tag_spec in tag_specs: + try: + key, value = tag_spec.split('=', 1) + except ValueError: + raise InvalidTagValueList() + tags[key.strip()] = value.strip() + return tags