diff --git a/radicale/__init__.py b/radicale/__init__.py index a126bcf..ba49af5 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -473,8 +473,6 @@ class Application: to_path = storage.sanitize_path(to_url.path) if not self._access(user, to_path, "w"): return NOT_ALLOWED - if to_path.strip("/").startswith(path.strip("/")): - return client.CONFLICT, {}, None with self._lock_collection("w", user): item = next(self.Collection.discover(path), None) @@ -488,15 +486,17 @@ class Application: return client.CONFLICT, {}, None to_item = next(self.Collection.discover(to_path), None) + if (isinstance(to_item, self.Collection) or + to_item and environ.get("HTTP_OVERWRITE", "F") != "T"): + return client.CONFLICT, {}, None to_parent_path = storage.sanitize_path( "/%s/" % posixpath.dirname(to_path.strip("/"))) to_collection = next( self.Collection.discover(to_parent_path), None) - if not to_collection or to_item: + if not to_collection: return client.CONFLICT, {}, None to_href = posixpath.basename(to_path.strip("/")) - to_collection.upload(to_href, item.item) - item.collection.delete(item.href) + self.Collection.move(item, to_collection, to_href) return client.CREATED, {}, None def do_OPTIONS(self, environ, path, content, user): diff --git a/radicale/storage.py b/radicale/storage.py index b8cb7bb..e5ced8f 100644 --- a/radicale/storage.py +++ b/radicale/storage.py @@ -265,6 +265,26 @@ class BaseCollection: """ raise NotImplementedError + @classmethod + def move(cls, item, to_collection, to_href): + """Move an object. + + ``item`` is the item to move. + + ``to_collection`` is the target collection. + + ``to_href`` is the target name in ``to_collection``. An item with the + same name might already exist. + + """ + if item.collection.path == to_collection.path and item.href == to_href: + return + if to_collection.has(to_href): + to_collection.update(to_href, item.item) + else: + to_collection.upload(to_href, item.item) + item.collection.delete(item.href) + @property def etag(self): return get_etag(self.serialize()) @@ -514,6 +534,15 @@ class Collection(BaseCollection): return cls(sane_path, principal=principal) + @classmethod + def move(cls, item, to_collection, to_href): + os.rename( + path_to_filesystem(item.collection._filesystem_path, item.href), + path_to_filesystem(to_collection._filesystem_path, to_href)) + sync_directory(to_collection._filesystem_path) + if item.collection._filesystem_path != to_collection._filesystem_path: + sync_directory(item.collection._filesystem_path) + def list(self): try: hrefs = os.listdir(self._filesystem_path)