Merge branch 'fixed-propfind-handling' of https://github.com/muggenhor/Radicale into muggenhor-fixed-propfind-handling
This commit is contained in:
commit
b9db971060
@ -675,10 +675,11 @@ class Collection(BaseCollection):
|
|||||||
os.remove(path)
|
os.remove(path)
|
||||||
self._sync_directory(os.path.dirname(path))
|
self._sync_directory(os.path.dirname(path))
|
||||||
|
|
||||||
def get_meta(self, key):
|
def get_meta(self, key=None):
|
||||||
if os.path.exists(self._props_path):
|
if os.path.exists(self._props_path):
|
||||||
with open(self._props_path, encoding=self.encoding) as prop:
|
with open(self._props_path, encoding=self.encoding) as prop:
|
||||||
return json.load(prop).get(key)
|
meta = json.load(prop)
|
||||||
|
return meta.get(key) if key else meta
|
||||||
|
|
||||||
def set_meta(self, props):
|
def set_meta(self, props):
|
||||||
if os.path.exists(self._props_path):
|
if os.path.exists(self._props_path):
|
||||||
|
@ -53,7 +53,8 @@ for short, url in NAMESPACES.items():
|
|||||||
NAMESPACES_REV[url] = short
|
NAMESPACES_REV[url] = short
|
||||||
ET.register_namespace("" if short == "D" else short, url)
|
ET.register_namespace("" if short == "D" else short, url)
|
||||||
|
|
||||||
CLARK_TAG_REGEX = re.compile(r" {(?P<namespace>[^}]*)}(?P<tag>.*)", re.VERBOSE)
|
CLARK_TAG_REGEX = re.compile(r"{(?P<namespace>[^}]*)}(?P<tag>.*)", re.VERBOSE)
|
||||||
|
HUMAN_REGEX = re.compile(r"(?P<namespace>[^:{}]*)(?P<tag>.*)", re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
def _pretty_xml(element, level=0):
|
def _pretty_xml(element, level=0):
|
||||||
@ -97,6 +98,14 @@ def _tag_from_clark(name):
|
|||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def _tag_from_human(name):
|
||||||
|
"""Get an XML Clark notation tag from human-readable variant ``name``."""
|
||||||
|
match = HUMAN_REGEX.match(name)
|
||||||
|
if match and match.group("namespace") in NAMESPACES:
|
||||||
|
return _tag(match.group("namespace"), match.group("tag"))
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _response(code):
|
def _response(code):
|
||||||
"""Return full W3C names from HTTP status codes."""
|
"""Return full W3C names from HTTP status codes."""
|
||||||
return "HTTP/1.1 %i %s" % (code, client.responses[code])
|
return "HTTP/1.1 %i %s" % (code, client.responses[code])
|
||||||
@ -501,10 +510,15 @@ def propfind(base_prefix, path, xml_request, read_collections,
|
|||||||
in the output.
|
in the output.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if xml_request:
|
# Reading request
|
||||||
root = ET.fromstring(xml_request.encode("utf8"))
|
root = ET.fromstring(xml_request.encode("utf8")) if xml_request else None
|
||||||
props = [prop.tag for prop in root.find(_tag("D", "prop"))]
|
|
||||||
else:
|
# A client may choose not to submit a request body. An empty PROPFIND
|
||||||
|
# request body MUST be treated as if it were an 'allprop' request.
|
||||||
|
top_tag = root[0] if root is not None else ET.Element(_tag("D", "allprop"))
|
||||||
|
|
||||||
|
props = ()
|
||||||
|
if top_tag.tag == _tag("D", "allprop"):
|
||||||
props = [
|
props = [
|
||||||
_tag("D", "getcontenttype"),
|
_tag("D", "getcontenttype"),
|
||||||
_tag("D", "resourcetype"),
|
_tag("D", "resourcetype"),
|
||||||
@ -512,7 +526,12 @@ def propfind(base_prefix, path, xml_request, read_collections,
|
|||||||
_tag("D", "owner"),
|
_tag("D", "owner"),
|
||||||
_tag("D", "getetag"),
|
_tag("D", "getetag"),
|
||||||
_tag("ICAL", "calendar-color"),
|
_tag("ICAL", "calendar-color"),
|
||||||
_tag("CS", "getctag")]
|
_tag("CS", "getctag"),
|
||||||
|
_tag("C", "supported-calendar-component-set"),
|
||||||
|
_tag("D", "supported-report-set"),
|
||||||
|
]
|
||||||
|
elif top_tag.tag == _tag("D", "prop"):
|
||||||
|
props = [prop.tag for prop in top_tag]
|
||||||
|
|
||||||
if _tag("D", "current-user-principal") in props and not user:
|
if _tag("D", "current-user-principal") in props and not user:
|
||||||
# Ask for authentication
|
# Ask for authentication
|
||||||
@ -520,24 +539,37 @@ def propfind(base_prefix, path, xml_request, read_collections,
|
|||||||
# RFC 5397 doesn't seem to work with DAVdroid.
|
# RFC 5397 doesn't seem to work with DAVdroid.
|
||||||
return client.FORBIDDEN, None
|
return client.FORBIDDEN, None
|
||||||
|
|
||||||
|
# Writing answer
|
||||||
multistatus = ET.Element(_tag("D", "multistatus"))
|
multistatus = ET.Element(_tag("D", "multistatus"))
|
||||||
|
|
||||||
collections = []
|
collections = []
|
||||||
for collection in write_collections:
|
for collection in write_collections:
|
||||||
collections.append(collection)
|
collections.append(collection)
|
||||||
response = _propfind_response(
|
if top_tag.tag == _tag("D", "propname"):
|
||||||
base_prefix, path, collection, props, user, write=True)
|
response = _propfind_response(
|
||||||
|
base_prefix, path, collection, (), user, write=True,
|
||||||
|
propnames=True)
|
||||||
|
else:
|
||||||
|
response = _propfind_response(
|
||||||
|
base_prefix, path, collection, props, user, write=True)
|
||||||
multistatus.append(response)
|
multistatus.append(response)
|
||||||
for collection in read_collections:
|
for collection in read_collections:
|
||||||
if collection in collections:
|
if collection in collections:
|
||||||
continue
|
continue
|
||||||
response = _propfind_response(
|
if top_tag.tag == _tag("D", "propname"):
|
||||||
base_prefix, path, collection, props, user, write=False)
|
response = _propfind_response(
|
||||||
|
base_prefix, path, collection, (), user, write=False,
|
||||||
|
propnames=True)
|
||||||
|
else:
|
||||||
|
response = _propfind_response(
|
||||||
|
base_prefix, path, collection, props, user, write=False)
|
||||||
multistatus.append(response)
|
multistatus.append(response)
|
||||||
|
|
||||||
return client.MULTI_STATUS, _pretty_xml(multistatus)
|
return client.MULTI_STATUS, _pretty_xml(multistatus)
|
||||||
|
|
||||||
|
|
||||||
def _propfind_response(base_prefix, path, item, props, user, write=False):
|
def _propfind_response(base_prefix, path, item, props, user, write=False,
|
||||||
|
propnames=False):
|
||||||
"""Build and return a PROPFIND response."""
|
"""Build and return a PROPFIND response."""
|
||||||
is_collection = isinstance(item, storage.BaseCollection)
|
is_collection = isinstance(item, storage.BaseCollection)
|
||||||
if is_collection:
|
if is_collection:
|
||||||
@ -568,6 +600,35 @@ def _propfind_response(base_prefix, path, item, props, user, write=False):
|
|||||||
prop404 = ET.Element(_tag("D", "prop"))
|
prop404 = ET.Element(_tag("D", "prop"))
|
||||||
propstat404.append(prop404)
|
propstat404.append(prop404)
|
||||||
|
|
||||||
|
if propnames:
|
||||||
|
# Should list all properties that can be retrieved by the code below
|
||||||
|
prop200.append(ET.Element(_tag("D", "getetag")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "principal-URL")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "principal-collection-set")))
|
||||||
|
prop200.append(ET.Element(_tag("C", "calendar-user-address-set")))
|
||||||
|
prop200.append(ET.Element(_tag("CR", "addressbook-home-set")))
|
||||||
|
prop200.append(ET.Element(_tag("C", "calendar-home-set")))
|
||||||
|
prop200.append(ET.Element(
|
||||||
|
_tag("C", "supported-calendar-component-set")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "current-user-privilege-set")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "supported-report-set")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "getcontenttype")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "resourcetype")))
|
||||||
|
|
||||||
|
if is_collection:
|
||||||
|
prop200.append(ET.Element(_tag("CS", "getctag")))
|
||||||
|
prop200.append(ET.Element(_tag("C", "calendar-timezone")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "displayname")))
|
||||||
|
prop200.append(ET.Element(_tag("ICAL", "calendar-color")))
|
||||||
|
prop200.append(ET.Element(_tag("D", "owner")))
|
||||||
|
|
||||||
|
if is_leaf:
|
||||||
|
meta = item.get_meta()
|
||||||
|
for tag in meta:
|
||||||
|
clark_tag = _tag_from_human(tag)
|
||||||
|
if prop200.find(clark_tag) is None:
|
||||||
|
prop200.append(ET.Element(clark_tag))
|
||||||
|
|
||||||
for tag in props:
|
for tag in props:
|
||||||
element = ET.Element(tag)
|
element = ET.Element(tag)
|
||||||
is404 = False
|
is404 = False
|
||||||
|
Loading…
Reference in New Issue
Block a user