From 8f5fdfb2d4ea5f5388292d860b7d2b0c9613042d Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 17 May 2014 15:20:46 +0200 Subject: [PATCH] Hash items resilient against formatting --- tests/__init__.py | 2 ++ tests/utils/test_vobject.py | 13 +++++++++++-- vdirsyncer/storage/base.py | 9 +++------ vdirsyncer/utils/vobject.py | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 67706f2..e013bde 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -85,4 +85,6 @@ END:VCALENDAR''' SIMPLE_TEMPLATE = u'''BEGIN:FOO UID:{r} +X-SOMETHING:{r} +HAHA:YES END:FOO''' diff --git a/tests/utils/test_vobject.py b/tests/utils/test_vobject.py index 9fed788..4cddd6a 100644 --- a/tests/utils/test_vobject.py +++ b/tests/utils/test_vobject.py @@ -7,9 +7,11 @@ :license: MIT, see LICENSE for more details. ''' -from vdirsyncer.utils.vobject import split_collection, join_collection +from vdirsyncer.utils.vobject import split_collection, join_collection, \ + hash_item -from .. import normalize_item, SIMPLE_TEMPLATE, BARE_EVENT_TEMPLATE +from .. import normalize_item, SIMPLE_TEMPLATE, BARE_EVENT_TEMPLATE, \ + EVENT_TEMPLATE _simple_joined = u'\r\n'.join(( @@ -74,3 +76,10 @@ def test_split_collection_timezones(): ) assert given == expected + + +def test_hash_item(): + a = EVENT_TEMPLATE.format(r=1) + b = u'\n'.join(line for line in a.splitlines() + if u'PRODID' not in line and u'VERSION' not in line) + assert hash_item(a) == hash_item(b) diff --git a/vdirsyncer/storage/base.py b/vdirsyncer/storage/base.py index 6b78575..1d71425 100644 --- a/vdirsyncer/storage/base.py +++ b/vdirsyncer/storage/base.py @@ -7,7 +7,6 @@ :license: MIT, see LICENSE for more details. ''' -import hashlib from .. import exceptions from .. import utils @@ -35,17 +34,15 @@ class Item(object): def __init__(self, raw): assert isinstance(raw, utils.text_type) - raw = raw.splitlines() - for line in raw: + for line in raw.splitlines(): if line.startswith(u'UID:'): uid = line[4:].strip() if uid: self.uid = uid - self.raw = u'\n'.join(raw) - self.hash = hashlib.sha256( - self.raw.strip().encode('utf-8')).hexdigest() + self.raw = raw + self.hash = utils.vobject.hash_item(raw) self.ident = self.uid or self.hash diff --git a/vdirsyncer/utils/vobject.py b/vdirsyncer/utils/vobject.py index 2c6fdea..86a80fc 100644 --- a/vdirsyncer/utils/vobject.py +++ b/vdirsyncer/utils/vobject.py @@ -6,12 +6,27 @@ :copyright: (c) 2014 Markus Unterwaditzer :license: MIT, see LICENSE for more details. ''' +import hashlib + import icalendar.cal import icalendar.parser from . import text_type, itervalues +def hash_item(text): + try: + lines = to_unicode_lines(icalendar.cal.Component.from_ical(text)) + except Exception: + lines = sorted(text.splitlines()) + + hashable = u'\r\n'.join(line.strip() for line in lines + if line.strip() and + u'PRODID' not in line and + u'VERSION' not in line) + return hashlib.sha256(hashable.encode('utf-8')).hexdigest() + + def split_collection(text, inline=(u'VTIMEZONE',), wrap_items_with=(u'VCALENDAR',)): '''Emits items in the order they occur in the text.'''