diff --git a/pykolab/itip/__init__.py b/pykolab/itip/__init__.py --- a/pykolab/itip/__init__.py +++ b/pykolab/itip/__init__.py @@ -2,12 +2,14 @@ import pykolab import traceback import kolabformat +import re from pykolab.xml import to_dt from pykolab.xml import event_from_ical from pykolab.xml import todo_from_ical from pykolab.xml import participant_status_label from pykolab.translate import _ +from tzlocal import windows_tz log = pykolab.getLogger('pykolab.wallace') @@ -18,7 +20,6 @@ def todos_from_message(message, methods=None): return objects_from_message(message, ["VTODO"], methods) - def objects_from_message(message, objnames, methods=None): """ Obtain the iTip payload from email.message @@ -51,6 +52,9 @@ log.debug(_("Raw iTip payload (%r): %r") % (part.get_param('charset'), itip_payload), level=9) + # Convert unsupported timezones, etc. + itip_payload = _convert_itip_payload(itip_payload) + # Python iCalendar prior to 3.0 uses "from_string". if hasattr(icalendar.Calendar, 'from_ical'): cal = icalendar.Calendar.from_ical(itip_payload) @@ -141,7 +145,6 @@ return itip_objects - def check_event_conflict(kolab_event, itip_event): """ Determine whether the given kolab event conflicts with the given itip event @@ -210,10 +213,35 @@ return conflict - def _is_transparent(event): return event.get_transparency() or event.get_status() == kolabformat.StatusCancelled +def _convert_itip_payload(itip): + matchlist = re.findall("^((DTSTART|DTEND|DUE|EXDATE|COMPLETED)[:;][^\n]+)$", itip, re.MULTILINE) + + for match in matchlist: + match = match[0] + search = re.search(";TZID=([^:;]+)", match) + + if search: + tzorig = tzdest = search.group(1).replace('"', '') + + # timezone in Olson-database format, nothing to convert + if re.match("[a-zA-Z]+/[a-zA-Z0-9_+-]+", tzorig): + continue + + # convert timezone from windows format to Olson + if tzorig in windows_tz.win_tz: + tzdest = windows_tz.win_tz[tzorig] + + # @TODO: Should be prefer server time if it has the same offset? + + # replace old with new timezone name + if tzorig != tzdest: + replace = match.replace(search.group(0), ";TZID=" + tzdest) + itip = itip.replace("\n" + match, "\n" + replace) + + return itip def check_date_conflict(_es, _ee, _is, _ie): """ @@ -238,7 +266,7 @@ conflict = True else: conflict = False - + return conflict diff --git a/tests/unit/test-011-itip.py b/tests/unit/test-011-itip.py --- a/tests/unit/test-011-itip.py +++ b/tests/unit/test-011-itip.py @@ -286,7 +286,7 @@ BEGIN:VEVENT UID:eea25142-fb1c-4831-a02d-ac9fb4c16b70 DTSTAMP:20140213T125414Z -DTSTART;TZID=3DEurope/London:20140713T100000 +DTSTART;TZID=3D"W. Europe Standard Time":20140713T100000 DTEND;TZID=3DEurope/London:20140713T140000 SUMMARY:Testing =C3=9Cmlauts DESCRIPTION:Testing =C3=9Cmlauts @@ -375,6 +375,11 @@ self.assertEqual(xml.get_summary(), "Testing Ümlauts") self.assertEqual(xml.get_location(), "Rue the Genève") + # Timezone conversion + itips = itip.events_from_message(message_from_string(itip_unicode)) + xml = itips[0]['xml'] + self.assertEqual(xml.get_start().tzinfo.__str__(), "Europe/Berlin") + def test_002_check_date_conflict(self): astart = datetime.datetime(2014, 7, 13, 10, 0, 0) aend = astart + datetime.timedelta(hours=2)