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