1 /*
2 * device_enumeration.cpp
3 * Copyright (C) 2012 Kovid Goyal <kovid at kovidgoyal.net>
4 *
5 * Distributed under terms of the GPL3 license.
6 */
7
8 #include "global.h"
9
10 namespace wpd {
11
12 IPortableDevice*
open_device(const wchar_t * pnp_id,CComPtr<IPortableDeviceValues> & client_information)13 open_device(const wchar_t *pnp_id, CComPtr<IPortableDeviceValues> &client_information) { // {{{
14 CComPtr<IPortableDevice> device;
15 HRESULT hr;
16
17 Py_BEGIN_ALLOW_THREADS;
18 hr = device.CoCreateInstance(CLSID_PortableDevice, NULL, CLSCTX_INPROC_SERVER);
19 Py_END_ALLOW_THREADS;
20 if (FAILED(hr)) { hresult_set_exc("Failed to create IPortableDevice", hr); device = NULL; }
21 else {
22 Py_BEGIN_ALLOW_THREADS;
23 hr = device->Open(pnp_id, client_information);
24 Py_END_ALLOW_THREADS;
25 if FAILED(hr) {
26 Py_BEGIN_ALLOW_THREADS;
27 device.Release();
28 Py_END_ALLOW_THREADS;
29 hresult_set_exc((hr == E_ACCESSDENIED) ? "Read/write access to device is denied": "Failed to open device", hr);
30 }
31 }
32
33 return device.Detach();
34
35 } // }}}
36
37 static PyObject*
get_storage_info(IPortableDevice * device)38 get_storage_info(IPortableDevice *device) { // {{{
39 HRESULT hr, hr2;
40 CComPtr<IPortableDeviceContent> content = NULL;
41 CComPtr<IEnumPortableDeviceObjectIDs> objects = NULL;
42 CComPtr<IPortableDeviceProperties> properties = NULL;
43 CComPtr<IPortableDeviceKeyCollection> storage_properties = NULL;
44 DWORD fetched, i;
45 GUID guid;
46 ULONGLONG capacity, free_space, capacity_objects, free_objects;
47 ULONG access, storage_type = WPD_STORAGE_TYPE_UNDEFINED;
48
49 pyobject_raii storage(PyList_New(0));
50 if (!storage) return NULL;
51
52 Py_BEGIN_ALLOW_THREADS;
53 hr = device->Content(&content);
54 Py_END_ALLOW_THREADS;
55 if (FAILED(hr)) {hresult_set_exc("Failed to get content interface from device", hr); return NULL;}
56
57 Py_BEGIN_ALLOW_THREADS;
58 hr = content->Properties(&properties);
59 Py_END_ALLOW_THREADS;
60 if (FAILED(hr)) {hresult_set_exc("Failed to get properties interface", hr); return NULL;}
61
62 Py_BEGIN_ALLOW_THREADS;
63 hr = storage_properties.CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, CLSCTX_INPROC_SERVER);
64 Py_END_ALLOW_THREADS;
65 if (FAILED(hr)) {hresult_set_exc("Failed to create storage properties collection", hr); return NULL;}
66
67 #define A(what) hr = storage_properties->Add(what); if (FAILED(hr)) { hresult_set_exc("Failed to add storage property " #what " for storage query", hr); return NULL; }
68 A(WPD_OBJECT_CONTENT_TYPE);
69 A(WPD_FUNCTIONAL_OBJECT_CATEGORY);
70 A(WPD_STORAGE_DESCRIPTION);
71 A(WPD_STORAGE_CAPACITY);
72 A(WPD_STORAGE_CAPACITY_IN_OBJECTS);
73 A(WPD_STORAGE_FREE_SPACE_IN_BYTES);
74 A(WPD_STORAGE_FREE_SPACE_IN_OBJECTS);
75 A(WPD_STORAGE_ACCESS_CAPABILITY);
76 A(WPD_STORAGE_FILE_SYSTEM_TYPE);
77 A(WPD_STORAGE_TYPE);
78 A(WPD_OBJECT_NAME);
79 #undef A
80
81 Py_BEGIN_ALLOW_THREADS;
82 hr = content->EnumObjects(0, WPD_DEVICE_OBJECT_ID, NULL, &objects);
83 Py_END_ALLOW_THREADS;
84 if (FAILED(hr)) {hresult_set_exc("Failed to get objects from device", hr); return NULL;}
85
86 hr = S_OK;
87 while (hr == S_OK) {
88 generic_raii_array<wchar_t*, co_task_mem_free, 16> object_ids;
89 Py_BEGIN_ALLOW_THREADS;
90 hr = objects->Next((ULONG)object_ids.size(), object_ids.ptr(), &fetched);
91 Py_END_ALLOW_THREADS;
92 if (SUCCEEDED(hr)) {
93 for(i = 0; i < fetched; i++) {
94 CComPtr<IPortableDeviceValues> values;
95 Py_BEGIN_ALLOW_THREADS;
96 hr2 = properties->GetValues(object_ids[i], storage_properties, &values);
97 Py_END_ALLOW_THREADS;
98 if (SUCCEEDED(hr2)) {
99 if (
100 SUCCEEDED(values->GetGuidValue(WPD_OBJECT_CONTENT_TYPE, &guid)) && IsEqualGUID(guid, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT) &&
101 SUCCEEDED(values->GetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, &guid)) && IsEqualGUID(guid, WPD_FUNCTIONAL_CATEGORY_STORAGE)
102 ) {
103 capacity = 0; capacity_objects = 0; free_space = 0; free_objects = 0;
104 values->GetUnsignedLargeIntegerValue(WPD_STORAGE_CAPACITY, &capacity);
105 values->GetUnsignedLargeIntegerValue(WPD_STORAGE_CAPACITY_IN_OBJECTS, &capacity_objects);
106 values->GetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_BYTES, &free_space);
107 values->GetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_OBJECTS, &free_objects);
108 values->GetUnsignedIntegerValue(WPD_STORAGE_TYPE, &storage_type);
109 PyObject *paccess = Py_False;
110 if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_STORAGE_ACCESS_CAPABILITY, &access)) && access == WPD_STORAGE_ACCESS_CAPABILITY_READWRITE) paccess = Py_True;
111 pyobject_raii soid(PyUnicode_FromWideChar(object_ids[i], -1));
112 if (!soid) return NULL;
113 pyobject_raii so(Py_BuildValue("{s:K, s:K, s:K, s:K, s:O, s:O}",
114 "capacity", capacity, "capacity_objects", capacity_objects, "free_space", free_space, "free_objects", free_objects, "rw", paccess, "id", soid.ptr()));
115 if (!so) return NULL;
116 #define A(which, key) { com_wchar_raii buf; if (SUCCEEDED(values->GetStringValue(which, buf.unsafe_address()))) { \
117 pyobject_raii d(PyUnicode_FromWideChar(buf.ptr(), -1)); \
118 if (d) PyDict_SetItemString(so.ptr(), key, d.ptr()); \
119 else PyErr_Clear(); \
120 }}
121 A(WPD_STORAGE_DESCRIPTION, "description");
122 A(WPD_OBJECT_NAME, "name");
123 A(WPD_STORAGE_FILE_SYSTEM_TYPE, "filesystem");
124 #undef A
125 const wchar_t *st;
126 switch(storage_type) {
127 case WPD_STORAGE_TYPE_REMOVABLE_RAM:
128 st = L"removable_ram";
129 break;
130 case WPD_STORAGE_TYPE_REMOVABLE_ROM:
131 st = L"removable_rom";
132 break;
133 case WPD_STORAGE_TYPE_FIXED_RAM:
134 st = L"fixed_ram";
135 break;
136 case WPD_STORAGE_TYPE_FIXED_ROM:
137 st = L"fixed_rom";
138 break;
139 default:
140 st = L"unknown_unknown";
141 }
142 pyobject_raii dt(PyUnicode_FromWideChar(st, -1));
143 if (dt) PyDict_SetItemString(so.ptr(), "type", dt.ptr());
144 if (PyList_Append(storage.ptr(), so.ptr()) != 0) return NULL;
145 }
146 }
147 }
148 }// if(SUCCEEDED(hr))
149 }
150 return storage.detach();
151 } // }}}
152
153 PyObject*
get_device_information(CComPtr<IPortableDevice> & device,CComPtr<IPortableDevicePropertiesBulk> & pb)154 get_device_information(CComPtr<IPortableDevice> &device, CComPtr<IPortableDevicePropertiesBulk> &pb) { // {{{
155 CComPtr<IPortableDeviceContent> content = NULL;
156 CComPtr<IPortableDeviceProperties> properties = NULL;
157 CComPtr<IPortableDeviceKeyCollection> keys = NULL;
158 CComPtr<IPortableDeviceValues> values = NULL;
159 CComPtr<IPortableDeviceCapabilities> capabilities = NULL;
160 CComPtr<IPortableDevicePropVariantCollection> categories = NULL;
161 HRESULT hr;
162 DWORD num_of_categories, i;
163 ULONG ti;
164 PyObject *ans = NULL;
165 const char *type = NULL;
166
167 Py_BEGIN_ALLOW_THREADS;
168 hr = keys.CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, CLSCTX_INPROC_SERVER);
169 Py_END_ALLOW_THREADS;
170 if (FAILED(hr)) return hresult_set_exc("Failed to create IPortableDeviceKeyCollection", hr);
171
172 #define A(what) hr = keys->Add(what); if (FAILED(hr)) { return hresult_set_exc("Failed to add key " #what " to IPortableDeviceKeyCollection", hr); }
173 A(WPD_DEVICE_PROTOCOL);
174 // Despite the MSDN documentation, this does not exist in PortableDevice.h
175 // hr = keys->Add(WPD_DEVICE_TRANSPORT);
176 A(WPD_DEVICE_FRIENDLY_NAME);
177 A(WPD_DEVICE_MANUFACTURER);
178 A(WPD_DEVICE_MODEL);
179 A(WPD_DEVICE_SERIAL_NUMBER);
180 A(WPD_DEVICE_FIRMWARE_VERSION);
181 A(WPD_DEVICE_TYPE);
182 #undef A
183
184 Py_BEGIN_ALLOW_THREADS;
185 hr = device->Content(&content);
186 Py_END_ALLOW_THREADS;
187 if (FAILED(hr)) return hresult_set_exc("Failed to get IPortableDeviceContent", hr);
188
189 Py_BEGIN_ALLOW_THREADS;
190 hr = content->Properties(&properties);
191 Py_END_ALLOW_THREADS;
192 if (FAILED(hr)) return hresult_set_exc("Failed to get IPortableDeviceProperties", hr);
193
194 Py_BEGIN_ALLOW_THREADS;
195 hr = properties->GetValues(WPD_DEVICE_OBJECT_ID, keys, &values);
196 Py_END_ALLOW_THREADS;
197 if(FAILED(hr)) return hresult_set_exc("Failed to get device info", hr);
198
199 Py_BEGIN_ALLOW_THREADS;
200 hr = device->Capabilities(&capabilities);
201 Py_END_ALLOW_THREADS;
202 if(FAILED(hr)) return hresult_set_exc("Failed to get device capabilities", hr);
203
204 Py_BEGIN_ALLOW_THREADS;
205 hr = capabilities->GetFunctionalCategories(&categories);
206 Py_END_ALLOW_THREADS;
207 if(FAILED(hr)) return hresult_set_exc("Failed to get device functional categories", hr);
208
209 Py_BEGIN_ALLOW_THREADS;
210 hr = categories->GetCount(&num_of_categories);
211 Py_END_ALLOW_THREADS;
212 if(FAILED(hr)) return hresult_set_exc("Failed to get device functional categories number", hr);
213
214 ans = PyDict_New();
215 if (ans == NULL) return NULL;
216
217 #define S(what, key) { \
218 com_wchar_raii temp; \
219 if (SUCCEEDED(values->GetStringValue(what, temp.unsafe_address()))) { \
220 pyobject_raii t(PyUnicode_FromWideChar(temp.ptr(), -1)); \
221 if (t) if (PyDict_SetItemString(ans, key, t.ptr()) != 0) PyErr_Clear(); \
222 }}
223
224 S(WPD_DEVICE_PROTOCOL, "protocol");
225
226 if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_DEVICE_TYPE, &ti))) {
227 type = "unknown";
228 switch (ti) {
229 case WPD_DEVICE_TYPE_CAMERA:
230 type = "camera"; break;
231 case WPD_DEVICE_TYPE_MEDIA_PLAYER:
232 type = "media player"; break;
233 case WPD_DEVICE_TYPE_PHONE:
234 type = "phone"; break;
235 case WPD_DEVICE_TYPE_VIDEO:
236 type = "video"; break;
237 case WPD_DEVICE_TYPE_PERSONAL_INFORMATION_MANAGER:
238 type = "personal information manager"; break;
239 case WPD_DEVICE_TYPE_AUDIO_RECORDER:
240 type = "audio recorder"; break;
241 case WPD_DEVICE_TYPE_GENERIC:
242 break;
243 }
244 pyobject_raii t(PyUnicode_FromString(type));
245 if (t) PyDict_SetItemString(ans, "type", t.ptr());
246 }
247
248 S(WPD_DEVICE_FRIENDLY_NAME, "friendly_name");
249 S(WPD_DEVICE_MANUFACTURER, "manufacturer_name");
250 S(WPD_DEVICE_MODEL, "model_name");
251 S(WPD_DEVICE_SERIAL_NUMBER, "serial_number");
252 S(WPD_DEVICE_FIRMWARE_VERSION, "device_version");
253 #undef S
254
255 bool has_storage = false;
256 for (i = 0; i < num_of_categories && !has_storage; i++) {
257 prop_variant pv;
258 if (SUCCEEDED(categories->GetAt(i, &pv)) && pv.puuid != NULL) {
259 if (IsEqualGUID(WPD_FUNCTIONAL_CATEGORY_STORAGE, *pv.puuid)) has_storage = true;
260 }
261 }
262 PyDict_SetItemString(ans, "has_storage", has_storage ? Py_True : Py_False);
263
264 if (has_storage) {
265 pyobject_raii storage(get_storage_info(device));
266 if (!storage) {
267 pyobject_raii exc_type, exc_value, exc_tb;
268 PyErr_Fetch(exc_type.unsafe_address(), exc_value.unsafe_address(), exc_tb.unsafe_address());
269 if (exc_type) {
270 PyErr_NormalizeException(exc_type.unsafe_address(), exc_value.unsafe_address(), exc_tb.unsafe_address());
271 PyDict_SetItemString(ans, "storage_error", exc_value.ptr());
272 } else {
273 pyobject_raii t(PyUnicode_FromString("get_storage_info() failed without an error set"));
274 if (t) PyDict_SetItemString(ans, "storage_error", t.ptr());
275 }
276 } else {
277 PyDict_SetItemString(ans, "storage", storage.ptr());
278 }
279 }
280
281 Py_BEGIN_ALLOW_THREADS;
282 hr = properties->QueryInterface(IID_PPV_ARGS(&pb));
283 Py_END_ALLOW_THREADS;
284 PyDict_SetItemString(ans, "has_bulk_properties", (FAILED(hr)) ? Py_False: Py_True);
285 return ans;
286 } // }}}
287
288 } // namespace wpd
289