1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2020 Руслан Ижбулатов <lrn1986@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 /* Queries the system (Windows 8 or newer) for the list
21 * of UWP packages, parses their manifests and invokes
22 * a user-provided callback with the needed application
23 * info.
24 */
25
26 #include "config.h"
27
28 #define COBJMACROS
29 #define INITGUID
30 #include <windows.h>
31 #include <winstring.h>
32 #include <roapi.h>
33 #include <stdio.h>
34 #include <shlwapi.h>
35
36 #include "gwin32api-storage.h"
37 #include "gwin32api-misc.h"
38 #include "gwin32api-iterator.h"
39 #include "gwin32api-package.h"
40
41 #include <xmllite.h>
42
43 #include <glib.h>
44
45 #include "gwin32file-sync-stream.h"
46 #include "gwin32packageparser.h"
47
48 #ifdef G_WINAPI_ONLY_APP
49 #define LoadedRoActivateInstance RoActivateInstance
50 #define LoadedWindowsCreateStringReference WindowsCreateStringReference
51 #define LoadedWindowsDeleteString WindowsDeleteString
52 #define sax_WindowsGetStringRawBuffer WindowsGetStringRawBuffer
53 #define LoadedWindowsGetStringRawBuffer WindowsGetStringRawBuffer
54 #define sax_CreateXmlReader CreateXmlReader
55 #else
56 typedef HRESULT (WINAPI *RoActivateInstance_func)(HSTRING activatableClassId, IInspectable **instance);
57 typedef HRESULT (WINAPI *WindowsCreateStringReference_func)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string);
58 typedef HRESULT (WINAPI *WindowsDeleteString_func)(HSTRING string);
59 typedef PCWSTR (WINAPI *WindowsGetStringRawBuffer_func)(HSTRING string, UINT32 *length);
60 typedef HRESULT (STDAPICALLTYPE *CreateXmlReader_func)(REFIID riid, void **ppvObject, IMalloc *pMalloc);
61 #define sax_WindowsGetStringRawBuffer sax->WindowsGetStringRawBuffer
62 #define sax_CreateXmlReader sax->CreateXmlReader
63 #endif
64
65 static gsize
g_utf16_len(const gunichar2 * str)66 g_utf16_len (const gunichar2 *str)
67 {
68 gsize result;
69
70 for (result = 0; str[0] != 0; str++, result++)
71 ;
72
73 return result;
74 }
75
76 static gunichar2 *
g_wcsdup(const gunichar2 * str,gssize str_len)77 g_wcsdup (const gunichar2 *str, gssize str_len)
78 {
79 gsize str_len_unsigned;
80 gsize str_size;
81
82 g_return_val_if_fail (str != NULL, NULL);
83
84 if (str_len < 0)
85 str_len_unsigned = g_utf16_len (str);
86 else
87 str_len_unsigned = (gsize) str_len;
88
89 g_assert (str_len_unsigned <= G_MAXSIZE / sizeof (gunichar2) - 1);
90 str_size = (str_len_unsigned + 1) * sizeof (gunichar2);
91
92 return g_memdup2 (str, str_size);
93 }
94
95 static BOOL
WIN32_FROM_HRESULT(HRESULT hresult,DWORD * win32_error_code)96 WIN32_FROM_HRESULT (HRESULT hresult,
97 DWORD *win32_error_code)
98 {
99 if ((hresult & 0xFFFF0000) == MAKE_HRESULT (SEVERITY_ERROR, FACILITY_WIN32, 0) ||
100 hresult == S_OK)
101 {
102 *win32_error_code = HRESULT_CODE (hresult);
103 return TRUE;
104 }
105
106 return FALSE;
107 }
108
109 static GIOErrorEnum
gio_error_from_hresult(HRESULT hresult)110 gio_error_from_hresult (HRESULT hresult)
111 {
112 DWORD error;
113
114 if (WIN32_FROM_HRESULT (hresult, &error))
115 return g_io_error_from_errno (error);
116
117 return G_IO_ERROR_FAILED;
118 }
119
120 static void
free_extgroup(GWin32PackageExtGroup * g)121 free_extgroup (GWin32PackageExtGroup *g)
122 {
123 g_ptr_array_unref (g->verbs);
124 g_ptr_array_unref (g->extensions);
125 g_free (g);
126 }
127
128 struct _xml_sax_state
129 {
130 #ifndef G_WINAPI_ONLY_APP
131 WindowsGetStringRawBuffer_func WindowsGetStringRawBuffer;
132 CreateXmlReader_func CreateXmlReader;
133 #endif
134
135 GWin32PackageParserCallback callback;
136 gpointer user_data;
137
138 const wchar_t *manifest_filename;
139 gsize package_index;
140 const wchar_t *wcs_full_name;
141 const wchar_t *wcs_name;
142 HSTRING package_family;
143
144 gboolean applist;
145 gboolean exit_early;
146
147 UINT64 in_package;
148 UINT64 in_applications;
149 UINT64 in_application;
150 UINT64 in_extensions;
151 UINT64 in_extension_protocol;
152 UINT64 in_extension_fta;
153 UINT64 in_fta_group;
154 UINT64 in_sfp;
155 UINT64 in_filetype;
156 UINT64 in_sv;
157 GPtrArray *supported_extensions;
158 GPtrArray *supported_protocols;
159 GPtrArray *supported_verbs;
160 GPtrArray *supported_extgroups;
161 wchar_t *application_usermodelid;
162 };
163
164 static gboolean parse_manifest_file (struct _xml_sax_state *sax);
165 static gboolean xml_parser_iteration (struct _xml_sax_state *sax,
166 IXmlReader *xml_reader);
167 static gboolean xml_parser_get_current_state (struct _xml_sax_state *sax,
168 IXmlReader *xml_reader,
169 const wchar_t **local_name,
170 const wchar_t **prefix,
171 const wchar_t **value);
172
173 gboolean
g_win32_package_parser_enum_packages(GWin32PackageParserCallback callback,gpointer user_data,GError ** error)174 g_win32_package_parser_enum_packages (GWin32PackageParserCallback callback,
175 gpointer user_data,
176 GError **error)
177 {
178 gboolean result = TRUE;
179 HRESULT hr;
180 HSTRING packagemanager_name = NULL;
181 HSTRING_HEADER packageanager_name_header;
182 const wchar_t *packman_id = L"Windows.Management.Deployment.PackageManager";
183 IInspectable *ii_pm = NULL;
184 IPackageManager *pm = NULL;
185 IIterable *packages_iterable = NULL;
186 IIterator *packages_iterator = NULL;
187 CHAR has_current;
188 gsize package_index;
189 const wchar_t *bslash_appmanifest = L"\\AppxManifest.xml";
190 struct _xml_sax_state sax_stack;
191 struct _xml_sax_state *sax = &sax_stack;
192
193 #ifndef G_WINAPI_ONLY_APP
194 HMODULE xmllite = NULL;
195 HMODULE combase = NULL;
196 HMODULE winrt = NULL;
197 RoActivateInstance_func LoadedRoActivateInstance;
198 WindowsCreateStringReference_func LoadedWindowsCreateStringReference;
199 WindowsDeleteString_func LoadedWindowsDeleteString;
200 WindowsGetStringRawBuffer_func LoadedWindowsGetStringRawBuffer;
201 CreateXmlReader_func LoadedCreateXmlReader;
202
203 winrt = LoadLibraryW (L"api-ms-win-core-winrt-l1-1-0.dll");
204 if (winrt == NULL)
205 {
206 result = FALSE;
207 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
208 "Failed to load api-ms-win-core-winrt-l1-1-0.dll");
209 goto cleanup;
210 }
211
212 combase = LoadLibraryW (L"combase.dll");
213 if (combase == NULL)
214 {
215 result = FALSE;
216 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
217 "Failed to load combase.dll");
218 goto cleanup;
219 }
220
221 xmllite = LoadLibraryW (L"xmllite.dll");
222 if (xmllite == NULL)
223 {
224 result = FALSE;
225 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
226 "Failed to load xmllite.dll");
227 goto cleanup;
228 }
229
230 LoadedCreateXmlReader = (CreateXmlReader_func) GetProcAddress (xmllite, "CreateXmlReader");
231 if (LoadedCreateXmlReader == NULL)
232 {
233 result = FALSE;
234 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
235 "CreateXmlReader entry point is not found in xmllite.dll");
236 goto cleanup;
237 }
238
239 LoadedRoActivateInstance = (RoActivateInstance_func) GetProcAddress (winrt, "RoActivateInstance");
240 if (LoadedRoActivateInstance == NULL)
241 {
242 result = FALSE;
243 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
244 "RoActivateInstance entry point is not found in api-ms-win-core-winrt-l1-1-0.dll");
245 goto cleanup;
246 }
247
248 LoadedWindowsCreateStringReference = (WindowsCreateStringReference_func) GetProcAddress (combase, "WindowsCreateStringReference");
249 if (LoadedWindowsCreateStringReference == NULL)
250 {
251 result = FALSE;
252 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
253 "WindowsCreateStringReference entry point is not found in combase.dll");
254 goto cleanup;
255 }
256
257 LoadedWindowsDeleteString = (WindowsDeleteString_func) GetProcAddress (combase, "WindowsDeleteString");
258 if (LoadedWindowsDeleteString == NULL)
259 {
260 result = FALSE;
261 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
262 "WindowsDeleteString entry point is not found in combase.dll");
263 goto cleanup;
264 }
265
266 LoadedWindowsGetStringRawBuffer = (WindowsGetStringRawBuffer_func) GetProcAddress (combase, "WindowsGetStringRawBuffer");
267 if (LoadedWindowsGetStringRawBuffer == NULL)
268 {
269 result = FALSE;
270 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
271 "WindowsGetStringRawBuffer entry point is not found in combase.dll");
272 goto cleanup;
273 }
274 #endif
275
276 /* This essentially locks current GLib thread into apartment COM model. */
277 hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
278 /* Can return S_FALSE if COM is already initialized,
279 * which is not an error, and we still need to uninitialize after that.
280 */
281 if (hr != S_OK && hr != S_FALSE)
282 {
283 result = FALSE;
284 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
285 "CoInitializeEx(COINIT_APARTMENTTHREADED) failed with code 0x%lx", hr);
286 goto cleanup;
287 }
288
289 #define canned_com_error_handler(function_name_literal, where_to_go) \
290 do \
291 { \
292 if (FAILED (hr)) \
293 { \
294 result = FALSE; \
295 g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \
296 function_name_literal " failed with code 0x%lx", hr); \
297 goto where_to_go; \
298 } \
299 } while (0)
300
301 hr = LoadedWindowsCreateStringReference (packman_id, wcslen (packman_id), &packageanager_name_header, &packagemanager_name);
302 canned_com_error_handler ("WindowsCreateStringReference()", cleanup);
303
304 hr = LoadedRoActivateInstance (packagemanager_name, &ii_pm);
305 canned_com_error_handler ("RoActivateInstance()", cleanup);
306
307 hr = IInspectable_QueryInterface (ii_pm, &IID_IPackageManager, (void**) &pm);
308 canned_com_error_handler ("IInspectable_QueryInterface()", cleanup);
309
310 hr = IPackageManager_FindPackagesByUserSecurityId (pm, 0, &packages_iterable);
311 canned_com_error_handler ("IPackageManager_FindPackagesByUserSecurityId()", cleanup);
312
313 hr = IIterable_First (packages_iterable, &packages_iterator);
314 canned_com_error_handler ("IIterable_First()", cleanup);
315
316 hr = IIterator_get_HasCurrent (packages_iterator, &has_current);
317 canned_com_error_handler ("IIterator_get_HasCurrent()", cleanup);
318
319 for (package_index = 0; SUCCEEDED (hr) && has_current; package_index++)
320 {
321 IUnknown *item = NULL;
322 IPackage *ipackage = NULL;
323 IPackageId *ipackageid = NULL;
324 IUnknown *package_install_location = NULL;
325 IStorageItem *storage_item = NULL;
326 HSTRING path = NULL;
327 HSTRING name = NULL;
328 HSTRING full_name = NULL;
329 HSTRING package_family = NULL;
330 size_t manifest_filename_size;
331 const wchar_t *wcs_path;
332 const wchar_t *wcs_full_name;
333 const wchar_t *wcs_name;
334 wchar_t *manifest_filename = NULL;
335
336 #define canned_com_error_handler_pkg(function_name_literal, where_to_go) \
337 do \
338 { \
339 if (FAILED (hr)) \
340 { \
341 result = FALSE; \
342 g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \
343 function_name_literal " for package #%zu failed with code 0x%lx", package_index, hr); \
344 goto where_to_go; \
345 } \
346 } while (0)
347
348 hr = IIterator_get_Current (packages_iterator, &item);
349 canned_com_error_handler_pkg ("IIterator_get_Current()", package_cleanup);
350
351 hr = IUnknown_QueryInterface (item, &IID_IPackage, (void **) &ipackage);
352 canned_com_error_handler_pkg ("IUnknown_QueryInterface(IID_IPackage)", package_cleanup);
353
354 hr = IPackage_get_Id (ipackage, &ipackageid);
355 canned_com_error_handler_pkg ("IPackage_get_Id()", package_cleanup);
356
357 hr = IPackageId_get_FullName (ipackageid, &full_name);
358 canned_com_error_handler_pkg ("IPackageId_get_FullName()", package_cleanup);
359
360 hr = IPackageId_get_Name (ipackageid, &name);
361 canned_com_error_handler_pkg ("IPackageId_get_Name()", package_cleanup);
362
363 wcs_full_name = LoadedWindowsGetStringRawBuffer (full_name, NULL);
364 wcs_name = LoadedWindowsGetStringRawBuffer (name, NULL);
365
366 #define canned_com_error_handler_pkg_named(function_name_literal, where_to_go) \
367 do \
368 { \
369 if (FAILED (hr)) \
370 { \
371 result = FALSE; \
372 g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \
373 function_name_literal " for package #%zu (`%S') failed with code 0x%lx", package_index, wcs_full_name, hr); \
374 goto where_to_go; \
375 } \
376 } while (0)
377
378 hr = IPackage_get_InstalledLocation (ipackage, &package_install_location);
379 canned_com_error_handler_pkg_named ("IPackage_get_InstalledLocation()", package_cleanup);
380
381 hr = IUnknown_QueryInterface (package_install_location, &IID_IStorageItem, (void **) &storage_item);
382 canned_com_error_handler_pkg_named ("IUnknown_QueryInterface(IID_IStorageItem)", package_cleanup);
383
384 hr = IPackageId_get_FamilyName (ipackageid, &package_family);
385 canned_com_error_handler_pkg_named ("IPackageId_get_FamilyName()", package_cleanup);
386
387 hr = IStorageItem_get_Path (storage_item, &path);
388 canned_com_error_handler_pkg_named ("IStorageItem_get_Path()", package_cleanup);
389
390 wcs_path = LoadedWindowsGetStringRawBuffer (path, NULL);
391 manifest_filename_size = wcslen (wcs_path) + wcslen (bslash_appmanifest);
392 manifest_filename = g_new (wchar_t, manifest_filename_size + 1);
393 memcpy (manifest_filename, wcs_path, manifest_filename_size * sizeof (wchar_t));
394 memcpy (&manifest_filename[wcslen (wcs_path)], bslash_appmanifest, (wcslen (bslash_appmanifest) + 1) * sizeof (wchar_t));
395
396 memset (sax, 0, sizeof (*sax));
397 sax->callback = callback;
398 sax->user_data = user_data;
399 sax->manifest_filename = manifest_filename;
400 sax->package_index = package_index;
401 sax->wcs_full_name = wcs_full_name;
402 sax->wcs_name = wcs_name;
403 sax->package_family = package_family;
404 sax->applist = TRUE;
405 sax->exit_early = FALSE;
406 #ifndef G_WINAPI_ONLY_APP
407 sax->CreateXmlReader = LoadedCreateXmlReader;
408 sax->WindowsGetStringRawBuffer = LoadedWindowsGetStringRawBuffer;
409 #endif
410 /* Result isn't checked. If we fail to parse the manifest,
411 * just try the next package, no need to bail out.
412 */
413 parse_manifest_file (sax);
414
415 hr = IIterator_MoveNext (packages_iterator, &has_current);
416 canned_com_error_handler_pkg_named ("IIterator_MoveNext()", package_cleanup);
417
418 #undef canned_com_error_handler_pkg_named
419 #undef canned_com_error_handler_pkg
420 #undef canned_com_error_handler
421
422 package_cleanup:
423 g_clear_pointer (&manifest_filename, g_free);
424
425 if (path)
426 LoadedWindowsDeleteString (path);
427 if (storage_item)
428 (void) IStorageItem_Release (storage_item);
429 if (package_install_location)
430 (void) IUnknown_Release (package_install_location);
431 if (ipackage)
432 (void) IPackage_Release (ipackage);
433 if (item)
434 (void) IUnknown_Release (item);
435
436 if (package_family)
437 LoadedWindowsDeleteString (package_family);
438 if (name)
439 LoadedWindowsDeleteString (name);
440 if (full_name)
441 LoadedWindowsDeleteString (full_name);
442
443 if (ipackageid)
444 (void) IPackageId_Release (ipackageid);
445 if (sax->exit_early)
446 break;
447 }
448
449 cleanup:
450 if (packages_iterator)
451 (void) IIterator_Release (packages_iterator);
452 if (packages_iterable)
453 (void) IIterable_Release (packages_iterable);
454 if (pm)
455 (void) IPackageManager_Release (pm);
456 if (ii_pm)
457 (void) IInspectable_Release (ii_pm);
458
459 CoUninitialize ();
460
461 #ifndef G_WINAPI_ONLY_APP
462 if (xmllite)
463 (void) FreeLibrary (xmllite);
464 if (combase)
465 (void) FreeLibrary (combase);
466 if (winrt)
467 (void) FreeLibrary (winrt);
468 #endif
469
470 return result;
471 }
472
473 static gboolean
parse_manifest_file(struct _xml_sax_state * sax)474 parse_manifest_file (struct _xml_sax_state *sax)
475 {
476 HRESULT hr;
477 HANDLE file_handle = INVALID_HANDLE_VALUE;
478 IStream *file_stream = NULL;
479 gboolean result;
480 IXmlReader *xml_reader;
481
482 file_handle = CreateFileW (sax->manifest_filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
483 if (file_handle == INVALID_HANDLE_VALUE)
484 {
485 g_warning ("Failed to open application manifest `%S' for package #%zu (`%S'): error code 0x%lx",
486 sax->manifest_filename, sax->package_index, sax->wcs_full_name, GetLastError ());
487 return FALSE;
488 }
489
490 file_stream = g_win32_file_sync_stream_new (file_handle, TRUE, STGM_READ | STGM_SHARE_DENY_WRITE, &hr);
491 if (file_stream == NULL)
492 {
493 g_warning ("Failed to create an IStream for application manifest `%S' for package #%zu (`%S'): HRESULT 0x%lx",
494 sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
495 CloseHandle (file_handle);
496 return FALSE;
497 }
498
499 /* file_stream owns it now */
500 file_handle = NULL;
501
502 hr = sax_CreateXmlReader (&IID_IXmlReader, (void **) &xml_reader, NULL);
503 /* Slightly incorrect - xml reader is not created for any particular file,
504 * in theory we could re-use the same xml reader instance for all files.
505 */
506 if (FAILED (hr))
507 {
508 g_warning ("CreateXmlReader() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
509 sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
510 (void) IStream_Release (file_stream);
511 return FALSE;
512 }
513
514 hr = IXmlReader_SetInput (xml_reader, (IUnknown *) file_stream);
515 if (FAILED (hr))
516 {
517 g_warning ("IXmlReader_SetInput() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
518 sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
519 (void) IXmlReader_Release (xml_reader);
520 (void) IStream_Release (file_stream);
521 return FALSE;
522 }
523
524 sax->supported_extensions = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
525 sax->supported_protocols = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
526 sax->supported_verbs = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
527 sax->supported_extgroups = g_ptr_array_new_full (0, (GDestroyNotify) free_extgroup);
528
529 result = TRUE;
530
531 while (!sax->exit_early && result && !IXmlReader_IsEOF (xml_reader))
532 result = xml_parser_iteration (sax, xml_reader);
533
534 g_clear_pointer (&sax->application_usermodelid, g_free);
535 g_clear_pointer (&sax->supported_extensions, g_ptr_array_unref);
536 g_clear_pointer (&sax->supported_verbs, g_ptr_array_unref);
537 g_clear_pointer (&sax->supported_extgroups, g_ptr_array_unref);
538 g_clear_pointer (&sax->supported_protocols, g_ptr_array_unref);
539
540 (void) IXmlReader_Release (xml_reader);
541 (void) IStream_Release (file_stream);
542
543 return result;
544 }
545
546 static gboolean
xml_parser_get_current_state(struct _xml_sax_state * sax,IXmlReader * xml_reader,const wchar_t ** local_name,const wchar_t ** prefix,const wchar_t ** value)547 xml_parser_get_current_state (struct _xml_sax_state *sax,
548 IXmlReader *xml_reader,
549 const wchar_t **local_name,
550 const wchar_t **prefix,
551 const wchar_t **value)
552 {
553 HRESULT hr;
554 UINT xml_line_number;
555 UINT xml_line_position;
556
557 hr = IXmlReader_GetLineNumber (xml_reader, &xml_line_number);
558 if (FAILED (hr))
559 {
560 g_warning ("IXmlReader_GetLineNumber() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
561 sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
562 return FALSE;
563 }
564
565 hr = IXmlReader_GetLinePosition (xml_reader, &xml_line_position);
566 if (FAILED (hr))
567 {
568 g_warning ("IXmlReader_GetLinePosition() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
569 sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
570 return FALSE;
571 }
572
573 hr = IXmlReader_GetLocalName (xml_reader, local_name, NULL);
574 if (FAILED (hr))
575 {
576 g_warning ("IXmlReader_GetLocalName() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx",
577 sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr);
578 return FALSE;
579 }
580
581 hr = IXmlReader_GetPrefix (xml_reader, prefix, NULL);
582 if (FAILED (hr))
583 {
584 g_warning ("IXmlReader_GetPrefix() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx",
585 sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr);
586 return FALSE;
587 }
588
589 hr = IXmlReader_GetValue (xml_reader, value, NULL);
590 if (FAILED (hr))
591 {
592 g_warning ("IXmlReader_GetValue() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx",
593 sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr);
594 return FALSE;
595 }
596
597 return TRUE;
598 }
599
600 static gboolean
xml_parser_iteration(struct _xml_sax_state * sax,IXmlReader * xml_reader)601 xml_parser_iteration (struct _xml_sax_state *sax,
602 IXmlReader *xml_reader)
603 {
604 HRESULT hr;
605 XmlNodeType xml_node_type;
606 const wchar_t *local_name;
607 const wchar_t *prefix;
608 const wchar_t *value;
609 BOOL is_visual_elements = FALSE;
610 BOOL is_extension = FALSE;
611 BOOL is_protocol = FALSE;
612 BOOL is_empty;
613 BOOL is_application = FALSE;
614 BOOL is_verb = FALSE;
615
616 hr = IXmlReader_Read (xml_reader, &xml_node_type);
617 if (FAILED (hr))
618 {
619 g_warning ("IXmlReader_Read() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
620 sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
621 return FALSE;
622 }
623
624 if (!xml_parser_get_current_state (sax, xml_reader, &local_name, &prefix, &value))
625 return FALSE;
626
627 switch (xml_node_type)
628 {
629 case XmlNodeType_Element:
630 is_empty = IXmlReader_IsEmptyElement (xml_reader);
631 g_assert (local_name != NULL);
632
633 if (!is_empty &&
634 _wcsicmp (local_name, L"Package") == 0 &&
635 prefix[0] == 0)
636 sax->in_package += 1;
637 else if (!is_empty &&
638 sax->in_package == 1 &&
639 _wcsicmp (local_name, L"Applications") == 0 &&
640 prefix[0] == 0)
641 sax->in_applications += 1;
642 else if (!is_empty &&
643 sax->in_applications == 1 &&
644 _wcsicmp (local_name, L"Application") == 0 &&
645 prefix[0] == 0)
646 {
647 sax->in_application += 1;
648 is_application = TRUE;
649 sax->applist = TRUE;
650 g_clear_pointer (&sax->application_usermodelid, g_free);
651 }
652 else if (sax->in_application == 1 &&
653 _wcsicmp (local_name, L"VisualElements") == 0 &&
654 (_wcsicmp (prefix, L"uap") == 0 || _wcsicmp (prefix, L"uap3") == 0))
655 is_visual_elements = TRUE;
656 else if (!is_empty &&
657 sax->in_application == 1 &&
658 _wcsicmp (local_name, L"Extensions") == 0 &&
659 prefix[0] == 0)
660 sax->in_extensions += 1;
661 else if (!is_empty &&
662 sax->in_application == 1 &&
663 _wcsicmp (local_name, L"Extension") == 0 &&
664 _wcsicmp (prefix, L"uap") == 0)
665 is_extension = TRUE;
666 else if (sax->in_extension_protocol == 1 &&
667 _wcsicmp (local_name, L"Protocol") == 0 &&
668 _wcsicmp (prefix, L"uap") == 0)
669 is_protocol = TRUE;
670 else if (!is_empty &&
671 sax->in_extension_fta == 1 &&
672 _wcsicmp (local_name, L"FileTypeAssociation") == 0 &&
673 _wcsicmp (prefix, L"uap") == 0)
674 sax->in_fta_group += 1;
675 else if (!is_empty &&
676 sax->in_fta_group == 1 &&
677 _wcsicmp (local_name, L"SupportedFileTypes") == 0 &&
678 _wcsicmp (prefix, L"uap") == 0)
679 sax->in_sfp += 1;
680 else if (!is_empty &&
681 sax->in_fta_group == 1 &&
682 _wcsicmp (local_name, L"SupportedVerbs") == 0 &&
683 _wcsicmp (prefix, L"uap2") == 0)
684 sax->in_sv += 1;
685 else if (!is_empty &&
686 sax->in_sfp == 1 &&
687 _wcsicmp (local_name, L"FileType") == 0 &&
688 _wcsicmp (prefix, L"uap") == 0)
689 sax->in_filetype += 1;
690 else if (!is_empty &&
691 sax->in_sv == 1 &&
692 _wcsicmp (local_name, L"Verb") == 0 &&
693 _wcsicmp (prefix, L"uap3") == 0)
694 is_verb = TRUE;
695
696 hr = IXmlReader_MoveToFirstAttribute (xml_reader);
697 while (hr == S_OK)
698 {
699 if (!xml_parser_get_current_state (sax, xml_reader, &local_name, &prefix, &value))
700 return FALSE;
701
702 g_assert (local_name != NULL);
703 g_assert (value != NULL);
704 g_assert (prefix != NULL);
705
706 if (is_application &&
707 sax->application_usermodelid == NULL &&
708 _wcsicmp (local_name, L"Id") == 0)
709 {
710 size_t id_len = 0;
711 size_t value_len = wcslen (value);
712 const wchar_t *wcs_package_family;
713 size_t wcs_package_family_len;
714
715 wcs_package_family = sax_WindowsGetStringRawBuffer (sax->package_family, NULL);
716 wcs_package_family_len = wcslen (wcs_package_family);
717 id_len += wcs_package_family_len + 1 + value_len;
718 sax->application_usermodelid = g_new (wchar_t, id_len + 1);
719 /* AppUserModelId = <family>!<id> */
720 memcpy (&sax->application_usermodelid[0], wcs_package_family, wcs_package_family_len * sizeof (wchar_t));
721 memcpy (&sax->application_usermodelid[wcs_package_family_len], L"!", sizeof (wchar_t));
722 memcpy (&sax->application_usermodelid[wcs_package_family_len + 1], value, (value_len + 1) * sizeof (wchar_t));
723 }
724 else if (is_visual_elements &&
725 _wcsicmp (local_name, L"AppListEntry") == 0 &&
726 _wcsicmp (value, L"none") == 0)
727 sax->applist = FALSE;
728 else if (is_extension &&
729 _wcsicmp (local_name, L"Category") == 0 &&
730 _wcsicmp (value, L"windows.protocol") == 0)
731 sax->in_extension_protocol += 1;
732 else if (is_extension &&
733 _wcsicmp (local_name, L"Category") == 0 &&
734 _wcsicmp (value, L"windows.fileTypeAssociation") == 0)
735 sax->in_extension_fta += 1;
736 else if (is_protocol &&
737 _wcsicmp (local_name, L"Name") == 0)
738 g_ptr_array_add (sax->supported_protocols, g_wcsdup (value, -1));
739 else if (is_verb &&
740 _wcsicmp (local_name, L"Id") == 0)
741 g_ptr_array_add (sax->supported_verbs, g_wcsdup (value, -1));
742
743 hr = IXmlReader_MoveToNextAttribute (xml_reader);
744 }
745 break;
746 case XmlNodeType_Text:
747 g_assert (value != NULL);
748
749 if (sax->in_filetype && value[0] != 0)
750 g_ptr_array_add (sax->supported_extensions, g_wcsdup (value, -1));
751 break;
752 case XmlNodeType_EndElement:
753 g_assert (local_name != NULL);
754
755 if (_wcsicmp (local_name, L"Package") == 0 &&
756 prefix[0] == 0)
757 sax->in_package -= 1;
758 else if (sax->in_package == 1 &&
759 _wcsicmp (local_name, L"Applications") == 0 &&
760 prefix[0] == 0)
761 sax->in_applications -= 1;
762 else if (sax->in_application == 1 &&
763 _wcsicmp (local_name, L"Extensions") == 0 &&
764 prefix[0] == 0)
765 sax->in_extensions -= 1;
766 else if (sax->in_extension_protocol == 1 &&
767 _wcsicmp (local_name, L"Extension") == 0 &&
768 _wcsicmp (prefix, L"uap") == 0)
769 sax->in_extension_protocol -= 1;
770 else if (sax->in_extension_fta == 1 &&
771 _wcsicmp (local_name, L"Extension") == 0 &&
772 _wcsicmp (prefix, L"uap") == 0)
773 sax->in_extension_fta -= 1;
774 else if (sax->in_fta_group == 1 &&
775 _wcsicmp (local_name, L"SupportedFileTypes") == 0 &&
776 _wcsicmp (prefix, L"uap") == 0)
777 sax->in_sfp -= 1;
778 else if (sax->in_sfp == 1 &&
779 _wcsicmp (local_name, L"FileType") == 0 &&
780 _wcsicmp (prefix, L"uap") == 0)
781 sax->in_filetype -= 1;
782 else if (sax->in_fta_group == 1 &&
783 _wcsicmp (local_name, L"SupportedVerbs") == 0 &&
784 _wcsicmp (prefix, L"uap2") == 0)
785 sax->in_sv -= 1;
786 else if (sax->in_applications == 1 &&
787 _wcsicmp (local_name, L"Application") == 0 &&
788 prefix[0] == 0)
789 {
790 if (sax->application_usermodelid != NULL)
791 sax->exit_early = !sax->callback (sax->user_data, sax->wcs_full_name, sax->wcs_name,
792 sax->application_usermodelid, sax->applist,
793 sax->supported_extgroups, sax->supported_protocols);
794 g_clear_pointer (&sax->supported_extgroups, g_ptr_array_unref);
795 g_clear_pointer (&sax->supported_protocols, g_ptr_array_unref);
796 sax->supported_protocols = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
797 sax->supported_extgroups = g_ptr_array_new_full (0, (GDestroyNotify) free_extgroup);
798 sax->in_application -= 1;
799 }
800 else if (sax->in_extension_fta == 1 &&
801 _wcsicmp (local_name, L"FileTypeAssociation") == 0 &&
802 _wcsicmp (prefix, L"uap") == 0)
803 {
804 GWin32PackageExtGroup *new_group = g_new0 (GWin32PackageExtGroup, 1);
805 new_group->extensions = g_steal_pointer (&sax->supported_extensions);
806 sax->supported_extensions = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
807 new_group->verbs = g_steal_pointer (&sax->supported_verbs);
808 sax->supported_verbs = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
809 g_ptr_array_add (sax->supported_extgroups, new_group);
810 sax->in_fta_group -= 1;
811 }
812 break;
813 default:
814 break;
815 }
816
817 return TRUE;
818 }