Create always safe hrefs

The set of safe characters was inspired by the set of safe characters in
URLs.

Fixes #229
This commit is contained in:
Markus Unterwaditzer 2015-07-12 23:18:38 +02:00
parent 04b3379172
commit 73e2ccf46a
6 changed files with 16 additions and 16 deletions

View file

@ -14,7 +14,7 @@ from .. import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE, \
def format_item(item_template, uid=None): def format_item(item_template, uid=None):
# assert that special chars are handled correctly. # 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)) return Item(item_template.format(r=r, uid=uid or r))

View file

@ -113,7 +113,7 @@ class ServerMixin(object):
url = url.rstrip('/') + '/' + collection url = url.rstrip('/') + '/' + collection
rv = {'url': url, 'username': 'bob', 'password': 'bob', rv = {'url': url, 'username': 'bob', 'password': 'bob',
'collection': collection, 'unsafe_href_chars': ''} 'collection': collection}
if collection is not None: if collection is not None:
s = self.storage_class(**rv) s = self.storage_class(**rv)

View file

@ -44,7 +44,7 @@ class TestFilesystemStorage(StorageTests):
s = self.storage_class(str(tmpdir), '.txt') s = self.storage_class(str(tmpdir), '.txt')
s.upload(Item(u'UID:a/b/c')) s.upload(Item(u'UID:a/b/c'))
item_file, = tmpdir.listdir() 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): def test_too_long_uid(self, tmpdir):
s = self.storage_class(str(tmpdir), '.txt') s = self.storage_class(str(tmpdir), '.txt')

View file

@ -341,15 +341,14 @@ class DavStorage(Storage):
} }
def __init__(self, url, username='', password='', verify=True, auth=None, def __init__(self, url, username='', password='', verify=True, auth=None,
useragent=USERAGENT, unsafe_href_chars='@', useragent=USERAGENT, verify_fingerprint=None, auth_cert=None,
verify_fingerprint=None, auth_cert=None, **kwargs): **kwargs):
super(DavStorage, self).__init__(**kwargs) super(DavStorage, self).__init__(**kwargs)
url = url.rstrip('/') + '/' url = url.rstrip('/') + '/'
self.session = DavSession(url, username, password, verify, auth, self.session = DavSession(url, username, password, verify, auth,
useragent, verify_fingerprint, useragent, verify_fingerprint,
auth_cert) auth_cert)
self.unsafe_href_chars = unsafe_href_chars
# defined for _repr_attributes # defined for _repr_attributes
self.username = username self.username = username
@ -369,7 +368,7 @@ class DavStorage(Storage):
return _normalize_href(self.session.url, *args, **kwargs) return _normalize_href(self.session.url, *args, **kwargs)
def _get_href(self, item): 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) return self._normalize_href(href + self.fileext)
def _is_item_mimetype(self, mimetype): def _is_item_mimetype(self, mimetype):

View file

@ -87,9 +87,7 @@ class FilesystemStorage(Storage):
return os.path.join(self.path, to_native(href, self.encoding)) return os.path.join(self.path, to_native(href, self.encoding))
def _get_href(self, ident): def _get_href(self, ident):
# XXX: POSIX only defines / and \0 as invalid chars, but we should make return generate_href(ident) + self.fileext
# this work crossplatform.
return generate_href(ident, '/') + self.fileext
def list(self): def list(self):
for fname in os.listdir(self.path): for fname in os.listdir(self.path):

View file

@ -8,6 +8,11 @@ from .compat import iteritems, to_unicode
from .. import exceptions from .. import exceptions
SAFE_UID_CHARS = ('abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'0123456789_.-+')
_missing = object() _missing = object()
@ -159,10 +164,8 @@ class cached_property(object):
return result return result
def generate_href(ident=None, unsafe=''): def generate_href(ident=None, safe=SAFE_UID_CHARS):
if ident and \ if not ident or set(ident) - set(safe):
ident.encode('ascii', 'ignore').decode('ascii') == ident: return to_unicode(uuid.uuid4().hex)
for char in unsafe: else:
ident = ident.replace(char, '_')
return ident return ident
return to_unicode(uuid.uuid4().hex)