From 89ac2fb3979b11901935f7f3f4964b70d20a6535 Mon Sep 17 00:00:00 2001 From: Unrud Date: Sat, 6 Aug 2016 04:44:18 +0200 Subject: [PATCH 1/4] Remove faulty check This should have been (to_path.strip("/")+"/").startswith(path.strip("/")+"/"). But it's not required as we don't support moving collections. --- radicale/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index a126bcf..2416ce4 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) From 17ff22cae451493aac12635447a784cb3a3b859d Mon Sep 17 00:00:00 2001 From: Unrud Date: Sat, 6 Aug 2016 04:45:44 +0200 Subject: [PATCH 2/4] Support replacing in MOVE method --- radicale/__init__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index 2416ce4..b7b70c7 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -486,15 +486,22 @@ 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) + if path.strip("/") != to_path.strip("/"): + if to_item: + to_collection.update(to_href, item.item) + else: + to_collection.upload(to_href, item.item) + item.collection.delete(item.href) return client.CREATED, {}, None def do_OPTIONS(self, environ, path, content, user): From 23582c8208ad74f7ca30c68b5d6b6bb42bee109f Mon Sep 17 00:00:00 2001 From: Unrud Date: Sat, 6 Aug 2016 04:47:17 +0200 Subject: [PATCH 3/4] Expose low level MOVE operation in storage.BaseCollection --- radicale/__init__.py | 7 +------ radicale/storage.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index b7b70c7..ba49af5 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -496,12 +496,7 @@ class Application: if not to_collection: return client.CONFLICT, {}, None to_href = posixpath.basename(to_path.strip("/")) - if path.strip("/") != to_path.strip("/"): - if to_item: - to_collection.update(to_href, item.item) - else: - 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 d14797f..e8d84b1 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()) From 5f66d009d6566a4e7fd990b440e7be8219aa0daf Mon Sep 17 00:00:00 2001 From: Unrud Date: Sat, 6 Aug 2016 04:47:49 +0200 Subject: [PATCH 4/4] Atomic MOVE in multifilesystem --- radicale/storage.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/radicale/storage.py b/radicale/storage.py index e8d84b1..afe4213 100644 --- a/radicale/storage.py +++ b/radicale/storage.py @@ -534,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)