• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }