move.py 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. # This file is part of Radicale Server - Calendar Server
  2. # Copyright © 2008 Nicolas Kandel
  3. # Copyright © 2008 Pascal Halter
  4. # Copyright © 2008-2017 Guillaume Ayoub
  5. # Copyright © 2017-2018 Unrud <unrud@outlook.com>
  6. #
  7. # This library is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This library is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  19. import posixpath
  20. from http import client
  21. from urllib.parse import urlparse
  22. from radicale import httputils, pathutils, storage, types
  23. from radicale.app.base import Access, ApplicationBase
  24. from radicale.log import logger
  25. class ApplicationPartMove(ApplicationBase):
  26. def do_MOVE(self, environ: types.WSGIEnviron, base_prefix: str,
  27. path: str, user: str) -> types.WSGIResponse:
  28. """Manage MOVE request."""
  29. raw_dest = environ.get("HTTP_DESTINATION", "")
  30. to_url = urlparse(raw_dest)
  31. if to_url.netloc != environ["HTTP_HOST"]:
  32. logger.info("Unsupported destination address: %r", raw_dest)
  33. # Remote destination server, not supported
  34. return httputils.REMOTE_DESTINATION
  35. access = Access(self._rights, user, path)
  36. if not access.check("w"):
  37. return httputils.NOT_ALLOWED
  38. to_path = pathutils.sanitize_path(to_url.path)
  39. if not (to_path + "/").startswith(base_prefix + "/"):
  40. logger.warning("Destination %r from MOVE request on %r doesn't "
  41. "start with base prefix", to_path, path)
  42. return httputils.NOT_ALLOWED
  43. to_path = to_path[len(base_prefix):]
  44. to_access = Access(self._rights, user, to_path)
  45. if not to_access.check("w"):
  46. return httputils.NOT_ALLOWED
  47. with self._storage.acquire_lock("w", user):
  48. item = next(iter(self._storage.discover(path)), None)
  49. if not item:
  50. return httputils.NOT_FOUND
  51. if (not access.check("w", item) or
  52. not to_access.check("w", item)):
  53. return httputils.NOT_ALLOWED
  54. if isinstance(item, storage.BaseCollection):
  55. # TODO: support moving collections
  56. return httputils.METHOD_NOT_ALLOWED
  57. to_item = next(iter(self._storage.discover(to_path)), None)
  58. if isinstance(to_item, storage.BaseCollection):
  59. return httputils.FORBIDDEN
  60. to_parent_path = pathutils.unstrip_path(
  61. posixpath.dirname(pathutils.strip_path(to_path)), True)
  62. to_collection = next(iter(
  63. self._storage.discover(to_parent_path)), None)
  64. if not to_collection:
  65. return httputils.CONFLICT
  66. assert isinstance(to_collection, storage.BaseCollection)
  67. assert item.collection is not None
  68. collection_tag = item.collection.tag
  69. if not collection_tag or collection_tag != to_collection.tag:
  70. return httputils.FORBIDDEN
  71. if to_item and environ.get("HTTP_OVERWRITE", "F") != "T":
  72. return httputils.PRECONDITION_FAILED
  73. if (to_item and item.uid != to_item.uid or
  74. not to_item and
  75. to_collection.path != item.collection.path and
  76. to_collection.has_uid(item.uid)):
  77. return self._webdav_error_response(
  78. client.CONFLICT, "%s:no-uid-conflict" % (
  79. "C" if collection_tag == "VCALENDAR" else "CR"))
  80. to_href = posixpath.basename(pathutils.strip_path(to_path))
  81. try:
  82. self._storage.move(item, to_collection, to_href)
  83. except ValueError as e:
  84. logger.warning(
  85. "Bad MOVE request on %r: %s", path, e, exc_info=True)
  86. return httputils.BAD_REQUEST
  87. return client.NO_CONTENT if to_item else client.CREATED, {}, None