Merge branch 'muggenhor-fixed-propfind-handling'
This commit is contained in:
commit
0d47a7f0be
@ -675,10 +675,11 @@ class Collection(BaseCollection):
|
||||
os.remove(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):
|
||||
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):
|
||||
if os.path.exists(self._props_path):
|
||||
|
@ -53,7 +53,8 @@ for short, url in NAMESPACES.items():
|
||||
NAMESPACES_REV[url] = short
|
||||
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):
|
||||
@ -97,6 +98,14 @@ def _tag_from_clark(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):
|
||||
"""Return full W3C names from HTTP status codes."""
|
||||
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.
|
||||
|
||||
"""
|
||||
if xml_request:
|
||||
root = ET.fromstring(xml_request.encode("utf8"))
|
||||
props = [prop.tag for prop in root.find(_tag("D", "prop"))]
|
||||
else:
|
||||
# Reading request
|
||||
root = ET.fromstring(xml_request.encode("utf8")) if xml_request else None
|
||||
|
||||
# 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 = [
|
||||
_tag("D", "getcontenttype"),
|
||||
_tag("D", "resourcetype"),
|
||||
@ -512,7 +526,12 @@ def propfind(base_prefix, path, xml_request, read_collections,
|
||||
_tag("D", "owner"),
|
||||
_tag("D", "getetag"),
|
||||
_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:
|
||||
# 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.
|
||||
return client.FORBIDDEN, None
|
||||
|
||||
# Writing answer
|
||||
multistatus = ET.Element(_tag("D", "multistatus"))
|
||||
|
||||
collections = []
|
||||
for collection in write_collections:
|
||||
collections.append(collection)
|
||||
response = _propfind_response(
|
||||
base_prefix, path, collection, props, user, write=True)
|
||||
if top_tag.tag == _tag("D", "propname"):
|
||||
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)
|
||||
for collection in read_collections:
|
||||
if collection in collections:
|
||||
continue
|
||||
response = _propfind_response(
|
||||
base_prefix, path, collection, props, user, write=False)
|
||||
if top_tag.tag == _tag("D", "propname"):
|
||||
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)
|
||||
|
||||
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."""
|
||||
is_collection = isinstance(item, storage.BaseCollection)
|
||||
if is_collection:
|
||||
@ -568,6 +600,35 @@ def _propfind_response(base_prefix, path, item, props, user, write=False):
|
||||
prop404 = ET.Element(_tag("D", "prop"))
|
||||
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:
|
||||
element = ET.Element(tag)
|
||||
is404 = False
|
||||
|
Loading…
Reference in New Issue
Block a user