Rework XML helpers functions
- Merge make_tag, tag_from_clark and tag_from_human into make_clark and make_human - Don't use RegEx for parsing
This commit is contained in:
parent
262d76cc87
commit
d3776e55fb
@ -374,10 +374,9 @@ class Application(
|
||||
xml_declaration=True)
|
||||
return f.getvalue()
|
||||
|
||||
def _webdav_error_response(self, namespace, name,
|
||||
def _webdav_error_response(self, human_tag,
|
||||
status=httputils.WEBDAV_PRECONDITION_FAILED[0]):
|
||||
"""Generate XML error response."""
|
||||
headers = {"Content-Type": "text/xml; charset=%s" % self._encoding}
|
||||
content = self._write_xml_content(
|
||||
xmlutils.webdav_error(namespace, name))
|
||||
content = self._write_xml_content(xmlutils.webdav_error(human_tag))
|
||||
return status, headers, content
|
||||
|
@ -31,15 +31,15 @@ def xml_delete(base_prefix, path, collection, href=None):
|
||||
"""
|
||||
collection.delete(href)
|
||||
|
||||
multistatus = ET.Element(xmlutils.make_tag("D", "multistatus"))
|
||||
response = ET.Element(xmlutils.make_tag("D", "response"))
|
||||
multistatus = ET.Element(xmlutils.make_clark("D:multistatus"))
|
||||
response = ET.Element(xmlutils.make_clark("D:response"))
|
||||
multistatus.append(response)
|
||||
|
||||
href = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
href = ET.Element(xmlutils.make_clark("D:href"))
|
||||
href.text = xmlutils.make_href(base_prefix, path)
|
||||
response.append(href)
|
||||
|
||||
status = ET.Element(xmlutils.make_tag("D", "status"))
|
||||
status = ET.Element(xmlutils.make_clark("D:status"))
|
||||
status.text = xmlutils.make_response(200)
|
||||
response.append(status)
|
||||
|
||||
|
@ -54,8 +54,7 @@ class ApplicationMkcalendarMixin:
|
||||
with self._storage.acquire_lock("w", user):
|
||||
item = next(self._storage.discover(path), None)
|
||||
if item:
|
||||
return self._webdav_error_response(
|
||||
"D", "resource-must-be-null")
|
||||
return self._webdav_error_response("D:resource-must-be-null")
|
||||
parent_path = pathutils.unstrip_path(
|
||||
posixpath.dirname(pathutils.strip_path(path)), True)
|
||||
parent_item = next(self._storage.discover(parent_path), None)
|
||||
|
@ -74,8 +74,8 @@ class ApplicationMoveMixin:
|
||||
not to_item and
|
||||
to_collection.path != item.collection.path and
|
||||
to_collection.has_uid(item.uid)):
|
||||
return self._webdav_error_response(
|
||||
"C" if tag == "VCALENDAR" else "CR", "no-uid-conflict")
|
||||
return self._webdav_error_response("%s:no-uid-conflict" % (
|
||||
"C" if tag == "VCALENDAR" else "CR"))
|
||||
to_href = posixpath.basename(pathutils.strip_path(to_path))
|
||||
try:
|
||||
self._storage.move(item, to_collection, to_href)
|
||||
|
@ -41,26 +41,26 @@ def xml_propfind(base_prefix, path, xml_request, allowed_items, user,
|
||||
# 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 = (xml_request[0] if xml_request is not None else
|
||||
ET.Element(xmlutils.make_tag("D", "allprop")))
|
||||
ET.Element(xmlutils.make_clark("D:allprop")))
|
||||
|
||||
props = ()
|
||||
allprop = False
|
||||
propname = False
|
||||
if top_tag.tag == xmlutils.make_tag("D", "allprop"):
|
||||
if top_tag.tag == xmlutils.make_clark("D:allprop"):
|
||||
allprop = True
|
||||
elif top_tag.tag == xmlutils.make_tag("D", "propname"):
|
||||
elif top_tag.tag == xmlutils.make_clark("D:propname"):
|
||||
propname = True
|
||||
elif top_tag.tag == xmlutils.make_tag("D", "prop"):
|
||||
elif top_tag.tag == xmlutils.make_clark("D:prop"):
|
||||
props = [prop.tag for prop in top_tag]
|
||||
|
||||
if xmlutils.make_tag("D", "current-user-principal") in props and not user:
|
||||
if xmlutils.make_clark("D:current-user-principal") in props and not user:
|
||||
# Ask for authentication
|
||||
# Returning the DAV:unauthenticated pseudo-principal as specified in
|
||||
# RFC 5397 doesn't seem to work with DAVdroid.
|
||||
return client.FORBIDDEN, None
|
||||
|
||||
# Writing answer
|
||||
multistatus = ET.Element(xmlutils.make_tag("D", "multistatus"))
|
||||
multistatus = ET.Element(xmlutils.make_clark("D:multistatus"))
|
||||
|
||||
for item, permission in allowed_items:
|
||||
write = permission == "w"
|
||||
@ -83,9 +83,8 @@ def xml_propfind_response(base_prefix, path, item, props, user, encoding,
|
||||
else:
|
||||
collection = item.collection
|
||||
|
||||
response = ET.Element(xmlutils.make_tag("D", "response"))
|
||||
|
||||
href = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
response = ET.Element(xmlutils.make_clark("D:response"))
|
||||
href = ET.Element(xmlutils.make_clark("D:href"))
|
||||
if is_collection:
|
||||
# Some clients expect collections to end with /
|
||||
uri = pathutils.unstrip_path(item.path, True)
|
||||
@ -98,39 +97,39 @@ def xml_propfind_response(base_prefix, path, item, props, user, encoding,
|
||||
if propname or allprop:
|
||||
props = []
|
||||
# Should list all properties that can be retrieved by the code below
|
||||
props.append(xmlutils.make_tag("D", "principal-collection-set"))
|
||||
props.append(xmlutils.make_tag("D", "current-user-principal"))
|
||||
props.append(xmlutils.make_tag("D", "current-user-privilege-set"))
|
||||
props.append(xmlutils.make_tag("D", "supported-report-set"))
|
||||
props.append(xmlutils.make_tag("D", "resourcetype"))
|
||||
props.append(xmlutils.make_tag("D", "owner"))
|
||||
props.append(xmlutils.make_clark("D:principal-collection-set"))
|
||||
props.append(xmlutils.make_clark("D:current-user-principal"))
|
||||
props.append(xmlutils.make_clark("D:current-user-privilege-set"))
|
||||
props.append(xmlutils.make_clark("D:supported-report-set"))
|
||||
props.append(xmlutils.make_clark("D:resourcetype"))
|
||||
props.append(xmlutils.make_clark("D:owner"))
|
||||
|
||||
if is_collection and collection.is_principal:
|
||||
props.append(xmlutils.make_tag("C", "calendar-user-address-set"))
|
||||
props.append(xmlutils.make_tag("D", "principal-URL"))
|
||||
props.append(xmlutils.make_tag("CR", "addressbook-home-set"))
|
||||
props.append(xmlutils.make_tag("C", "calendar-home-set"))
|
||||
props.append(xmlutils.make_clark("C:calendar-user-address-set"))
|
||||
props.append(xmlutils.make_clark("D:principal-URL"))
|
||||
props.append(xmlutils.make_clark("CR:addressbook-home-set"))
|
||||
props.append(xmlutils.make_clark("C:calendar-home-set"))
|
||||
|
||||
if not is_collection or is_leaf:
|
||||
props.append(xmlutils.make_tag("D", "getetag"))
|
||||
props.append(xmlutils.make_tag("D", "getlastmodified"))
|
||||
props.append(xmlutils.make_tag("D", "getcontenttype"))
|
||||
props.append(xmlutils.make_tag("D", "getcontentlength"))
|
||||
props.append(xmlutils.make_clark("D:getetag"))
|
||||
props.append(xmlutils.make_clark("D:getlastmodified"))
|
||||
props.append(xmlutils.make_clark("D:getcontenttype"))
|
||||
props.append(xmlutils.make_clark("D:getcontentlength"))
|
||||
|
||||
if is_collection:
|
||||
if is_leaf:
|
||||
props.append(xmlutils.make_tag("D", "displayname"))
|
||||
props.append(xmlutils.make_tag("D", "sync-token"))
|
||||
props.append(xmlutils.make_clark("D:displayname"))
|
||||
props.append(xmlutils.make_clark("D:sync-token"))
|
||||
if collection.get_meta("tag") == "VCALENDAR":
|
||||
props.append(xmlutils.make_tag("CS", "getctag"))
|
||||
props.append(xmlutils.make_clark("CS:getctag"))
|
||||
props.append(
|
||||
xmlutils.make_tag("C", "supported-calendar-component-set"))
|
||||
xmlutils.make_clark("C:supported-calendar-component-set"))
|
||||
|
||||
meta = item.get_meta()
|
||||
for tag in meta:
|
||||
if tag == "tag":
|
||||
continue
|
||||
clark_tag = xmlutils.tag_from_human(tag)
|
||||
clark_tag = xmlutils.make_clark(tag)
|
||||
if clark_tag not in props:
|
||||
props.append(clark_tag)
|
||||
|
||||
@ -142,30 +141,30 @@ def xml_propfind_response(base_prefix, path, item, props, user, encoding,
|
||||
for tag in props:
|
||||
element = ET.Element(tag)
|
||||
is404 = False
|
||||
if tag == xmlutils.make_tag("D", "getetag"):
|
||||
if tag == xmlutils.make_clark("D:getetag"):
|
||||
if not is_collection or is_leaf:
|
||||
element.text = item.etag
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("D", "getlastmodified"):
|
||||
elif tag == xmlutils.make_clark("D:getlastmodified"):
|
||||
if not is_collection or is_leaf:
|
||||
element.text = item.last_modified
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("D", "principal-collection-set"):
|
||||
tag = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
elif tag == xmlutils.make_clark("D:principal-collection-set"):
|
||||
tag = ET.Element(xmlutils.make_clark("D:href"))
|
||||
tag.text = xmlutils.make_href(base_prefix, "/")
|
||||
element.append(tag)
|
||||
elif (tag in (xmlutils.make_tag("C", "calendar-user-address-set"),
|
||||
xmlutils.make_tag("D", "principal-URL"),
|
||||
xmlutils.make_tag("CR", "addressbook-home-set"),
|
||||
xmlutils.make_tag("C", "calendar-home-set")) and
|
||||
elif (tag in (xmlutils.make_clark("C:calendar-user-address-set"),
|
||||
xmlutils.make_clark("D:principal-URL"),
|
||||
xmlutils.make_clark("CR:addressbook-home-set"),
|
||||
xmlutils.make_clark("C:calendar-home-set")) and
|
||||
collection.is_principal and is_collection):
|
||||
tag = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
tag = ET.Element(xmlutils.make_clark("D:href"))
|
||||
tag.text = xmlutils.make_href(base_prefix, path)
|
||||
element.append(tag)
|
||||
elif tag == xmlutils.make_tag("C", "supported-calendar-component-set"):
|
||||
human_tag = xmlutils.tag_from_clark(tag)
|
||||
elif tag == xmlutils.make_clark("C:supported-calendar-component-set"):
|
||||
human_tag = xmlutils.make_human_tag(tag)
|
||||
if is_collection and is_leaf:
|
||||
meta = item.get_meta(human_tag)
|
||||
if meta:
|
||||
@ -173,94 +172,91 @@ def xml_propfind_response(base_prefix, path, item, props, user, encoding,
|
||||
else:
|
||||
components = ("VTODO", "VEVENT", "VJOURNAL")
|
||||
for component in components:
|
||||
comp = ET.Element(xmlutils.make_tag("C", "comp"))
|
||||
comp = ET.Element(xmlutils.make_clark("C:comp"))
|
||||
comp.set("name", component)
|
||||
element.append(comp)
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("D", "current-user-principal"):
|
||||
elif tag == xmlutils.make_clark("D:current-user-principal"):
|
||||
if user:
|
||||
tag = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
tag = ET.Element(xmlutils.make_clark("D:href"))
|
||||
tag.text = xmlutils.make_href(base_prefix, "/%s/" % user)
|
||||
element.append(tag)
|
||||
else:
|
||||
element.append(ET.Element(
|
||||
xmlutils.make_tag("D", "unauthenticated")))
|
||||
elif tag == xmlutils.make_tag("D", "current-user-privilege-set"):
|
||||
privileges = [("D", "read")]
|
||||
xmlutils.make_clark("D:unauthenticated")))
|
||||
elif tag == xmlutils.make_clark("D:current-user-privilege-set"):
|
||||
privileges = ["D:read"]
|
||||
if write:
|
||||
privileges.append(("D", "all"))
|
||||
privileges.append(("D", "write"))
|
||||
privileges.append(("D", "write-properties"))
|
||||
privileges.append(("D", "write-content"))
|
||||
for ns, privilege_name in privileges:
|
||||
privilege = ET.Element(xmlutils.make_tag("D", "privilege"))
|
||||
privileges.append("D:all")
|
||||
privileges.append("D:write")
|
||||
privileges.append("D:write-properties")
|
||||
privileges.append("D:write-content")
|
||||
for human_tag in privileges:
|
||||
privilege = ET.Element(xmlutils.make_clark("D:privilege"))
|
||||
privilege.append(ET.Element(
|
||||
xmlutils.make_tag(ns, privilege_name)))
|
||||
xmlutils.make_clark(human_tag)))
|
||||
element.append(privilege)
|
||||
elif tag == xmlutils.make_tag("D", "supported-report-set"):
|
||||
elif tag == xmlutils.make_clark("D:supported-report-set"):
|
||||
# These 3 reports are not implemented
|
||||
reports = [
|
||||
("D", "expand-property"),
|
||||
("D", "principal-search-property-set"),
|
||||
("D", "principal-property-search")]
|
||||
reports = ["D:expand-property",
|
||||
"D:principal-search-property-set",
|
||||
"D:principal-property-search"]
|
||||
if is_collection and is_leaf:
|
||||
reports.append(("D", "sync-collection"))
|
||||
reports.append("D:sync-collection")
|
||||
if item.get_meta("tag") == "VADDRESSBOOK":
|
||||
reports.append(("CR", "addressbook-multiget"))
|
||||
reports.append(("CR", "addressbook-query"))
|
||||
reports.append("CR:addressbook-multiget")
|
||||
reports.append("CR:addressbook-query")
|
||||
elif item.get_meta("tag") == "VCALENDAR":
|
||||
reports.append(("C", "calendar-multiget"))
|
||||
reports.append(("C", "calendar-query"))
|
||||
for ns, report_name in reports:
|
||||
supported = ET.Element(
|
||||
xmlutils.make_tag("D", "supported-report"))
|
||||
report_tag = ET.Element(xmlutils.make_tag("D", "report"))
|
||||
supported_report_tag = ET.Element(
|
||||
xmlutils.make_tag(ns, report_name))
|
||||
report_tag.append(supported_report_tag)
|
||||
supported.append(report_tag)
|
||||
element.append(supported)
|
||||
elif tag == xmlutils.make_tag("D", "getcontentlength"):
|
||||
reports.append("C:calendar-multiget")
|
||||
reports.append("C:calendar-query")
|
||||
for human_tag in reports:
|
||||
supported_report = ET.Element(
|
||||
xmlutils.make_clark("D:supported-report"))
|
||||
report_tag = ET.Element(xmlutils.make_clark("D:report"))
|
||||
report_tag.append(ET.Element(xmlutils.make_clark(human_tag)))
|
||||
supported_report.append(report_tag)
|
||||
element.append(supported_report)
|
||||
elif tag == xmlutils.make_clark("D:getcontentlength"):
|
||||
if not is_collection or is_leaf:
|
||||
element.text = str(len(item.serialize().encode(encoding)))
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("D", "owner"):
|
||||
elif tag == xmlutils.make_clark("D:owner"):
|
||||
# return empty elment, if no owner available (rfc3744-5.1)
|
||||
if collection.owner:
|
||||
tag = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
tag = ET.Element(xmlutils.make_clark("D:href"))
|
||||
tag.text = xmlutils.make_href(
|
||||
base_prefix, "/%s/" % collection.owner)
|
||||
element.append(tag)
|
||||
elif is_collection:
|
||||
if tag == xmlutils.make_tag("D", "getcontenttype"):
|
||||
if tag == xmlutils.make_clark("D:getcontenttype"):
|
||||
if is_leaf:
|
||||
element.text = xmlutils.MIMETYPES[item.get_meta("tag")]
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("D", "resourcetype"):
|
||||
elif tag == xmlutils.make_clark("D:resourcetype"):
|
||||
if item.is_principal:
|
||||
tag = ET.Element(xmlutils.make_tag("D", "principal"))
|
||||
tag = ET.Element(xmlutils.make_clark("D:principal"))
|
||||
element.append(tag)
|
||||
if is_leaf:
|
||||
if item.get_meta("tag") == "VADDRESSBOOK":
|
||||
tag = ET.Element(
|
||||
xmlutils.make_tag("CR", "addressbook"))
|
||||
xmlutils.make_clark("CR:addressbook"))
|
||||
element.append(tag)
|
||||
elif item.get_meta("tag") == "VCALENDAR":
|
||||
tag = ET.Element(xmlutils.make_tag("C", "calendar"))
|
||||
tag = ET.Element(xmlutils.make_clark("C:calendar"))
|
||||
element.append(tag)
|
||||
tag = ET.Element(xmlutils.make_tag("D", "collection"))
|
||||
tag = ET.Element(xmlutils.make_clark("D:collection"))
|
||||
element.append(tag)
|
||||
elif tag == xmlutils.make_tag("RADICALE", "displayname"):
|
||||
elif tag == xmlutils.make_clark("RADICALE:displayname"):
|
||||
# Only for internal use by the web interface
|
||||
displayname = item.get_meta("D:displayname")
|
||||
if displayname is not None:
|
||||
element.text = displayname
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("D", "displayname"):
|
||||
elif tag == xmlutils.make_clark("D:displayname"):
|
||||
displayname = item.get_meta("D:displayname")
|
||||
if not displayname and is_leaf:
|
||||
displayname = item.path
|
||||
@ -268,27 +264,27 @@ def xml_propfind_response(base_prefix, path, item, props, user, encoding,
|
||||
element.text = displayname
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("CS", "getctag"):
|
||||
elif tag == xmlutils.make_clark("CS:getctag"):
|
||||
if is_leaf:
|
||||
element.text = item.etag
|
||||
else:
|
||||
is404 = True
|
||||
elif tag == xmlutils.make_tag("D", "sync-token"):
|
||||
elif tag == xmlutils.make_clark("D:sync-token"):
|
||||
if is_leaf:
|
||||
element.text, _ = item.sync()
|
||||
else:
|
||||
is404 = True
|
||||
else:
|
||||
human_tag = xmlutils.tag_from_clark(tag)
|
||||
human_tag = xmlutils.make_human_tag(tag)
|
||||
meta = item.get_meta(human_tag)
|
||||
if meta is not None:
|
||||
element.text = meta
|
||||
else:
|
||||
is404 = True
|
||||
# Not for collections
|
||||
elif tag == xmlutils.make_tag("D", "getcontenttype"):
|
||||
elif tag == xmlutils.make_clark("D:getcontenttype"):
|
||||
element.text = xmlutils.get_content_type(item, encoding)
|
||||
elif tag == xmlutils.make_tag("D", "resourcetype"):
|
||||
elif tag == xmlutils.make_clark("D:resourcetype"):
|
||||
# resourcetype must be returned empty for non-collection elements
|
||||
pass
|
||||
else:
|
||||
@ -299,12 +295,12 @@ def xml_propfind_response(base_prefix, path, item, props, user, encoding,
|
||||
for status_code, childs in responses.items():
|
||||
if not childs:
|
||||
continue
|
||||
propstat = ET.Element(xmlutils.make_tag("D", "propstat"))
|
||||
propstat = ET.Element(xmlutils.make_clark("D:propstat"))
|
||||
response.append(propstat)
|
||||
prop = ET.Element(xmlutils.make_tag("D", "prop"))
|
||||
prop = ET.Element(xmlutils.make_clark("D:prop"))
|
||||
prop.extend(childs)
|
||||
propstat.append(prop)
|
||||
status = ET.Element(xmlutils.make_tag("D", "status"))
|
||||
status = ET.Element(xmlutils.make_clark("D:status"))
|
||||
status.text = xmlutils.make_response(status_code)
|
||||
propstat.append(status)
|
||||
|
||||
|
@ -35,17 +35,17 @@ def xml_add_propstat_to(element, tag, status_number):
|
||||
``status_number``.
|
||||
|
||||
"""
|
||||
propstat = ET.Element(xmlutils.make_tag("D", "propstat"))
|
||||
propstat = ET.Element(xmlutils.make_clark("D:propstat"))
|
||||
element.append(propstat)
|
||||
|
||||
prop = ET.Element(xmlutils.make_tag("D", "prop"))
|
||||
prop = ET.Element(xmlutils.make_clark("D:prop"))
|
||||
propstat.append(prop)
|
||||
|
||||
clark_tag = tag if "{" in tag else xmlutils.make_tag(*tag.split(":", 1))
|
||||
clark_tag = xmlutils.make_clark(tag)
|
||||
prop_tag = ET.Element(clark_tag)
|
||||
prop.append(prop_tag)
|
||||
|
||||
status = ET.Element(xmlutils.make_tag("D", "status"))
|
||||
status = ET.Element(xmlutils.make_clark("D:status"))
|
||||
status.text = xmlutils.make_response(status_number)
|
||||
propstat.append(status)
|
||||
|
||||
@ -60,11 +60,11 @@ def xml_proppatch(base_prefix, path, xml_request, collection):
|
||||
props_to_remove = xmlutils.props_from_request(xml_request,
|
||||
actions=("remove",))
|
||||
|
||||
multistatus = ET.Element(xmlutils.make_tag("D", "multistatus"))
|
||||
response = ET.Element(xmlutils.make_tag("D", "response"))
|
||||
multistatus = ET.Element(xmlutils.make_clark("D:multistatus"))
|
||||
response = ET.Element(xmlutils.make_clark("D:response"))
|
||||
multistatus.append(response)
|
||||
|
||||
href = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
href = ET.Element(xmlutils.make_clark("D:href"))
|
||||
href.text = xmlutils.make_href(base_prefix, path)
|
||||
response.append(href)
|
||||
|
||||
|
@ -201,9 +201,8 @@ class ApplicationPutMixin:
|
||||
prepared_item, = prepared_items
|
||||
if (item and item.uid != prepared_item.uid or
|
||||
not item and parent_item.has_uid(prepared_item.uid)):
|
||||
return self._webdav_error_response(
|
||||
"C" if tag == "VCALENDAR" else "CR",
|
||||
"no-uid-conflict")
|
||||
return self._webdav_error_response("%s:no-uid-conflict" % (
|
||||
"C" if tag == "VCALENDAR" else "CR"))
|
||||
|
||||
href = posixpath.basename(pathutils.strip_path(path))
|
||||
try:
|
||||
|
@ -36,42 +36,42 @@ def xml_report(base_prefix, path, xml_request, collection, encoding,
|
||||
Read rfc3253-3.6 for info.
|
||||
|
||||
"""
|
||||
multistatus = ET.Element(xmlutils.make_tag("D", "multistatus"))
|
||||
multistatus = ET.Element(xmlutils.make_clark("D:multistatus"))
|
||||
if xml_request is None:
|
||||
return client.MULTI_STATUS, multistatus
|
||||
root = xml_request
|
||||
if root.tag in (
|
||||
xmlutils.make_tag("D", "principal-search-property-set"),
|
||||
xmlutils.make_tag("D", "principal-property-search"),
|
||||
xmlutils.make_tag("D", "expand-property")):
|
||||
xmlutils.make_clark("D:principal-search-property-set"),
|
||||
xmlutils.make_clark("D:principal-property-search"),
|
||||
xmlutils.make_clark("D:expand-property")):
|
||||
# We don't support searching for principals or indirect retrieving of
|
||||
# properties, just return an empty result.
|
||||
# InfCloud asks for expand-property reports (even if we don't announce
|
||||
# support for them) and stops working if an error code is returned.
|
||||
logger.warning("Unsupported REPORT method %r on %r requested",
|
||||
xmlutils.tag_from_clark(root.tag), path)
|
||||
xmlutils.make_human_tag(root.tag), path)
|
||||
return client.MULTI_STATUS, multistatus
|
||||
if (root.tag == xmlutils.make_tag("C", "calendar-multiget") and
|
||||
if (root.tag == xmlutils.make_clark("C:calendar-multiget") and
|
||||
collection.get_meta("tag") != "VCALENDAR" or
|
||||
root.tag == xmlutils.make_tag("CR", "addressbook-multiget") and
|
||||
root.tag == xmlutils.make_clark("CR:addressbook-multiget") and
|
||||
collection.get_meta("tag") != "VADDRESSBOOK" or
|
||||
root.tag == xmlutils.make_tag("D", "sync-collection") and
|
||||
root.tag == xmlutils.make_clark("D:sync-collection") and
|
||||
collection.get_meta("tag") not in ("VADDRESSBOOK", "VCALENDAR")):
|
||||
logger.warning("Invalid REPORT method %r on %r requested",
|
||||
xmlutils.tag_from_clark(root.tag), path)
|
||||
xmlutils.make_human_tag(root.tag), path)
|
||||
return (client.CONFLICT,
|
||||
xmlutils.webdav_error("D", "supported-report"))
|
||||
prop_element = root.find(xmlutils.make_tag("D", "prop"))
|
||||
xmlutils.webdav_error("D:supported-report"))
|
||||
prop_element = root.find(xmlutils.make_clark("D:prop"))
|
||||
props = (
|
||||
[prop.tag for prop in prop_element]
|
||||
if prop_element is not None else [])
|
||||
|
||||
if root.tag in (
|
||||
xmlutils.make_tag("C", "calendar-multiget"),
|
||||
xmlutils.make_tag("CR", "addressbook-multiget")):
|
||||
xmlutils.make_clark("C:calendar-multiget"),
|
||||
xmlutils.make_clark("CR:addressbook-multiget")):
|
||||
# Read rfc4791-7.9 for info
|
||||
hreferences = set()
|
||||
for href_element in root.findall(xmlutils.make_tag("D", "href")):
|
||||
for href_element in root.findall(xmlutils.make_clark("D:href")):
|
||||
href_path = pathutils.sanitize_path(
|
||||
unquote(urlparse(href_element.text).path))
|
||||
if (href_path + "/").startswith(base_prefix + "/"):
|
||||
@ -79,9 +79,9 @@ def xml_report(base_prefix, path, xml_request, collection, encoding,
|
||||
else:
|
||||
logger.warning("Skipping invalid path %r in REPORT request on "
|
||||
"%r", href_path, path)
|
||||
elif root.tag == xmlutils.make_tag("D", "sync-collection"):
|
||||
elif root.tag == xmlutils.make_clark("D:sync-collection"):
|
||||
old_sync_token_element = root.find(
|
||||
xmlutils.make_tag("D", "sync-token"))
|
||||
xmlutils.make_clark("D:sync-token"))
|
||||
old_sync_token = ""
|
||||
if old_sync_token_element is not None and old_sync_token_element.text:
|
||||
old_sync_token = old_sync_token_element.text.strip()
|
||||
@ -93,18 +93,18 @@ def xml_report(base_prefix, path, xml_request, collection, encoding,
|
||||
logger.warning("Client provided invalid sync token %r: %s",
|
||||
old_sync_token, e, exc_info=True)
|
||||
return (client.CONFLICT,
|
||||
xmlutils.webdav_error("D", "valid-sync-token"))
|
||||
xmlutils.webdav_error("D:valid-sync-token"))
|
||||
hreferences = (pathutils.unstrip_path(
|
||||
posixpath.join(collection.path, n)) for n in names)
|
||||
# Append current sync token to response
|
||||
sync_token_element = ET.Element(xmlutils.make_tag("D", "sync-token"))
|
||||
sync_token_element = ET.Element(xmlutils.make_clark("D:sync-token"))
|
||||
sync_token_element.text = sync_token
|
||||
multistatus.append(sync_token_element)
|
||||
else:
|
||||
hreferences = (path,)
|
||||
filters = (
|
||||
root.findall("./%s" % xmlutils.make_tag("C", "filter")) +
|
||||
root.findall("./%s" % xmlutils.make_tag("CR", "filter")))
|
||||
root.findall("./%s" % xmlutils.make_clark("C:filter")) +
|
||||
root.findall("./%s" % xmlutils.make_clark("CR:filter")))
|
||||
|
||||
def retrieve_items(collection, hreferences, multistatus):
|
||||
"""Retrieves all items that are referenced in ``hreferences`` from
|
||||
@ -157,18 +157,18 @@ def xml_report(base_prefix, path, xml_request, collection, encoding,
|
||||
def match(item, filter_):
|
||||
tag = collection_tag
|
||||
if (tag == "VCALENDAR" and
|
||||
filter_.tag != xmlutils.make_tag("C", filter_)):
|
||||
filter_.tag != xmlutils.make_clark("C:%s" % filter_)):
|
||||
if len(filter_) == 0:
|
||||
return True
|
||||
if len(filter_) > 1:
|
||||
raise ValueError("Filter with %d children" % len(filter_))
|
||||
if filter_[0].tag != xmlutils.make_tag("C", "comp-filter"):
|
||||
if filter_[0].tag != xmlutils.make_clark("C:comp-filter"):
|
||||
raise ValueError("Unexpected %r in filter" % filter_[0].tag)
|
||||
return radicale_filter.comp_match(item, filter_[0])
|
||||
if (tag == "VADDRESSBOOK" and
|
||||
filter_.tag != xmlutils.make_tag("CR", filter_)):
|
||||
filter_.tag != xmlutils.make_clark("CR:%s" % filter_)):
|
||||
for child in filter_:
|
||||
if child.tag != xmlutils.make_tag("CR", "prop-filter"):
|
||||
if child.tag != xmlutils.make_clark("CR:prop-filter"):
|
||||
raise ValueError("Unexpected %r in filter" % child.tag)
|
||||
test = filter_.get("test", "anyof")
|
||||
if test == "anyof":
|
||||
@ -203,15 +203,15 @@ def xml_report(base_prefix, path, xml_request, collection, encoding,
|
||||
|
||||
for tag in props:
|
||||
element = ET.Element(tag)
|
||||
if tag == xmlutils.make_tag("D", "getetag"):
|
||||
if tag == xmlutils.make_clark("D:getetag"):
|
||||
element.text = item.etag
|
||||
found_props.append(element)
|
||||
elif tag == xmlutils.make_tag("D", "getcontenttype"):
|
||||
elif tag == xmlutils.make_clark("D:getcontenttype"):
|
||||
element.text = xmlutils.get_content_type(item, encoding)
|
||||
found_props.append(element)
|
||||
elif tag in (
|
||||
xmlutils.make_tag("C", "calendar-data"),
|
||||
xmlutils.make_tag("CR", "address-data")):
|
||||
xmlutils.make_clark("C:calendar-data"),
|
||||
xmlutils.make_clark("CR:address-data")):
|
||||
element.text = item.serialize()
|
||||
found_props.append(element)
|
||||
else:
|
||||
@ -228,26 +228,26 @@ def xml_report(base_prefix, path, xml_request, collection, encoding,
|
||||
|
||||
def xml_item_response(base_prefix, href, found_props=(), not_found_props=(),
|
||||
found_item=True):
|
||||
response = ET.Element(xmlutils.make_tag("D", "response"))
|
||||
response = ET.Element(xmlutils.make_clark("D:response"))
|
||||
|
||||
href_tag = ET.Element(xmlutils.make_tag("D", "href"))
|
||||
href_tag = ET.Element(xmlutils.make_clark("D:href"))
|
||||
href_tag.text = xmlutils.make_href(base_prefix, href)
|
||||
response.append(href_tag)
|
||||
|
||||
if found_item:
|
||||
for code, props in ((200, found_props), (404, not_found_props)):
|
||||
if props:
|
||||
propstat = ET.Element(xmlutils.make_tag("D", "propstat"))
|
||||
status = ET.Element(xmlutils.make_tag("D", "status"))
|
||||
propstat = ET.Element(xmlutils.make_clark("D:propstat"))
|
||||
status = ET.Element(xmlutils.make_clark("D:status"))
|
||||
status.text = xmlutils.make_response(code)
|
||||
prop_tag = ET.Element(xmlutils.make_tag("D", "prop"))
|
||||
prop_tag = ET.Element(xmlutils.make_clark("D:prop"))
|
||||
for prop in props:
|
||||
prop_tag.append(prop)
|
||||
propstat.append(prop_tag)
|
||||
propstat.append(status)
|
||||
response.append(propstat)
|
||||
else:
|
||||
status = ET.Element(xmlutils.make_tag("D", "status"))
|
||||
status = ET.Element(xmlutils.make_clark("D:status"))
|
||||
status.text = xmlutils.make_response(404)
|
||||
response.append(status)
|
||||
|
||||
|
@ -75,7 +75,7 @@ def comp_match(item, filter_, level=0):
|
||||
# Point #1 of rfc4791-9.7.1
|
||||
return name == tag
|
||||
if len(filter_) == 1:
|
||||
if filter_[0].tag == xmlutils.make_tag("C", "is-not-defined"):
|
||||
if filter_[0].tag == xmlutils.make_clark("C:is-not-defined"):
|
||||
# Point #2 of rfc4791-9.7.1
|
||||
return name != tag
|
||||
if name != tag:
|
||||
@ -89,14 +89,14 @@ def comp_match(item, filter_, level=0):
|
||||
else list(getattr(item.vobject_item,
|
||||
"%s_list" % tag.lower())))
|
||||
for child in filter_:
|
||||
if child.tag == xmlutils.make_tag("C", "prop-filter"):
|
||||
if child.tag == xmlutils.make_clark("C:prop-filter"):
|
||||
if not any(prop_match(comp, child, "C")
|
||||
for comp in components):
|
||||
return False
|
||||
elif child.tag == xmlutils.make_tag("C", "time-range"):
|
||||
elif child.tag == xmlutils.make_clark("C:time-range"):
|
||||
if not time_range_match(item.vobject_item, filter_[0], tag):
|
||||
return False
|
||||
elif child.tag == xmlutils.make_tag("C", "comp-filter"):
|
||||
elif child.tag == xmlutils.make_clark("C:comp-filter"):
|
||||
if not comp_match(item, child, level=level + 1):
|
||||
return False
|
||||
else:
|
||||
@ -115,20 +115,20 @@ def prop_match(vobject_item, filter_, ns):
|
||||
# Point #1 of rfc4791-9.7.2
|
||||
return name in vobject_item.contents
|
||||
if len(filter_) == 1:
|
||||
if filter_[0].tag == xmlutils.make_tag("C", "is-not-defined"):
|
||||
if filter_[0].tag == xmlutils.make_clark("C:is-not-defined"):
|
||||
# Point #2 of rfc4791-9.7.2
|
||||
return name not in vobject_item.contents
|
||||
if name not in vobject_item.contents:
|
||||
return False
|
||||
# Point #3 and #4 of rfc4791-9.7.2
|
||||
for child in filter_:
|
||||
if ns == "C" and child.tag == xmlutils.make_tag("C", "time-range"):
|
||||
if ns == "C" and child.tag == xmlutils.make_clark("C:time-range"):
|
||||
if not time_range_match(vobject_item, child, name):
|
||||
return False
|
||||
elif child.tag == xmlutils.make_tag(ns, "text-match"):
|
||||
elif child.tag == xmlutils.make_clark("%s:text-match" % ns):
|
||||
if not text_match(vobject_item, child, name, ns):
|
||||
return False
|
||||
elif child.tag == xmlutils.make_tag(ns, "param-filter"):
|
||||
elif child.tag == xmlutils.make_clark("%s:param-filter" % ns):
|
||||
if not param_filter_match(vobject_item, child, name, ns):
|
||||
return False
|
||||
else:
|
||||
@ -464,10 +464,10 @@ def param_filter_match(vobject_item, filter_, parent_name, ns):
|
||||
children = getattr(vobject_item, "%s_list" % parent_name, [])
|
||||
condition = any(name in child.params for child in children)
|
||||
if len(filter_) > 0:
|
||||
if filter_[0].tag == xmlutils.make_tag(ns, "text-match"):
|
||||
if filter_[0].tag == xmlutils.make_clark("%s:text-match" % ns):
|
||||
return condition and text_match(
|
||||
vobject_item, filter_[0], parent_name, ns, name)
|
||||
elif filter_[0].tag == xmlutils.make_tag(ns, "is-not-defined"):
|
||||
elif filter_[0].tag == xmlutils.make_clark("%s:is-not-defined" % ns):
|
||||
return not condition
|
||||
else:
|
||||
return condition
|
||||
@ -488,18 +488,18 @@ def simplify_prefilters(filters, collection_tag="VCALENDAR"):
|
||||
if collection_tag != "VCALENDAR":
|
||||
simple = False
|
||||
break
|
||||
if (col_filter.tag != xmlutils.make_tag("C", "comp-filter") or
|
||||
if (col_filter.tag != xmlutils.make_clark("C:comp-filter") or
|
||||
col_filter.get("name").upper() != "VCALENDAR"):
|
||||
simple = False
|
||||
continue
|
||||
simple &= len(col_filter) <= 1
|
||||
for comp_filter in col_filter:
|
||||
if comp_filter.tag != xmlutils.make_tag("C", "comp-filter"):
|
||||
if comp_filter.tag != xmlutils.make_clark("C:comp-filter"):
|
||||
simple = False
|
||||
continue
|
||||
tag = comp_filter.get("name").upper()
|
||||
if comp_filter.find(
|
||||
xmlutils.make_tag("C", "is-not-defined")) is not None:
|
||||
xmlutils.make_clark("C:is-not-defined")) is not None:
|
||||
simple = False
|
||||
continue
|
||||
simple &= len(comp_filter) <= 1
|
||||
@ -507,7 +507,7 @@ def simplify_prefilters(filters, collection_tag="VCALENDAR"):
|
||||
if tag not in ("VTODO", "VEVENT", "VJOURNAL"):
|
||||
simple = False
|
||||
break
|
||||
if time_filter.tag != xmlutils.make_tag("C", "time-range"):
|
||||
if time_filter.tag != xmlutils.make_clark("C:time-range"):
|
||||
simple = False
|
||||
continue
|
||||
start = time_filter.get("start")
|
||||
|
@ -23,7 +23,6 @@ Helper functions for XML.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import OrderedDict
|
||||
from http import client
|
||||
@ -54,9 +53,6 @@ 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)
|
||||
HUMAN_REGEX = re.compile(r"(?P<namespace>[^:{}]*):(?P<tag>.*)", re.VERBOSE)
|
||||
|
||||
|
||||
def pretty_xml(element, level=0):
|
||||
"""Indent an ElementTree ``element`` and its children."""
|
||||
@ -79,34 +75,47 @@ def pretty_xml(element, level=0):
|
||||
return '<?xml version="1.0"?>\n%s' % ET.tostring(element, "unicode")
|
||||
|
||||
|
||||
def make_tag(short_name, local):
|
||||
"""Get XML Clark notation {uri(``short_name``)}``local``."""
|
||||
return "{%s}%s" % (NAMESPACES[short_name], local)
|
||||
def make_clark(human_tag):
|
||||
"""Get XML Clark notation from human tag ``human_tag``.
|
||||
|
||||
|
||||
def tag_from_clark(name):
|
||||
"""Get a human-readable variant of the XML Clark notation tag ``name``.
|
||||
|
||||
For a given name using the XML Clark notation, return a human-readable
|
||||
variant of the tag name for known namespaces. Otherwise, return the name as
|
||||
is.
|
||||
If ``human_tag`` is already in XML Clark notation it is returned as-is.
|
||||
|
||||
"""
|
||||
match = CLARK_TAG_REGEX.match(name)
|
||||
if match and match.group("namespace") in NAMESPACES_REV:
|
||||
args = {
|
||||
"ns": NAMESPACES_REV[match.group("namespace")],
|
||||
"tag": match.group("tag")}
|
||||
return "%(ns)s:%(tag)s" % args
|
||||
return name
|
||||
if human_tag.startswith("{"):
|
||||
ns, tag = human_tag[len("{"):].split("}", maxsplit=1)
|
||||
if not ns or not tag:
|
||||
raise ValueError("Invalid XML tag: %r" % human_tag)
|
||||
return human_tag
|
||||
ns_prefix, tag = human_tag.split(":", maxsplit=1)
|
||||
if not ns_prefix or not tag:
|
||||
raise ValueError("Invalid XML tag: %r" % human_tag)
|
||||
ns = NAMESPACES.get(ns_prefix)
|
||||
if not ns:
|
||||
raise ValueError("Unknown XML namespace prefix: %r" % human_tag)
|
||||
return "{%s}%s" % (ns, tag)
|
||||
|
||||
|
||||
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 make_tag(match.group("namespace"), match.group("tag"))
|
||||
return name
|
||||
def make_human_tag(clark_tag):
|
||||
"""Replace known namespaces in XML Clark notation ``clark_tag`` with
|
||||
prefix.
|
||||
|
||||
If the namespace is not in ``NAMESPACES`` the tag is returned as-is.
|
||||
|
||||
"""
|
||||
if not clark_tag.startswith("{"):
|
||||
ns_prefix, tag = clark_tag.split(":", maxsplit=1)
|
||||
if not ns_prefix or not tag:
|
||||
raise ValueError("Invalid XML tag: %r" % clark_tag)
|
||||
if ns_prefix not in NAMESPACES:
|
||||
raise ValueError("Unknown XML namespace prefix: %r" % clark_tag)
|
||||
return clark_tag
|
||||
ns, tag = clark_tag[len("{"):].split("}", maxsplit=1)
|
||||
if not ns or not tag:
|
||||
raise ValueError("Invalid XML tag: %r" % clark_tag)
|
||||
ns_prefix = NAMESPACES_REV.get(ns)
|
||||
if ns_prefix:
|
||||
return "%s:%s" % (ns_prefix, tag)
|
||||
return clark_tag
|
||||
|
||||
|
||||
def make_response(code):
|
||||
@ -120,10 +129,10 @@ def make_href(base_prefix, href):
|
||||
return quote("%s%s" % (base_prefix, href))
|
||||
|
||||
|
||||
def webdav_error(namespace, name):
|
||||
def webdav_error(human_tag):
|
||||
"""Generate XML error message."""
|
||||
root = ET.Element(make_tag("D", "error"))
|
||||
root.append(ET.Element(make_tag(namespace, name)))
|
||||
root = ET.Element(make_clark("D:error"))
|
||||
root.append(ET.Element(human_tag))
|
||||
return root
|
||||
|
||||
|
||||
@ -145,29 +154,29 @@ def props_from_request(xml_request, actions=("set", "remove")):
|
||||
return result
|
||||
|
||||
for action in actions:
|
||||
action_element = xml_request.find(make_tag("D", action))
|
||||
action_element = xml_request.find(make_clark("D:%s" % action))
|
||||
if action_element is not None:
|
||||
break
|
||||
else:
|
||||
action_element = xml_request
|
||||
|
||||
prop_element = action_element.find(make_tag("D", "prop"))
|
||||
prop_element = action_element.find(make_clark("D:prop"))
|
||||
if prop_element is not None:
|
||||
for prop in prop_element:
|
||||
if prop.tag == make_tag("D", "resourcetype"):
|
||||
if prop.tag == make_clark("D:resourcetype"):
|
||||
for resource_type in prop:
|
||||
if resource_type.tag == make_tag("C", "calendar"):
|
||||
if resource_type.tag == make_clark("C:calendar"):
|
||||
result["tag"] = "VCALENDAR"
|
||||
break
|
||||
elif resource_type.tag == make_tag("CR", "addressbook"):
|
||||
elif resource_type.tag == make_clark("CR:addressbook"):
|
||||
result["tag"] = "VADDRESSBOOK"
|
||||
break
|
||||
elif prop.tag == make_tag("C", "supported-calendar-component-set"):
|
||||
result[tag_from_clark(prop.tag)] = ",".join(
|
||||
elif prop.tag == make_clark("C:supported-calendar-component-set"):
|
||||
result[make_human_tag(prop.tag)] = ",".join(
|
||||
supported_comp.attrib["name"]
|
||||
for supported_comp in prop
|
||||
if supported_comp.tag == make_tag("C", "comp"))
|
||||
if supported_comp.tag == make_clark("C:comp"))
|
||||
else:
|
||||
result[tag_from_clark(prop.tag)] = prop.text
|
||||
result[make_human_tag(prop.tag)] = prop.text
|
||||
|
||||
return result
|
||||
|
Loading…
Reference in New Issue
Block a user