Check collection properties
This commit is contained in:
parent
863c70f35f
commit
05b1e8296c
@ -621,6 +621,7 @@ class Application:
|
||||
# TODO: use this?
|
||||
# timezone = props.get("C:calendar-timezone")
|
||||
try:
|
||||
storage.check_and_sanitize_props(props)
|
||||
self.Collection.create_collection(path, props=props)
|
||||
except ValueError as e:
|
||||
self.logger.warning(
|
||||
@ -647,6 +648,7 @@ class Application:
|
||||
return WEBDAV_PRECONDITION_FAILED
|
||||
props = xmlutils.props_from_request(xml_content)
|
||||
try:
|
||||
storage.check_and_sanitize_props(props)
|
||||
self.Collection.create_collection(path, props=props)
|
||||
except ValueError as e:
|
||||
self.logger.warning(
|
||||
@ -764,8 +766,13 @@ class Application:
|
||||
return WEBDAV_PRECONDITION_FAILED
|
||||
headers = {"DAV": DAV_HEADERS,
|
||||
"Content-Type": "text/xml; charset=%s" % self.encoding}
|
||||
try:
|
||||
xml_answer = xmlutils.proppatch(base_prefix, path, xml_content,
|
||||
item)
|
||||
except ValueError as e:
|
||||
self.logger.warning(
|
||||
"Bad PROPPATCH request on %r: %s", path, e, exc_info=True)
|
||||
return BAD_REQUEST
|
||||
return (client.MULTI_STATUS, headers,
|
||||
self._write_xml_content(xml_answer))
|
||||
|
||||
@ -841,18 +848,23 @@ class Application:
|
||||
return BAD_REQUEST
|
||||
|
||||
if write_whole_collection:
|
||||
props = {"tag": tag} if tag else {}
|
||||
try:
|
||||
storage.check_and_sanitize_props(props)
|
||||
new_item = self.Collection.create_collection(
|
||||
path, items, {"tag": tag} if tag else None)
|
||||
path, items, props)
|
||||
except ValueError as e:
|
||||
self.logger.warning(
|
||||
"Bad PUT request on %r: %s", path, e, exc_info=True)
|
||||
return BAD_REQUEST
|
||||
else:
|
||||
if tag and not parent_item.get_meta("tag"):
|
||||
parent_item.set_meta({"tag": tag})
|
||||
href = posixpath.basename(path.strip("/"))
|
||||
try:
|
||||
if tag and not parent_item.get_meta("tag"):
|
||||
new_props = parent_item.get_meta()
|
||||
new_props["tag"] = tag
|
||||
storage.check_and_sanitize_props(new_props)
|
||||
parent_item.set_meta_all(new_props)
|
||||
new_item = parent_item.upload(href, items[0])
|
||||
except ValueError as e:
|
||||
self.logger.warning(
|
||||
|
@ -178,6 +178,13 @@ def check_and_sanitize_item(vobject_item, is_collection=False, uid=None,
|
||||
(vobject_item.name, repr(tag) if tag else "generic"))
|
||||
|
||||
|
||||
def check_and_sanitize_props(props):
|
||||
"""Check collection properties for common errors."""
|
||||
tag = props.get("tag")
|
||||
if tag and tag not in ("VCALENDAR", "VADDRESSBOOK"):
|
||||
raise ValueError("Unsupported collection tag: %r" % tag)
|
||||
|
||||
|
||||
def random_uuid4():
|
||||
"""Generate a pseudo-random UUID"""
|
||||
r = "%016x" % getrandbits(128)
|
||||
@ -589,9 +596,24 @@ class BaseCollection:
|
||||
``props`` a dict with updates for properties. If a value is empty, the
|
||||
property must be deleted.
|
||||
|
||||
DEPRECATED: use ``set_meta_all`` instead
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_meta_all(self, props):
|
||||
"""Set metadata values for collection.
|
||||
|
||||
``props`` a dict with values for properties.
|
||||
|
||||
"""
|
||||
delta_props = self.get_meta()
|
||||
for key in delta_props.keys():
|
||||
if key not in props:
|
||||
delta_props[key] = ""
|
||||
delta_props.update(props)
|
||||
self.set_meta(self, delta_props)
|
||||
|
||||
@property
|
||||
def last_modified(self):
|
||||
"""Get the HTTP-datetime of when the collection was modified."""
|
||||
@ -850,7 +872,7 @@ class Collection(BaseCollection):
|
||||
tmp_filesystem_path = os.path.join(tmp_dir, "collection")
|
||||
os.makedirs(tmp_filesystem_path)
|
||||
self = cls("/", folder=tmp_filesystem_path)
|
||||
self.set_meta(props)
|
||||
self.set_meta_all(props)
|
||||
|
||||
if collection:
|
||||
if props.get("tag") == "VCALENDAR":
|
||||
@ -1291,24 +1313,21 @@ class Collection(BaseCollection):
|
||||
def get_meta(self, key=None):
|
||||
# reuse cached value if the storage is read-only
|
||||
if self._writer or self._meta_cache is None:
|
||||
try:
|
||||
try:
|
||||
with open(self._props_path, encoding=self.encoding) as f:
|
||||
self._meta_cache = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self._meta_cache = {}
|
||||
check_and_sanitize_props(self._meta_cache)
|
||||
except ValueError as e:
|
||||
raise RuntimeError("Failed to load properties of collect"
|
||||
"ion %r: %s" % (self.path, e)) from e
|
||||
raise RuntimeError("Failed to load properties of collection "
|
||||
"%r: %s" % (self.path, e)) from e
|
||||
return self._meta_cache.get(key) if key else self._meta_cache
|
||||
|
||||
def set_meta(self, props):
|
||||
new_props = self.get_meta()
|
||||
new_props.update(props)
|
||||
for key in tuple(new_props.keys()):
|
||||
if not new_props[key]:
|
||||
del new_props[key]
|
||||
def set_meta_all(self, props):
|
||||
with self._atomic_write(self._props_path, "w") as f:
|
||||
json.dump(new_props, f)
|
||||
json.dump(props, f)
|
||||
|
||||
@property
|
||||
def last_modified(self):
|
||||
|
@ -998,12 +998,18 @@ def proppatch(base_prefix, path, xml_request, collection):
|
||||
href.text = _href(base_prefix, path)
|
||||
response.append(href)
|
||||
|
||||
for short_name in props_to_remove:
|
||||
props_to_set[short_name] = ""
|
||||
collection.set_meta(props_to_set)
|
||||
|
||||
for short_name in props_to_set:
|
||||
new_props = collection.get_meta()
|
||||
for short_name, value in props_to_set.items():
|
||||
new_props[short_name] = value
|
||||
_add_propstat_to(response, short_name, 200)
|
||||
for short_name in props_to_remove:
|
||||
try:
|
||||
del new_props[short_name]
|
||||
except KeyError:
|
||||
pass
|
||||
_add_propstat_to(response, short_name, 200)
|
||||
storage.check_and_sanitize_props(new_props)
|
||||
collection.set_meta_all(new_props)
|
||||
|
||||
return multistatus
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user