-
Notifications
You must be signed in to change notification settings - Fork 94
Description
- webdav3-client python3 version: 3.14.6
- uses requests version: 2.25.1
- Nextcloud server version(s) affected: 19.0.5, 19.0.13, 20.0.14, 21.0.9, 22.2.10, 23.0.10, 24.0.6
In the client.clean method, the leading slash must be removed. :edit: but that still doesn't let me delete files from trash in Nextcloud.
Steps to reproduce issue
- Make a file called Some_Scribus_File_autosave_08_08_2021_02_23.sla in Nextcloud. Ensure it synchronizes or upload it directly to the web interface.
- Delete it.
- Run the script at https://github.com/poikilos/nextcloudops as follows:
python3 nextcloudops.py --scrub-scribus
Actual Behavior
Currently, the path given to client's "clean" method is transformed into a Urn instance, which prepends a slash. However, does not match the Nextcloud API documentation.
:edit: The nextcloudops.py removes the leading slash manually as per the Nextcloud documentation, but the file doesn't get deleted either way. Files can be listed but not deleted. self.client.verify = False in my delete method doesn't help.
Expected Behavior
The client's clean command should allow deleting files from the trash.
The correct call is execute_request(action='clean', path=urn.quote()[1:]), otherwise the resource is not found. I'm not sure what other methods are affected, but this fix is the only way I can delete files in Nextcloud may not use the API according to the documentation.
Starting with "DELETE remote.php/dav" exactly (regardless of whether nextcloud is in domain/nextcloud) is verified by the API documentation: https://docs.nextcloud.com/server/19/developer_manual/client_apis/WebDAV/basic.html#deleting-files-and-folders-rfc4918
For now, I did a workaround by using execute_request in https://github.com/poikilos/nextcloudops
which proves the change works on my Nextcloud instance even with the location for the site being example.com/nextcloud- but the res.text is an html page with:
Nextcloud
Error
App not installed:Nextcloud – a safe home for all your data
So the solution is not known. This issue may only apply to deleting things from trash, but that is an important use case since listing and deleting anything else is easy to do without a program in contrast to scrubbing the trash selectively like my project above does.
Already tried
- DELETE remote.php/dav/trashbin/$USER/trash/$filename (exactly as shown in the Nextcloud WebDAV API documentation)
- adding more parts of the path before remote.php, with or without a leading slash by changing the string after creating the Urn instance (see instances of
failsin my code in my repo above) - clean(rel_url) where rel_url starts with /trashbin or trashbin
- fails with "webdav3.exceptions.RemoteResourceNotFound: Remote resource: /nextcloud/remote.php/dav/trashbin/redacted/trash/filename not found" even though the resulting value for url (inside of the execute_request function clean calls) is correct (such as "https://example.com/nextcloud/remote.php/dav/trashbin/redacted/trash/filename")
Solving:
It turns out the URL is both mangled and url encoded twice if using the output from the list function.
It works when that is resolved.
Potential solutions are below.
(where "path" refers to a result obtained using client.list)
Approach 1
The caller (if wontfix) must manually parse items:
- remove /nextcloud/ if present
- remove remote.php/dav/ if present
urllib.parse import unquotepath = unquote- The user must send the path to
cleanformatted like: /trashbin/USER/trash/FILENAME (current behavior)
additions to help with that:
- Detect an encoded url (check for "%" maybe) and show a warning, but continue encoding in case not detected properly.
- check for the API route ("remote.php") in the URL, and if present, show an error saying the path must be relative to the API.
Approach 2
If URL starts with self.webdav.root assume item is output from list and:
- remove webdav_root from the start
- maybe don't encode the URL (assume it is output from list that is already encoded)
- Return a response so the caller can see what is going on (for example, 204 not 200 is expected when deleting a file from Nextcloud).
With both 1&2, it works fine (tested):
@wrap_connection_error
def clean(self, remote_path):
root = self.webdav.root
if (root is not None) and remote_path.startswith(root):
# It is a full path relative to hostname.
remote_path = remote_path[len(root):]
urn_quoted = remote_path
# ^ Assume full paths are from list() (already quoted).
# Re-encoding with quote() would corrupt %20 into %2520.
else:
urn = Urn(remote_path)
urn_quoted = urn.quote()
print("[client] urn_quoted={}".format(urn_quoted))
return self.execute_request(action='clean', path=urn_quoted)