• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  * Copyright (C) 2014 Руслан Ижбулатов
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors: Alexander Larsson <alexl@redhat.com>
20  *          Руслан Ижбулатов  <lrn1986@gmail.com>
21  */
22 
23 #include "config.h"
24 
25 #include <string.h>
26 
27 #include "gcontenttype.h"
28 #include "gwin32appinfo.h"
29 #include "gappinfo.h"
30 #include "gioerror.h"
31 #include "gfile.h"
32 #include <glib/gstdio.h>
33 #include "glibintl.h"
34 #include <gio/gwin32registrykey.h>
35 
36 #include <windows.h>
37 
38 /* We need to watch 8 places:
39  * 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
40  *    (anything below that key)
41  *    On change: re-enumerate subkeys, read their values.
42  * 1) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
43  *    (anything below that key)
44  *    On change: re-enumerate subkeys
45  * 2) HKEY_CURRENT_USER\\Software\\Clients (anything below that key)
46  *    On change: re-read the whole hierarchy of handlers
47  * 3) HKEY_LOCAL_MACHINE\\Software\\Clients (anything below that key)
48  *    On change: re-read the whole hierarchy of handlers
49  * 4) HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications (values of that key)
50  *    On change: re-read the value list of registered applications
51  * 5) HKEY_CURRENT_USER\\Software\\RegisteredApplications (values of that key)
52  *    On change: re-read the value list of registered applications
53  * 6) HKEY_CLASSES_ROOT\\Applications (anything below that key)
54  *    On change: re-read the whole hierarchy of apps
55  * 7) HKEY_CLASSES_ROOT (only its subkeys)
56  *    On change: re-enumerate subkeys, try to filter out wrong names.
57  *
58  */
59 
60 typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
61 typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
62 typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
63 typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
64 
65 typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
66 typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
67 typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
68 typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
69 
70 struct _GWin32AppInfoURLSchemaClass
71 {
72   GObjectClass parent_class;
73 };
74 
75 struct _GWin32AppInfoFileExtensionClass
76 {
77   GObjectClass parent_class;
78 };
79 
80 struct _GWin32AppInfoHandlerClass
81 {
82   GObjectClass parent_class;
83 };
84 
85 struct _GWin32AppInfoApplicationClass
86 {
87   GObjectClass parent_class;
88 };
89 
90 struct _GWin32AppInfoURLSchema {
91   GObject parent_instance;
92 
93   /* url schema (stuff before ':') */
94   gunichar2 *schema;
95 
96   /* url schema (stuff before ':'), in UTF-8 */
97   gchar *schema_u8;
98 
99   /* url schema (stuff before ':'), in UTF-8, folded */
100   gchar *schema_folded;
101 
102   /* Handler currently selected for this schema */
103   GWin32AppInfoHandler *chosen_handler;
104 
105   /* Maps folded handler IDs -> to other handlers for this schema */
106   GHashTable *handlers;
107 };
108 
109 struct _GWin32AppInfoHandler {
110   GObject parent_instance;
111 
112   /* Class name in HKCR */
113   gunichar2 *handler_id;
114 
115   /* Handler registry key (HKCR\\handler_id). Can be used to watch this handler. */
116   GWin32RegistryKey *key;
117 
118   /* Class name in HKCR, UTF-8, folded */
119   gchar *handler_id_folded;
120 
121   /* shell/open/command default value for the class named by class_id */
122   gunichar2 *handler_command;
123 
124   /* If handler_id class has no command, it might point to another class */
125   gunichar2 *proxy_id;
126 
127   /* Proxy registry key (HKCR\\proxy_id). Can be used to watch handler's proxy. */
128   GWin32RegistryKey *proxy_key;
129 
130   /* shell/open/command default value for the class named by proxy_id */
131   gunichar2 *proxy_command;
132 
133   /* Executable of the program (for matching, in folded form; UTF-8) */
134   gchar *executable_folded;
135 
136   /* Executable of the program (UTF-8) */
137   gchar *executable;
138 
139   /* Pointer to a location within @executable */
140   gchar *executable_basename;
141 
142   /* Icon of the application for this handler */
143   GIcon *icon;
144 
145   /* The application that is linked to this handler. */
146   GWin32AppInfoApplication *app;
147 };
148 
149 struct _GWin32AppInfoFileExtension {
150   GObject parent_instance;
151 
152   /* File extension (with leading '.') */
153   gunichar2 *extension;
154 
155   /* File extension (with leading '.'), in UTF-8 */
156   gchar *extension_u8;
157 
158   /* handler currently selected for this extension */
159   GWin32AppInfoHandler *chosen_handler;
160 
161   /* Maps folded handler IDs -> to other handlers for this extension */
162   GHashTable *handlers;
163 
164   /* Maps folded app exename -> to apps that support this extension.
165    * ONLY for apps that are not reachable via handlers (i.e. apps from
166    * the HKCR/Applications, which have no handlers). */
167   GHashTable *other_apps;
168 };
169 
170 struct _GWin32AppInfoApplication {
171   GObject parent_instance;
172 
173   /* Canonical name (used for key names). Can be NULL. */
174   gunichar2 *canonical_name;
175 
176   /* Canonical name (used for key names), in UTF-8. Can be NULL. */
177   gchar *canonical_name_u8;
178 
179   /* Canonical name (used for key names), in UTF-8, folded. Can be NULL. */
180   gchar *canonical_name_folded;
181 
182   /* Human-readable name in English. Can be NULL */
183   gunichar2 *pretty_name;
184 
185   /* Human-readable name in English, UTF-8. Can be NULL */
186   gchar *pretty_name_u8;
187 
188   /* Human-readable name in user's language. Can be NULL  */
189   gunichar2 *localized_pretty_name;
190 
191   /* Human-readable name in user's language, UTF-8. Can be NULL  */
192   gchar *localized_pretty_name_u8;
193 
194   /* Description, could be in user's language. Can be NULL */
195   gunichar2 *description;
196 
197   /* Description, could be in user's language, UTF-8. Can be NULL */
198   gchar *description_u8;
199 
200   /* shell/open/command for the application. Can be NULL (see executable). */
201   gunichar2 *command;
202 
203   /* shell/open/command for the application. Can be NULL (see executable). */
204   gchar *command_u8;
205 
206   /* Executable of the program (for matching, in folded form;
207    * UTF-8). Never NULL. */
208   gchar *executable_folded;
209 
210   /* Executable of the program (UTF-8). Never NULL. */
211   gchar *executable;
212 
213   /* Pointer to a location within @executable */
214   gchar *executable_basename;
215 
216   /* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
217    * UTF-8, folded) -> a GWin32AppInfoHandler
218    * Schema can be used as a key in the urls hashmap.
219    */
220   GHashTable *supported_urls;
221 
222   /* Explicitly supported extensions, hashmap from map-owned gchar ptr
223    * (.extension, UTF-8, folded) -> a GWin32AppInfoHandler
224    * Extension can be used as a key in the extensions hashmap.
225    */
226   GHashTable *supported_exts;
227 
228   /* Icon of the application (remember, handler can have its own icon too) */
229   GIcon *icon;
230 
231   /* Set to TRUE to prevent this app from appearing in lists of apps for
232    * opening files. This will not prevent it from appearing in lists of apps
233    * just for running, or lists of apps for opening exts/urls for which this
234    * app reports explicit support.
235    */
236   gboolean no_open_with;
237 
238   /* Set to TRUE for applications from HKEY_CURRENT_USER.
239    * Give them priority over applications from HKEY_LOCAL_MACHINE, when all
240    * other things are equal.
241    */
242   gboolean user_specific;
243 
244   /* Set to TRUE for applications that are machine-wide defaults (i.e. default
245    * browser) */
246   gboolean default_app;
247 };
248 
249 #define G_TYPE_WIN32_APPINFO_URL_SCHEMA           (g_win32_appinfo_url_schema_get_type ())
250 #define G_WIN32_APPINFO_URL_SCHEMA(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema))
251 
252 #define G_TYPE_WIN32_APPINFO_FILE_EXTENSION       (g_win32_appinfo_file_extension_get_type ())
253 #define G_WIN32_APPINFO_FILE_EXTENSION(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension))
254 
255 #define G_TYPE_WIN32_APPINFO_HANDLER              (g_win32_appinfo_handler_get_type ())
256 #define G_WIN32_APPINFO_HANDLER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler))
257 
258 #define G_TYPE_WIN32_APPINFO_APPLICATION          (g_win32_appinfo_application_get_type ())
259 #define G_WIN32_APPINFO_APPLICATION(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication))
260 
261 GType g_win32_appinfo_url_schema_get_type (void) G_GNUC_CONST;
262 GType g_win32_appinfo_file_extension_get_type (void) G_GNUC_CONST;
263 GType g_win32_appinfo_handler_get_type (void) G_GNUC_CONST;
264 GType g_win32_appinfo_application_get_type (void) G_GNUC_CONST;
265 
G_DEFINE_TYPE(GWin32AppInfoURLSchema,g_win32_appinfo_url_schema,G_TYPE_OBJECT)266 G_DEFINE_TYPE (GWin32AppInfoURLSchema, g_win32_appinfo_url_schema, G_TYPE_OBJECT)
267 G_DEFINE_TYPE (GWin32AppInfoFileExtension, g_win32_appinfo_file_extension, G_TYPE_OBJECT)
268 G_DEFINE_TYPE (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT)
269 G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT)
270 
271 static void
272 g_win32_appinfo_url_schema_dispose (GObject *object)
273 {
274   GWin32AppInfoURLSchema *url = G_WIN32_APPINFO_URL_SCHEMA (object);
275 
276   g_clear_pointer (&url->schema, g_free);
277   g_clear_pointer (&url->schema_u8, g_free);
278   g_clear_pointer (&url->schema_folded, g_free);
279   g_clear_object (&url->chosen_handler);
280   g_clear_pointer (&url->handlers, g_hash_table_destroy);
281   G_OBJECT_CLASS (g_win32_appinfo_url_schema_parent_class)->dispose (object);
282 }
283 
284 
285 static void
g_win32_appinfo_handler_dispose(GObject * object)286 g_win32_appinfo_handler_dispose (GObject *object)
287 {
288   GWin32AppInfoHandler *handler = G_WIN32_APPINFO_HANDLER (object);
289 
290   g_clear_pointer (&handler->handler_id, g_free);
291   g_clear_pointer (&handler->handler_id_folded, g_free);
292   g_clear_pointer (&handler->handler_command, g_free);
293   g_clear_pointer (&handler->proxy_id, g_free);
294   g_clear_pointer (&handler->proxy_command, g_free);
295   g_clear_pointer (&handler->executable_folded, g_free);
296   g_clear_pointer (&handler->executable, g_free);
297   g_clear_object (&handler->key);
298   g_clear_object (&handler->proxy_key);
299   g_clear_object (&handler->icon);
300   g_clear_object (&handler->app);
301   G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
302 }
303 
304 static void
g_win32_appinfo_file_extension_dispose(GObject * object)305 g_win32_appinfo_file_extension_dispose (GObject *object)
306 {
307   GWin32AppInfoFileExtension *ext = G_WIN32_APPINFO_FILE_EXTENSION (object);
308 
309   g_clear_pointer (&ext->extension, g_free);
310   g_clear_pointer (&ext->extension_u8, g_free);
311   g_clear_object (&ext->chosen_handler);
312   g_clear_pointer (&ext->handlers, g_hash_table_destroy);
313   g_clear_pointer (&ext->other_apps, g_hash_table_destroy);
314   G_OBJECT_CLASS (g_win32_appinfo_file_extension_parent_class)->dispose (object);
315 }
316 
317 static void
g_win32_appinfo_application_dispose(GObject * object)318 g_win32_appinfo_application_dispose (GObject *object)
319 {
320   GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object);
321 
322   g_clear_pointer (&app->canonical_name_u8, g_free);
323   g_clear_pointer (&app->canonical_name_folded, g_free);
324   g_clear_pointer (&app->canonical_name, g_free);
325   g_clear_pointer (&app->pretty_name, g_free);
326   g_clear_pointer (&app->localized_pretty_name, g_free);
327   g_clear_pointer (&app->description, g_free);
328   g_clear_pointer (&app->command, g_free);
329   g_clear_pointer (&app->pretty_name_u8, g_free);
330   g_clear_pointer (&app->localized_pretty_name_u8, g_free);
331   g_clear_pointer (&app->description_u8, g_free);
332   g_clear_pointer (&app->command_u8, g_free);
333   g_clear_pointer (&app->executable_folded, g_free);
334   g_clear_pointer (&app->executable, g_free);
335   g_clear_pointer (&app->supported_urls, g_hash_table_destroy);
336   g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
337   g_clear_object (&app->icon);
338   G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
339 }
340 
341 static void
g_win32_appinfo_url_schema_class_init(GWin32AppInfoURLSchemaClass * klass)342 g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
343 {
344   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
345 
346   gobject_class->dispose = g_win32_appinfo_url_schema_dispose;
347 }
348 
349 static void
g_win32_appinfo_file_extension_class_init(GWin32AppInfoFileExtensionClass * klass)350 g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klass)
351 {
352   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
353 
354   gobject_class->dispose = g_win32_appinfo_file_extension_dispose;
355 }
356 
357 static void
g_win32_appinfo_handler_class_init(GWin32AppInfoHandlerClass * klass)358 g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
359 {
360   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
361 
362   gobject_class->dispose = g_win32_appinfo_handler_dispose;
363 }
364 
365 static void
g_win32_appinfo_application_class_init(GWin32AppInfoApplicationClass * klass)366 g_win32_appinfo_application_class_init (GWin32AppInfoApplicationClass *klass)
367 {
368   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
369 
370   gobject_class->dispose = g_win32_appinfo_application_dispose;
371 }
372 
373 static void
g_win32_appinfo_url_schema_init(GWin32AppInfoURLSchema * self)374 g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
375 {
376   self->handlers = g_hash_table_new_full (g_str_hash,
377                                           g_str_equal,
378                                           g_free,
379                                           g_object_unref);
380 }
381 
382 static void
g_win32_appinfo_file_extension_init(GWin32AppInfoFileExtension * self)383 g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
384 {
385   self->handlers = g_hash_table_new_full (g_str_hash,
386                                           g_str_equal,
387                                           g_free,
388                                           g_object_unref);
389   self->other_apps = g_hash_table_new_full (g_str_hash,
390                                             g_str_equal,
391                                             g_free,
392                                             g_object_unref);
393 }
394 
395 static void
g_win32_appinfo_handler_init(GWin32AppInfoHandler * self)396 g_win32_appinfo_handler_init (GWin32AppInfoHandler *self)
397 {
398 }
399 
400 static void
g_win32_appinfo_application_init(GWin32AppInfoApplication * self)401 g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
402 {
403   self->supported_urls = g_hash_table_new_full (g_str_hash,
404                                                 g_str_equal,
405                                                 g_free,
406                                                 g_object_unref);
407   self->supported_exts = g_hash_table_new_full (g_str_hash,
408                                                 g_str_equal,
409                                                 g_free,
410                                                 g_object_unref);
411 }
412 
413 G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
414 
415 /* Map of owned ".ext" (with '.', UTF-8, folded)
416  * to GWin32AppInfoFileExtension ptr
417  */
418 static GHashTable *extensions = NULL;
419 
420 /* Map of owned "schema" (without ':', UTF-8, folded)
421  * to GWin32AppInfoURLSchema ptr
422  */
423 static GHashTable *urls = NULL;
424 
425 /* Map of owned "appID" (UTF-8, folded) to
426  * GWin32AppInfoApplication ptr
427  */
428 static GHashTable *apps_by_id = NULL;
429 
430 /* Map of owned "app.exe" (UTF-8, folded) to
431  * GWin32AppInfoApplication ptr.
432  * This map and its values are separate from apps_by_id. The fact that an app
433  * with known ID has the same executable [base]name as an app in this map does
434  * not mean that they are the same application.
435  */
436 static GHashTable *apps_by_exe = NULL;
437 
438 /* Map of owned "handler id" (UTF-8, folded)
439  * to GWin32AppInfoHandler ptr
440  */
441 static GHashTable *handlers = NULL;
442 
443 /* Watch this whole subtree */
444 static GWin32RegistryKey *url_associations_key;
445 
446 /* Watch this whole subtree */
447 static GWin32RegistryKey *file_exts_key;
448 
449 /* Watch this whole subtree */
450 static GWin32RegistryKey *user_clients_key;
451 
452 /* Watch this whole subtree */
453 static GWin32RegistryKey *system_clients_key;
454 
455 /* Watch this key */
456 static GWin32RegistryKey *user_registered_apps_key;
457 
458 /* Watch this key */
459 static GWin32RegistryKey *system_registered_apps_key;
460 
461 /* Watch this whole subtree */
462 static GWin32RegistryKey *applications_key;
463 
464 /* Watch this key */
465 static GWin32RegistryKey *classes_root_key;
466 
467 static gunichar2 *
g_wcsdup(const gunichar2 * str,gssize str_size)468 g_wcsdup (const gunichar2 *str, gssize str_size)
469 {
470   if (str_size == -1)
471     {
472       str_size = wcslen (str) + 1;
473       str_size *= sizeof (gunichar2);
474     }
475   return g_memdup (str, str_size);
476 }
477 
478 #define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
479 #define USER_CHOICE L"\\UserChoice"
480 #define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
481 #define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"
482 #define HKCR L"HKEY_CLASSES_ROOT\\"
483 #define HKCU L"HKEY_CURRENT_USER\\"
484 #define HKLM L"HKEY_LOCAL_MACHINE\\"
485 #define SHELL_OPEN_COMMAND L"\\shell\\open\\command"
486 #define REG_PATH_MAX 256
487 #define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
488 
489 static gunichar2 *
read_resource_string(gunichar2 * res)490 read_resource_string (gunichar2 *res)
491 {
492   gunichar2 *id_str;
493   gunichar2 *id_str_end;
494   gunichar2 *filename_str;
495   unsigned long id;
496   HMODULE resource_module;
497   gunichar2 *buffer;
498   int string_length;
499   int buffer_length;
500 
501   if (res == NULL || res[0] != L'@')
502     return res;
503 
504   id_str = wcsrchr (res, L'-');
505 
506   if (id_str == NULL || id_str[-1] != L',')
507     return res;
508 
509   id_str += 1;
510 
511   id = wcstoul (id_str, &id_str_end, 10);
512 
513   if (id_str_end == id_str || id_str_end[0] != L'\0' || id == ULONG_MAX)
514     return res;
515 
516   filename_str = &res[1];
517   id_str[-2] = L'\0';
518 
519   resource_module = LoadLibraryExW (filename_str,
520                                     0,
521                                     LOAD_LIBRARY_AS_DATAFILE |
522                                     LOAD_LIBRARY_AS_IMAGE_RESOURCE);
523 
524   g_free (res);
525 
526   if (resource_module == NULL)
527     return NULL;
528 
529   buffer_length = 256;
530   string_length = buffer_length - 1;
531 
532   while (TRUE)
533     {
534       buffer = g_malloc (buffer_length * sizeof (gunichar2));
535       string_length = LoadStringW (resource_module, id, buffer, buffer_length);
536 
537       if (string_length != 0 && string_length == buffer_length - 1)
538         {
539           g_free (buffer);
540           buffer_length *= 2;
541         }
542       else
543         {
544           if (string_length == 0)
545             g_clear_pointer (&buffer, g_free);
546 
547           break;
548         }
549     }
550 
551   FreeLibrary (resource_module);
552 
553   if (buffer)
554     {
555       gunichar2 *result = g_wcsdup (buffer, -1);
556       g_free (buffer);
557       return result;
558     }
559 
560   return NULL;
561 }
562 
563 static void
read_handler_icon(GWin32RegistryKey * proxy_key,GWin32RegistryKey * program_key,GIcon ** icon_out)564 read_handler_icon (GWin32RegistryKey  *proxy_key,
565                    GWin32RegistryKey  *program_key,
566                    GIcon             **icon_out)
567 {
568   gint counter;
569   GWin32RegistryKey *key;
570 
571   *icon_out = NULL;
572 
573   for (counter = 0; counter < 2; counter++)
574     {
575       GWin32RegistryKey *icon_key;
576 
577       if (counter == 0)
578         key = proxy_key;
579       else
580         key = program_key;
581 
582       if (!key)
583         continue;
584 
585       icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
586 
587       if (icon_key != NULL)
588         {
589           GWin32RegistryValueType default_type;
590           gchar *default_value;
591 
592           if (g_win32_registry_key_get_value (icon_key,
593                                               TRUE,
594                                               "",
595                                               &default_type,
596                                               (gpointer *) &default_value,
597                                               NULL,
598                                               NULL))
599             {
600               if (default_type == G_WIN32_REGISTRY_VALUE_STR ||
601                   default_value[0] != '\0')
602                 *icon_out = g_themed_icon_new (default_value);
603 
604               g_clear_pointer (&default_value, g_free);
605             }
606 
607           g_object_unref (icon_key);
608         }
609 
610       if (*icon_out)
611         break;
612     }
613 }
614 
615 static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
616 static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_list components);
617 
618 static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
619 
620 /* output_size is in *bytes*, not gunichar2s! */
621 static gboolean
build_registry_path(gunichar2 * output,gsize output_size,...)622 build_registry_path (gunichar2 *output, gsize output_size, ...)
623 {
624   va_list ap;
625   gboolean result;
626 
627   va_start (ap, output_size);
628 
629   result = build_registry_pathv (output, output_size, ap);
630 
631   va_end (ap);
632 
633   return result;
634 }
635 
636 /* output_size is in *bytes*, not gunichar2s! */
637 static gboolean
build_registry_pathv(gunichar2 * output,gsize output_size,va_list components)638 build_registry_pathv (gunichar2 *output, gsize output_size, va_list components)
639 {
640   va_list lentest;
641   gunichar2 *p;
642   gunichar2 *component;
643   gsize length;
644 
645   if (output == NULL)
646     return FALSE;
647 
648   G_VA_COPY (lentest, components);
649 
650   for (length = 0, component = va_arg (lentest, gunichar2 *);
651        component != NULL;
652        component = va_arg (lentest, gunichar2 *))
653     {
654       length += wcslen (component);
655     }
656 
657   va_end (lentest);
658 
659   if ((length >= REG_PATH_MAX_SIZE) ||
660       (length * sizeof (gunichar2) >= output_size))
661     return FALSE;
662 
663   output[0] = L'\0';
664 
665   for (p = output, component = va_arg (components, gunichar2 *);
666        component != NULL;
667        component = va_arg (components, gunichar2 *))
668     {
669       length = wcslen (component);
670       wcscat (p, component);
671       p += length;
672     }
673 
674   return TRUE;
675 }
676 
677 
678 static GWin32RegistryKey *
_g_win32_registry_key_build_and_new_w(GError ** error,...)679 _g_win32_registry_key_build_and_new_w (GError **error, ...)
680 {
681   va_list ap;
682   gunichar2 key_path[REG_PATH_MAX_SIZE + 1];
683   GWin32RegistryKey *key;
684 
685   va_start (ap, error);
686 
687   key = NULL;
688 
689   if (build_registry_pathv (key_path, sizeof (key_path), ap))
690     key = g_win32_registry_key_new_w (key_path, error);
691 
692   va_end (ap);
693 
694   return key;
695 }
696 
697 
698 static gboolean
utf8_and_fold(const gunichar2 * str,gchar ** str_u8,gchar ** str_u8_folded)699 utf8_and_fold (const gunichar2  *str,
700                gchar           **str_u8,
701                gchar           **str_u8_folded)
702 {
703   gchar *u8;
704   gchar *folded;
705   u8 = g_utf16_to_utf8 (str, -1, NULL, NULL, NULL);
706 
707   if (u8 == NULL)
708     return FALSE;
709 
710   folded = g_utf8_casefold (u8, -1);
711 
712   if (folded == NULL)
713     {
714       g_free (u8);
715       return FALSE;
716     }
717 
718   if (str_u8)
719     *str_u8 = u8;
720   else
721     g_free (u8);
722 
723   if (str_u8_folded)
724     *str_u8_folded = folded;
725   else
726     g_free (folded);
727 
728   return TRUE;
729 }
730 
731 
732 static gboolean
follow_class_chain_to_handler(const gunichar2 * program_id,gsize program_id_size,gunichar2 ** program_command,GWin32RegistryKey ** program_key,gunichar2 ** proxy_id,gunichar2 ** proxy_command,GWin32RegistryKey ** proxy_key,gchar ** program_id_u8,gchar ** program_id_folded)733 follow_class_chain_to_handler (const gunichar2    *program_id,
734                                gsize               program_id_size,
735                                gunichar2         **program_command,
736                                GWin32RegistryKey **program_key,
737                                gunichar2         **proxy_id,
738                                gunichar2         **proxy_command,
739                                GWin32RegistryKey **proxy_key,
740                                gchar             **program_id_u8,
741                                gchar             **program_id_folded)
742 {
743   GWin32RegistryKey *key;
744   GWin32RegistryValueType val_type;
745   gsize proxy_id_size;
746   gboolean got_value;
747 
748   g_assert (program_id && program_command && proxy_id && proxy_command);
749 
750   *program_command = NULL;
751   *proxy_id = NULL;
752   *proxy_command = NULL;
753 
754   if (program_key)
755     *program_key = NULL;
756 
757   if (proxy_key)
758     *proxy_key = NULL;
759 
760 
761   key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id,
762                                                SHELL_OPEN_COMMAND, NULL);
763 
764   if (key != NULL)
765     {
766       got_value = g_win32_registry_key_get_value_w (key,
767                                                     TRUE,
768                                                     L"",
769                                                     &val_type,
770                                                     (void **) program_command,
771                                                     NULL,
772                                                     NULL);
773       if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
774         {
775           if ((program_id_u8 != NULL || program_id_folded != NULL) &&
776               !utf8_and_fold (program_id, program_id_u8, program_id_folded))
777             {
778               g_object_unref (key);
779               g_free (program_command);
780 
781               return FALSE;
782             }
783           if (program_key == NULL)
784             g_object_unref (key);
785           else
786             *program_key = key;
787 
788           return TRUE;
789         }
790       else if (got_value)
791         g_clear_pointer (program_command, g_free);
792 
793       g_object_unref (key);
794     }
795 
796   key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, NULL);
797 
798   if (key == NULL)
799     return FALSE;
800 
801   got_value = g_win32_registry_key_get_value_w (key,
802                                                 TRUE,
803                                                 L"",
804                                                 &val_type,
805                                                 (void **) proxy_id,
806                                                 &proxy_id_size,
807                                                 NULL);
808   if (!got_value ||
809       (val_type != G_WIN32_REGISTRY_VALUE_STR))
810     {
811       g_object_unref (key);
812       g_clear_pointer (proxy_id, g_free);
813       return FALSE;
814     }
815 
816   if (proxy_key)
817     *proxy_key = key;
818   else
819     g_object_unref (key);
820 
821   key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id,
822                                                SHELL_OPEN_COMMAND, NULL);
823 
824   if (key == NULL)
825     {
826       g_clear_pointer (proxy_id, g_free);
827       if (proxy_key)
828         g_clear_object (proxy_key);
829       return FALSE;
830     }
831 
832   got_value = g_win32_registry_key_get_value_w (key,
833                                                 TRUE,
834                                                 L"",
835                                                 &val_type,
836                                                 (void **) proxy_command,
837                                                 NULL,
838                                                 NULL);
839   g_object_unref (key);
840 
841   if (!got_value ||
842       val_type != G_WIN32_REGISTRY_VALUE_STR ||
843       ((program_id_u8 != NULL || program_id_folded != NULL) &&
844        !utf8_and_fold (program_id, program_id_u8, program_id_folded)))
845     {
846       g_clear_pointer (proxy_id, g_free);
847       g_clear_pointer (proxy_command, g_free);
848       if (proxy_key)
849         g_clear_object (proxy_key);
850       return FALSE;
851     }
852 
853   return TRUE;
854 }
855 
856 
857 static void
extract_executable(gunichar2 * commandline,gchar ** ex_out,gchar ** ex_basename_out,gchar ** ex_folded_out,gchar ** ex_folded_basename_out)858 extract_executable (gunichar2  *commandline,
859                     gchar     **ex_out,
860                     gchar     **ex_basename_out,
861                     gchar     **ex_folded_out,
862                     gchar     **ex_folded_basename_out)
863 {
864   gchar *ex;
865   gchar *ex_folded;
866   gunichar2 *p;
867   gboolean quoted;
868   size_t len;
869   size_t execlen;
870   gunichar2 *exepart;
871   gboolean found;
872 
873   quoted = FALSE;
874   execlen = 0;
875   found = FALSE;
876   len = wcslen (commandline);
877   p = commandline;
878 
879   while (p < &commandline[len])
880     {
881       switch (p[0])
882         {
883         case L'"':
884           quoted = !quoted;
885           break;
886         case L' ':
887           if (!quoted)
888             {
889               execlen = p - commandline;
890               p = &commandline[len];
891               found = TRUE;
892             }
893           break;
894         default:
895           break;
896         }
897       p += 1;
898     }
899 
900   if (!found)
901     execlen = len;
902 
903   exepart = g_wcsdup (commandline, (execlen + 1) * sizeof (gunichar2));
904   exepart[execlen] = L'\0';
905 
906   p = &exepart[0];
907 
908   while (execlen > 0 && exepart[0] == L'"' && exepart[execlen - 1] == L'"')
909     {
910       p = &exepart[1];
911       exepart[execlen - 1] = L'\0';
912       execlen -= 2;
913     }
914 
915   if (!utf8_and_fold (p, &ex, &ex_folded))
916     /* Currently no code to handle this case. It shouldn't happen though... */
917     g_assert_not_reached ();
918 
919   g_free (exepart);
920 
921   if (ex_out)
922     {
923       *ex_out = ex;
924 
925       if (ex_basename_out)
926         {
927           *ex_basename_out = &ex[strlen (ex) - 1];
928 
929           while (*ex_basename_out > ex)
930             {
931               if ((*ex_basename_out)[0] == '/' ||
932                   (*ex_basename_out)[0] == '\\')
933                 {
934                   *ex_basename_out += 1;
935                   break;
936                 }
937 
938               *ex_basename_out -= 1;
939             }
940         }
941     }
942   else
943     {
944       g_free (ex);
945     }
946 
947   if (ex_folded_out)
948     {
949       *ex_folded_out = ex_folded;
950 
951       if (ex_folded_basename_out)
952         {
953           *ex_folded_basename_out = &ex_folded[strlen (ex_folded) - 1];
954 
955           while (*ex_folded_basename_out > ex_folded)
956             {
957               if ((*ex_folded_basename_out)[0] == '/' ||
958                   (*ex_folded_basename_out)[0] == '\\')
959                 {
960                   *ex_folded_basename_out += 1;
961                   break;
962                 }
963 
964               *ex_folded_basename_out -= 1;
965             }
966         }
967     }
968   else
969     {
970       g_free (ex_folded);
971     }
972 }
973 
974 static void
get_url_association(const gunichar2 * schema)975 get_url_association (const gunichar2 *schema)
976 {
977   GWin32AppInfoURLSchema *schema_rec;
978   GWin32AppInfoHandler *handler_rec;
979   GWin32AppInfoHandler *handler_rec_in_url;
980   gchar *schema_u8;
981   gchar *schema_folded;
982   GWin32RegistryKey *user_choice;
983   GWin32RegistryValueType val_type;
984   gunichar2 *program_id;
985   gsize program_id_size;
986   gunichar2 *program_command;
987   gunichar2 *proxy_id;
988   gunichar2 *proxy_command;
989   gchar *program_id_u8;
990   gchar *program_id_folded;
991   GWin32RegistryKey *program_key;
992   GWin32RegistryKey *proxy_key;
993 
994   user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
995                                                        schema, USER_CHOICE,
996                                                        NULL);
997 
998   if (user_choice == NULL)
999     return;
1000 
1001   if (!utf8_and_fold (schema, &schema_u8, &schema_folded))
1002     {
1003       g_object_unref (user_choice);
1004       return;
1005     }
1006 
1007   schema_rec = g_hash_table_lookup (urls, schema_folded);
1008 
1009   if (!g_win32_registry_key_get_value_w (user_choice,
1010                                          TRUE,
1011                                          L"Progid",
1012                                          &val_type,
1013                                          (void **) &program_id,
1014                                          &program_id_size,
1015                                          NULL))
1016     {
1017       g_free (schema_u8);
1018       g_free (schema_folded);
1019       g_object_unref (user_choice);
1020       return;
1021     }
1022 
1023   if (val_type != G_WIN32_REGISTRY_VALUE_STR)
1024     {
1025       g_free (schema_u8);
1026       g_free (schema_folded);
1027       g_free (program_id);
1028       g_object_unref (user_choice);
1029       return;
1030     }
1031 
1032   program_key = proxy_key = NULL;
1033   program_command = proxy_id = proxy_command = NULL;
1034 
1035   if (!follow_class_chain_to_handler (program_id,
1036                                       program_id_size,
1037                                       &program_command,
1038                                       &program_key,
1039                                       &proxy_id,
1040                                       &proxy_command,
1041                                       &proxy_key,
1042                                       &program_id_u8,
1043                                       &program_id_folded))
1044     {
1045       g_free (schema_u8);
1046       g_free (schema_folded);
1047       g_free (program_id);
1048       g_object_unref (user_choice);
1049       return;
1050     }
1051 
1052   handler_rec = g_hash_table_lookup (handlers, program_id_folded);
1053 
1054   if (handler_rec == NULL)
1055     {
1056       handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1057 
1058       handler_rec->proxy_key = proxy_key;
1059       handler_rec->key = program_key;
1060       handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
1061       handler_rec->handler_id_folded =
1062           g_strdup (program_id_folded);
1063       handler_rec->handler_command =
1064           program_command ? g_wcsdup (program_command, -1) : NULL;
1065       handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1066       handler_rec->proxy_command =
1067           proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1068       extract_executable (proxy_command ? proxy_command : program_command,
1069                           &handler_rec->executable,
1070                           &handler_rec->executable_basename,
1071                           &handler_rec->executable_folded,
1072                           NULL);
1073       read_handler_icon (proxy_key, program_key, &handler_rec->icon);
1074       g_hash_table_insert (handlers,
1075                            g_strdup (program_id_folded),
1076                            handler_rec);
1077     }
1078   else
1079     {
1080       g_clear_object (&program_key);
1081       g_clear_object (&proxy_key);
1082     }
1083 
1084   if (schema_rec == NULL)
1085     {
1086       schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
1087       schema_rec->schema = g_wcsdup (schema, -1);
1088       schema_rec->schema_u8 = g_strdup (schema_u8);
1089       schema_rec->schema_folded = g_strdup (schema_folded);
1090       schema_rec->chosen_handler = g_object_ref (handler_rec);
1091       g_hash_table_insert (urls, g_strdup (schema_folded), schema_rec);
1092     }
1093 
1094   if (schema_rec->chosen_handler == NULL)
1095     schema_rec->chosen_handler = g_object_ref (handler_rec);
1096 
1097   handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
1098                                             program_id_folded);
1099 
1100   if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
1101     g_hash_table_insert (schema_rec->handlers,
1102                          g_strdup (program_id_folded),
1103                          g_object_ref (handler_rec));
1104 
1105   g_free (schema_u8);
1106   g_free (schema_folded);
1107   g_free (program_id);
1108   g_free (program_id_u8);
1109   g_free (program_id_folded);
1110   g_free (program_command);
1111   g_free (proxy_id);
1112   g_free (proxy_command);
1113   g_object_unref (user_choice);
1114 }
1115 
1116 static void
get_file_ext(const gunichar2 * ext)1117 get_file_ext (const gunichar2 *ext)
1118 {
1119   GWin32AppInfoFileExtension *file_extn;
1120   gboolean file_ext_known;
1121   GWin32AppInfoHandler *handler_rec;
1122   GWin32AppInfoHandler *handler_rec_in_ext;
1123   gchar *ext_u8;
1124   gchar *ext_folded;
1125   GWin32RegistryKey *user_choice;
1126   GWin32RegistryKey *open_with_progids;
1127   GWin32RegistryValueType val_type;
1128   gsize program_id_size;
1129   gboolean found_handler;
1130   gunichar2 *program_id;
1131   gunichar2 *proxy_id;
1132   gchar *program_id_u8;
1133   gchar *program_id_folded;
1134   GWin32RegistryKey *program_key;
1135   GWin32RegistryKey *proxy_key;
1136   gunichar2 *program_command;
1137   gunichar2 *proxy_command;
1138 
1139   open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
1140                                                              ext,
1141                                                              OPEN_WITH_PROGIDS,
1142                                                              NULL);
1143 
1144   user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, ext,
1145                                                        USER_CHOICE, NULL);
1146 
1147   if (user_choice == NULL && open_with_progids == NULL)
1148     return;
1149 
1150   if (!utf8_and_fold (ext, &ext_u8, &ext_folded))
1151     {
1152       g_clear_object (&user_choice);
1153       g_clear_object (&open_with_progids);
1154       return;
1155     }
1156 
1157   file_extn = NULL;
1158   file_ext_known = g_hash_table_lookup_extended (extensions,
1159                                                  ext_folded,
1160                                                  NULL,
1161                                                  (void **) &file_extn);
1162 
1163   if (!file_ext_known)
1164     file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1165 
1166   found_handler = FALSE;
1167 
1168   if (user_choice != NULL)
1169     {
1170       if (g_win32_registry_key_get_value_w (user_choice,
1171                                             TRUE,
1172                                             L"Progid",
1173                                             &val_type,
1174                                             (void **) &program_id,
1175                                             &program_id_size,
1176                                             NULL))
1177         {
1178           program_key = proxy_key = NULL;
1179 
1180           if (val_type == G_WIN32_REGISTRY_VALUE_STR &&
1181               follow_class_chain_to_handler (program_id,
1182                                              program_id_size,
1183                                              &program_command,
1184                                              &program_key,
1185                                              &proxy_id,
1186                                              &proxy_command,
1187                                              &proxy_key,
1188                                              &program_id_u8,
1189                                              &program_id_folded))
1190             {
1191               handler_rec = g_hash_table_lookup (handlers,
1192                                                  program_id_folded);
1193 
1194               if (handler_rec == NULL)
1195                 {
1196                   handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER,
1197                                               NULL);
1198                   handler_rec->proxy_key = proxy_key;
1199                   handler_rec->key = program_key;
1200                   handler_rec->handler_id =
1201                       g_wcsdup (program_id, program_id_size);
1202                   handler_rec->handler_id_folded =
1203                       g_strdup (program_id_folded);
1204                   handler_rec->handler_command =
1205                       program_command ? g_wcsdup (program_command, -1) : NULL;
1206                   handler_rec->proxy_id =
1207                       proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1208                   handler_rec->proxy_command =
1209                       proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1210                   extract_executable (proxy_command ? proxy_command : program_command,
1211                                       &handler_rec->executable,
1212                                       &handler_rec->executable_basename,
1213                                       &handler_rec->executable_folded,
1214                                       NULL);
1215                   read_handler_icon (proxy_key,
1216                                      program_key,
1217                                      &handler_rec->icon);
1218                   g_hash_table_insert (handlers,
1219                                        g_strdup (program_id_folded),
1220                                        handler_rec);
1221                 }
1222               else
1223                 {
1224                   g_clear_object (&program_key);
1225                   g_clear_object (&proxy_key);
1226                 }
1227 
1228               found_handler = TRUE;
1229 
1230               handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
1231                                                         program_id_folded);
1232 
1233               if (file_extn->chosen_handler == NULL)
1234                 {
1235                   g_hash_table_insert (file_extn->handlers,
1236                                        g_strdup (program_id_folded),
1237                                        g_object_ref (handler_rec));
1238                 }
1239               else if (handler_rec_in_ext == NULL)
1240                 {
1241                   if (file_extn->chosen_handler->handler_id_folded &&
1242                       strcmp (file_extn->chosen_handler->handler_id_folded,
1243                               program_id_folded) != 0)
1244                     g_hash_table_insert (file_extn->handlers,
1245                                          g_strdup (program_id_folded),
1246                                          g_object_ref (handler_rec));
1247                 }
1248 
1249               g_free (program_id_u8);
1250               g_free (program_id_folded);
1251               g_free (program_command);
1252               g_free (proxy_id);
1253               g_free (proxy_command);
1254             }
1255 
1256           g_free (program_id);
1257         }
1258 
1259       g_object_unref (user_choice);
1260     }
1261 
1262   if (open_with_progids != NULL)
1263     {
1264       GWin32RegistryValueIter iter;
1265 
1266       if (g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
1267         {
1268           gunichar2 *value_name;
1269           gunichar2 *value_data;
1270           gsize      value_name_len;
1271           gsize      value_data_size;
1272           GWin32RegistryValueType value_type;
1273 
1274           while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1275             {
1276               gsize value_name_size;
1277               program_key = proxy_key = NULL;
1278 
1279               if ((!g_win32_registry_value_iter_get_value_type (&iter,
1280                                                                 &value_type,
1281                                                                 NULL)) ||
1282                   ((val_type != G_WIN32_REGISTRY_VALUE_STR) &&
1283                    (val_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
1284                   (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
1285                                                             &value_name_len,
1286                                                             NULL)) ||
1287                   (value_name_len <= 0) ||
1288                   (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1289                                                             (void **) &value_data,
1290                                                             &value_data_size,
1291                                                             NULL)) ||
1292                   (value_data_size < sizeof (gunichar2)) ||
1293                   (value_data[0] == L'\0'))
1294                 continue;
1295 
1296               value_name_size = sizeof (gunichar2) * (value_name_len + 1);
1297 
1298               if (!follow_class_chain_to_handler (value_name,
1299                                                   value_name_size,
1300                                                   &program_command,
1301                                                   &program_key,
1302                                                   &proxy_id,
1303                                                   &proxy_command,
1304                                                   &proxy_key,
1305                                                   &program_id_u8,
1306                                                   &program_id_folded))
1307                 continue;
1308 
1309               handler_rec = g_hash_table_lookup (handlers,
1310                                                  program_id_folded);
1311 
1312               if (handler_rec == NULL)
1313                 {
1314                   handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1315 
1316                   handler_rec->proxy_key = proxy_key;
1317                   handler_rec->key = program_key;
1318                   handler_rec->handler_id =
1319                       g_wcsdup (value_name, value_name_size);
1320                   handler_rec->handler_id_folded =
1321                       g_strdup (program_id_folded);
1322                   handler_rec->handler_command =
1323                       program_command ? g_wcsdup (program_command, -1) : NULL;
1324                   handler_rec->proxy_id =
1325                       proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1326                   handler_rec->proxy_command =
1327                       proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1328                   extract_executable (proxy_command ? proxy_command : program_command,
1329                                       &handler_rec->executable,
1330                                       &handler_rec->executable_basename,
1331                                       &handler_rec->executable_folded,
1332                                       NULL);
1333                   read_handler_icon (proxy_key,
1334                                      program_key,
1335                                      &handler_rec->icon);
1336                   g_hash_table_insert (handlers,
1337                                        g_strdup (program_id_folded),
1338                                        handler_rec);
1339                 }
1340               else
1341                 {
1342                   g_clear_object (&program_key);
1343                   g_clear_object (&proxy_key);
1344                 }
1345 
1346               found_handler = TRUE;
1347 
1348               handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
1349                                                         program_id_folded);
1350 
1351               if (handler_rec_in_ext == NULL)
1352                 {
1353                   if (file_extn->chosen_handler == NULL)
1354                     g_hash_table_insert (file_extn->handlers,
1355                                          g_strdup (program_id_folded),
1356                                          g_object_ref (handler_rec));
1357                   else if (file_extn->chosen_handler->handler_id_folded &&
1358                            strcmp (file_extn->chosen_handler->handler_id_folded,
1359                                    program_id_folded) != 0)
1360                     g_hash_table_insert (file_extn->handlers,
1361                                          g_strdup (program_id_folded),
1362                                          g_object_ref (handler_rec));
1363                 }
1364 
1365               g_free (program_id_u8);
1366               g_free (program_id_folded);
1367               g_free (program_command);
1368               g_free (proxy_id);
1369               g_free (proxy_command);
1370             }
1371 
1372           g_win32_registry_value_iter_clear (&iter);
1373         }
1374 
1375       g_object_unref (open_with_progids);
1376     }
1377 
1378   if (!found_handler)
1379     {
1380       if (!file_ext_known)
1381         g_object_unref (file_extn);
1382     }
1383   else if (!file_ext_known)
1384     {
1385       file_extn->extension = g_wcsdup (ext, -1);
1386       file_extn->extension_u8 = g_strdup (ext_u8);
1387       g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
1388     }
1389 
1390   g_free (ext_u8);
1391   g_free (ext_folded);
1392 }
1393 
1394 static void
collect_capable_apps_from_clients(GPtrArray * capable_apps,GPtrArray * priority_capable_apps,gboolean user_registry)1395 collect_capable_apps_from_clients (GPtrArray *capable_apps,
1396                                    GPtrArray *priority_capable_apps,
1397                                    gboolean   user_registry)
1398 {
1399   GWin32RegistryKey *clients;
1400   GWin32RegistrySubkeyIter clients_iter;
1401 
1402   gunichar2 *client_type_name;
1403   gsize client_type_name_len;
1404 
1405 
1406   if (user_registry)
1407     clients =
1408         g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
1409                                      NULL);
1410   else
1411     clients =
1412         g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
1413                                      NULL);
1414 
1415   if (clients == NULL)
1416     return;
1417 
1418   if (!g_win32_registry_subkey_iter_init (&clients_iter, clients, NULL))
1419     {
1420       g_object_unref (clients);
1421       return;
1422     }
1423 
1424   while (g_win32_registry_subkey_iter_next (&clients_iter, TRUE, NULL))
1425     {
1426       GWin32RegistrySubkeyIter subkey_iter;
1427       GWin32RegistryKey *system_client_type;
1428       GWin32RegistryValueType default_type;
1429       gunichar2 *default_value = NULL;
1430       gunichar2 *client_name;
1431       gsize client_name_len;
1432 
1433       if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
1434                                                     &client_type_name,
1435                                                     &client_type_name_len,
1436                                                     NULL))
1437         continue;
1438 
1439       system_client_type = g_win32_registry_key_get_child_w (clients,
1440                                                              client_type_name,
1441                                                              NULL);
1442 
1443       if (system_client_type == NULL)
1444         continue;
1445 
1446       if (g_win32_registry_key_get_value_w (system_client_type,
1447                                             TRUE,
1448                                             L"",
1449                                             &default_type,
1450                                             (gpointer *) &default_value,
1451                                             NULL,
1452                                             NULL))
1453         {
1454           if (default_type != G_WIN32_REGISTRY_VALUE_STR ||
1455               default_value[0] == L'\0')
1456             g_clear_pointer (&default_value, g_free);
1457         }
1458 
1459       if (!g_win32_registry_subkey_iter_init (&subkey_iter,
1460                                               system_client_type,
1461                                               NULL))
1462         {
1463           g_clear_pointer (&default_value, g_free);
1464           g_object_unref (system_client_type);
1465           continue;
1466         }
1467 
1468       while (g_win32_registry_subkey_iter_next (&subkey_iter, TRUE, NULL))
1469         {
1470           GWin32RegistryKey *system_client;
1471           GWin32RegistryKey *system_client_assoc;
1472           gboolean add;
1473           gunichar2 *keyname;
1474 
1475           if (!g_win32_registry_subkey_iter_get_name_w (&subkey_iter,
1476                                                         &client_name,
1477                                                         &client_name_len,
1478                                                         NULL))
1479             continue;
1480 
1481           system_client = g_win32_registry_key_get_child_w (system_client_type,
1482                                                             client_name,
1483                                                             NULL);
1484 
1485           if (system_client == NULL)
1486             continue;
1487 
1488           add = FALSE;
1489 
1490           system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1491                                                                   L"Capabilities\\FileAssociations",
1492                                                                   NULL);
1493 
1494           if (system_client_assoc != NULL)
1495             {
1496               add = TRUE;
1497               g_object_unref (system_client_assoc);
1498             }
1499           else
1500             {
1501               system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1502                                                                       L"Capabilities\\UrlAssociations",
1503                                                                       NULL);
1504 
1505               if (system_client_assoc != NULL)
1506                 {
1507                   add = TRUE;
1508                   g_object_unref (system_client_assoc);
1509                 }
1510             }
1511 
1512           if (add)
1513             {
1514               keyname = g_wcsdup (g_win32_registry_key_get_path_w (system_client), -1);
1515 
1516               if (default_value && wcscmp (default_value, client_name) == 0)
1517                 g_ptr_array_add (priority_capable_apps, keyname);
1518               else
1519                 g_ptr_array_add (capable_apps, keyname);
1520             }
1521 
1522           g_object_unref (system_client);
1523         }
1524 
1525       g_win32_registry_subkey_iter_clear (&subkey_iter);
1526       g_clear_pointer (&default_value, g_free);
1527       g_object_unref (system_client_type);
1528     }
1529 
1530   g_win32_registry_subkey_iter_clear (&clients_iter);
1531   g_object_unref (clients);
1532 }
1533 
1534 static void
collect_capable_apps_from_registered_apps(GPtrArray * capable_apps,gboolean user_registry)1535 collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
1536                                            gboolean   user_registry)
1537 {
1538   GWin32RegistryValueIter iter;
1539 
1540   gunichar2 *value_data;
1541   gsize      value_data_size;
1542   GWin32RegistryValueType value_type;
1543   GWin32RegistryKey *registered_apps;
1544 
1545   if (user_registry)
1546     registered_apps =
1547         g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
1548                                     NULL);
1549   else
1550     registered_apps =
1551         g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
1552                                     NULL);
1553 
1554   if (!registered_apps)
1555     return;
1556 
1557   if (!g_win32_registry_value_iter_init (&iter, registered_apps, NULL))
1558     {
1559       g_object_unref (registered_apps);
1560       return;
1561     }
1562 
1563   while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1564     {
1565       gunichar2 possible_location[REG_PATH_MAX_SIZE + 1];
1566       GWin32RegistryKey *location = NULL;
1567 
1568       if ((!g_win32_registry_value_iter_get_value_type (&iter,
1569                                                         &value_type,
1570                                                         NULL)) ||
1571           (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
1572           (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1573                                                     (void **) &value_data,
1574                                                     &value_data_size,
1575                                                     NULL)) ||
1576           (value_data_size < sizeof (gunichar2)) ||
1577           (value_data[0] == L'\0'))
1578         continue;
1579 
1580       if (build_registry_path (possible_location, sizeof (possible_location),
1581                                HKCU, value_data, NULL))
1582         location = g_win32_registry_key_new_w (possible_location, NULL);
1583 
1584       if (location)
1585         {
1586           gunichar2 *p = wcsrchr (possible_location, L'\\');
1587 
1588           if (p)
1589             *p = L'\0';
1590 
1591           g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
1592           g_object_unref (location);
1593           continue;
1594         }
1595 
1596       if (!build_registry_path (possible_location, sizeof (possible_location),
1597                                 user_registry ? HKCU : HKLM, value_data, NULL))
1598         continue;
1599 
1600       location = g_win32_registry_key_new_w (possible_location, NULL);
1601 
1602       if (location)
1603         {
1604           gunichar2 *p = wcsrchr (possible_location, L'\\');
1605           if (p)
1606             *p = L'\0';
1607           g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
1608           g_object_unref (location);
1609         }
1610     }
1611 
1612   g_win32_registry_value_iter_clear (&iter);
1613   g_object_unref (registered_apps);
1614 }
1615 
1616 static void
read_capable_app(gunichar2 * input_app_key_path,gboolean user_specific,gboolean default_app)1617 read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolean default_app)
1618 {
1619   GWin32AppInfoApplication *app;
1620   gunichar2 *app_key_path;
1621   gunichar2 *canonical_name;
1622   gchar *canonical_name_u8;
1623   gchar *canonical_name_folded;
1624   GWin32RegistryKey *appkey;
1625   gunichar2 *fallback_friendly_name;
1626   GWin32RegistryValueType vtype;
1627   gboolean success;
1628   gunichar2 *friendly_name;
1629   gunichar2 *description;
1630   gunichar2 *narrow_application_name;
1631   gunichar2 *icon_source;
1632   GWin32RegistryKey *capabilities;
1633   GWin32RegistryKey *default_icon_key;
1634   GWin32RegistryKey *shell_open_command_key;
1635   gunichar2 *shell_open_command;
1636   gchar *app_executable;
1637   gchar *app_executable_basename;
1638   gchar *app_executable_folded;
1639   gchar *app_executable_folded_basename;
1640   GWin32RegistryKey *associations;
1641 
1642   app_key_path = g_wcsdup (input_app_key_path, -1);
1643 
1644   canonical_name = wcsrchr (app_key_path, L'\\');
1645 
1646   if (canonical_name == NULL)
1647     {
1648       /* The key must have at least one '\\' */
1649       g_free (app_key_path);
1650       return;
1651     }
1652 
1653   canonical_name += 1;
1654 
1655   if (!utf8_and_fold (canonical_name, &canonical_name_u8, &canonical_name_folded))
1656     {
1657       g_free (app_key_path);
1658       return;
1659     }
1660 
1661   appkey = g_win32_registry_key_new_w (app_key_path, NULL);
1662 
1663   if (appkey == NULL)
1664     {
1665       g_free (canonical_name_u8);
1666       g_free (canonical_name_folded);
1667       g_free (app_key_path);
1668       return;
1669     }
1670 
1671   capabilities =
1672       g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL);
1673 
1674   if (capabilities == NULL)
1675     {
1676       /* Must have capabilities */
1677       g_free (canonical_name_u8);
1678       g_free (canonical_name_folded);
1679       g_free (app_key_path);
1680       return;
1681     }
1682 
1683   shell_open_command_key =
1684       g_win32_registry_key_get_child_w (appkey,
1685                                         L"shell\\open\\command",
1686                                         NULL);
1687 
1688   if (shell_open_command_key == NULL)
1689     {
1690       g_object_unref (capabilities);
1691       g_free (canonical_name_u8);
1692       g_free (canonical_name_folded);
1693       g_free (app_key_path);
1694       g_object_unref (appkey);
1695       return ;
1696     }
1697 
1698   shell_open_command = NULL;
1699 
1700   success = g_win32_registry_key_get_value_w (shell_open_command_key,
1701                                               TRUE,
1702                                               L"",
1703                                               &vtype,
1704                                               (gpointer *) &shell_open_command,
1705                                               NULL,
1706                                               NULL);
1707 
1708   if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1709     {
1710       /* Must have a command */
1711       g_clear_pointer (&shell_open_command, g_free);
1712       g_object_unref (capabilities);
1713       g_free (canonical_name_u8);
1714       g_free (canonical_name_folded);
1715       g_free (app_key_path);
1716       g_object_unref (appkey);
1717       return;
1718     }
1719 
1720   extract_executable (shell_open_command,
1721                       &app_executable,
1722                       &app_executable_basename,
1723                       &app_executable_folded,
1724                       &app_executable_folded_basename);
1725 
1726   app = g_hash_table_lookup (apps_by_id, canonical_name_folded);
1727 
1728   if (app == NULL)
1729     {
1730       app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
1731 
1732       app->canonical_name = g_wcsdup (canonical_name, -1);
1733       app->canonical_name_u8 = g_strdup (canonical_name_u8);
1734       app->canonical_name_folded =
1735           g_strdup (canonical_name_folded);
1736 
1737       app->command = g_wcsdup (shell_open_command, -1);
1738       app->command_u8 =
1739           g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
1740       app->executable = g_strdup (app_executable);
1741       app->executable_basename =
1742           &app->executable[app_executable_basename - app_executable];
1743       app->executable_folded =
1744           g_strdup (app_executable_folded);
1745 
1746       app->no_open_with = FALSE;
1747 
1748       app->user_specific = user_specific;
1749       app->default_app = default_app;
1750 
1751       g_hash_table_insert (apps_by_id,
1752                            g_strdup (canonical_name_folded),
1753                            app);
1754     }
1755 
1756   fallback_friendly_name = NULL;
1757   success = g_win32_registry_key_get_value_w (appkey,
1758                                               TRUE,
1759                                               L"",
1760                                               &vtype,
1761                                               (void **) &fallback_friendly_name,
1762                                               NULL,
1763                                               NULL);
1764 
1765   if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1766     g_clear_pointer (&fallback_friendly_name, g_free);
1767 
1768   if (fallback_friendly_name && app->pretty_name == NULL)
1769     {
1770       app->pretty_name = g_wcsdup (fallback_friendly_name, -1);
1771       g_clear_pointer (&app->pretty_name_u8, g_free);
1772       app->pretty_name_u8 = g_utf16_to_utf8 (fallback_friendly_name,
1773                                              -1,
1774                                              NULL,
1775                                              NULL,
1776                                              NULL);
1777     }
1778 
1779   friendly_name = NULL;
1780   success = g_win32_registry_key_get_value_w (capabilities,
1781                                               TRUE,
1782                                               L"LocalizedString",
1783                                               &vtype,
1784                                               (void **) &friendly_name,
1785                                               NULL,
1786                                               NULL);
1787 
1788   if (success && (vtype != G_WIN32_REGISTRY_VALUE_STR || friendly_name[0] != L'@'))
1789     g_clear_pointer (&friendly_name, g_free);
1790 
1791   friendly_name = read_resource_string (friendly_name);
1792 
1793   if (friendly_name && app->localized_pretty_name == NULL)
1794     {
1795       app->localized_pretty_name = g_wcsdup (friendly_name, -1);
1796       g_clear_pointer (&app->localized_pretty_name_u8, g_free);
1797       app->localized_pretty_name_u8 = g_utf16_to_utf8 (friendly_name,
1798                                                        -1,
1799                                                        NULL,
1800                                                        NULL,
1801                                                        NULL);
1802     }
1803 
1804   description = NULL;
1805   success = g_win32_registry_key_get_value_w (capabilities,
1806                                               TRUE,
1807                                               L"ApplicationDescription",
1808                                               &vtype,
1809                                               (void **) &description,
1810                                               NULL,
1811                                               NULL);
1812 
1813   if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1814     g_clear_pointer (&description, g_free);
1815 
1816   description = read_resource_string (description);
1817 
1818   if (description && app->description == NULL)
1819     {
1820       app->description = g_wcsdup (description, -1);
1821       g_clear_pointer (&app->description_u8, g_free);
1822       app->description_u8 = g_utf16_to_utf8 (description, -1, NULL, NULL, NULL);
1823     }
1824 
1825   default_icon_key = g_win32_registry_key_get_child_w (appkey,
1826                                                        L"DefaultIcon",
1827                                                        NULL);
1828 
1829   icon_source = NULL;
1830 
1831   if (default_icon_key != NULL)
1832     {
1833       success = g_win32_registry_key_get_value_w (default_icon_key,
1834                                                   TRUE,
1835                                                   L"",
1836                                                   &vtype,
1837                                                   (void **) &icon_source,
1838                                                   NULL,
1839                                                   NULL);
1840 
1841       if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1842         g_clear_pointer (&icon_source, g_free);
1843 
1844       g_object_unref (default_icon_key);
1845     }
1846 
1847   if (icon_source == NULL)
1848     {
1849       success = g_win32_registry_key_get_value_w (capabilities,
1850                                                   TRUE,
1851                                                   L"ApplicationIcon",
1852                                                   &vtype,
1853                                                   (void **) &icon_source,
1854                                                   NULL,
1855                                                   NULL);
1856 
1857       if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1858         g_clear_pointer (&icon_source, g_free);
1859     }
1860 
1861   if (icon_source && app->icon == NULL)
1862     {
1863       gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
1864       app->icon = g_themed_icon_new (name);
1865       g_free (name);
1866     }
1867 
1868   narrow_application_name = NULL;
1869   success = g_win32_registry_key_get_value_w (capabilities,
1870                                               TRUE,
1871                                               L"ApplicationName",
1872                                               &vtype,
1873                                               (void **) &narrow_application_name,
1874                                               NULL,
1875                                               NULL);
1876 
1877   if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1878     g_clear_pointer (&narrow_application_name, g_free);
1879 
1880   narrow_application_name = read_resource_string (narrow_application_name);
1881 
1882   /* TODO: do something with the narrow name. Maybe make a kind of sub-app?
1883    * Narrow name is a more precise name of the application in given context.
1884    * I.e. Thunderbird's name is "Thunderbird", whereas its narrow name is
1885    * "Thunderbird (news)" when registering it as a news client.
1886    * Maybe we should consider applications with different narrow names as
1887    * different applications altogether?
1888    */
1889 
1890   associations = g_win32_registry_key_get_child_w (capabilities,
1891                                                    L"FileAssociations",
1892                                                    NULL);
1893 
1894   if (associations != NULL)
1895     {
1896       GWin32RegistryValueIter iter;
1897 
1898       if (g_win32_registry_value_iter_init (&iter, associations, NULL))
1899         {
1900           gunichar2 *file_extension;
1901           gunichar2 *extension_handler;
1902           gsize      file_extension_len;
1903           gsize      extension_handler_size;
1904           GWin32RegistryValueType value_type;
1905 
1906           while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1907             {
1908               GWin32AppInfoHandler *handler_rec;
1909               GWin32AppInfoHandler *handler_rec_in_ext;
1910               GWin32AppInfoFileExtension *ext;
1911               gunichar2 *program_command;
1912               gunichar2 *proxy_id;
1913               gunichar2 *proxy_command;
1914               GWin32RegistryKey *program_key;
1915               GWin32RegistryKey *proxy_key;
1916               gchar *program_id_u8;
1917               gchar *program_id_folded;
1918               gchar *file_extension_u8;
1919               gchar *file_extension_folded;
1920 
1921               if ((!g_win32_registry_value_iter_get_value_type (&iter,
1922                                                                 &value_type,
1923                                                                 NULL)) ||
1924                   (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
1925                   (!g_win32_registry_value_iter_get_name_w (&iter,
1926                                                             &file_extension,
1927                                                             &file_extension_len,
1928                                                             NULL)) ||
1929                   (file_extension_len <= 0) ||
1930                   (file_extension[0] != L'.') ||
1931                   (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1932                                                             (void **) &extension_handler,
1933                                                             &extension_handler_size,
1934                                                             NULL)) ||
1935                   (extension_handler_size < sizeof (gunichar2)) ||
1936                   (extension_handler[0] == L'\0'))
1937                 continue;
1938 
1939               if (!follow_class_chain_to_handler (extension_handler,
1940                                                   extension_handler_size,
1941                                                   &program_command,
1942                                                   &program_key,
1943                                                   &proxy_id,
1944                                                   &proxy_command,
1945                                                   &proxy_key,
1946                                                   &program_id_u8,
1947                                                   &program_id_folded))
1948                 continue;
1949 
1950               handler_rec = g_hash_table_lookup (handlers,
1951                                                  program_id_folded);
1952 
1953               if (handler_rec == NULL)
1954                 {
1955                   handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1956 
1957                   handler_rec->proxy_key = proxy_key;
1958                   handler_rec->key = program_key;
1959                   handler_rec->handler_id =
1960                       g_wcsdup (extension_handler,extension_handler_size);
1961                   handler_rec->handler_id_folded =
1962                       g_strdup (program_id_folded);
1963                   handler_rec->handler_command =
1964                       program_command ? g_wcsdup (program_command, -1) : NULL;
1965                   handler_rec->proxy_id =
1966                       proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1967                   handler_rec->proxy_command =
1968                       proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1969                   extract_executable (proxy_command ? proxy_command : program_command,
1970                                       &handler_rec->executable,
1971                                       &handler_rec->executable_basename,
1972                                       &handler_rec->executable_folded,
1973                                       NULL);
1974                   read_handler_icon (proxy_key,
1975                                      program_key,
1976                                      &handler_rec->icon);
1977                   g_hash_table_insert (handlers,
1978                                        g_strdup (program_id_folded),
1979                                        handler_rec);
1980                 }
1981               else
1982                 {
1983                   g_clear_object (&program_key);
1984                   g_clear_object (&proxy_key);
1985                 }
1986 
1987                 if (utf8_and_fold (file_extension,
1988                                    &file_extension_u8,
1989                                    &file_extension_folded))
1990                   {
1991                     ext = g_hash_table_lookup (extensions,
1992                                                file_extension_folded);
1993 
1994                     if (ext == NULL)
1995                       {
1996                         ext = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1997 
1998                         ext->extension = g_wcsdup (file_extension, -1);
1999                         ext->extension_u8 = g_strdup (file_extension_u8);
2000                         g_hash_table_insert (extensions, g_strdup (file_extension_folded), ext);
2001                       }
2002 
2003                     handler_rec_in_ext =
2004                         g_hash_table_lookup (ext->handlers,
2005                                              program_id_folded);
2006 
2007                     if (handler_rec_in_ext == NULL)
2008                       {
2009                         if (ext->chosen_handler == NULL)
2010                           g_hash_table_insert (ext->handlers,
2011                                                g_strdup (program_id_folded),
2012                                                g_object_ref (handler_rec));
2013                         else if (ext->chosen_handler->handler_id_folded &&
2014                                  strcmp (ext->chosen_handler->handler_id_folded,
2015                                          program_id_folded) != 0)
2016                           g_hash_table_insert (ext->handlers,
2017                                                g_strdup (program_id_folded),
2018                                                g_object_ref (handler_rec));
2019                       }
2020 
2021                     handler_rec_in_ext =
2022                         g_hash_table_lookup (app->supported_exts,
2023                                              file_extension_folded);
2024 
2025                     if (handler_rec_in_ext == NULL)
2026                       g_hash_table_insert (app->supported_exts,
2027                                            g_strdup (file_extension_folded),
2028                                            g_object_ref (handler_rec));
2029 
2030                     g_free (file_extension_u8);
2031                     g_free (file_extension_folded);
2032                   }
2033 
2034               g_free (program_id_u8);
2035               g_free (program_id_folded);
2036               g_free (program_command);
2037               g_free (proxy_id);
2038               g_free (proxy_command);
2039             }
2040 
2041           g_win32_registry_value_iter_clear (&iter);
2042         }
2043 
2044       g_object_unref (associations);
2045     }
2046 
2047   associations = g_win32_registry_key_get_child_w (capabilities, L"URLAssociations", NULL);
2048 
2049   if (associations != NULL)
2050     {
2051       GWin32RegistryValueIter iter;
2052 
2053       if (g_win32_registry_value_iter_init (&iter, associations, NULL))
2054         {
2055           gunichar2 *url_schema;
2056           gunichar2 *schema_handler;
2057           gsize      url_schema_len;
2058           gsize      schema_handler_size;
2059           GWin32RegistryValueType value_type;
2060 
2061           while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2062             {
2063               GWin32AppInfoHandler *handler_rec;
2064               GWin32AppInfoHandler *handler_rec_in_url;
2065               GWin32AppInfoURLSchema *schema;
2066               gunichar2 *program_command;
2067               gunichar2 *proxy_id;
2068               gunichar2 *proxy_command;
2069               GWin32RegistryKey *program_key;
2070               GWin32RegistryKey *proxy_key;
2071               gchar *program_id_u8;
2072               gchar *program_id_folded;
2073               gchar *schema_u8;
2074               gchar *schema_folded;
2075 
2076               if ((!g_win32_registry_value_iter_get_value_type (&iter,
2077                                                                 &value_type,
2078                                                                 NULL)) ||
2079                   ((value_type != G_WIN32_REGISTRY_VALUE_STR) &&
2080                    (value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
2081                   (!g_win32_registry_value_iter_get_name_w (&iter,
2082                                                             &url_schema,
2083                                                             &url_schema_len,
2084                                                             NULL)) ||
2085                   (url_schema_len <= 0) ||
2086                   (url_schema[0] == L'\0') ||
2087                   (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2088                                                             (void **) &schema_handler,
2089                                                             &schema_handler_size,
2090                                                             NULL)) ||
2091                   (schema_handler_size < sizeof (gunichar2)) ||
2092                   (schema_handler[0] == L'\0'))
2093                 continue;
2094 
2095               if (!follow_class_chain_to_handler (schema_handler,
2096                                                   schema_handler_size,
2097                                                   &program_command,
2098                                                   &program_key,
2099                                                   &proxy_id,
2100                                                   &proxy_command,
2101                                                   &proxy_key,
2102                                                   &program_id_u8,
2103                                                   &program_id_folded))
2104                 continue;
2105 
2106 
2107               handler_rec = g_hash_table_lookup (handlers, program_id_folded);
2108 
2109               if (handler_rec == NULL)
2110                 {
2111                   handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2112 
2113                   handler_rec->proxy_key = proxy_key;
2114                   handler_rec->key = program_key;
2115                   handler_rec->handler_id =
2116                       g_wcsdup (schema_handler, schema_handler_size);
2117                   handler_rec->handler_id_folded =
2118                       g_strdup (program_id_folded);
2119                   handler_rec->handler_command = program_command ?
2120                       g_wcsdup (program_command, -1) : NULL;
2121                   handler_rec->proxy_id =
2122                       proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2123                   handler_rec->proxy_command =
2124                       proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2125                   extract_executable (proxy_command ? proxy_command : program_command,
2126                                       &handler_rec->executable,
2127                                       &handler_rec->executable_basename,
2128                                       &handler_rec->executable_folded,
2129                                       NULL);
2130                   read_handler_icon (proxy_key,
2131                                      program_key,
2132                                      &handler_rec->icon);
2133                   g_hash_table_insert (handlers,
2134                                        g_strdup (program_id_folded),
2135                                        handler_rec);
2136                 }
2137               else
2138                 {
2139                   g_clear_object (&program_key);
2140                   g_clear_object (&proxy_key);
2141                 }
2142 
2143                 if (utf8_and_fold (url_schema,
2144                                    &schema_u8,
2145                                    &schema_folded))
2146                   {
2147                     schema = g_hash_table_lookup (urls,
2148                                                   schema_folded);
2149 
2150                     if (schema == NULL)
2151                       {
2152                         schema = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
2153 
2154                         schema->schema = g_wcsdup (url_schema, -1);
2155                         schema->schema_u8 = g_strdup (schema_u8);
2156                         schema->schema_folded =
2157                             g_strdup (schema_folded);
2158                         g_hash_table_insert (urls,
2159                                              g_strdup (schema_folded),
2160                                              schema);
2161                       }
2162 
2163                     handler_rec_in_url =
2164                         g_hash_table_lookup (schema->handlers,
2165                                              program_id_folded);
2166 
2167                     if (handler_rec_in_url == NULL)
2168                       g_hash_table_insert (schema->handlers,
2169                                            g_strdup (program_id_folded),
2170                                            g_object_ref (handler_rec));
2171 
2172                     handler_rec_in_url =
2173                         g_hash_table_lookup (app->supported_urls,
2174                                              schema_folded);
2175 
2176                     if (handler_rec_in_url == NULL)
2177                       g_hash_table_insert (app->supported_urls,
2178                                            g_strdup (schema_folded),
2179                                            g_object_ref (handler_rec));
2180 
2181                     g_free (schema_u8);
2182                     g_free (schema_folded);
2183                   }
2184 
2185               g_free (program_id_u8);
2186               g_free (program_id_folded);
2187               g_free (program_command);
2188               g_free (proxy_id);
2189               g_free (proxy_command);
2190             }
2191 
2192           g_win32_registry_value_iter_clear (&iter);
2193         }
2194 
2195       g_object_unref (associations);
2196     }
2197 
2198   g_clear_pointer (&app_executable, g_free);
2199   g_clear_pointer (&app_executable_folded, g_free);
2200   g_clear_pointer (&fallback_friendly_name, g_free);
2201   g_clear_pointer (&description, g_free);
2202   g_clear_pointer (&icon_source, g_free);
2203   g_clear_pointer (&narrow_application_name, g_free);
2204   g_clear_pointer (&shell_open_command, g_free);
2205 
2206   g_object_unref (appkey);
2207   g_object_unref (shell_open_command_key);
2208   g_object_unref (capabilities);
2209   g_free (canonical_name_u8);
2210   g_free (canonical_name_folded);
2211   g_free (app_key_path);
2212 }
2213 
2214 static void
read_urls(GWin32RegistryKey * url_associations)2215 read_urls (GWin32RegistryKey *url_associations)
2216 {
2217   GWin32RegistrySubkeyIter url_iter;
2218   gunichar2 *url_schema;
2219   gsize url_schema_len;
2220 
2221   if (url_associations == NULL)
2222     return;
2223 
2224   if (!g_win32_registry_subkey_iter_init (&url_iter, url_associations, NULL))
2225     return;
2226 
2227   while (g_win32_registry_subkey_iter_next (&url_iter, TRUE, NULL))
2228     {
2229       if (!g_win32_registry_subkey_iter_get_name_w (&url_iter,
2230                                                     &url_schema,
2231                                                     &url_schema_len,
2232                                                     NULL))
2233         continue;
2234 
2235       get_url_association (url_schema);
2236     }
2237 
2238   g_win32_registry_subkey_iter_clear (&url_iter);
2239 }
2240 
2241 static void
read_exeapps(void)2242 read_exeapps (void)
2243 {
2244   GWin32RegistryKey *applications_key;
2245   GWin32RegistrySubkeyIter app_iter;
2246   gunichar2 *app_exe_basename;
2247   gsize app_exe_basename_len;
2248 
2249   applications_key =
2250       g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
2251 
2252   if (applications_key == NULL)
2253     return;
2254 
2255   if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
2256     {
2257       g_object_unref (applications_key);
2258       return;
2259     }
2260 
2261   while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
2262     {
2263       GWin32RegistryKey *incapable_app;
2264       gunichar2 *friendly_app_name;
2265       gboolean success;
2266       gboolean no_open_with;
2267       GWin32RegistryValueType vtype;
2268       GWin32RegistryKey *default_icon_key;
2269       gunichar2 *icon_source;
2270       GIcon *icon = NULL;
2271       gchar *appexe;
2272       gchar *appexe_basename;
2273       gchar *appexe_folded;
2274       gchar *appexe_folded_basename;
2275       GWin32AppInfoApplication *app;
2276       GWin32RegistryKey *shell_open_command_key;
2277       gunichar2 *shell_open_command;
2278       GWin32RegistryKey *supported_key;
2279 
2280       if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
2281                                                     &app_exe_basename,
2282                                                     &app_exe_basename_len,
2283                                                     NULL))
2284         continue;
2285 
2286       incapable_app =
2287           g_win32_registry_key_get_child_w (applications_key,
2288                                             app_exe_basename,
2289                                             NULL);
2290 
2291       if (incapable_app == NULL)
2292         continue;
2293 
2294       extract_executable (app_exe_basename,
2295                           &appexe,
2296                           &appexe_basename,
2297                           &appexe_folded,
2298                           &appexe_folded_basename);
2299 
2300       shell_open_command_key =
2301           g_win32_registry_key_get_child_w (incapable_app,
2302                                             L"shell\\open\\command",
2303                                             NULL);
2304 
2305       shell_open_command = NULL;
2306 
2307       if (shell_open_command_key != NULL)
2308         {
2309           success = g_win32_registry_key_get_value_w (shell_open_command_key,
2310                                                       TRUE,
2311                                                       L"",
2312                                                       &vtype,
2313                                                       (gpointer *) &shell_open_command,
2314                                                       NULL,
2315                                                       NULL);
2316 
2317           if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2318             {
2319               g_clear_pointer (&shell_open_command, g_free);
2320             }
2321 
2322           g_object_unref (shell_open_command_key);
2323         }
2324 
2325       friendly_app_name = NULL;
2326       success = g_win32_registry_key_get_value_w (incapable_app,
2327                                                   TRUE,
2328                                                   L"FriendlyAppName",
2329                                                   &vtype,
2330                                                   (void **) &friendly_app_name,
2331                                                   NULL,
2332                                                   NULL);
2333 
2334       if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2335         g_clear_pointer (&friendly_app_name, g_free);
2336 
2337       friendly_app_name = read_resource_string (friendly_app_name);
2338 
2339       no_open_with = FALSE;
2340       success = g_win32_registry_key_get_value_w (incapable_app,
2341                                                   TRUE,
2342                                                   L"NoOpenWith",
2343                                                   &vtype,
2344                                                   NULL,
2345                                                   NULL,
2346                                                   NULL);
2347 
2348       if (success)
2349         no_open_with = TRUE;
2350 
2351       default_icon_key =
2352           g_win32_registry_key_get_child_w (incapable_app,
2353                                             L"DefaultIcon",
2354                                             NULL);
2355 
2356       icon_source = NULL;
2357 
2358       if (default_icon_key != NULL)
2359       {
2360         success =
2361             g_win32_registry_key_get_value_w (default_icon_key,
2362                                               TRUE,
2363                                               L"",
2364                                               &vtype,
2365                                               (void **) &icon_source,
2366                                               NULL,
2367                                               NULL);
2368 
2369         if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2370           g_clear_pointer (&icon_source, g_free);
2371 
2372         g_object_unref (default_icon_key);
2373       }
2374 
2375       if (icon_source)
2376         {
2377           gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
2378           icon = g_themed_icon_new (name);
2379           g_free (name);
2380         }
2381 
2382       app = g_hash_table_lookup (apps_by_exe, appexe_folded_basename);
2383 
2384       if (app == NULL)
2385         {
2386           app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
2387 
2388           app->command =
2389               shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL;
2390 
2391           if (shell_open_command)
2392             app->command_u8 = g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
2393 
2394           app->executable = g_strdup (appexe);
2395           app->executable_basename = &app->executable[appexe_basename - appexe];
2396           app->executable_folded = g_strdup (appexe_folded);
2397 
2398           app->no_open_with = no_open_with;
2399 
2400           if (friendly_app_name)
2401             {
2402               app->localized_pretty_name = g_wcsdup (friendly_app_name, -1);
2403               g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2404               app->localized_pretty_name_u8 =
2405                   g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL);
2406             }
2407 
2408           if (icon)
2409             app->icon = g_object_ref (icon);
2410 
2411           app->user_specific = FALSE;
2412           app->default_app = FALSE;
2413 
2414           g_hash_table_insert (apps_by_exe,
2415                                g_strdup (appexe_folded_basename),
2416                                app);
2417         }
2418 
2419       supported_key =
2420           g_win32_registry_key_get_child_w (incapable_app,
2421                                             L"SupportedTypes",
2422                                             NULL);
2423 
2424       if (supported_key)
2425         {
2426           GWin32RegistryValueIter sup_iter;
2427           if (g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
2428             {
2429               gunichar2 *ext_name;
2430               gsize      ext_name_len;
2431 
2432               while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
2433                 {
2434                   gchar *ext_u8;
2435                   gchar *ext_folded;
2436                   GWin32AppInfoFileExtension *file_extn;
2437                   gboolean file_ext_known;
2438 
2439                   if ((!g_win32_registry_value_iter_get_name_w (&sup_iter,
2440                                                                 &ext_name,
2441                                                                 &ext_name_len,
2442                                                                 NULL)) ||
2443                       (ext_name_len <= 0) ||
2444                       (ext_name[0] != L'.') ||
2445                       (!utf8_and_fold (ext_name,
2446                                        &ext_u8,
2447                                        &ext_folded)))
2448                     continue;
2449 
2450                   file_extn = NULL;
2451                   file_ext_known =
2452                       g_hash_table_lookup_extended (extensions,
2453                                                     ext_folded,
2454                                                     NULL,
2455                                                     (void **) &file_extn);
2456 
2457                   if (!file_ext_known)
2458                     {
2459                       file_extn =
2460                           g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
2461                       file_extn->extension = g_wcsdup (ext_name, -1);
2462                       file_extn->extension_u8 = g_strdup (ext_u8);
2463                       g_hash_table_insert (extensions,
2464                                            g_strdup (ext_folded),
2465                                            file_extn);
2466                     }
2467 
2468                   g_hash_table_insert (file_extn->other_apps,
2469                                        g_strdup (appexe_folded),
2470                                        g_object_ref (app));
2471 
2472                   g_free (ext_u8);
2473                   g_free (ext_folded);
2474                 }
2475 
2476               g_win32_registry_value_iter_clear (&sup_iter);
2477             }
2478 
2479           g_object_unref (supported_key);
2480         }
2481 
2482 
2483       g_free (appexe);
2484       g_free (appexe_folded);
2485       g_free (shell_open_command);
2486       g_free (friendly_app_name);
2487       g_free (icon_source);
2488 
2489       g_clear_object (&icon);
2490       g_clear_object (&incapable_app);
2491     }
2492 
2493   g_win32_registry_subkey_iter_clear (&app_iter);
2494   g_object_unref (applications_key);
2495 }
2496 
2497 
2498 static void
read_exts(GWin32RegistryKey * file_exts)2499 read_exts (GWin32RegistryKey *file_exts)
2500 {
2501   GWin32RegistrySubkeyIter ext_iter;
2502   gunichar2 *file_extension;
2503   gsize file_extension_len;
2504 
2505   if (file_exts == NULL)
2506     return;
2507 
2508   if (!g_win32_registry_subkey_iter_init (&ext_iter, file_exts, NULL))
2509     return;
2510 
2511   while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
2512     {
2513       if (!g_win32_registry_subkey_iter_get_name_w (&ext_iter,
2514                                                     &file_extension,
2515                                                     &file_extension_len,
2516                                                     NULL))
2517         continue;
2518 
2519       get_file_ext (file_extension);
2520     }
2521 
2522   g_win32_registry_subkey_iter_clear (&ext_iter);
2523 }
2524 
2525 static void
read_class_extension(GWin32RegistryKey * classes_root,gunichar2 * class_name,gsize class_name_len)2526 read_class_extension (GWin32RegistryKey *classes_root,
2527                       gunichar2         *class_name,
2528                       gsize              class_name_len)
2529 {
2530   gchar *ext_u8;
2531   gchar *ext_folded;
2532   GWin32AppInfoFileExtension *file_extn;
2533   GWin32AppInfoHandler *handler_rec;
2534   GWin32AppInfoHandler *handler_rec_in_ext;
2535   GWin32RegistryKey *class_key;
2536   gsize program_id_size;
2537   gunichar2 *program_id;
2538   gunichar2 *proxy_id;
2539   GWin32RegistryKey *program_key;
2540   GWin32RegistryKey *proxy_key;
2541   gunichar2 *program_command;
2542   gunichar2 *proxy_command;
2543 
2544   class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2545 
2546   if (class_key == NULL)
2547     return;
2548 
2549   program_id = class_name;
2550   program_id_size = (class_name_len + 1) * sizeof (gunichar2);
2551   program_key = proxy_key = NULL;
2552   program_command = proxy_command = NULL;
2553 
2554   if (!follow_class_chain_to_handler (program_id,
2555                                       program_id_size,
2556                                       &program_command,
2557                                       &program_key,
2558                                       &proxy_id,
2559                                       &proxy_command,
2560                                       &proxy_key,
2561                                       &ext_u8,
2562                                       &ext_folded))
2563     {
2564       g_object_unref (class_key);
2565       return;
2566     }
2567 
2568 
2569   file_extn = g_hash_table_lookup (extensions, ext_folded);
2570   handler_rec = g_hash_table_lookup (handlers, ext_folded);
2571 
2572   if (file_extn == NULL)
2573     {
2574       file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
2575       file_extn->extension = g_wcsdup (class_name, -1);
2576       file_extn->extension_u8 = g_strdup (ext_u8);
2577       g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
2578     }
2579 
2580   if (handler_rec == NULL)
2581     {
2582       handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2583 
2584       handler_rec->proxy_key = proxy_key;
2585       handler_rec->key = program_key;
2586       handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
2587       handler_rec->handler_id_folded = g_strdup (ext_folded);
2588       handler_rec->handler_command =
2589           program_command ? g_wcsdup (program_command, -1) : NULL;
2590       handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2591       handler_rec->proxy_command =
2592           proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2593       extract_executable (proxy_command ? proxy_command : program_command,
2594                           &handler_rec->executable,
2595                           &handler_rec->executable_basename,
2596                           &handler_rec->executable_folded,
2597                           NULL);
2598       read_handler_icon (proxy_key, program_key, &handler_rec->icon);
2599       g_hash_table_insert (handlers,
2600                            g_strdup (ext_folded),
2601                            handler_rec);
2602     }
2603   else
2604     {
2605       g_clear_object (&program_key);
2606       g_clear_object (&proxy_key);
2607     }
2608 
2609   handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
2610                                             ext_folded);
2611 
2612   if (file_extn->chosen_handler == NULL)
2613     g_hash_table_insert (file_extn->handlers,
2614                          g_strdup (ext_folded),
2615                          g_object_ref (handler_rec));
2616   else if (handler_rec_in_ext == NULL)
2617     {
2618       if (file_extn->chosen_handler->handler_id_folded &&
2619           strcmp (file_extn->chosen_handler->handler_id_folded,
2620                   ext_folded) != 0)
2621         g_hash_table_insert (file_extn->handlers,
2622                              g_strdup (ext_folded),
2623                              g_object_ref (handler_rec));
2624     }
2625 
2626   g_free (program_command);
2627   g_free (proxy_id);
2628   g_free (proxy_command);
2629   g_free (ext_u8);
2630   g_free (ext_folded);
2631   g_object_unref (class_key);
2632 }
2633 
2634 static void
read_class_url(GWin32RegistryKey * classes_root,gunichar2 * class_name,gsize class_name_len)2635 read_class_url (GWin32RegistryKey *classes_root,
2636                 gunichar2         *class_name,
2637                 gsize              class_name_len)
2638 {
2639   GWin32RegistryKey *class_key;
2640   gboolean success;
2641   GWin32RegistryValueType vtype;
2642   GWin32AppInfoURLSchema *schema_rec;
2643   GWin32AppInfoHandler *handler_rec;
2644   GWin32AppInfoHandler *handler_rec_in_url;
2645   gunichar2 *program_id;
2646   gsize program_id_size;
2647   gunichar2 *program_command;
2648   gunichar2 *proxy_id;
2649   gunichar2 *proxy_command;
2650   gchar *program_id_u8;
2651   gchar *program_id_folded;
2652   GWin32RegistryKey *program_key;
2653   GWin32RegistryKey *proxy_key;
2654 
2655   class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2656 
2657   if (class_key == NULL)
2658     return;
2659 
2660   success = g_win32_registry_key_get_value_w (class_key,
2661                                               TRUE,
2662                                               L"URL Protocol",
2663                                               &vtype,
2664                                               NULL,
2665                                               NULL,
2666                                               NULL);
2667 
2668   if (!success ||
2669       vtype != G_WIN32_REGISTRY_VALUE_STR)
2670     {
2671       g_object_unref (class_key);
2672       return;
2673     }
2674 
2675   program_id = class_name;
2676   program_id_size = (class_name_len + 1) * sizeof (gunichar2);
2677   proxy_key = program_key = NULL;
2678   program_command = proxy_id = proxy_command = NULL;
2679 
2680   if (!follow_class_chain_to_handler (program_id,
2681                                       program_id_size,
2682                                       &program_command,
2683                                       &program_key,
2684                                       &proxy_id,
2685                                       &proxy_command,
2686                                       &proxy_key,
2687                                       &program_id_u8,
2688                                       &program_id_folded))
2689     {
2690       g_object_unref (class_key);
2691       return;
2692     }
2693 
2694   schema_rec = g_hash_table_lookup (urls, program_id_folded);
2695   handler_rec = g_hash_table_lookup (handlers, program_id_folded);
2696 
2697   if (handler_rec == NULL)
2698     {
2699       handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2700 
2701       handler_rec->proxy_key = proxy_key;
2702       handler_rec->key = program_key;
2703       handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
2704       handler_rec->handler_id_folded =
2705           g_strdup (program_id_folded);
2706       handler_rec->handler_command =
2707           program_command ? g_wcsdup (program_command, -1) : NULL;
2708       handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2709       handler_rec->proxy_command =
2710           proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2711       extract_executable (proxy_command ? proxy_command : program_command,
2712                           &handler_rec->executable,
2713                           &handler_rec->executable_basename,
2714                           &handler_rec->executable_folded,
2715                           NULL);
2716       read_handler_icon (proxy_key, program_key, &handler_rec->icon);
2717       g_hash_table_insert (handlers,
2718                            g_strdup (program_id_folded),
2719                            handler_rec);
2720     }
2721   else
2722     {
2723       g_clear_object (&program_key);
2724       g_clear_object (&proxy_key);
2725     }
2726 
2727   if (schema_rec == NULL)
2728     {
2729       schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
2730       schema_rec->schema = g_wcsdup (class_name, -1);
2731       schema_rec->schema_u8 = g_strdup (program_id_u8);
2732       schema_rec->schema_folded = g_strdup (program_id_folded);
2733       schema_rec->chosen_handler = g_object_ref (handler_rec);
2734       g_hash_table_insert (urls,
2735                            g_strdup (program_id_folded),
2736                            schema_rec);
2737     }
2738 
2739   if (schema_rec->chosen_handler == NULL)
2740     schema_rec->chosen_handler = g_object_ref (handler_rec);
2741 
2742   handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
2743                                             program_id_folded);
2744 
2745   if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
2746     g_hash_table_insert (schema_rec->handlers,
2747                          g_strdup (program_id_folded),
2748                          g_object_ref (handler_rec));
2749 
2750   g_free (program_id_u8);
2751   g_free (program_id_folded);
2752   g_free (program_command);
2753   g_free (proxy_id);
2754   g_free (proxy_command);
2755   g_object_unref (class_key);
2756 }
2757 
2758 static void
read_classes(GWin32RegistryKey * classes_root)2759 read_classes (GWin32RegistryKey *classes_root)
2760 {
2761   GWin32RegistrySubkeyIter class_iter;
2762   gunichar2 *class_name;
2763   gsize class_name_len;
2764 
2765   if (classes_root == NULL)
2766     return;
2767 
2768   if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL))
2769     return;
2770 
2771   while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL))
2772     {
2773       if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
2774                                                      &class_name,
2775                                                      &class_name_len,
2776                                                      NULL)) ||
2777           (class_name_len <= 1))
2778         continue;
2779 
2780       if (class_name[0] == L'.')
2781         read_class_extension (classes_root, class_name, class_name_len);
2782       else
2783         {
2784           gsize i;
2785 
2786           for (i = 0; i < class_name_len; i++)
2787             if (!iswalpha (class_name[i]))
2788               break;
2789 
2790           if (i == class_name_len)
2791             read_class_url (classes_root, class_name, class_name_len);
2792         }
2793     }
2794 
2795   g_win32_registry_subkey_iter_clear (&class_iter);
2796 }
2797 
2798 static void
link_chosen_handlers(void)2799 link_chosen_handlers (void)
2800 {
2801   GHashTableIter iter;
2802   GHashTableIter handler_iter;
2803   gchar *schema_folded;
2804   GWin32AppInfoURLSchema *schema;
2805   gchar *handler_id_folded;
2806   GWin32AppInfoHandler *handler;
2807   gchar *ext_folded;
2808   GWin32AppInfoFileExtension *ext;
2809 
2810   g_hash_table_iter_init (&iter, urls);
2811 
2812   while (g_hash_table_iter_next (&iter,
2813                                 (gpointer *) &schema_folded,
2814                                 (gpointer *) &schema))
2815     {
2816       if (schema->chosen_handler != NULL)
2817         continue;
2818 
2819       g_hash_table_iter_init (&handler_iter, schema->handlers);
2820 
2821       while (g_hash_table_iter_next (&handler_iter,
2822                                      (gpointer *) &handler_id_folded,
2823                                      (gpointer *) &handler))
2824         {
2825           gchar *proxy_id_folded;
2826 
2827           if (schema->chosen_handler != NULL)
2828             break;
2829 
2830           if (strcmp (handler_id_folded, schema_folded) != 0)
2831             continue;
2832 
2833           if (handler->proxy_command &&
2834               handler->proxy_id &&
2835               utf8_and_fold (handler->proxy_id,
2836                              NULL,
2837                              &proxy_id_folded))
2838             {
2839               GWin32AppInfoHandler *proxy;
2840 
2841               proxy = g_hash_table_lookup (handlers, proxy_id_folded);
2842 
2843               if (proxy)
2844                 {
2845                   schema->chosen_handler = g_object_ref (proxy);
2846                   g_debug ("Linking schema %s to proxy handler %c ? \"%S\" : %S\n",
2847                            schema->schema_u8,
2848                            schema->chosen_handler->proxy_id ? 'P' : 'T',
2849                            schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
2850                            schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
2851                 }
2852 
2853               g_free (proxy_id_folded);
2854             }
2855 
2856           if (schema->chosen_handler == NULL)
2857             {
2858               schema->chosen_handler = g_object_ref (handler);
2859               g_debug ("Linking schema %s to handler %c ? \"%S\" : %S\n",
2860                        schema->schema_u8,
2861                        schema->chosen_handler->proxy_id ? 'P' : 'T',
2862                        schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
2863                        schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
2864             }
2865         }
2866     }
2867 
2868   g_hash_table_iter_init (&iter, extensions);
2869 
2870   while (g_hash_table_iter_next (&iter,
2871                                  (gpointer *) &ext_folded,
2872                                  (gpointer *) &ext))
2873     {
2874       if (ext->chosen_handler != NULL)
2875         continue;
2876 
2877       g_hash_table_iter_init (&handler_iter, ext->handlers);
2878 
2879       while (g_hash_table_iter_next (&handler_iter,
2880                                      (gpointer *) &handler_id_folded,
2881                                      (gpointer *) &handler))
2882         {
2883           gchar *proxy_id_folded;
2884 
2885           if (ext->chosen_handler != NULL)
2886             break;
2887 
2888           if (strcmp (handler_id_folded, ext_folded) != 0)
2889             continue;
2890 
2891           if (handler->proxy_command &&
2892               handler->proxy_id &&
2893               utf8_and_fold (handler->proxy_id,
2894                              NULL,
2895                              &proxy_id_folded))
2896             {
2897               GWin32AppInfoHandler *proxy;
2898 
2899               proxy = g_hash_table_lookup (handlers, proxy_id_folded);
2900 
2901               if (proxy)
2902                 {
2903                   ext->chosen_handler = g_object_ref (proxy);
2904                   g_debug ("Linking ext %s to proxy handler %c ? \"%S\" : %S\n",
2905                            ext->extension_u8,
2906                            ext->chosen_handler->proxy_id ? 'P' : 'T',
2907                            ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
2908                            ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
2909                 }
2910 
2911               g_free (proxy_id_folded);
2912             }
2913 
2914           if (ext->chosen_handler == NULL)
2915             {
2916               ext->chosen_handler = g_object_ref (handler);
2917               g_debug ("Linking ext %s to handler %c ? \"%S\" : %S\n",
2918                        ext->extension_u8,
2919                        ext->chosen_handler->proxy_id ? 'P' : 'T',
2920                        ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
2921                        ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
2922             }
2923         }
2924     }
2925 }
2926 
2927 static void
link_handlers_to_registered_apps(void)2928 link_handlers_to_registered_apps (void)
2929 {
2930   GHashTableIter iter;
2931   GHashTableIter sup_iter;
2932   gchar *app_id_folded;
2933   GWin32AppInfoApplication *app;
2934   gchar *schema_folded;
2935   GWin32AppInfoURLSchema *schema;
2936   gchar *ext_folded;
2937   GWin32AppInfoFileExtension *ext;
2938   gsize unhandled_exts;
2939 
2940   g_hash_table_iter_init (&sup_iter, urls);
2941   while (g_hash_table_iter_next (&sup_iter,
2942                                  (gpointer *) &schema_folded,
2943                                  (gpointer *) &schema))
2944     {
2945       if (schema->chosen_handler == NULL)
2946         g_debug ("WARNING: schema %s has no chosen handler\n", schema->schema_u8);
2947     }
2948   unhandled_exts= 0;
2949   g_hash_table_iter_init (&sup_iter, extensions);
2950   while (g_hash_table_iter_next (&sup_iter,
2951                                  (gpointer *) &ext_folded,
2952                                  (gpointer *) &ext))
2953     {
2954       if (ext->chosen_handler == NULL)
2955         {
2956           g_debug ("WARNING: extension %s has no chosen handler\n",
2957                    ext->extension_u8);
2958           unhandled_exts += 1;
2959         }
2960     }
2961 
2962   g_hash_table_iter_init (&iter, apps_by_id);
2963   while (g_hash_table_iter_next (&iter,
2964                                  (gpointer *) &app_id_folded,
2965                                  (gpointer *) &app))
2966     {
2967       if (app->supported_urls)
2968         {
2969           GWin32AppInfoHandler *handler;
2970 
2971           g_hash_table_iter_init (&sup_iter, app->supported_urls);
2972           while (g_hash_table_iter_next (&sup_iter,
2973                                          (gpointer *) &schema_folded,
2974                                          (gpointer *) &handler))
2975             {
2976               schema = g_hash_table_lookup (urls, schema_folded);
2977 
2978               g_assert (schema != NULL);
2979 
2980               if (schema->chosen_handler != NULL &&
2981                   schema->chosen_handler->app == NULL)
2982                 {
2983                   schema->chosen_handler->app = g_object_ref (app);
2984                   g_debug ("Linking %S", app->canonical_name);
2985 
2986                   if (app->localized_pretty_name)
2987                     g_debug (" '%S'", app->localized_pretty_name);
2988                   else if (app->pretty_name)
2989                     g_debug (" '%S'", app->pretty_name);
2990                   else
2991                     g_debug (" '%s'", app->executable);
2992 
2993                   if (app->command)
2994                     g_debug (" %S", app->command);
2995 
2996                   g_debug ("\n to schema %s handler %c ? \"%S\" : %S\n",
2997                            schema->schema_u8,
2998                            schema->chosen_handler->proxy_id ? 'P' : 'T',
2999                            schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
3000                            schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
3001                 }
3002             }
3003 
3004           g_hash_table_iter_init (&sup_iter, app->supported_urls);
3005           while (g_hash_table_iter_next (&sup_iter,
3006                                          (gpointer *) &schema_folded,
3007                                          (gpointer *) &handler))
3008             {
3009               if (handler->app == NULL)
3010                 {
3011                   handler->app = g_object_ref (app);
3012                   g_debug ("Linking %S", app->canonical_name);
3013 
3014                   if (app->localized_pretty_name)
3015                     g_debug (" '%S'", app->localized_pretty_name);
3016                   else if (app->pretty_name)
3017                     g_debug (" '%S'", app->pretty_name);
3018                   else
3019                     g_debug (" '%s'", app->executable);
3020 
3021                   if (app->command)
3022                     g_debug (" %S", app->command);
3023 
3024                   g_debug ("\n directly to schema handler to %c ? \"%S\" : %S\n",
3025                            handler->proxy_id ? 'P' : 'T',
3026                            handler->proxy_id ? handler->proxy_id : handler->handler_id,
3027                            handler->proxy_command ? handler->proxy_command : handler->handler_command);
3028                 }
3029             }
3030         }
3031 
3032       if (app->supported_exts)
3033         {
3034           GWin32AppInfoHandler *handler;
3035 
3036           g_hash_table_iter_init (&sup_iter, app->supported_exts);
3037           while (g_hash_table_iter_next (&sup_iter,
3038                                          (gpointer *) &ext_folded,
3039                                          (gpointer *) &handler))
3040             {
3041               ext = g_hash_table_lookup (extensions, ext_folded);
3042 
3043               g_assert (ext != NULL);
3044 
3045               if (ext->chosen_handler != NULL &&
3046                   ext->chosen_handler->app == NULL)
3047                 {
3048                   ext->chosen_handler->app = g_object_ref (app);
3049                   g_debug ("Linking %S", app->canonical_name);
3050 
3051                   if (app->localized_pretty_name)
3052                     g_debug (" '%S'", app->localized_pretty_name);
3053                   else if (app->pretty_name)
3054                     g_debug (" '%S'", app->pretty_name);
3055                   else
3056                     g_debug (" '%s'", app->executable);
3057 
3058                   if (app->command)
3059                     g_debug (" %S", app->command);
3060 
3061                   g_debug ("\n to ext %s handler %c ? \"%S\" : %S\n",
3062                            ext->extension_u8,
3063                            ext->chosen_handler->proxy_id ? 'P' : 'T',
3064                            ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
3065                            ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
3066                 }
3067             }
3068 
3069           g_hash_table_iter_init (&sup_iter, app->supported_exts);
3070           while (g_hash_table_iter_next (&sup_iter,
3071                                          (gpointer *) &ext_folded,
3072                                          (gpointer *) &handler))
3073             {
3074               if (handler->app == NULL)
3075                 {
3076                   handler->app = g_object_ref (app);
3077                   g_debug ("Linking %S", app->canonical_name);
3078 
3079                   if (app->localized_pretty_name)
3080                     g_debug (" '%S'", app->localized_pretty_name);
3081                   else if (app->pretty_name)
3082                     g_debug (" '%S'", app->pretty_name);
3083                   else
3084                     g_debug (" '%s'", app->executable);
3085 
3086                   if (app->command)
3087                     g_debug (" %S", app->command);
3088 
3089                   g_debug ("\n directly to ext handler %c ? \"%S\" : %S\n",
3090                            handler->proxy_id ? 'P' : 'T',
3091                            handler->proxy_id ? handler->proxy_id : handler->handler_id,
3092                            handler->proxy_command ? handler->proxy_command : handler->handler_command);
3093                 }
3094             }
3095         }
3096     }
3097 
3098   g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
3099   unhandled_exts= 0;
3100   g_hash_table_iter_init (&sup_iter, extensions);
3101   while (g_hash_table_iter_next (&sup_iter,
3102                                  (gpointer *) &ext_folded,
3103                                  (gpointer *) &ext))
3104     {
3105       if (ext->chosen_handler == NULL)
3106         {
3107           g_debug ("WARNING: extension %s has no chosen handler\n",
3108                    ext->extension_u8);
3109           unhandled_exts += 1;
3110         }
3111     }
3112   g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
3113 }
3114 
3115 static void
link_handlers_to_unregistered_apps(void)3116 link_handlers_to_unregistered_apps (void)
3117 {
3118   GHashTableIter iter;
3119   GHashTableIter app_iter;
3120   GWin32AppInfoHandler *handler;
3121   gchar *handler_id_fc;
3122   GWin32AppInfoApplication *app;
3123   gchar *canonical_name_fc;
3124   gchar *appexe_fc_basename;
3125 
3126   g_hash_table_iter_init (&iter, handlers);
3127   while (g_hash_table_iter_next (&iter,
3128                                  (gpointer *) &handler_id_fc,
3129                                  (gpointer *) &handler))
3130     {
3131       gchar *hndexe_fc_basename;
3132 
3133       if ((handler->app != NULL) ||
3134           (handler->executable_folded == NULL))
3135         continue;
3136 
3137       hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
3138 
3139       if (hndexe_fc_basename == NULL)
3140         continue;
3141 
3142       g_hash_table_iter_init (&app_iter, apps_by_id);
3143 
3144       while (g_hash_table_iter_next (&app_iter,
3145                                      (gpointer *) &canonical_name_fc,
3146                                      (gpointer *) &app))
3147         {
3148           if (app->executable_folded == NULL)
3149             continue;
3150 
3151           if (strcmp (app->executable_folded,
3152                       handler->executable_folded) != 0)
3153             continue;
3154 
3155           handler->app = app;
3156           break;
3157         }
3158 
3159       if (handler->app != NULL)
3160         continue;
3161 
3162       g_hash_table_iter_init (&app_iter, apps_by_exe);
3163 
3164       while ((hndexe_fc_basename != NULL) &&
3165              (g_hash_table_iter_next (&app_iter,
3166                                       (gpointer *) &appexe_fc_basename,
3167                                       (gpointer *) &app)))
3168         {
3169           /* Use basename because apps_by_exe only has basenames */
3170           if (strcmp (hndexe_fc_basename, appexe_fc_basename) != 0)
3171             continue;
3172 
3173           handler->app = app;
3174           break;
3175         }
3176 
3177       g_free (hndexe_fc_basename);
3178 
3179       if (handler->app == NULL)
3180         g_debug ("WARNING: handler that runs %s has no corresponding app\n",
3181                  handler->executable);
3182     }
3183 }
3184 
3185 
3186 static void
update_registry_data(void)3187 update_registry_data (void)
3188 {
3189   guint i;
3190   GPtrArray *capable_apps_keys;
3191   GPtrArray *user_capable_apps_keys;
3192   GPtrArray *priority_capable_apps_keys;
3193   GWin32RegistryKey *url_associations;
3194   GWin32RegistryKey *file_exts;
3195   GWin32RegistryKey *classes_root;
3196   DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, postproc_end;
3197 
3198   url_associations =
3199       g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3200                                    NULL);
3201   file_exts =
3202       g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3203                                    NULL);
3204   classes_root = g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT", NULL);
3205 
3206   capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3207   user_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3208   priority_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3209 
3210   g_clear_pointer (&apps_by_id, g_hash_table_destroy);
3211   g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
3212   g_clear_pointer (&urls, g_hash_table_destroy);
3213   g_clear_pointer (&extensions, g_hash_table_destroy);
3214   g_clear_pointer (&handlers, g_hash_table_destroy);
3215 
3216   collect_start = GetTickCount ();
3217   collect_capable_apps_from_clients (capable_apps_keys,
3218                                      priority_capable_apps_keys,
3219                                      FALSE);
3220   collect_capable_apps_from_clients (user_capable_apps_keys,
3221                                      priority_capable_apps_keys,
3222                                      TRUE);
3223   collect_capable_apps_from_registered_apps (user_capable_apps_keys,
3224                                              TRUE);
3225   collect_capable_apps_from_registered_apps (capable_apps_keys,
3226                                              FALSE);
3227   collect_end = GetTickCount ();
3228 
3229   apps_by_id =
3230       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3231   apps_by_exe =
3232       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3233   urls =
3234       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3235   extensions =
3236       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3237   handlers =
3238       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3239   alloc_end = GetTickCount ();
3240 
3241   for (i = 0; i < priority_capable_apps_keys->len; i++)
3242     read_capable_app (g_ptr_array_index (priority_capable_apps_keys, i),
3243                       TRUE,
3244                       TRUE);
3245   for (i = 0; i < user_capable_apps_keys->len; i++)
3246     read_capable_app (g_ptr_array_index (user_capable_apps_keys, i),
3247                       TRUE,
3248                       FALSE);
3249   for (i = 0; i < capable_apps_keys->len; i++)
3250     read_capable_app (g_ptr_array_index (capable_apps_keys, i),
3251                       FALSE,
3252                       FALSE);
3253   capable_end = GetTickCount ();
3254 
3255   read_urls (url_associations);
3256   url_end = GetTickCount ();
3257   read_exts (file_exts);
3258   ext_end = GetTickCount ();
3259   read_exeapps ();
3260   exeapp_end = GetTickCount ();
3261   read_classes (classes_root);
3262   classes_end = GetTickCount ();
3263   link_chosen_handlers ();
3264   link_handlers_to_registered_apps ();
3265   link_handlers_to_unregistered_apps ();
3266   postproc_end = GetTickCount ();
3267 
3268   g_debug ("Collecting capable appnames: %lums\n"
3269            "Allocating hashtables:...... %lums\n"
3270            "Reading capable apps:        %lums\n"
3271            "Reading URL associations:... %lums\n"
3272            "Reading extension assocs:    %lums\n"
3273            "Reading exe-only apps:...... %lums\n"
3274            "Reading classes:             %lums\n"
3275            "Postprocessing:..............%lums\n"
3276            "TOTAL:                       %lums\n",
3277            collect_end - collect_start,
3278            alloc_end - collect_end,
3279            capable_end - alloc_end,
3280            url_end - capable_end,
3281            ext_end - url_end,
3282            exeapp_end - ext_end,
3283            classes_end - exeapp_end,
3284            postproc_end - classes_end,
3285            postproc_end - collect_start);
3286 
3287   g_clear_object (&classes_root);
3288   g_clear_object (&url_associations);
3289   g_clear_object (&file_exts);
3290   g_ptr_array_free (capable_apps_keys, TRUE);
3291   g_ptr_array_free (user_capable_apps_keys, TRUE);
3292   g_ptr_array_free (priority_capable_apps_keys, TRUE);
3293 
3294   return;
3295 }
3296 
3297 static void
watch_keys(void)3298 watch_keys (void)
3299 {
3300   if (url_associations_key)
3301     g_win32_registry_key_watch (url_associations_key,
3302                                 TRUE,
3303                                 G_WIN32_REGISTRY_WATCH_NAME |
3304                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3305                                 G_WIN32_REGISTRY_WATCH_VALUES,
3306                                 NULL,
3307                                 NULL,
3308                                 NULL);
3309 
3310   if (file_exts_key)
3311     g_win32_registry_key_watch (file_exts_key,
3312                                 TRUE,
3313                                 G_WIN32_REGISTRY_WATCH_NAME |
3314                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3315                                 G_WIN32_REGISTRY_WATCH_VALUES,
3316                                 NULL,
3317                                 NULL,
3318                                 NULL);
3319 
3320   if (user_clients_key)
3321     g_win32_registry_key_watch (user_clients_key,
3322                                 TRUE,
3323                                 G_WIN32_REGISTRY_WATCH_NAME |
3324                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3325                                 G_WIN32_REGISTRY_WATCH_VALUES,
3326                                 NULL,
3327                                 NULL,
3328                                 NULL);
3329 
3330   if (system_clients_key)
3331     g_win32_registry_key_watch (system_clients_key,
3332                                 TRUE,
3333                                 G_WIN32_REGISTRY_WATCH_NAME |
3334                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3335                                 G_WIN32_REGISTRY_WATCH_VALUES,
3336                                 NULL,
3337                                 NULL,
3338                                 NULL);
3339 
3340   if (applications_key)
3341     g_win32_registry_key_watch (applications_key,
3342                                 TRUE,
3343                                 G_WIN32_REGISTRY_WATCH_NAME |
3344                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3345                                 G_WIN32_REGISTRY_WATCH_VALUES,
3346                                 NULL,
3347                                 NULL,
3348                                 NULL);
3349 
3350   if (user_registered_apps_key)
3351     g_win32_registry_key_watch (user_registered_apps_key,
3352                                 TRUE,
3353                                 G_WIN32_REGISTRY_WATCH_NAME |
3354                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3355                                 G_WIN32_REGISTRY_WATCH_VALUES,
3356                                 NULL,
3357                                 NULL,
3358                                 NULL);
3359 
3360   if (system_registered_apps_key)
3361     g_win32_registry_key_watch (system_registered_apps_key,
3362                                 TRUE,
3363                                 G_WIN32_REGISTRY_WATCH_NAME |
3364                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3365                                 G_WIN32_REGISTRY_WATCH_VALUES,
3366                                 NULL,
3367                                 NULL,
3368                                 NULL);
3369 
3370   if (classes_root_key)
3371     g_win32_registry_key_watch (classes_root_key,
3372                                 FALSE,
3373                                 G_WIN32_REGISTRY_WATCH_NAME |
3374                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3375                                 G_WIN32_REGISTRY_WATCH_VALUES,
3376                                 NULL,
3377                                 NULL,
3378                                 NULL);
3379 }
3380 
3381 
3382 static void
g_win32_appinfo_init(void)3383 g_win32_appinfo_init (void)
3384 {
3385   static gsize initialized;
3386 
3387   if (g_once_init_enter (&initialized))
3388     {
3389       url_associations_key =
3390           g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3391                                        NULL);
3392       file_exts_key =
3393           g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3394                                        NULL);
3395       user_clients_key =
3396           g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
3397                                        NULL);
3398       system_clients_key =
3399           g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
3400                                        NULL);
3401       applications_key =
3402           g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications",
3403                                        NULL);
3404       user_registered_apps_key =
3405           g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
3406                                        NULL);
3407       system_registered_apps_key =
3408           g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
3409                                        NULL);
3410       classes_root_key =
3411           g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT",
3412                                        NULL);
3413 
3414       watch_keys ();
3415 
3416       update_registry_data ();
3417 
3418       g_once_init_leave (&initialized, TRUE);
3419     }
3420 
3421   if ((url_associations_key       && g_win32_registry_key_has_changed (url_associations_key))       ||
3422       (file_exts_key              && g_win32_registry_key_has_changed (file_exts_key))              ||
3423       (user_clients_key           && g_win32_registry_key_has_changed (user_clients_key))           ||
3424       (system_clients_key         && g_win32_registry_key_has_changed (system_clients_key))         ||
3425       (applications_key           && g_win32_registry_key_has_changed (applications_key))           ||
3426       (user_registered_apps_key   && g_win32_registry_key_has_changed (user_registered_apps_key))   ||
3427       (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
3428       (classes_root_key           && g_win32_registry_key_has_changed (classes_root_key)))
3429     {
3430       G_LOCK (gio_win32_appinfo);
3431       update_registry_data ();
3432       watch_keys ();
3433       G_UNLOCK (gio_win32_appinfo);
3434     }
3435 }
3436 
3437 
3438 static void g_win32_app_info_iface_init (GAppInfoIface *iface);
3439 
3440 struct _GWin32AppInfo
3441 {
3442   GObject parent_instance;
3443 
3444   /*<private>*/
3445   gchar **supported_types;
3446 
3447   GWin32AppInfoApplication *app;
3448 
3449   GWin32AppInfoHandler *handler;
3450 
3451   guint startup_notify : 1;
3452 };
3453 
G_DEFINE_TYPE_WITH_CODE(GWin32AppInfo,g_win32_app_info,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,g_win32_app_info_iface_init))3454 G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
3455                          G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
3456                                                 g_win32_app_info_iface_init))
3457 
3458 
3459 static void
3460 g_win32_app_info_finalize (GObject *object)
3461 {
3462   GWin32AppInfo *info;
3463 
3464   info = G_WIN32_APP_INFO (object);
3465 
3466   g_clear_pointer (&info->supported_types, g_free);
3467   g_clear_object (&info->app);
3468   g_clear_object (&info->handler);
3469 
3470   G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
3471 }
3472 
3473 static void
g_win32_app_info_class_init(GWin32AppInfoClass * klass)3474 g_win32_app_info_class_init (GWin32AppInfoClass *klass)
3475 {
3476   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3477 
3478   gobject_class->finalize = g_win32_app_info_finalize;
3479 }
3480 
3481 static void
g_win32_app_info_init(GWin32AppInfo * local)3482 g_win32_app_info_init (GWin32AppInfo *local)
3483 {
3484 }
3485 
3486 static GAppInfo *
g_win32_app_info_new_from_app(GWin32AppInfoApplication * app,GWin32AppInfoHandler * handler)3487 g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
3488                                GWin32AppInfoHandler     *handler)
3489 {
3490   GWin32AppInfo *new_info;
3491   GHashTableIter iter;
3492   gpointer ext;
3493   int i;
3494 
3495   new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3496 
3497   new_info->app = g_object_ref (app);
3498 
3499   g_win32_appinfo_init ();
3500   G_LOCK (gio_win32_appinfo);
3501 
3502   i = 0;
3503   g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3504 
3505   while (g_hash_table_iter_next (&iter, &ext, NULL))
3506     {
3507       if (ext)
3508         i += 1;
3509     }
3510 
3511   new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3512 
3513   i = 0;
3514   g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3515 
3516   while (g_hash_table_iter_next (&iter, &ext, NULL))
3517     {
3518       if (!ext)
3519         continue;
3520 
3521       new_info->supported_types[i] = (gchar *) ext;
3522       i += 1;
3523     }
3524 
3525   G_UNLOCK (gio_win32_appinfo);
3526 
3527   new_info->supported_types[i] = NULL;
3528 
3529   new_info->handler = handler ? g_object_ref (handler) : NULL;
3530 
3531   return G_APP_INFO (new_info);
3532 }
3533 
3534 
3535 static GAppInfo *
g_win32_app_info_dup(GAppInfo * appinfo)3536 g_win32_app_info_dup (GAppInfo *appinfo)
3537 {
3538   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3539   GWin32AppInfo *new_info;
3540 
3541   new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3542 
3543   if (info->app)
3544     new_info->app = g_object_ref (info->app);
3545 
3546   if (info->handler)
3547     new_info->handler = g_object_ref (info->handler);
3548 
3549   new_info->startup_notify = info->startup_notify;
3550 
3551   if (info->supported_types)
3552     {
3553       int i;
3554 
3555       for (i = 0; info->supported_types[i]; i++)
3556         break;
3557 
3558       new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3559 
3560       for (i = 0; info->supported_types[i]; i++)
3561         new_info->supported_types[i] = g_strdup (info->supported_types[i]);
3562 
3563       new_info->supported_types[i] = NULL;
3564     }
3565 
3566   return G_APP_INFO (new_info);
3567 }
3568 
3569 static gboolean
g_win32_app_info_equal(GAppInfo * appinfo1,GAppInfo * appinfo2)3570 g_win32_app_info_equal (GAppInfo *appinfo1,
3571                         GAppInfo *appinfo2)
3572 {
3573   GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
3574   GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
3575 
3576   if (info1->app == NULL ||
3577       info2->app == NULL)
3578     return info1 == info2;
3579 
3580   if (info1->app->canonical_name_folded != NULL &&
3581       info2->app->canonical_name_folded != NULL)
3582     return (strcmp (info1->app->canonical_name_folded,
3583                     info2->app->canonical_name_folded)) == 0;
3584 
3585   if (info1->app->executable_folded != NULL &&
3586       info2->app->executable_folded != NULL)
3587     return (strcmp (info1->app->executable_folded,
3588                     info2->app->executable_folded)) == 0;
3589 
3590   return info1->app == info2->app;
3591 }
3592 
3593 static const char *
g_win32_app_info_get_id(GAppInfo * appinfo)3594 g_win32_app_info_get_id (GAppInfo *appinfo)
3595 {
3596   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3597 
3598   if (info->app == NULL)
3599     return NULL;
3600 
3601   if (info->app->canonical_name_u8)
3602     return info->app->canonical_name_u8;
3603 
3604   if (info->app->executable_basename)
3605     return info->app->executable_basename;
3606 
3607   return NULL;
3608 }
3609 
3610 static const char *
g_win32_app_info_get_name(GAppInfo * appinfo)3611 g_win32_app_info_get_name (GAppInfo *appinfo)
3612 {
3613   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3614 
3615   if (info->app && info->app->canonical_name_u8)
3616     return info->app->canonical_name_u8;
3617   else
3618     return P_("Unnamed");
3619 }
3620 
3621 static const char *
g_win32_app_info_get_display_name(GAppInfo * appinfo)3622 g_win32_app_info_get_display_name (GAppInfo *appinfo)
3623 {
3624   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3625 
3626   if (info->app)
3627     {
3628       if (info->app->localized_pretty_name_u8)
3629         return info->app->localized_pretty_name_u8;
3630       else if (info->app->pretty_name_u8)
3631         return info->app->pretty_name_u8;
3632     }
3633 
3634   return g_win32_app_info_get_name (appinfo);
3635 }
3636 
3637 static const char *
g_win32_app_info_get_description(GAppInfo * appinfo)3638 g_win32_app_info_get_description (GAppInfo *appinfo)
3639 {
3640   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3641 
3642   if (info->app == NULL)
3643     return NULL;
3644 
3645   return info->app->description_u8;
3646 }
3647 
3648 static const char *
g_win32_app_info_get_executable(GAppInfo * appinfo)3649 g_win32_app_info_get_executable (GAppInfo *appinfo)
3650 {
3651   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3652 
3653   if (info->app == NULL)
3654     return NULL;
3655 
3656   return info->app->executable;
3657 }
3658 
3659 static const char *
g_win32_app_info_get_commandline(GAppInfo * appinfo)3660 g_win32_app_info_get_commandline (GAppInfo *appinfo)
3661 {
3662   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3663 
3664   if (info->app == NULL)
3665     return NULL;
3666 
3667   return info->app->command_u8;
3668 }
3669 
3670 static GIcon *
g_win32_app_info_get_icon(GAppInfo * appinfo)3671 g_win32_app_info_get_icon (GAppInfo *appinfo)
3672 {
3673   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3674 
3675   if (info->app == NULL)
3676     return NULL;
3677 
3678   return info->app->icon;
3679 }
3680 
3681 typedef struct _file_or_uri {
3682   gchar *uri;
3683   gchar *file;
3684 } file_or_uri;
3685 
3686 static char *
expand_macro_single(char macro,file_or_uri * obj)3687 expand_macro_single (char macro, file_or_uri *obj)
3688 {
3689   char *result = NULL;
3690 
3691   switch (macro)
3692     {
3693     case '*':
3694     case '0':
3695     case '1':
3696     case 'l':
3697     case 'd':
3698     case '2':
3699     case '3':
3700     case '4':
3701     case '5':
3702     case '6':
3703     case '7':
3704     case '8':
3705     case '9':
3706       /* TODO: handle 'l' and 'd' differently (longname and desktop name) */
3707       if (obj->uri)
3708         result = g_strdup (obj->uri);
3709       else if (obj->file)
3710         result = g_strdup (obj->file);
3711       break;
3712     case 'u':
3713     case 'U':
3714       if (obj->uri)
3715         result = g_shell_quote (obj->uri);
3716       break;
3717     case 'f':
3718     case 'F':
3719       if (obj->file)
3720         result = g_shell_quote (obj->file);
3721       break;
3722     }
3723 
3724   return result;
3725 }
3726 
3727 static gboolean
expand_macro(char macro,GString * exec,GWin32AppInfo * info,GList ** stat_obj_list,GList ** obj_list)3728 expand_macro (char               macro,
3729               GString           *exec,
3730               GWin32AppInfo     *info,
3731               GList            **stat_obj_list,
3732               GList            **obj_list)
3733 {
3734   GList *objs = *obj_list;
3735   char *expanded;
3736   gboolean result = FALSE;
3737 
3738   g_return_val_if_fail (exec != NULL, FALSE);
3739 
3740 /*
3741 Legend: (from http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
3742 %* - replace with all parameters
3743 %~ - replace with all parameters starting with and following the second parameter
3744 %0 or %1 the first file parameter. For example "C:\\Users\\Eric\\Destop\\New Text Document.txt". Generally this should be in quotes and the applications command line parsing should accept quotes to disambiguate files with spaces in the name and different command line parameters (this is a security best practice and I believe mentioned in MSDN).
3745 %<n> (where N is 2 - 9), replace with the nth parameter
3746 %s - show command
3747 %h - hotkey value
3748 %i - IDList stored in a shared memory handle is passed here.
3749 %l - long file name form of the first parameter. Note win32 applications will be passed the long file name, win16 applications get the short file name. Specifying %L is preferred as it avoids the need to probe for the application type.
3750 %d - desktop absolute parsing name of the first parameter (for items that don't have file system paths)
3751 %v - for verbs that are none implies all, if there is no parameter passed this is the working directory
3752 %w - the working directory
3753 */
3754 
3755   switch (macro)
3756     {
3757     case '*':
3758     case '~':
3759       if (*stat_obj_list)
3760         {
3761           gint i;
3762           GList *o;
3763 
3764           for (o = *stat_obj_list, i = 0;
3765                macro == '~' && o && i < 2;
3766                o = o->next, i++);
3767 
3768           for (; o; o = o->next)
3769             {
3770               expanded = expand_macro_single (macro, o->data);
3771 
3772               if (expanded)
3773                 {
3774                   if (o != *stat_obj_list)
3775                     g_string_append (exec, " ");
3776 
3777                   g_string_append (exec, expanded);
3778                   g_free (expanded);
3779                 }
3780             }
3781 
3782           objs = NULL;
3783           result = TRUE;
3784         }
3785       break;
3786     case '0':
3787     case '1':
3788     case 'l':
3789     case 'd':
3790       if (*stat_obj_list)
3791         {
3792           GList *o;
3793 
3794           o = *stat_obj_list;
3795 
3796           if (o)
3797             {
3798               expanded = expand_macro_single (macro, o->data);
3799 
3800               if (expanded)
3801                 {
3802                   if (o != *stat_obj_list)
3803                     g_string_append (exec, " ");
3804 
3805                   g_string_append (exec, expanded);
3806                   g_free (expanded);
3807                 }
3808             }
3809 
3810           if (objs)
3811             objs = objs->next;
3812 
3813           result = TRUE;
3814         }
3815       break;
3816     case '2':
3817     case '3':
3818     case '4':
3819     case '5':
3820     case '6':
3821     case '7':
3822     case '8':
3823     case '9':
3824       if (*stat_obj_list)
3825         {
3826           gint i;
3827           GList *o;
3828           gint n;
3829 
3830           switch (macro)
3831             {
3832             case '2':
3833               n = 2;
3834               break;
3835             case '3':
3836               n = 3;
3837               break;
3838             case '4':
3839               n = 4;
3840               break;
3841             case '5':
3842               n = 5;
3843               break;
3844             case '6':
3845               n = 6;
3846               break;
3847             case '7':
3848               n = 7;
3849               break;
3850             case '8':
3851               n = 8;
3852               break;
3853             case '9':
3854               n = 9;
3855               break;
3856             }
3857 
3858           for (o = *stat_obj_list, i = 0; o && i < n; o = o->next, i++);
3859 
3860           if (o)
3861             {
3862               expanded = expand_macro_single (macro, o->data);
3863 
3864               if (expanded)
3865                 {
3866                   if (o != *stat_obj_list)
3867                     g_string_append (exec, " ");
3868 
3869                   g_string_append (exec, expanded);
3870                   g_free (expanded);
3871                 }
3872             }
3873           result = TRUE;
3874 
3875           if (objs)
3876             objs = NULL;
3877         }
3878       break;
3879     case 's':
3880       break;
3881     case 'h':
3882       break;
3883     case 'i':
3884       break;
3885     case 'v':
3886       break;
3887     case 'w':
3888       expanded = g_get_current_dir ();
3889       g_string_append (exec, expanded);
3890       g_free (expanded);
3891       break;
3892     case 'u':
3893     case 'f':
3894       if (objs)
3895         {
3896           expanded = expand_macro_single (macro, objs->data);
3897 
3898           if (expanded)
3899             {
3900               g_string_append (exec, expanded);
3901               g_free (expanded);
3902             }
3903           objs = objs->next;
3904           result = TRUE;
3905         }
3906 
3907       break;
3908 
3909     case 'U':
3910     case 'F':
3911       while (objs)
3912         {
3913           expanded = expand_macro_single (macro, objs->data);
3914 
3915           if (expanded)
3916             {
3917               g_string_append (exec, expanded);
3918               g_free (expanded);
3919             }
3920 
3921           objs = objs->next;
3922           result = TRUE;
3923 
3924           if (objs != NULL && expanded)
3925             g_string_append_c (exec, ' ');
3926         }
3927 
3928       break;
3929 
3930     case 'c':
3931       if (info->app && info->app->localized_pretty_name_u8)
3932         {
3933           expanded = g_shell_quote (info->app->localized_pretty_name_u8);
3934           g_string_append (exec, expanded);
3935           g_free (expanded);
3936         }
3937       break;
3938 
3939     case 'm': /* deprecated */
3940     case 'n': /* deprecated */
3941     case 'N': /* deprecated */
3942     /*case 'd': *//* deprecated */
3943     case 'D': /* deprecated */
3944       break;
3945 
3946     case '%':
3947       g_string_append_c (exec, '%');
3948       break;
3949     }
3950 
3951   *obj_list = objs;
3952 
3953   return result;
3954 }
3955 
3956 static gboolean
expand_application_parameters(GWin32AppInfo * info,const gchar * exec_line,GList ** objs,int * argc,char *** argv,GError ** error)3957 expand_application_parameters (GWin32AppInfo   *info,
3958                                const gchar     *exec_line,
3959                                GList          **objs,
3960                                int             *argc,
3961                                char          ***argv,
3962                                GError         **error)
3963 {
3964   GList *obj_list = *objs;
3965   GList **stat_obj_list = objs;
3966   const char *p = exec_line;
3967   GString *expanded_exec;
3968   gboolean res;
3969   gchar *a_char;
3970 
3971   if (exec_line == NULL)
3972     {
3973       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3974                            P_("Application registry did not specify"
3975                               " a shell\\open\\command"));
3976       return FALSE;
3977     }
3978 
3979   expanded_exec = g_string_new (NULL);
3980   res = FALSE;
3981 
3982   while (*p)
3983     {
3984       if (p[0] == '%' && p[1] != '\0')
3985         {
3986           if (expand_macro (p[1],
3987                             expanded_exec,
3988                             info, stat_obj_list,
3989                             objs))
3990             res = TRUE;
3991 
3992           p++;
3993         }
3994       else
3995         g_string_append_c (expanded_exec, *p);
3996 
3997       p++;
3998     }
3999 
4000   /* No file substitutions */
4001   if (obj_list == *objs && obj_list != NULL && !res)
4002     {
4003       /* If there is no macro default to %f. This is also what KDE does */
4004       g_string_append_c (expanded_exec, ' ');
4005       expand_macro ('f', expanded_exec, info, stat_obj_list, objs);
4006     }
4007 
4008   /* Replace '\\' with '/', because g_shell_parse_argv considers them
4009    * to be escape sequences.
4010    */
4011   for (a_char = expanded_exec->str;
4012        a_char <= &expanded_exec->str[expanded_exec->len];
4013        a_char++)
4014     {
4015       if (*a_char == '\\')
4016         *a_char = '/';
4017     }
4018 
4019   res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
4020   g_string_free (expanded_exec, TRUE);
4021   return res;
4022 }
4023 
4024 
4025 static gchar *
get_appath_for_exe(gunichar2 * exe_basename)4026 get_appath_for_exe (gunichar2 *exe_basename)
4027 {
4028   GWin32RegistryKey *apppath_key = NULL;
4029   GWin32RegistryValueType val_type;
4030   gunichar2 *appath = NULL;
4031   gboolean got_value;
4032   gchar *result = NULL;
4033 
4034   apppath_key = _g_win32_registry_key_build_and_new_w (NULL, L"HKEY_LOCAL_MACHINE\\"
4035                                                        L"\\SOFTWARE"
4036                                                        L"\\Microsoft"
4037                                                        L"\\Windows"
4038                                                        L"\\CurrentVersion"
4039                                                        L"\\App Paths\\",
4040                                                        exe_basename, NULL);
4041 
4042   if (apppath_key == NULL)
4043     return NULL;
4044 
4045   got_value = g_win32_registry_key_get_value_w (apppath_key,
4046                                                 TRUE,
4047                                                 L"Path",
4048                                                 &val_type,
4049                                                 (void **) &appath,
4050                                                 NULL,
4051                                                 NULL);
4052 
4053   g_object_unref (apppath_key);
4054 
4055   if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
4056     result = g_utf16_to_utf8 (appath, -1, NULL,NULL, NULL);
4057 
4058   g_clear_pointer (&appath, g_free);
4059 
4060   return result;
4061 }
4062 
4063 
4064 static gboolean
g_win32_app_info_launch_internal(GWin32AppInfo * info,GList * objs,GAppLaunchContext * launch_context,GSpawnFlags spawn_flags,GError ** error)4065 g_win32_app_info_launch_internal (GWin32AppInfo      *info,
4066                                   GList              *objs,
4067                                   GAppLaunchContext  *launch_context,
4068                                   GSpawnFlags         spawn_flags,
4069                                   GError            **error)
4070 {
4071   gboolean completed = FALSE;
4072   char **argv, **envp;
4073   int argc;
4074   gchar *command;
4075   gchar *apppath;
4076   gunichar2 *exe_basename;
4077 
4078   g_return_val_if_fail (info != NULL, FALSE);
4079   g_return_val_if_fail (info->app != NULL, FALSE);
4080 
4081   argv = NULL;
4082 
4083   if (launch_context)
4084     envp = g_app_launch_context_get_environment (launch_context);
4085   else
4086     envp = g_get_environ ();
4087 
4088   command = NULL;
4089   exe_basename = NULL;
4090 
4091   if (info->handler)
4092     {
4093       if (info->handler->handler_command)
4094         {
4095           command = g_utf16_to_utf8 (info->handler->handler_command,
4096                                      -1,
4097                                      NULL,
4098                                      NULL,
4099                                      NULL);
4100           exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
4101                                           -1,
4102                                           NULL,
4103                                           NULL,
4104                                           NULL);
4105         }
4106       else if (info->handler->proxy_command)
4107         {
4108           command = g_utf16_to_utf8 (info->handler->proxy_command,
4109                                      -1,
4110                                      NULL,
4111                                      NULL,
4112                                      NULL);
4113           exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
4114                                           -1,
4115                                           NULL,
4116                                           NULL,
4117                                           NULL);
4118         }
4119     }
4120 
4121   if (command == NULL)
4122     {
4123       command = g_strdup (info->app->command_u8);
4124       exe_basename = g_utf8_to_utf16 (info->app->executable_basename,
4125                                       -1,
4126                                       NULL,
4127                                       NULL,
4128                                       NULL);
4129     }
4130 
4131   apppath = get_appath_for_exe (exe_basename);
4132 
4133   g_free (exe_basename);
4134 
4135   if (apppath)
4136     {
4137       gchar **p;
4138       gint p_index;
4139 
4140       for (p = envp, p_index = 0; p[0]; p++, p_index++)
4141         if ((p[0][0] == 'p' || p[0][0] == 'P') &&
4142             (p[0][1] == 'a' || p[0][1] == 'A') &&
4143             (p[0][2] == 't' || p[0][2] == 'T') &&
4144             (p[0][3] == 'h' || p[0][3] == 'H') &&
4145             (p[0][4] == '='))
4146           break;
4147 
4148       if (p[0] == NULL)
4149         {
4150           gchar **new_envp;
4151           new_envp = g_new (char *, g_strv_length (envp) + 2);
4152           new_envp[0] = g_strdup_printf ("PATH=%s", apppath);
4153 
4154           for (p_index = 0; p_index <= g_strv_length (envp); p_index++)
4155             new_envp[1 + p_index] = envp[p_index];
4156 
4157           g_free (envp);
4158           envp = new_envp;
4159         }
4160       else
4161         {
4162           gchar *p_path;
4163 
4164           p_path = &p[0][5];
4165 
4166           if (p_path[0] != '\0')
4167             envp[p_index] = g_strdup_printf ("PATH=%s%c%s",
4168                                              apppath,
4169                                              G_SEARCHPATH_SEPARATOR,
4170                                              p_path);
4171           else
4172             envp[p_index] = g_strdup_printf ("PATH=%s", apppath);
4173 
4174           g_free (&p_path[-5]);
4175         }
4176     }
4177 
4178   do
4179     {
4180       GPid pid;
4181 
4182       if (!expand_application_parameters (info,
4183                                           command,
4184                                           &objs,
4185                                           &argc,
4186                                           &argv,
4187                                           error))
4188         goto out;
4189 
4190       if (!g_spawn_async (NULL,
4191                           argv,
4192                           envp,
4193                           spawn_flags,
4194                           NULL,
4195                           NULL,
4196                           &pid,
4197                           error))
4198           goto out;
4199 
4200       if (launch_context != NULL)
4201         {
4202           GVariantBuilder builder;
4203           GVariant *platform_data;
4204 
4205           g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
4206           g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
4207 
4208           platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
4209           g_signal_emit_by_name (launch_context, "launched", info, platform_data);
4210           g_variant_unref (platform_data);
4211         }
4212 
4213       g_strfreev (argv);
4214       argv = NULL;
4215     }
4216   while (objs != NULL);
4217 
4218   completed = TRUE;
4219 
4220  out:
4221   g_strfreev (argv);
4222   g_strfreev (envp);
4223   g_free (command);
4224 
4225   return completed;
4226 }
4227 
4228 static void
free_file_or_uri(gpointer ptr)4229 free_file_or_uri (gpointer ptr)
4230 {
4231   file_or_uri *obj = ptr;
4232   g_free (obj->file);
4233   g_free (obj->uri);
4234   g_free (obj);
4235 }
4236 
4237 
4238 static gboolean
g_win32_app_supports_uris(GWin32AppInfoApplication * app)4239 g_win32_app_supports_uris (GWin32AppInfoApplication *app)
4240 {
4241   gssize num_of_uris_supported;
4242 
4243   if (app == NULL)
4244     return FALSE;
4245 
4246   num_of_uris_supported = (gssize) g_hash_table_size (app->supported_urls);
4247 
4248   if (g_hash_table_lookup (app->supported_urls, "file"))
4249     num_of_uris_supported -= 1;
4250 
4251   return num_of_uris_supported > 0;
4252 }
4253 
4254 
4255 static gboolean
g_win32_app_info_supports_uris(GAppInfo * appinfo)4256 g_win32_app_info_supports_uris (GAppInfo *appinfo)
4257 {
4258   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4259 
4260   if (info->app == NULL)
4261     return FALSE;
4262 
4263   return g_win32_app_supports_uris (info->app);
4264 }
4265 
4266 
4267 static gboolean
g_win32_app_info_supports_files(GAppInfo * appinfo)4268 g_win32_app_info_supports_files (GAppInfo *appinfo)
4269 {
4270   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4271 
4272   if (info->app == NULL)
4273     return FALSE;
4274 
4275   return g_hash_table_size (info->app->supported_exts) > 0;
4276 }
4277 
4278 
4279 static gboolean
g_win32_app_info_launch_uris(GAppInfo * appinfo,GList * uris,GAppLaunchContext * launch_context,GError ** error)4280 g_win32_app_info_launch_uris (GAppInfo           *appinfo,
4281                               GList              *uris,
4282                               GAppLaunchContext  *launch_context,
4283                               GError            **error)
4284 {
4285   gboolean res = FALSE;
4286   gboolean do_files;
4287   GList *objs;
4288 
4289   do_files = g_win32_app_info_supports_files (appinfo);
4290 
4291   objs = NULL;
4292   while (uris)
4293     {
4294       file_or_uri *obj;
4295       obj = g_new0 (file_or_uri, 1);
4296 
4297       if (do_files)
4298         {
4299           GFile *file;
4300           gchar *path;
4301 
4302           file = g_file_new_for_uri (uris->data);
4303           path = g_file_get_path (file);
4304           obj->file = path;
4305           g_object_unref (file);
4306         }
4307 
4308       obj->uri = g_strdup (uris->data);
4309 
4310       objs = g_list_prepend (objs, obj);
4311       uris = uris->next;
4312     }
4313 
4314   objs = g_list_reverse (objs);
4315 
4316   res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4317                                           objs,
4318                                           launch_context,
4319                                           G_SPAWN_SEARCH_PATH,
4320                                           error);
4321 
4322   g_list_free_full (objs, free_file_or_uri);
4323 
4324   return res;
4325 }
4326 
4327 static gboolean
g_win32_app_info_launch(GAppInfo * appinfo,GList * files,GAppLaunchContext * launch_context,GError ** error)4328 g_win32_app_info_launch (GAppInfo           *appinfo,
4329                          GList              *files,
4330                          GAppLaunchContext  *launch_context,
4331                          GError            **error)
4332 {
4333   gboolean res = FALSE;
4334   gboolean do_uris;
4335   GList *objs;
4336 
4337   do_uris = g_win32_app_info_supports_uris (appinfo);
4338 
4339   objs = NULL;
4340   while (files)
4341     {
4342       file_or_uri *obj;
4343       obj = g_new0 (file_or_uri, 1);
4344       obj->file = g_file_get_path (G_FILE (files->data));
4345 
4346       if (do_uris)
4347         obj->uri = g_file_get_uri (G_FILE (files->data));
4348 
4349       objs = g_list_prepend (objs, obj);
4350       files = files->next;
4351     }
4352 
4353   objs = g_list_reverse (objs);
4354 
4355   res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4356                                           objs,
4357                                           launch_context,
4358                                           G_SPAWN_SEARCH_PATH,
4359                                           error);
4360 
4361   g_list_free_full (objs, free_file_or_uri);
4362 
4363   return res;
4364 }
4365 
4366 static const char **
g_win32_app_info_get_supported_types(GAppInfo * appinfo)4367 g_win32_app_info_get_supported_types (GAppInfo *appinfo)
4368 {
4369   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4370 
4371   return (const char**) info->supported_types;
4372 }
4373 
4374 GAppInfo *
g_app_info_create_from_commandline(const char * commandline,const char * application_name,GAppInfoCreateFlags flags,GError ** error)4375 g_app_info_create_from_commandline (const char           *commandline,
4376                                     const char           *application_name,
4377                                     GAppInfoCreateFlags   flags,
4378                                     GError              **error)
4379 {
4380   GWin32AppInfo *info;
4381   GWin32AppInfoApplication *app;
4382 
4383   g_return_val_if_fail (commandline, NULL);
4384 
4385   info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
4386 
4387   app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
4388 
4389   if (application_name)
4390     {
4391       app->canonical_name = g_utf8_to_utf16 (application_name,
4392                                              -1,
4393                                              NULL,
4394                                              NULL,
4395                                              NULL);
4396       app->canonical_name_u8 = g_strdup (application_name);
4397       app->canonical_name_folded = g_utf8_casefold (application_name, -1);
4398     }
4399 
4400   app->command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
4401   app->command_u8 = g_strdup (commandline);
4402 
4403   extract_executable (app->command,
4404                       &app->executable,
4405                       &app->executable_basename,
4406                       &app->executable_folded,
4407                       NULL);
4408 
4409   app->no_open_with = FALSE;
4410   app->user_specific = FALSE;
4411   app->default_app = FALSE;
4412 
4413   info->app = app;
4414   info->handler = NULL;
4415 
4416   return G_APP_INFO (info);
4417 }
4418 
4419 /* GAppInfo interface init */
4420 
4421 static void
g_win32_app_info_iface_init(GAppInfoIface * iface)4422 g_win32_app_info_iface_init (GAppInfoIface *iface)
4423 {
4424   iface->dup = g_win32_app_info_dup;
4425   iface->equal = g_win32_app_info_equal;
4426   iface->get_id = g_win32_app_info_get_id;
4427   iface->get_name = g_win32_app_info_get_name;
4428   iface->get_description = g_win32_app_info_get_description;
4429   iface->get_executable = g_win32_app_info_get_executable;
4430   iface->get_icon = g_win32_app_info_get_icon;
4431   iface->launch = g_win32_app_info_launch;
4432   iface->supports_uris = g_win32_app_info_supports_uris;
4433   iface->supports_files = g_win32_app_info_supports_files;
4434   iface->launch_uris = g_win32_app_info_launch_uris;
4435 /*  iface->should_show = g_win32_app_info_should_show;*/
4436 /*  iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
4437 /*  iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
4438 /*  iface->add_supports_type = g_win32_app_info_add_supports_type;*/
4439 /*  iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
4440 /*  iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
4441 /*  iface->can_delete = g_win32_app_info_can_delete;*/
4442 /*  iface->do_delete = g_win32_app_info_delete;*/
4443   iface->get_commandline = g_win32_app_info_get_commandline;
4444   iface->get_display_name = g_win32_app_info_get_display_name;
4445 /*  iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
4446   iface->get_supported_types = g_win32_app_info_get_supported_types;
4447 }
4448 
4449 GAppInfo *
g_app_info_get_default_for_uri_scheme(const char * uri_scheme)4450 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
4451 {
4452   GWin32AppInfoURLSchema *scheme;
4453   char *scheme_down;
4454   GAppInfo *result;
4455 
4456   scheme_down = g_utf8_casefold (uri_scheme, -1);
4457 
4458   if (!scheme_down)
4459     return NULL;
4460 
4461   if (strcmp (scheme_down, "file") == 0)
4462     {
4463       g_free (scheme_down);
4464       return NULL;
4465     }
4466 
4467   g_win32_appinfo_init ();
4468   G_LOCK (gio_win32_appinfo);
4469 
4470   scheme = g_hash_table_lookup (urls, scheme_down);
4471   g_free (scheme_down);
4472 
4473   if (scheme)
4474     g_object_ref (scheme);
4475 
4476   G_UNLOCK (gio_win32_appinfo);
4477 
4478   result = NULL;
4479 
4480   if (scheme != NULL &&
4481       scheme->chosen_handler != NULL &&
4482       scheme->chosen_handler->app != NULL)
4483     result = g_win32_app_info_new_from_app (scheme->chosen_handler->app,
4484                                             scheme->chosen_handler);
4485 
4486   if (scheme)
4487     g_object_unref (scheme);
4488 
4489   return result;
4490 }
4491 
4492 GAppInfo *
g_app_info_get_default_for_type(const char * content_type,gboolean must_support_uris)4493 g_app_info_get_default_for_type (const char *content_type,
4494                                  gboolean    must_support_uris)
4495 {
4496   GWin32AppInfoFileExtension *ext;
4497   char *ext_down;
4498   GWin32AppInfoHandler *handler;
4499   GAppInfo *result;
4500   GWin32AppInfoApplication *app;
4501   GHashTableIter iter;
4502 
4503   ext_down = g_utf8_casefold (content_type, -1);
4504 
4505   if (!ext_down)
4506     return NULL;
4507 
4508   g_win32_appinfo_init ();
4509   G_LOCK (gio_win32_appinfo);
4510 
4511   /* Assuming that "content_type" is a file extension, not a MIME type */
4512   ext = g_hash_table_lookup (extensions, ext_down);
4513   g_free (ext_down);
4514 
4515   result = NULL;
4516 
4517   if (ext != NULL)
4518     g_object_ref (ext);
4519 
4520   G_UNLOCK (gio_win32_appinfo);
4521 
4522   if (ext != NULL)
4523     {
4524       if (ext->chosen_handler != NULL &&
4525           ext->chosen_handler->app != NULL &&
4526           (!must_support_uris ||
4527            g_win32_app_supports_uris (ext->chosen_handler->app)))
4528         result = g_win32_app_info_new_from_app (ext->chosen_handler->app,
4529                                                 ext->chosen_handler);
4530       else
4531         {
4532           g_hash_table_iter_init (&iter, ext->handlers);
4533 
4534           while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4535             {
4536               if (handler->app &&
4537                   (!must_support_uris ||
4538                    g_win32_app_supports_uris (ext->chosen_handler->app)))
4539                 {
4540                   result = g_win32_app_info_new_from_app (handler->app, handler);
4541                   break;
4542                 }
4543             }
4544 
4545           if (result == NULL)
4546             {
4547               g_hash_table_iter_init (&iter, ext->other_apps);
4548               while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
4549                 {
4550                   if (!must_support_uris ||
4551                        g_win32_app_supports_uris (ext->chosen_handler->app))
4552                     {
4553                       result = g_win32_app_info_new_from_app (app, NULL);
4554                       break;
4555                     }
4556                 }
4557             }
4558         }
4559       g_object_unref (ext);
4560     }
4561 
4562   return result;
4563 }
4564 
4565 GList *
g_app_info_get_all(void)4566 g_app_info_get_all (void)
4567 {
4568   GHashTableIter iter;
4569   gpointer value;
4570   GList *infos;
4571   GList *apps;
4572   GList *apps_i;
4573 
4574   g_win32_appinfo_init ();
4575   G_LOCK (gio_win32_appinfo);
4576 
4577   apps = NULL;
4578   g_hash_table_iter_init (&iter, apps_by_id);
4579   while (g_hash_table_iter_next (&iter, NULL, &value))
4580     apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
4581 
4582   G_UNLOCK (gio_win32_appinfo);
4583 
4584   infos = NULL;
4585   for (apps_i = apps; apps_i; apps_i = apps_i->next)
4586     infos = g_list_prepend (infos,
4587                             g_win32_app_info_new_from_app (apps_i->data, NULL));
4588 
4589   g_list_free_full (apps, g_object_unref);
4590 
4591   return infos;
4592 }
4593 
4594 GList *
g_app_info_get_all_for_type(const char * content_type)4595 g_app_info_get_all_for_type (const char *content_type)
4596 {
4597   GWin32AppInfoFileExtension *ext;
4598   char *ext_down;
4599   GWin32AppInfoHandler *handler;
4600   GWin32AppInfoApplication *app;
4601   GHashTableIter iter;
4602   GList *result;
4603 
4604   ext_down = g_utf8_casefold (content_type, -1);
4605 
4606   if (!ext_down)
4607     return NULL;
4608 
4609   g_win32_appinfo_init ();
4610   G_LOCK (gio_win32_appinfo);
4611 
4612   /* Assuming that "content_type" is a file extension, not a MIME type */
4613   ext = g_hash_table_lookup (extensions, ext_down);
4614   g_free (ext_down);
4615 
4616   result = NULL;
4617 
4618   if (ext != NULL)
4619     g_object_ref (ext);
4620 
4621   G_UNLOCK (gio_win32_appinfo);
4622 
4623   if (ext == NULL)
4624     return NULL;
4625 
4626   if (ext->chosen_handler != NULL &&
4627       ext->chosen_handler->app != NULL)
4628     result = g_list_prepend (result,
4629                              g_win32_app_info_new_from_app (ext->chosen_handler->app,
4630                                                             ext->chosen_handler));
4631 
4632   g_hash_table_iter_init (&iter, ext->handlers);
4633 
4634   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4635     {
4636       if (handler->app &&
4637           (ext->chosen_handler == NULL || ext->chosen_handler->app != handler->app))
4638           result = g_list_prepend (result,
4639                                    g_win32_app_info_new_from_app (handler->app,
4640                                                                   handler));
4641     }
4642 
4643   g_hash_table_iter_init (&iter, ext->other_apps);
4644 
4645   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
4646     {
4647       result = g_list_prepend (result, g_win32_app_info_new_from_app (app, NULL));
4648     }
4649 
4650   g_object_unref (ext);
4651 
4652   result = g_list_reverse (result);
4653 
4654   return result;
4655 }
4656 
4657 GList *
g_app_info_get_fallback_for_type(const gchar * content_type)4658 g_app_info_get_fallback_for_type (const gchar *content_type)
4659 {
4660   /* TODO: fix this once gcontenttype support is improved */
4661   return g_app_info_get_all_for_type (content_type);
4662 }
4663 
4664 GList *
g_app_info_get_recommended_for_type(const gchar * content_type)4665 g_app_info_get_recommended_for_type (const gchar *content_type)
4666 {
4667   /* TODO: fix this once gcontenttype support is improved */
4668   return g_app_info_get_all_for_type (content_type);
4669 }
4670 
4671 void
g_app_info_reset_type_associations(const char * content_type)4672 g_app_info_reset_type_associations (const char *content_type)
4673 {
4674   /* nothing to do */
4675 }
4676