
commit 2a952ec98054cf656efd5d9b2bffa1960cdf5b42 Author: Damian Johnson <atagar@torproject.org> Date: Sat Feb 9 15:29:00 2013 -0800 Accepting "NEVER" expiration in ADDRMAP events The expiry value in ADDRMAP events can be 'NEVER'. This is a little troublesome since it means that the field might or might not be quoted (making this unique among all tor events). Caught by Desoxy on 'https://trac.torproject.org/8162'. --- stem/response/events.py | 21 ++++++++++++++------- test/unit/response/events.py | 12 ++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/stem/response/events.py b/stem/response/events.py index 24692d6..861459a 100644 --- a/stem/response/events.py +++ b/stem/response/events.py @@ -30,10 +30,11 @@ class Event(stem.response.ControlMessage): :var dict keyword_args: key/value arguments of the event """ - _POSITIONAL_ARGS = () # attribute names for recognized positional arguments - _KEYWORD_ARGS = {} # map of 'keyword => attribute' for recognized attributes - _QUOTED = () # positional arguments that are quoted - _SKIP_PARSING = False # skip parsing contents into our positional_args and keyword_args + _POSITIONAL_ARGS = () # attribute names for recognized positional arguments + _KEYWORD_ARGS = {} # map of 'keyword => attribute' for recognized attributes + _QUOTED = () # positional arguments that are quoted + _OPTIONALLY_QUOTED = () # positional arguments that may or may not be quoted + _SKIP_PARSING = False # skip parsing contents into our positional_args and keyword_args _VERSION_ADDED = stem.version.Version('0.1.1.1-alpha') # minimum version with control-spec V1 event support def _parse_message(self, arrived_at): @@ -93,7 +94,7 @@ class Event(stem.response.ControlMessage): attr_value = None if positional: - if attr_name in self._QUOTED: + if attr_name in self._QUOTED or (attr_name in self._OPTIONALLY_QUOTED and positional[0].startswith('"')): attr_values = [positional.pop(0)] if not attr_values[0].startswith('"'): @@ -164,14 +165,20 @@ class AddrMapEvent(Event): "error": "error", "EXPIRES": "utc_expiry", } - _QUOTED = ("expiry") + _OPTIONALLY_QUOTED = ("expiry") def _parse(self): if self.destination == "<error>": self.destination = None if self.expiry is not None: - self.expiry = datetime.datetime.strptime(self.expiry, "%Y-%m-%d %H:%M:%S") + if self.expiry == "NEVER": + self.expiry = None + else: + try: + self.expiry = datetime.datetime.strptime(self.expiry, "%Y-%m-%d %H:%M:%S") + except ValueError: + raise stem.ProtocolError("Unable to parse date in ADDRMAP event: %s" % self) if self.utc_expiry is not None: self.utc_expiry = datetime.datetime.strptime(self.utc_expiry, "%Y-%m-%d %H:%M:%S") diff --git a/test/unit/response/events.py b/test/unit/response/events.py index dddb854..8a685ff 100644 --- a/test/unit/response/events.py +++ b/test/unit/response/events.py @@ -18,6 +18,8 @@ from test import mocking ADDRMAP = '650 ADDRMAP www.atagar.com 75.119.206.243 "2012-11-19 00:50:13" \ EXPIRES="2012-11-19 08:50:13"' +ADDRMAP_NO_EXPIRATION = '650 ADDRMAP www.atagar.com 75.119.206.243 NEVER' + ADDRMAP_ERROR_EVENT = '650 ADDRMAP www.atagar.com <error> "2012-11-19 00:50:13" \ error=yes EXPIRES="2012-11-19 08:50:13"' @@ -380,6 +382,16 @@ class TestEvents(unittest.TestCase): self.assertEqual(None, event.error) self.assertEqual(datetime.datetime(2012, 11, 19, 8, 50, 13), event.utc_expiry) + event = _get_event(ADDRMAP_NO_EXPIRATION) + + self.assertTrue(isinstance(event, stem.response.events.AddrMapEvent)) + self.assertEqual(ADDRMAP_NO_EXPIRATION.lstrip("650 "), str(event)) + self.assertEqual("www.atagar.com", event.hostname) + self.assertEqual("75.119.206.243", event.destination) + self.assertEqual(None, event.expiry) + self.assertEqual(None, event.error) + self.assertEqual(None, event.utc_expiry) + event = _get_event(ADDRMAP_ERROR_EVENT) self.assertTrue(isinstance(event, stem.response.events.AddrMapEvent))