diff --git a/radicale/app/__init__.py b/radicale/app/__init__.py index e138362..0f9ba95 100644 --- a/radicale/app/__init__.py +++ b/radicale/app/__init__.py @@ -49,6 +49,7 @@ from radicale.app.mkcalendar import ApplicationMkcalendarMixin from radicale.app.mkcol import ApplicationMkcolMixin from radicale.app.move import ApplicationMoveMixin from radicale.app.options import ApplicationOptionsMixin +from radicale.app.post import ApplicationPostMixin from radicale.app.propfind import ApplicationPropfindMixin from radicale.app.proppatch import ApplicationProppatchMixin from radicale.app.put import ApplicationPutMixin @@ -63,7 +64,8 @@ class Application( ApplicationMkcalendarMixin, ApplicationMkcolMixin, ApplicationMoveMixin, ApplicationOptionsMixin, ApplicationPropfindMixin, ApplicationProppatchMixin, - ApplicationPutMixin, ApplicationReportMixin): + ApplicationPostMixin, ApplicationPutMixin, + ApplicationReportMixin): """WSGI application.""" diff --git a/radicale/app/post.py b/radicale/app/post.py new file mode 100644 index 0000000..723b8e9 --- /dev/null +++ b/radicale/app/post.py @@ -0,0 +1,30 @@ +# This file is part of Radicale Server - Calendar Server +# Copyright © 2008 Nicolas Kandel +# Copyright © 2008 Pascal Halter +# Copyright © 2008-2017 Guillaume Ayoub +# Copyright © 2017-2018 Unrud +# Copyright © 2020 Tom Hacohen +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Radicale. If not, see . + +from radicale import httputils + + +class ApplicationPostMixin: + def do_POST(self, environ, base_prefix, path, user): + """Manage POST request.""" + if path == "/.web" or path.startswith("/.web/"): + return self._web.post(environ, base_prefix, path, user) + + return httputils.METHOD_NOT_ALLOWED diff --git a/radicale/tests/__init__.py b/radicale/tests/__init__.py index 1e18615..1824bd1 100644 --- a/radicale/tests/__init__.py +++ b/radicale/tests/__init__.py @@ -104,6 +104,11 @@ class BaseTest: self._check_status(status, 200, check) return status, answer + def post(self, path, check=True, **args): + status, _, answer = self.request("POST", path, **args) + self._check_status(status, 200, check) + return status, answer + def put(self, path, data, check=True, **args): status, _, answer = self.request("PUT", path, data, **args) self._check_status(status, 201, check) diff --git a/radicale/tests/custom/web.py b/radicale/tests/custom/web.py index ff45feb..7d11902 100644 --- a/radicale/tests/custom/web.py +++ b/radicale/tests/custom/web.py @@ -27,3 +27,6 @@ from radicale import web class Web(web.BaseWeb): def get(self, environ, base_prefix, path, user): return client.OK, {"Content-Type": "text/plain"}, "custom" + + def post(self, environ, base_prefix, path, user): + return client.OK, {"Content-Type": "text/plain"}, "custom post" diff --git a/radicale/tests/test_web.py b/radicale/tests/test_web.py index 578897a..f5fed54 100644 --- a/radicale/tests/test_web.py +++ b/radicale/tests/test_web.py @@ -63,3 +63,6 @@ class TestBaseWebRequests(BaseTest): self.application = Application(self.configuration) _, answer = self.get("/.web") assert answer == "custom" + + _, answer = self.post("/.web") + assert answer == "custom post" diff --git a/radicale/web/__init__.py b/radicale/web/__init__.py index 0a40285..6d87833 100644 --- a/radicale/web/__init__.py +++ b/radicale/web/__init__.py @@ -21,7 +21,7 @@ Take a look at the class ``BaseWeb`` if you want to implement your own. """ -from radicale import utils +from radicale import httputils, utils INTERNAL_TYPES = ("none", "internal") @@ -52,4 +52,16 @@ class BaseWeb: ``user`` is empty for anonymous users. """ - raise NotImplementedError + return httputils.METHOD_NOT_ALLOWED + + def post(self, environ, base_prefix, path, user): + """POST request. + + ``base_prefix`` is sanitized and never ends with "/". + + ``path`` is sanitized and always starts with "/.web" + + ``user`` is empty for anonymous users. + + """ + return httputils.METHOD_NOT_ALLOWED