From 41f8e9674047e17b3c44680d88f1ede7ebf2d5c2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 22 Aug 2012 17:36:09 +0530 Subject: [PATCH] WPD: Implement deleting objects --- src/calibre/devices/mtp/filesystem_cache.py | 15 ++++++- .../mtp/windows/content_enumeration.cpp | 40 +++++++++++++++++++ src/calibre/devices/mtp/windows/device.cpp | 37 ++++++++++++++--- src/calibre/devices/mtp/windows/driver.py | 1 + src/calibre/devices/mtp/windows/global.h | 1 + 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/calibre/devices/mtp/filesystem_cache.py b/src/calibre/devices/mtp/filesystem_cache.py index cc7d41e09b..449afb8d38 100644 --- a/src/calibre/devices/mtp/filesystem_cache.py +++ b/src/calibre/devices/mtp/filesystem_cache.py @@ -20,12 +20,13 @@ class FileOrFolder(object): self.object_id = entry['id'] self.is_folder = entry['is_folder'] self.name = force_unicode(entry.get('name', '___'), 'utf-8') + self.storage_id = entry.get('storage_id', None) self.persistent_id = entry.get('persistent_id', self.object_id) self.size = entry.get('size', 0) # self.parent_id is None for storage objects self.parent_id = entry.get('parent_id', None) if self.parent_id == 0: - sid = entry['storage_id'] + sid = self.storage_id if sid not in all_storage_ids: sid = all_storage_ids[0] self.parent_id = sid @@ -79,7 +80,17 @@ class FilesystemCache(object): FileOrFolder(entry, self, all_storage_ids) for item in self.id_map.itervalues(): - p = item.parent + try: + p = item.parent + except KeyError: + # Parent does not exist, set the parent to be the storage + # object + sid = p.storage_id + if sid not in all_storage_ids: + sid = all_storage_ids[0] + item.parent_id = sid + p = item.parent + if p is not None: t = p.folders if item.is_folder else p.files t.append(item) diff --git a/src/calibre/devices/mtp/windows/content_enumeration.cpp b/src/calibre/devices/mtp/windows/content_enumeration.cpp index 84dd1d0c14..b87d7ba01c 100644 --- a/src/calibre/devices/mtp/windows/content_enumeration.cpp +++ b/src/calibre/devices/mtp/windows/content_enumeration.cpp @@ -572,4 +572,44 @@ end: } // }}} +PyObject* wpd::delete_object(IPortableDevice *device, const wchar_t *object_id) { // {{{ + IPortableDeviceContent *content = NULL; + HRESULT hr; + BOOL ok = FALSE; + PROPVARIANT pv; + IPortableDevicePropVariantCollection *object_ids = NULL; + + PropVariantInit(&pv); + pv.vt = VT_LPWSTR; + + Py_BEGIN_ALLOW_THREADS; + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&object_ids)); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) { hresult_set_exc("Failed to create propvariantcollection", hr); goto end; } + pv.pwszVal = (wchar_t*)object_id; + hr = object_ids->Add(&pv); + pv.pwszVal = NULL; + if (FAILED(hr)) { hresult_set_exc("Failed to add device id to propvariantcollection", hr); goto end; } + + Py_BEGIN_ALLOW_THREADS; + hr = device->Content(&content); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) { hresult_set_exc("Failed to create content interface", hr); goto end; } + + hr = content->Delete(PORTABLE_DEVICE_DELETE_NO_RECURSION, object_ids, NULL); + if (hr == E_ACCESSDENIED) PyErr_SetString(WPDError, "Do not have permission to delete this object"); + else if (hr == HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) || hr == HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION)) PyErr_SetString(WPDError, "Cannot delete object as it has children"); + else if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) || SUCCEEDED(hr)) ok = TRUE; + else hresult_set_exc("Cannot delete object", hr); + +end: + PropVariantClear(&pv); + if (content != NULL) content->Release(); + if (object_ids != NULL) object_ids->Release(); + if (!ok) return NULL; + Py_RETURN_NONE; + +} // }}} + } // namespace wpd diff --git a/src/calibre/devices/mtp/windows/device.cpp b/src/calibre/devices/mtp/windows/device.cpp index 7ae1c00216..33b70ff51b 100644 --- a/src/calibre/devices/mtp/windows/device.cpp +++ b/src/calibre/devices/mtp/windows/device.cpp @@ -78,20 +78,22 @@ update_data(Device *self, PyObject *args, PyObject *kwargs) { // get_filesystem() {{{ static PyObject* py_get_filesystem(Device *self, PyObject *args, PyObject *kwargs) { - PyObject *storage_id; + PyObject *storage_id, *ret; wchar_t *storage; if (!PyArg_ParseTuple(args, "O", &storage_id)) return NULL; storage = unicode_to_wchar(storage_id); if (storage == NULL) return NULL; - return wpd::get_filesystem(self->device, storage, self->bulk_properties); + ret = wpd::get_filesystem(self->device, storage, self->bulk_properties); + free(storage); + return ret; } // }}} // get_file() {{{ static PyObject* py_get_file(Device *self, PyObject *args, PyObject *kwargs) { - PyObject *object_id, *stream, *callback = NULL; + PyObject *object_id, *stream, *callback = NULL, *ret; wchar_t *object; if (!PyArg_ParseTuple(args, "OO|O", &object_id, &stream, &callback)) return NULL; @@ -100,13 +102,15 @@ py_get_file(Device *self, PyObject *args, PyObject *kwargs) { if (callback == NULL || !PyCallable_Check(callback)) callback = NULL; - return wpd::get_file(self->device, object, stream, callback); + ret = wpd::get_file(self->device, object, stream, callback); + free(object); + return ret; } // }}} // create_folder() {{{ static PyObject* py_create_folder(Device *self, PyObject *args, PyObject *kwargs) { - PyObject *pparent_id, *pname; + PyObject *pparent_id, *pname, *ret; wchar_t *parent_id, *name; if (!PyArg_ParseTuple(args, "OO", &pparent_id, &pname)) return NULL; @@ -114,7 +118,24 @@ py_create_folder(Device *self, PyObject *args, PyObject *kwargs) { name = unicode_to_wchar(pname); if (parent_id == NULL || name == NULL) return NULL; - return wpd::create_folder(self->device, parent_id, name); + ret = wpd::create_folder(self->device, parent_id, name); + free(parent_id); free(name); + return ret; +} // }}} + +// delete_object() {{{ +static PyObject* +py_delete_object(Device *self, PyObject *args, PyObject *kwargs) { + PyObject *pobject_id, *ret; + wchar_t *object_id; + + if (!PyArg_ParseTuple(args, "O", &pobject_id)) return NULL; + object_id = unicode_to_wchar(pobject_id); + if (object_id == NULL) return NULL; + + ret = wpd::delete_object(self->device, object_id); + free(object_id); + return ret; } // }}} static PyMethodDef Device_methods[] = { @@ -134,6 +155,10 @@ static PyMethodDef Device_methods[] = { "create_folder(parent_id, name) -> Create a folder. Returns the folder metadata." }, + {"delete_object", (PyCFunction)py_delete_object, METH_VARARGS, + "delete_object(object_id) -> Delete the object identified by object_id. Note that trying to delete a non-empty folder will raise an error." + }, + {NULL} }; diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index e97461bc15..c3314616a0 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -156,6 +156,7 @@ class MTP_DEVICE(MTPDeviceBase): storage = {'id':storage_id, 'size':capacity, 'name':name, 'is_folder':True} id_map = self.dev.get_filesystem(storage_id) + for x in id_map.itervalues(): x['storage_id'] = storage_id all_storage.append(storage) items.append(id_map.itervalues()) self._filesystem_cache = FilesystemCache(all_storage, chain(*items)) diff --git a/src/calibre/devices/mtp/windows/global.h b/src/calibre/devices/mtp/windows/global.h index 0acd1c7e9c..3b0309ce16 100644 --- a/src/calibre/devices/mtp/windows/global.h +++ b/src/calibre/devices/mtp/windows/global.h @@ -59,6 +59,7 @@ extern PyObject* get_device_information(IPortableDevice *device, IPortableDevice extern PyObject* get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties); extern PyObject* get_file(IPortableDevice *device, const wchar_t *object_id, PyObject *dest, PyObject *callback); extern PyObject* create_folder(IPortableDevice *device, const wchar_t *parent_id, const wchar_t *name); +extern PyObject* delete_object(IPortableDevice *device, const wchar_t *object_id); }