From 1453c0b72c5059bceb6654d3f14dd1542796ce6e Mon Sep 17 00:00:00 2001 From: Unrud Date: Tue, 14 Jan 2020 06:19:11 +0100 Subject: [PATCH] Mark attributes for internal use with underscore --- radicale/app/__init__.py | 38 +++++++++++++++++++------------------- radicale/app/delete.py | 6 +++--- radicale/app/get.py | 10 +++++----- radicale/app/mkcalendar.py | 10 +++++----- radicale/app/mkcol.py | 10 +++++----- radicale/app/move.py | 10 +++++----- radicale/app/propfind.py | 28 +++++++++++++++------------- radicale/app/proppatch.py | 6 +++--- radicale/app/put.py | 17 +++++++++-------- radicale/app/report.py | 13 +++++++------ radicale/xmlutils.py | 3 +-- 11 files changed, 77 insertions(+), 74 deletions(-) diff --git a/radicale/app/__init__.py b/radicale/app/__init__.py index 10982c4..a410bf3 100644 --- a/radicale/app/__init__.py +++ b/radicale/app/__init__.py @@ -76,11 +76,11 @@ class Application( """ super().__init__() self.configuration = configuration - self.auth = auth.load(configuration) - self.storage = storage.load(configuration) - self.rights = rights.load(configuration) - self.Web = web.load(configuration) - self.encoding = configuration.get("encoding", "request") + self._auth = auth.load(configuration) + self._storage = storage.load(configuration) + self._rights = rights.load(configuration) + self._web = web.load(configuration) + self._encoding = configuration.get("encoding", "request") def _headers_log(self, environ): """Sanitize headers for logging.""" @@ -107,7 +107,7 @@ class Application( charsets.append( content_type.split("charset=")[1].split(";")[0].strip()) # Then append default Radicale charset - charsets.append(self.encoding) + charsets.append(self._encoding) # Then append various fallbacks charsets.append("utf-8") charsets.append("iso8859-1") @@ -153,8 +153,8 @@ class Application( if answer: if hasattr(answer, "encode"): logger.debug("Response content:\n%s", answer) - headers["Content-Type"] += "; charset=%s" % self.encoding - answer = answer.encode(self.encoding) + headers["Content-Type"] += "; charset=%s" % self._encoding + answer = answer.encode(self._encoding) accept_encoding = [ encoding.strip() for encoding in environ.get("HTTP_ACCEPT_ENCODING", "").split(",") @@ -231,7 +231,7 @@ class Application( # Ask authentication backend to check rights login = password = "" - external_login = self.auth.get_external_login(environ) + external_login = self._auth.get_external_login(environ) authorization = environ.get("HTTP_AUTHORIZATION", "") if external_login: login, password = external_login @@ -241,7 +241,7 @@ class Application( login, password = self.decode(base64.b64decode( authorization.encode("ascii")), environ).split(":", 1) - user = self.auth.login(login, password) or "" if login else "" + user = self._auth.login(login, password) or "" if login else "" if user and login == user: logger.info("Successful login: %r", user) elif user: @@ -263,15 +263,15 @@ class Application( # Create principal collection if user: principal_path = "/%s/" % user - if self.rights.authorized(user, principal_path, "W"): - with self.storage.acquire_lock("r", user): + if self._rights.authorized(user, principal_path, "W"): + with self._storage.acquire_lock("r", user): principal = next( - self.storage.discover(principal_path, depth="1"), + self._storage.discover(principal_path, depth="1"), None) if not principal: - with self.storage.acquire_lock("w", user): + with self._storage.acquire_lock("w", user): try: - self.storage.create_collection(principal_path) + self._storage.create_collection(principal_path) except ValueError as e: logger.warning("Failed to create principal " "collection %r: %s", user, e) @@ -327,12 +327,12 @@ class Application( else: permissions = "" parent_permissions = permission - if permissions and self.rights.authorized(user, path, permissions): + if permissions and self._rights.authorized(user, path, permissions): return True if parent_permissions: parent_path = pathutils.unstrip_path( posixpath.dirname(pathutils.strip_path(path)), True) - if self.rights.authorized(user, parent_path, parent_permissions): + if self._rights.authorized(user, parent_path, parent_permissions): return True return False @@ -369,14 +369,14 @@ class Application( logger.debug("Response content:\n%s", xmlutils.pretty_xml(xml_content)) f = io.BytesIO() - ET.ElementTree(xml_content).write(f, encoding=self.encoding, + ET.ElementTree(xml_content).write(f, encoding=self._encoding, xml_declaration=True) return f.getvalue() def webdav_error_response(self, namespace, name, status=httputils.WEBDAV_PRECONDITION_FAILED[0]): """Generate XML error response.""" - headers = {"Content-Type": "text/xml; charset=%s" % self.encoding} + headers = {"Content-Type": "text/xml; charset=%s" % self._encoding} content = self.write_xml_content( xmlutils.webdav_error(namespace, name)) return status, headers, content diff --git a/radicale/app/delete.py b/radicale/app/delete.py index 0eb8577..83db11b 100644 --- a/radicale/app/delete.py +++ b/radicale/app/delete.py @@ -51,8 +51,8 @@ class ApplicationDeleteMixin: """Manage DELETE request.""" if not self.access(user, path, "w"): return httputils.NOT_ALLOWED - with self.storage.acquire_lock("w", user): - item = next(self.storage.discover(path), None) + with self._storage.acquire_lock("w", user): + item = next(self._storage.discover(path), None) if not item: return httputils.NOT_FOUND if not self.access(user, path, "w", item): @@ -66,5 +66,5 @@ class ApplicationDeleteMixin: else: xml_answer = xml_delete( base_prefix, path, item.collection, item.href) - headers = {"Content-Type": "text/xml; charset=%s" % self.encoding} + headers = {"Content-Type": "text/xml; charset=%s" % self._encoding} return client.OK, headers, self.write_xml_content(xml_answer) diff --git a/radicale/app/get.py b/radicale/app/get.py index c34efc9..5a3d7b7 100644 --- a/radicale/app/get.py +++ b/radicale/app/get.py @@ -47,13 +47,13 @@ class ApplicationGetMixin: def _content_disposition_attachement(self, filename): value = "attachement" try: - encoded_filename = quote(filename, encoding=self.encoding) + encoded_filename = quote(filename, encoding=self._encoding) except UnicodeEncodeError: logger.warning("Failed to encode filename: %r", filename, exc_info=True) encoded_filename = "" if encoded_filename: - value += "; filename*=%s''%s" % (self.encoding, encoded_filename) + value += "; filename*=%s''%s" % (self._encoding, encoded_filename) return value def do_GET(self, environ, base_prefix, path, user): @@ -69,11 +69,11 @@ class ApplicationGetMixin: "Redirected to %s" % web_path) # Dispatch .web URL to web module if path == "/.web" or path.startswith("/.web/"): - return self.Web.get(environ, base_prefix, path, user) + return self._web.get(environ, base_prefix, path, user) if not self.access(user, path, "r"): return httputils.NOT_ALLOWED - with self.storage.acquire_lock("r", user): - item = next(self.storage.discover(path), None) + with self._storage.acquire_lock("r", user): + item = next(self._storage.discover(path), None) if not item: return httputils.NOT_FOUND if not self.access(user, path, "r", item): diff --git a/radicale/app/mkcalendar.py b/radicale/app/mkcalendar.py index 49217a5..ade1f27 100644 --- a/radicale/app/mkcalendar.py +++ b/radicale/app/mkcalendar.py @@ -30,7 +30,7 @@ from radicale.log import logger class ApplicationMkcalendarMixin: def do_MKCALENDAR(self, environ, base_prefix, path, user): """Manage MKCALENDAR request.""" - if not self.rights.authorized(user, path, "w"): + if not self._rights.authorized(user, path, "w"): return httputils.NOT_ALLOWED try: xml_content = self.read_xml_content(environ) @@ -51,21 +51,21 @@ class ApplicationMkcalendarMixin: except ValueError as e: logger.warning( "Bad MKCALENDAR request on %r: %s", path, e, exc_info=True) - with self.storage.acquire_lock("w", user): - item = next(self.storage.discover(path), None) + 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") parent_path = pathutils.unstrip_path( posixpath.dirname(pathutils.strip_path(path)), True) - parent_item = next(self.storage.discover(parent_path), None) + parent_item = next(self._storage.discover(parent_path), None) if not parent_item: return httputils.CONFLICT if (not isinstance(parent_item, storage.BaseCollection) or parent_item.get_meta("tag")): return httputils.FORBIDDEN try: - self.storage.create_collection(path, props=props) + self._storage.create_collection(path, props=props) except ValueError as e: logger.warning( "Bad MKCALENDAR request on %r: %s", path, e, exc_info=True) diff --git a/radicale/app/mkcol.py b/radicale/app/mkcol.py index 54830dc..8d31861 100644 --- a/radicale/app/mkcol.py +++ b/radicale/app/mkcol.py @@ -30,7 +30,7 @@ from radicale.log import logger class ApplicationMkcolMixin: def do_MKCOL(self, environ, base_prefix, path, user): """Manage MKCOL request.""" - permissions = self.rights.authorized(user, path, "Ww") + permissions = self._rights.authorized(user, path, "Ww") if not permissions: return httputils.NOT_ALLOWED try: @@ -53,20 +53,20 @@ class ApplicationMkcolMixin: if (props.get("tag") and "w" not in permissions or not props.get("tag") and "W" not in permissions): return httputils.NOT_ALLOWED - with self.storage.acquire_lock("w", user): - item = next(self.storage.discover(path), None) + with self._storage.acquire_lock("w", user): + item = next(self._storage.discover(path), None) if item: return httputils.METHOD_NOT_ALLOWED parent_path = pathutils.unstrip_path( posixpath.dirname(pathutils.strip_path(path)), True) - parent_item = next(self.storage.discover(parent_path), None) + parent_item = next(self._storage.discover(parent_path), None) if not parent_item: return httputils.CONFLICT if (not isinstance(parent_item, storage.BaseCollection) or parent_item.get_meta("tag")): return httputils.FORBIDDEN try: - self.storage.create_collection(path, props=props) + self._storage.create_collection(path, props=props) except ValueError as e: logger.warning( "Bad MKCOL request on %r: %s", path, e, exc_info=True) diff --git a/radicale/app/move.py b/radicale/app/move.py index b1f7ca5..46feda0 100644 --- a/radicale/app/move.py +++ b/radicale/app/move.py @@ -45,8 +45,8 @@ class ApplicationMoveMixin: if not self.access(user, to_path, "w"): return httputils.NOT_ALLOWED - with self.storage.acquire_lock("w", user): - item = next(self.storage.discover(path), None) + with self._storage.acquire_lock("w", user): + item = next(self._storage.discover(path), None) if not item: return httputils.NOT_FOUND if (not self.access(user, path, "w", item) or @@ -56,13 +56,13 @@ class ApplicationMoveMixin: # TODO: support moving collections return httputils.METHOD_NOT_ALLOWED - to_item = next(self.storage.discover(to_path), None) + to_item = next(self._storage.discover(to_path), None) if isinstance(to_item, storage.BaseCollection): return httputils.FORBIDDEN to_parent_path = pathutils.unstrip_path( posixpath.dirname(pathutils.strip_path(to_path)), True) to_collection = next( - self.storage.discover(to_parent_path), None) + self._storage.discover(to_parent_path), None) if not to_collection: return httputils.CONFLICT tag = item.collection.get_meta("tag") @@ -78,7 +78,7 @@ class ApplicationMoveMixin: "C" if tag == "VCALENDAR" else "CR", "no-uid-conflict") to_href = posixpath.basename(pathutils.strip_path(to_path)) try: - self.storage.move(item, to_collection, to_href) + self._storage.move(item, to_collection, to_href) except ValueError as e: logger.warning( "Bad MOVE request on %r: %s", path, e, exc_info=True) diff --git a/radicale/app/propfind.py b/radicale/app/propfind.py index 5feb70a..214c11e 100644 --- a/radicale/app/propfind.py +++ b/radicale/app/propfind.py @@ -27,7 +27,8 @@ from radicale import httputils, pathutils, rights, storage, xmlutils from radicale.log import logger -def xml_propfind(base_prefix, path, xml_request, allowed_items, user): +def xml_propfind(base_prefix, path, xml_request, allowed_items, user, + encoding): """Read and answer PROPFIND requests. Read rfc4918-9.1 for info. @@ -63,7 +64,7 @@ def xml_propfind(base_prefix, path, xml_request, allowed_items, user): for item, permission in allowed_items: write = permission == "w" response = xml_propfind_response( - base_prefix, path, item, props, user, write=write, + base_prefix, path, item, props, user, encoding, write=write, allprop=allprop, propname=propname) if response: multistatus.append(response) @@ -71,8 +72,8 @@ def xml_propfind(base_prefix, path, xml_request, allowed_items, user): return client.MULTI_STATUS, multistatus -def xml_propfind_response(base_prefix, path, item, props, user, write=False, - propname=False, allprop=False): +def xml_propfind_response(base_prefix, path, item, props, user, encoding, + write=False, propname=False, allprop=False): """Build and return a PROPFIND response.""" if propname and allprop or (props and (propname or allprop)): raise ValueError("Only use one of props, propname and allprops") @@ -234,7 +235,6 @@ def xml_propfind_response(base_prefix, path, item, props, user, write=False, element.append(supported) elif tag == xmlutils.make_tag("D", "getcontentlength"): if not is_collection or is_leaf: - encoding = collection.configuration.get("encoding", "request") element.text = str(len(item.serialize().encode(encoding))) else: is404 = True @@ -299,7 +299,7 @@ def xml_propfind_response(base_prefix, path, item, props, user, write=False, is404 = True # Not for collections elif tag == xmlutils.make_tag("D", "getcontenttype"): - element.text = xmlutils.get_content_type(item) + element.text = xmlutils.get_content_type(item, encoding) elif tag == xmlutils.make_tag("D", "resourcetype"): # resourcetype must be returned empty for non-collection elements pass @@ -331,14 +331,14 @@ class ApplicationPropfindMixin: if isinstance(item, storage.BaseCollection): path = pathutils.unstrip_path(item.path, True) if item.get_meta("tag"): - permissions = self.rights.authorized(user, path, "rw") + permissions = self._rights.authorized(user, path, "rw") target = "collection with tag %r" % item.path else: - permissions = self.rights.authorized(user, path, "RW") + permissions = self._rights.authorized(user, path, "RW") target = "collection %r" % item.path else: path = pathutils.unstrip_path(item.collection.path, True) - permissions = self.rights.authorized(user, path, "rw") + permissions = self._rights.authorized(user, path, "rw") target = "item %r from %r" % (item.href, item.collection.path) if rights.intersect_permissions(permissions, "Ww"): permission = "w" @@ -368,8 +368,9 @@ class ApplicationPropfindMixin: except socket.timeout: logger.debug("client timed out", exc_info=True) return httputils.REQUEST_TIMEOUT - with self.storage.acquire_lock("r", user): - items = self.storage.discover(path, environ.get("HTTP_DEPTH", "0")) + with self._storage.acquire_lock("r", user): + items = self._storage.discover( + path, environ.get("HTTP_DEPTH", "0")) # take root item for rights checking item = next(items, None) if not item: @@ -380,9 +381,10 @@ class ApplicationPropfindMixin: items = itertools.chain([item], items) allowed_items = self._collect_allowed_items(items, user) headers = {"DAV": httputils.DAV_HEADERS, - "Content-Type": "text/xml; charset=%s" % self.encoding} + "Content-Type": "text/xml; charset=%s" % self._encoding} status, xml_answer = xml_propfind( - base_prefix, path, xml_content, allowed_items, user) + base_prefix, path, xml_content, allowed_items, user, + self._encoding) if status == client.FORBIDDEN: return httputils.NOT_ALLOWED return status, headers, self.write_xml_content(xml_answer) diff --git a/radicale/app/proppatch.py b/radicale/app/proppatch.py index 93c91a6..f291d80 100644 --- a/radicale/app/proppatch.py +++ b/radicale/app/proppatch.py @@ -98,8 +98,8 @@ class ApplicationProppatchMixin: except socket.timeout: logger.debug("client timed out", exc_info=True) return httputils.REQUEST_TIMEOUT - with self.storage.acquire_lock("w", user): - item = next(self.storage.discover(path), None) + with self._storage.acquire_lock("w", user): + item = next(self._storage.discover(path), None) if not item: return httputils.NOT_FOUND if not self.access(user, path, "w", item): @@ -107,7 +107,7 @@ class ApplicationProppatchMixin: if not isinstance(item, storage.BaseCollection): return httputils.FORBIDDEN headers = {"DAV": httputils.DAV_HEADERS, - "Content-Type": "text/xml; charset=%s" % self.encoding} + "Content-Type": "text/xml; charset=%s" % self._encoding} try: xml_answer = xml_proppatch(base_prefix, path, xml_content, item) diff --git a/radicale/app/put.py b/radicale/app/put.py index c2c8c4b..2996a3f 100644 --- a/radicale/app/put.py +++ b/radicale/app/put.py @@ -47,8 +47,8 @@ class ApplicationPutMixin: # Prepare before locking parent_path = pathutils.unstrip_path( posixpath.dirname(pathutils.strip_path(path)), True) - permissions = self.rights.authorized(user, path, "Ww") - parent_permissions = self.rights.authorized(user, parent_path, "w") + permissions = self._rights.authorized(user, path, "Ww") + parent_permissions = self._rights.authorized(user, parent_path, "w") def prepare(vobject_items, tag=None, write_whole_collection=None): if (write_whole_collection or @@ -149,9 +149,9 @@ class ApplicationPutMixin: (prepared_items, prepared_tag, prepared_write_whole_collection, prepared_props, prepared_exc_info) = prepare(vobject_items) - with self.storage.acquire_lock("w", user): - item = next(self.storage.discover(path), None) - parent_item = next(self.storage.discover(parent_path), None) + with self._storage.acquire_lock("w", user): + item = next(self._storage.discover(path), None) + parent_item = next(self._storage.discover(parent_path), None) if not parent_item: return httputils.CONFLICT @@ -165,9 +165,10 @@ class ApplicationPutMixin: tag = parent_item.get_meta("tag") if write_whole_collection: - if not self.rights.authorized(user, path, "w" if tag else "W"): + if not self._rights.authorized( + user, path, "w" if tag else "W"): return httputils.NOT_ALLOWED - elif not self.rights.authorized(user, parent_path, "w"): + elif not self._rights.authorized(user, parent_path, "w"): return httputils.NOT_ALLOWED etag = environ.get("HTTP_IF_MATCH", "") @@ -197,7 +198,7 @@ class ApplicationPutMixin: if write_whole_collection: try: - etag = self.storage.create_collection( + etag = self._storage.create_collection( path, prepared_items, props).etag except ValueError as e: logger.warning( diff --git a/radicale/app/report.py b/radicale/app/report.py index 80cf1ed..8a3be3a 100644 --- a/radicale/app/report.py +++ b/radicale/app/report.py @@ -29,7 +29,8 @@ from radicale.item import filter as radicale_filter from radicale.log import logger -def xml_report(base_prefix, path, xml_request, collection, unlock_storage_fn): +def xml_report(base_prefix, path, xml_request, collection, encoding, + unlock_storage_fn): """Read and answer REPORT requests. Read rfc3253-3.6 for info. @@ -208,7 +209,7 @@ def xml_report(base_prefix, path, xml_request, collection, unlock_storage_fn): element.text = item.etag found_props.append(element) elif tag == xmlutils.make_tag("D", "getcontenttype"): - element.text = xmlutils.get_content_type(item) + element.text = xmlutils.get_content_type(item, encoding) found_props.append(element) elif tag in ( xmlutils.make_tag("C", "calendar-data"), @@ -270,8 +271,8 @@ class ApplicationReportMixin: logger.debug("client timed out", exc_info=True) return httputils.REQUEST_TIMEOUT with contextlib.ExitStack() as lock_stack: - lock_stack.enter_context(self.storage.acquire_lock("r", user)) - item = next(self.storage.discover(path), None) + lock_stack.enter_context(self._storage.acquire_lock("r", user)) + item = next(self._storage.discover(path), None) if not item: return httputils.NOT_FOUND if not self.access(user, path, "r", item): @@ -280,10 +281,10 @@ class ApplicationReportMixin: collection = item else: collection = item.collection - headers = {"Content-Type": "text/xml; charset=%s" % self.encoding} + headers = {"Content-Type": "text/xml; charset=%s" % self._encoding} try: status, xml_answer = xml_report( - base_prefix, path, xml_content, collection, + base_prefix, path, xml_content, collection, self._encoding, lock_stack.close) except ValueError as e: logger.warning( diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index 3dca5db..cbe82bb 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -127,11 +127,10 @@ def webdav_error(namespace, name): return root -def get_content_type(item): +def get_content_type(item, encoding): """Get the content-type of an item with charset and component parameters. """ mimetype = OBJECT_MIMETYPES[item.name] - encoding = item.collection.configuration.get("encoding", "request") tag = item.component_name content_type = "%s;charset=%s" % (mimetype, encoding) if tag: