Workaround for broken contact PHOTO from InfCloud

See issue #1205
This commit is contained in:
Unrud 2022-01-15 23:39:05 +01:00
parent 4a0bcde7a3
commit 75df1093be
5 changed files with 28 additions and 5 deletions

View File

@ -141,7 +141,7 @@ class ApplicationPartPut(ApplicationBase):
content_type = environ.get("CONTENT_TYPE", "").split(";", content_type = environ.get("CONTENT_TYPE", "").split(";",
maxsplit=1)[0] maxsplit=1)[0]
try: try:
vobject_items = list(vobject.readComponents(content or "")) vobject_items = radicale_item.read_components(content or "")
except Exception as e: except Exception as e:
logger.warning( logger.warning(
"Bad PUT request on %r: %s", path, e, exc_info=True) "Bad PUT request on %r: %s", path, e, exc_info=True)

View File

@ -27,6 +27,7 @@ import binascii
import contextlib import contextlib
import math import math
import os import os
import re
import sys import sys
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import sha256 from hashlib import sha256
@ -42,6 +43,16 @@ from radicale.item import filter as radicale_filter
from radicale.log import logger from radicale.log import logger
def read_components(s: str) -> List[vobject.base.Component]:
"""Wrapper for vobject.readComponents"""
# Workaround for bug in InfCloud
# PHOTO is a data URI
s = re.sub(r"^(PHOTO(?:;[^:\r\n]*)?;ENCODING=b(?:;[^:\r\n]*)?:)"
r"data:[^;,\r\n]*;base64,", r"\1", s,
flags=re.MULTILINE | re.IGNORECASE)
return list(vobject.readComponents(s))
def predict_tag_of_parent_collection( def predict_tag_of_parent_collection(
vobject_items: Sequence[vobject.base.Component]) -> Optional[str]: vobject_items: Sequence[vobject.base.Component]) -> Optional[str]:
"""Returns the predicted tag or `None`""" """Returns the predicted tag or `None`"""

View File

@ -21,8 +21,6 @@ import sys
import time import time
from typing import Iterable, Iterator, Optional, Tuple from typing import Iterable, Iterator, Optional, Tuple
import vobject
import radicale.item as radicale_item import radicale.item as radicale_item
from radicale import pathutils from radicale import pathutils
from radicale.log import logger from radicale.log import logger
@ -93,8 +91,8 @@ class CollectionPartGet(CollectionPartCache, CollectionPartLock,
cache_content = self._load_item_cache(href, cache_hash) cache_content = self._load_item_cache(href, cache_hash)
if cache_content is None: if cache_content is None:
try: try:
vobject_items = list(vobject.readComponents( vobject_items = radicale_item.read_components(
raw_text.decode(self._encoding))) raw_text.decode(self._encoding))
radicale_item.check_and_sanitize_items( radicale_item.check_and_sanitize_items(
vobject_items, tag=self.tag) vobject_items, tag=self.tag)
vobject_item, = vobject_items vobject_item, = vobject_items

View File

@ -0,0 +1,8 @@
BEGIN:VCARD
VERSION:3.0
UID:contact
N:Contact;;;;
FN:Contact
NICKNAME:test
PHOTO;ENCODING=b;TYPE=png:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAD0lEQVQIHQEEAPv/AP///wX+Av4DfRnGAAAAAElFTkSuQmCC
END:VCARD

View File

@ -139,6 +139,12 @@ permissions: RrWw""")
_, answer = self.get(path) _, answer = self.get(path)
assert "UID:contact1" in answer assert "UID:contact1" in answer
def test_add_contact_photo_with_data_uri(self) -> None:
"""Test workaround for broken PHOTO data from InfCloud"""
self.create_addressbook("/contacts.vcf/")
contact = get_file_content("contact_photo_with_data_uri.vcf")
self.put("/contacts.vcf/contact.vcf", contact)
def test_add_contact_without_uid(self) -> None: def test_add_contact_without_uid(self) -> None:
"""Add a contact without UID.""" """Add a contact without UID."""
self.create_addressbook("/contacts.vcf/") self.create_addressbook("/contacts.vcf/")