diff --git a/radicale/storage.py b/radicale/storage.py index 7ee34a7..f56bf50 100644 --- a/radicale/storage.py +++ b/radicale/storage.py @@ -596,14 +596,56 @@ class BaseCollection: def serialize(self): """Get the unicode string representing the whole collection.""" if self.get_meta("tag") == "VCALENDAR": - collection = vobject.iCalendar() + in_vcalendar = False + vtimezones = "" + included_tzids = set() + vtimezone = [] + tzid = None + components = "" + # Concatenate all child elements of VCALENDAR from all items + # together, while preventing duplicated VTIMEZONE entries. + # VTIMEZONEs are only distinguished by their TZID, if different + # timezones share the same TZID this produces errornous ouput. + # VObject fails at this too. for item in self.get_all(): - for content in ("vevent", "vtodo", "vjournal"): - for component in getattr(item, "%s_list" % content, ()): - collection.add(component) - return collection.serialize() + depth = 0 + for line in item.serialize().split("\r\n"): + if line.startswith("BEGIN:"): + depth += 1 + if depth == 1 and line == "BEGIN:VCALENDAR": + in_vcalendar = True + elif in_vcalendar: + if depth == 1 and line.startswith("END:"): + in_vcalendar = False + if depth == 2 and line == "BEGIN:VTIMEZONE": + vtimezone.append(line) + elif vtimezone: + vtimezone.append(line) + if depth == 2 and line.startswith("TZID:"): + tzid = line[len("TZID:"):] + elif depth == 2 and line.startswith("END:"): + if tzid is None or tzid not in included_tzids: + if vtimezones: + vtimezones += "\r\n" + vtimezones += "\r\n".join(vtimezone) + included_tzids.add(tzid) + vtimezone.clear() + tzid = None + elif depth >= 2: + if components: + components += "\r\n" + components += line + if line.startswith("END:"): + depth -= 1 + return "\r\n".join(filter(bool, ( + "BEGIN:VCALENDAR", + "VERSION:2.0", + "PRODID:-//PYVOBJECT//NONSGML Version 1//EN", + vtimezones, + components, + "END:VCALENDAR"))) elif self.get_meta("tag") == "VADDRESSBOOK": - return "".join(item.serialize() for item in self.get_all()) + return "".join((item.serialize() for item in self.get_all())) return "" @classmethod @@ -1272,61 +1314,6 @@ class Collection(BaseCollection): last = max(map(os.path.getmtime, relevant_files)) return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(last)) - def serialize(self): - # serialize collection - if self.get_meta("tag") == "VCALENDAR": - in_vcalendar = False - vtimezones = "" - included_tzids = set() - vtimezone = [] - tzid = None - components = "" - # Concatenate all child elements of VCALENDAR from all items - # together, while preventing duplicated VTIMEZONE entries. - # VTIMEZONEs are only distinguished by their TZID, if different - # timezones share the same TZID this produces errornous ouput. - # VObject fails at this too. - for item in self.get_all(): - depth = 0 - for line in item.serialize().split("\r\n"): - if line.startswith("BEGIN:"): - depth += 1 - if depth == 1 and line == "BEGIN:VCALENDAR": - in_vcalendar = True - elif in_vcalendar: - if depth == 1 and line.startswith("END:"): - in_vcalendar = False - if depth == 2 and line == "BEGIN:VTIMEZONE": - vtimezone.append(line) - elif vtimezone: - vtimezone.append(line) - if depth == 2 and line.startswith("TZID:"): - tzid = line[len("TZID:"):] - elif depth == 2 and line.startswith("END:"): - if tzid is None or tzid not in included_tzids: - if vtimezones: - vtimezones += "\r\n" - vtimezones += "\r\n".join(vtimezone) - included_tzids.add(tzid) - vtimezone.clear() - tzid = None - elif depth >= 2: - if components: - components += "\r\n" - components += line - if line.startswith("END:"): - depth -= 1 - return "\r\n".join(filter(bool, ( - "BEGIN:VCALENDAR", - "VERSION:2.0", - "PRODID:-//PYVOBJECT//NONSGML Version 1//EN", - vtimezones, - components, - "END:VCALENDAR"))) - elif self.get_meta("tag") == "VADDRESSBOOK": - return "".join((item.serialize() for item in self.get_all())) - return "" - @property def etag(self): # reuse cached value if the storage is read-only