diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py index b72655a..63450b7 100644 --- a/tests/storage/__init__.py +++ b/tests/storage/__init__.py @@ -14,7 +14,7 @@ from .. import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE, \ def format_item(item_template, uid=None): # assert that special chars are handled correctly. - r = '{}@vdirsyncer'.format(random.random()) + r = random.random() return Item(item_template.format(r=r, uid=uid or r)) diff --git a/tests/storage/dav/servers/radicale/__init__.py b/tests/storage/dav/servers/radicale/__init__.py index d25a189..1b7bd44 100644 --- a/tests/storage/dav/servers/radicale/__init__.py +++ b/tests/storage/dav/servers/radicale/__init__.py @@ -113,7 +113,7 @@ class ServerMixin(object): url = url.rstrip('/') + '/' + collection rv = {'url': url, 'username': 'bob', 'password': 'bob', - 'collection': collection, 'unsafe_href_chars': ''} + 'collection': collection} if collection is not None: s = self.storage_class(**rv) diff --git a/tests/storage/test_filesystem.py b/tests/storage/test_filesystem.py index d4f6d54..11b5cc2 100644 --- a/tests/storage/test_filesystem.py +++ b/tests/storage/test_filesystem.py @@ -44,7 +44,7 @@ class TestFilesystemStorage(StorageTests): s = self.storage_class(str(tmpdir), '.txt') s.upload(Item(u'UID:a/b/c')) item_file, = tmpdir.listdir() - assert str(item_file).endswith('a_b_c.txt') + assert '/' not in item_file.basename and item_file.isfile() def test_too_long_uid(self, tmpdir): s = self.storage_class(str(tmpdir), '.txt') diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 2e693af..eb0ce46 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -341,15 +341,14 @@ class DavStorage(Storage): } def __init__(self, url, username='', password='', verify=True, auth=None, - useragent=USERAGENT, unsafe_href_chars='@', - verify_fingerprint=None, auth_cert=None, **kwargs): + useragent=USERAGENT, verify_fingerprint=None, auth_cert=None, + **kwargs): super(DavStorage, self).__init__(**kwargs) url = url.rstrip('/') + '/' self.session = DavSession(url, username, password, verify, auth, useragent, verify_fingerprint, auth_cert) - self.unsafe_href_chars = unsafe_href_chars # defined for _repr_attributes self.username = username @@ -369,7 +368,7 @@ class DavStorage(Storage): return _normalize_href(self.session.url, *args, **kwargs) def _get_href(self, item): - href = utils.generate_href(item.ident, unsafe=self.unsafe_href_chars) + href = utils.generate_href(item.ident) return self._normalize_href(href + self.fileext) def _is_item_mimetype(self, mimetype): diff --git a/vdirsyncer/storage/filesystem.py b/vdirsyncer/storage/filesystem.py index 7b844ae..19b985a 100644 --- a/vdirsyncer/storage/filesystem.py +++ b/vdirsyncer/storage/filesystem.py @@ -87,9 +87,7 @@ class FilesystemStorage(Storage): return os.path.join(self.path, to_native(href, self.encoding)) def _get_href(self, ident): - # XXX: POSIX only defines / and \0 as invalid chars, but we should make - # this work crossplatform. - return generate_href(ident, '/') + self.fileext + return generate_href(ident) + self.fileext def list(self): for fname in os.listdir(self.path): diff --git a/vdirsyncer/utils/__init__.py b/vdirsyncer/utils/__init__.py index 8f88311..5f926a4 100644 --- a/vdirsyncer/utils/__init__.py +++ b/vdirsyncer/utils/__init__.py @@ -8,6 +8,11 @@ from .compat import iteritems, to_unicode from .. import exceptions +SAFE_UID_CHARS = ('abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + '0123456789_.-+') + + _missing = object() @@ -159,10 +164,8 @@ class cached_property(object): return result -def generate_href(ident=None, unsafe=''): - if ident and \ - ident.encode('ascii', 'ignore').decode('ascii') == ident: - for char in unsafe: - ident = ident.replace(char, '_') +def generate_href(ident=None, safe=SAFE_UID_CHARS): + if not ident or set(ident) - set(safe): + return to_unicode(uuid.uuid4().hex) + else: return ident - return to_unicode(uuid.uuid4().hex)