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