Add a wrapper for the AddFull document portal api
[glib.git] / gio / gwin32appinfo.c
blob499bbb3513b4b6bf76a5d4d663fff8d82e8c8d89
1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright (C) 2014 Руслан Ижбулатов
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.
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.
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/>.
19 * Authors: Alexander Larsson <alexl@redhat.com>
20 * Руслан Ижбулатов <lrn1986@gmail.com>
23 #include "config.h"
25 #include <string.h>
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>
36 #include <windows.h>
38 /* We need to watch 8 places:
39 * 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
40 * (anything below that key)
41 * On change: re-enumerate subkeys, read their values.
42 * 1) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
43 * (anything below that key)
44 * On change: re-enumerate subkeys
45 * 2) HKEY_CURRENT_USER\\Software\\Clients (anything below that key)
46 * On change: re-read the whole hierarchy of handlers
47 * 3) HKEY_LOCAL_MACHINE\\Software\\Clients (anything below that key)
48 * On change: re-read the whole hierarchy of handlers
49 * 4) HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications (values of that key)
50 * On change: re-read the value list of registered applications
51 * 5) HKEY_CURRENT_USER\\Software\\RegisteredApplications (values of that key)
52 * On change: re-read the value list of registered applications
53 * 6) HKEY_CLASSES_ROOT\\Applications (anything below that key)
54 * On change: re-read the whole hierarchy of apps
55 * 7) HKEY_CLASSES_ROOT (only its subkeys)
56 * On change: re-enumerate subkeys, try to filter out wrong names.
60 typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
61 typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
62 typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
63 typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
65 typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
66 typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
67 typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
68 typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
70 struct _GWin32AppInfoURLSchemaClass
72 GObjectClass parent_class;
75 struct _GWin32AppInfoFileExtensionClass
77 GObjectClass parent_class;
80 struct _GWin32AppInfoHandlerClass
82 GObjectClass parent_class;
85 struct _GWin32AppInfoApplicationClass
87 GObjectClass parent_class;
90 struct _GWin32AppInfoURLSchema {
91 GObject parent_instance;
93 /* url schema (stuff before ':') */
94 gunichar2 *schema;
96 /* url schema (stuff before ':'), in UTF-8 */
97 gchar *schema_u8;
99 /* url schema (stuff before ':'), in UTF-8, folded */
100 gchar *schema_folded;
102 /* Handler currently selected for this schema */
103 GWin32AppInfoHandler *chosen_handler;
105 /* Maps folded handler IDs -> to other handlers for this schema */
106 GHashTable *handlers;
109 struct _GWin32AppInfoHandler {
110 GObject parent_instance;
112 /* Class name in HKCR */
113 gunichar2 *handler_id;
115 /* Handler registry key (HKCR\\handler_id). Can be used to watch this handler. */
116 GWin32RegistryKey *key;
118 /* Class name in HKCR, UTF-8, folded */
119 gchar *handler_id_folded;
121 /* shell/open/command default value for the class named by class_id */
122 gunichar2 *handler_command;
124 /* If handler_id class has no command, it might point to another class */
125 gunichar2 *proxy_id;
127 /* Proxy registry key (HKCR\\proxy_id). Can be used to watch handler's proxy. */
128 GWin32RegistryKey *proxy_key;
130 /* shell/open/command default value for the class named by proxy_id */
131 gunichar2 *proxy_command;
133 /* Executable of the program (for matching, in folded form; UTF-8) */
134 gchar *executable_folded;
136 /* Executable of the program (UTF-8) */
137 gchar *executable;
139 /* Pointer to a location within @executable */
140 gchar *executable_basename;
142 /* Icon of the application for this handler */
143 GIcon *icon;
145 /* The application that is linked to this handler. */
146 GWin32AppInfoApplication *app;
149 struct _GWin32AppInfoFileExtension {
150 GObject parent_instance;
152 /* File extension (with leading '.') */
153 gunichar2 *extension;
155 /* File extension (with leading '.'), in UTF-8 */
156 gchar *extension_u8;
158 /* handler currently selected for this extension */
159 GWin32AppInfoHandler *chosen_handler;
161 /* Maps folded handler IDs -> to other handlers for this extension */
162 GHashTable *handlers;
164 /* Maps folded app exename -> to apps that support this extension.
165 * ONLY for apps that are not reachable via handlers (i.e. apps from
166 * the HKCR/Applications, which have no handlers). */
167 GHashTable *other_apps;
170 struct _GWin32AppInfoApplication {
171 GObject parent_instance;
173 /* Canonical name (used for key names). Can be NULL. */
174 gunichar2 *canonical_name;
176 /* Canonical name (used for key names), in UTF-8. Can be NULL. */
177 gchar *canonical_name_u8;
179 /* Canonical name (used for key names), in UTF-8, folded. Can be NULL. */
180 gchar *canonical_name_folded;
182 /* Human-readable name in English. Can be NULL */
183 gunichar2 *pretty_name;
185 /* Human-readable name in English, UTF-8. Can be NULL */
186 gchar *pretty_name_u8;
188 /* Human-readable name in user's language. Can be NULL */
189 gunichar2 *localized_pretty_name;
191 /* Human-readable name in user's language, UTF-8. Can be NULL */
192 gchar *localized_pretty_name_u8;
194 /* Description, could be in user's language. Can be NULL */
195 gunichar2 *description;
197 /* Description, could be in user's language, UTF-8. Can be NULL */
198 gchar *description_u8;
200 /* shell/open/command for the application. Can be NULL (see executable). */
201 gunichar2 *command;
203 /* shell/open/command for the application. Can be NULL (see executable). */
204 gchar *command_u8;
206 /* Executable of the program (for matching, in folded form;
207 * UTF-8). Never NULL. */
208 gchar *executable_folded;
210 /* Executable of the program (UTF-8). Never NULL. */
211 gchar *executable;
213 /* Pointer to a location within @executable */
214 gchar *executable_basename;
216 /* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
217 * UTF-8, folded) -> a GWin32AppInfoHandler
218 * Schema can be used as a key in the urls hashmap.
220 GHashTable *supported_urls;
222 /* Explicitly supported extensions, hashmap from map-owned gchar ptr
223 * (.extension, UTF-8, folded) -> a GWin32AppInfoHandler
224 * Extension can be used as a key in the extensions hashmap.
226 GHashTable *supported_exts;
228 /* Icon of the application (remember, handler can have its own icon too) */
229 GIcon *icon;
231 /* Set to TRUE to prevent this app from appearing in lists of apps for
232 * opening files. This will not prevent it from appearing in lists of apps
233 * just for running, or lists of apps for opening exts/urls for which this
234 * app reports explicit support.
236 gboolean no_open_with;
238 /* Set to TRUE for applications from HKEY_CURRENT_USER.
239 * Give them priority over applications from HKEY_LOCAL_MACHINE, when all
240 * other things are equal.
242 gboolean user_specific;
244 /* Set to TRUE for applications that are machine-wide defaults (i.e. default
245 * browser) */
246 gboolean default_app;
249 #define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ())
250 #define G_WIN32_APPINFO_URL_SCHEMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema))
252 #define G_TYPE_WIN32_APPINFO_FILE_EXTENSION (g_win32_appinfo_file_extension_get_type ())
253 #define G_WIN32_APPINFO_FILE_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension))
255 #define G_TYPE_WIN32_APPINFO_HANDLER (g_win32_appinfo_handler_get_type ())
256 #define G_WIN32_APPINFO_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler))
258 #define G_TYPE_WIN32_APPINFO_APPLICATION (g_win32_appinfo_application_get_type ())
259 #define G_WIN32_APPINFO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication))
261 GType g_win32_appinfo_url_schema_get_type (void) G_GNUC_CONST;
262 GType g_win32_appinfo_file_extension_get_type (void) G_GNUC_CONST;
263 GType g_win32_appinfo_handler_get_type (void) G_GNUC_CONST;
264 GType g_win32_appinfo_application_get_type (void) G_GNUC_CONST;
266 G_DEFINE_TYPE (GWin32AppInfoURLSchema, g_win32_appinfo_url_schema, G_TYPE_OBJECT)
267 G_DEFINE_TYPE (GWin32AppInfoFileExtension, g_win32_appinfo_file_extension, G_TYPE_OBJECT)
268 G_DEFINE_TYPE (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT)
269 G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT)
271 static void
272 g_win32_appinfo_url_schema_dispose (GObject *object)
274 GWin32AppInfoURLSchema *url = G_WIN32_APPINFO_URL_SCHEMA (object);
276 g_clear_pointer (&url->schema, g_free);
277 g_clear_pointer (&url->schema_u8, g_free);
278 g_clear_pointer (&url->schema_folded, g_free);
279 g_clear_object (&url->chosen_handler);
280 g_clear_pointer (&url->handlers, g_hash_table_destroy);
281 G_OBJECT_CLASS (g_win32_appinfo_url_schema_parent_class)->dispose (object);
285 static void
286 g_win32_appinfo_handler_dispose (GObject *object)
288 GWin32AppInfoHandler *handler = G_WIN32_APPINFO_HANDLER (object);
290 g_clear_pointer (&handler->handler_id, g_free);
291 g_clear_pointer (&handler->handler_id_folded, g_free);
292 g_clear_pointer (&handler->handler_command, g_free);
293 g_clear_pointer (&handler->proxy_id, g_free);
294 g_clear_pointer (&handler->proxy_command, g_free);
295 g_clear_pointer (&handler->executable_folded, g_free);
296 g_clear_pointer (&handler->executable, g_free);
297 g_clear_object (&handler->key);
298 g_clear_object (&handler->proxy_key);
299 g_clear_object (&handler->icon);
300 g_clear_object (&handler->app);
301 G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
304 static void
305 g_win32_appinfo_file_extension_dispose (GObject *object)
307 GWin32AppInfoFileExtension *ext = G_WIN32_APPINFO_FILE_EXTENSION (object);
309 g_clear_pointer (&ext->extension, g_free);
310 g_clear_pointer (&ext->extension_u8, g_free);
311 g_clear_object (&ext->chosen_handler);
312 g_clear_pointer (&ext->handlers, g_hash_table_destroy);
313 g_clear_pointer (&ext->other_apps, g_hash_table_destroy);
314 G_OBJECT_CLASS (g_win32_appinfo_file_extension_parent_class)->dispose (object);
317 static void
318 g_win32_appinfo_application_dispose (GObject *object)
320 GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object);
322 g_clear_pointer (&app->canonical_name_u8, g_free);
323 g_clear_pointer (&app->canonical_name_folded, g_free);
324 g_clear_pointer (&app->canonical_name, g_free);
325 g_clear_pointer (&app->pretty_name, g_free);
326 g_clear_pointer (&app->localized_pretty_name, g_free);
327 g_clear_pointer (&app->description, g_free);
328 g_clear_pointer (&app->command, g_free);
329 g_clear_pointer (&app->pretty_name_u8, g_free);
330 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
331 g_clear_pointer (&app->description_u8, g_free);
332 g_clear_pointer (&app->command_u8, g_free);
333 g_clear_pointer (&app->executable_folded, g_free);
334 g_clear_pointer (&app->executable, g_free);
335 g_clear_pointer (&app->supported_urls, g_hash_table_destroy);
336 g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
337 g_clear_object (&app->icon);
338 G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
341 static void
342 g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
344 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
346 gobject_class->dispose = g_win32_appinfo_url_schema_dispose;
349 static void
350 g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klass)
352 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
354 gobject_class->dispose = g_win32_appinfo_file_extension_dispose;
357 static void
358 g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
360 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
362 gobject_class->dispose = g_win32_appinfo_handler_dispose;
365 static void
366 g_win32_appinfo_application_class_init (GWin32AppInfoApplicationClass *klass)
368 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
370 gobject_class->dispose = g_win32_appinfo_application_dispose;
373 static void
374 g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
376 self->handlers = g_hash_table_new_full (g_str_hash,
377 g_str_equal,
378 g_free,
379 g_object_unref);
382 static void
383 g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
385 self->handlers = g_hash_table_new_full (g_str_hash,
386 g_str_equal,
387 g_free,
388 g_object_unref);
389 self->other_apps = g_hash_table_new_full (g_str_hash,
390 g_str_equal,
391 g_free,
392 g_object_unref);
395 static void
396 g_win32_appinfo_handler_init (GWin32AppInfoHandler *self)
400 static void
401 g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
403 self->supported_urls = g_hash_table_new_full (g_str_hash,
404 g_str_equal,
405 g_free,
406 g_object_unref);
407 self->supported_exts = g_hash_table_new_full (g_str_hash,
408 g_str_equal,
409 g_free,
410 g_object_unref);
413 G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
415 /* Map of owned ".ext" (with '.', UTF-8, folded)
416 * to GWin32AppInfoFileExtension ptr
418 static GHashTable *extensions = NULL;
420 /* Map of owned "schema" (without ':', UTF-8, folded)
421 * to GWin32AppInfoURLSchema ptr
423 static GHashTable *urls = NULL;
425 /* Map of owned "appID" (UTF-8, folded) to
426 * GWin32AppInfoApplication ptr
428 static GHashTable *apps_by_id = NULL;
430 /* Map of owned "app.exe" (UTF-8, folded) to
431 * GWin32AppInfoApplication ptr.
432 * This map and its values are separate from apps_by_id. The fact that an app
433 * with known ID has the same executable [base]name as an app in this map does
434 * not mean that they are the same application.
436 static GHashTable *apps_by_exe = NULL;
438 /* Map of owned "handler id" (UTF-8, folded)
439 * to GWin32AppInfoHandler ptr
441 static GHashTable *handlers = NULL;
443 /* Watch this whole subtree */
444 static GWin32RegistryKey *url_associations_key;
446 /* Watch this whole subtree */
447 static GWin32RegistryKey *file_exts_key;
449 /* Watch this whole subtree */
450 static GWin32RegistryKey *user_clients_key;
452 /* Watch this whole subtree */
453 static GWin32RegistryKey *system_clients_key;
455 /* Watch this key */
456 static GWin32RegistryKey *user_registered_apps_key;
458 /* Watch this key */
459 static GWin32RegistryKey *system_registered_apps_key;
461 /* Watch this whole subtree */
462 static GWin32RegistryKey *applications_key;
464 /* Watch this key */
465 static GWin32RegistryKey *classes_root_key;
467 static gunichar2 *
468 g_wcsdup (const gunichar2 *str, gssize str_size)
470 if (str_size == -1)
472 str_size = wcslen (str) + 1;
473 str_size *= sizeof (gunichar2);
475 return g_memdup (str, str_size);
478 #define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
479 #define USER_CHOICE L"\\UserChoice"
480 #define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
481 #define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"
482 #define HKCR L"HKEY_CLASSES_ROOT\\"
483 #define HKCU L"HKEY_CURRENT_USER\\"
484 #define HKLM L"HKEY_LOCAL_MACHINE\\"
485 #define SHELL_OPEN_COMMAND L"\\shell\\open\\command"
486 #define REG_PATH_MAX 256
487 #define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
489 static gunichar2 *
490 read_resource_string (gunichar2 *res)
492 gunichar2 *id_str;
493 gunichar2 *id_str_end;
494 gunichar2 *filename_str;
495 unsigned long id;
496 HMODULE resource_module;
497 gunichar2 *buffer;
498 int string_length;
499 int buffer_length;
501 if (res == NULL || res[0] != L'@')
502 return res;
504 id_str = wcsrchr (res, L'-');
506 if (id_str == NULL || id_str[-1] != L',')
507 return res;
509 id_str += 1;
511 id = wcstoul (id_str, &id_str_end, 10);
513 if (id_str_end == id_str || id_str_end[0] != L'\0' || id == ULONG_MAX)
514 return res;
516 filename_str = &res[1];
517 id_str[-2] = L'\0';
519 resource_module = LoadLibraryExW (filename_str,
521 LOAD_LIBRARY_AS_DATAFILE |
522 LOAD_LIBRARY_AS_IMAGE_RESOURCE);
524 g_free (res);
526 if (resource_module == NULL)
527 return NULL;
529 buffer_length = 256;
530 string_length = buffer_length - 1;
532 while (TRUE)
534 buffer = g_malloc (buffer_length * sizeof (gunichar2));
535 string_length = LoadStringW (resource_module, id, buffer, buffer_length);
537 if (string_length != 0 && string_length == buffer_length - 1)
539 g_free (buffer);
540 buffer_length *= 2;
542 else
544 if (string_length == 0)
545 g_clear_pointer (&buffer, g_free);
547 break;
551 FreeLibrary (resource_module);
553 if (buffer)
555 gunichar2 *result = g_wcsdup (buffer, -1);
556 g_free (buffer);
557 return result;
560 return NULL;
563 static void
564 read_handler_icon (GWin32RegistryKey *proxy_key,
565 GWin32RegistryKey *program_key,
566 GIcon **icon_out)
568 gint counter;
569 GWin32RegistryKey *key;
571 *icon_out = NULL;
573 for (counter = 0; counter < 2; counter++)
575 GWin32RegistryKey *icon_key;
577 if (counter == 0)
578 key = proxy_key;
579 else
580 key = program_key;
582 if (!key)
583 continue;
585 icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
587 if (icon_key != NULL)
589 GWin32RegistryValueType default_type;
590 gchar *default_value;
592 if (g_win32_registry_key_get_value (icon_key,
593 TRUE,
595 &default_type,
596 (gpointer *) &default_value,
597 NULL,
598 NULL))
600 if (default_type == G_WIN32_REGISTRY_VALUE_STR ||
601 default_value[0] != '\0')
602 *icon_out = g_themed_icon_new (default_value);
604 g_clear_pointer (&default_value, g_free);
607 g_object_unref (icon_key);
610 if (*icon_out)
611 break;
615 static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
616 static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_list components);
618 static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
620 /* output_size is in *bytes*, not gunichar2s! */
621 static gboolean
622 build_registry_path (gunichar2 *output, gsize output_size, ...)
624 va_list ap;
625 gboolean result;
627 va_start (ap, output_size);
629 result = build_registry_pathv (output, output_size, ap);
631 va_end (ap);
633 return result;
636 /* output_size is in *bytes*, not gunichar2s! */
637 static gboolean
638 build_registry_pathv (gunichar2 *output, gsize output_size, va_list components)
640 va_list lentest;
641 gunichar2 *p;
642 gunichar2 *component;
643 gsize length;
645 if (output == NULL)
646 return FALSE;
648 G_VA_COPY (lentest, components);
650 for (length = 0, component = va_arg (lentest, gunichar2 *);
651 component != NULL;
652 component = va_arg (lentest, gunichar2 *))
654 length += wcslen (component);
657 va_end (lentest);
659 if ((length >= REG_PATH_MAX_SIZE) ||
660 (length * sizeof (gunichar2) >= output_size))
661 return FALSE;
663 output[0] = L'\0';
665 for (p = output, component = va_arg (components, gunichar2 *);
666 component != NULL;
667 component = va_arg (components, gunichar2 *))
669 length = wcslen (component);
670 wcscat (p, component);
671 p += length;
674 return TRUE;
678 static GWin32RegistryKey *
679 _g_win32_registry_key_build_and_new_w (GError **error, ...)
681 va_list ap;
682 gunichar2 key_path[REG_PATH_MAX_SIZE + 1];
683 GWin32RegistryKey *key;
685 va_start (ap, error);
687 key = NULL;
689 if (build_registry_pathv (key_path, sizeof (key_path), ap))
690 key = g_win32_registry_key_new_w (key_path, error);
692 va_end (ap);
694 return key;
698 static gboolean
699 utf8_and_fold (const gunichar2 *str,
700 gchar **str_u8,
701 gchar **str_u8_folded)
703 gchar *u8;
704 gchar *folded;
705 u8 = g_utf16_to_utf8 (str, -1, NULL, NULL, NULL);
707 if (u8 == NULL)
708 return FALSE;
710 folded = g_utf8_casefold (u8, -1);
712 if (folded == NULL)
714 g_free (u8);
715 return FALSE;
718 if (str_u8)
719 *str_u8 = u8;
720 else
721 g_free (u8);
723 if (str_u8_folded)
724 *str_u8_folded = folded;
725 else
726 g_free (folded);
728 return TRUE;
732 static gboolean
733 follow_class_chain_to_handler (const gunichar2 *program_id,
734 gsize program_id_size,
735 gunichar2 **program_command,
736 GWin32RegistryKey **program_key,
737 gunichar2 **proxy_id,
738 gunichar2 **proxy_command,
739 GWin32RegistryKey **proxy_key,
740 gchar **program_id_u8,
741 gchar **program_id_folded)
743 GWin32RegistryKey *key;
744 GWin32RegistryValueType val_type;
745 gsize proxy_id_size;
746 gboolean got_value;
748 g_assert (program_id && program_command && proxy_id && proxy_command);
750 *program_command = NULL;
751 *proxy_id = NULL;
752 *proxy_command = NULL;
754 if (program_key)
755 *program_key = NULL;
757 if (proxy_key)
758 *proxy_key = NULL;
761 key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id,
762 SHELL_OPEN_COMMAND, NULL);
764 if (key != NULL)
766 got_value = g_win32_registry_key_get_value_w (key,
767 TRUE,
768 L"",
769 &val_type,
770 (void **) program_command,
771 NULL,
772 NULL);
773 if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
775 if ((program_id_u8 != NULL || program_id_folded != NULL) &&
776 !utf8_and_fold (program_id, program_id_u8, program_id_folded))
778 g_object_unref (key);
779 g_free (program_command);
781 return FALSE;
783 if (program_key == NULL)
784 g_object_unref (key);
785 else
786 *program_key = key;
788 return TRUE;
790 else if (got_value)
791 g_clear_pointer (program_command, g_free);
793 g_object_unref (key);
796 key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, NULL);
798 if (key == NULL)
799 return FALSE;
801 got_value = g_win32_registry_key_get_value_w (key,
802 TRUE,
803 L"",
804 &val_type,
805 (void **) proxy_id,
806 &proxy_id_size,
807 NULL);
808 if (!got_value ||
809 (val_type != G_WIN32_REGISTRY_VALUE_STR))
811 g_object_unref (key);
812 g_clear_pointer (proxy_id, g_free);
813 return FALSE;
816 if (proxy_key)
817 *proxy_key = key;
818 else
819 g_object_unref (key);
821 key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id,
822 SHELL_OPEN_COMMAND, NULL);
824 if (key == NULL)
826 g_clear_pointer (proxy_id, g_free);
827 if (proxy_key)
828 g_clear_object (proxy_key);
829 return FALSE;
832 got_value = g_win32_registry_key_get_value_w (key,
833 TRUE,
834 L"",
835 &val_type,
836 (void **) proxy_command,
837 NULL,
838 NULL);
839 g_object_unref (key);
841 if (!got_value ||
842 val_type != G_WIN32_REGISTRY_VALUE_STR ||
843 ((program_id_u8 != NULL || program_id_folded != NULL) &&
844 !utf8_and_fold (program_id, program_id_u8, program_id_folded)))
846 g_clear_pointer (proxy_id, g_free);
847 g_clear_pointer (proxy_command, g_free);
848 if (proxy_key)
849 g_clear_object (proxy_key);
850 return FALSE;
853 return TRUE;
857 static void
858 extract_executable (gunichar2 *commandline,
859 gchar **ex_out,
860 gchar **ex_basename_out,
861 gchar **ex_folded_out,
862 gchar **ex_folded_basename_out)
864 gchar *ex;
865 gchar *ex_folded;
866 gunichar2 *p;
867 gboolean quoted;
868 size_t len;
869 size_t execlen;
870 gunichar2 *exepart;
871 gboolean found;
873 quoted = FALSE;
874 execlen = 0;
875 found = FALSE;
876 len = wcslen (commandline);
877 p = commandline;
879 while (p < &commandline[len])
881 switch (p[0])
883 case L'"':
884 quoted = !quoted;
885 break;
886 case L' ':
887 if (!quoted)
889 execlen = p - commandline;
890 p = &commandline[len];
891 found = TRUE;
893 break;
894 default:
895 break;
897 p += 1;
900 if (!found)
901 execlen = len;
903 exepart = g_wcsdup (commandline, (execlen + 1) * sizeof (gunichar2));
904 exepart[execlen] = L'\0';
906 p = &exepart[0];
908 while (execlen > 0 && exepart[0] == L'"' && exepart[execlen - 1] == L'"')
910 p = &exepart[1];
911 exepart[execlen - 1] = L'\0';
912 execlen -= 2;
915 if (!utf8_and_fold (p, &ex, &ex_folded))
916 /* Currently no code to handle this case. It shouldn't happen though... */
917 g_assert_not_reached ();
919 g_free (exepart);
921 if (ex_out)
923 *ex_out = ex;
925 if (ex_basename_out)
927 *ex_basename_out = &ex[strlen (ex) - 1];
929 while (*ex_basename_out > ex)
931 if ((*ex_basename_out)[0] == '/' ||
932 (*ex_basename_out)[0] == '\\')
934 *ex_basename_out += 1;
935 break;
938 *ex_basename_out -= 1;
942 else
944 g_free (ex);
947 if (ex_folded_out)
949 *ex_folded_out = ex_folded;
951 if (ex_folded_basename_out)
953 *ex_folded_basename_out = &ex_folded[strlen (ex_folded) - 1];
955 while (*ex_folded_basename_out > ex_folded)
957 if ((*ex_folded_basename_out)[0] == '/' ||
958 (*ex_folded_basename_out)[0] == '\\')
960 *ex_folded_basename_out += 1;
961 break;
964 *ex_folded_basename_out -= 1;
968 else
970 g_free (ex_folded);
974 static void
975 get_url_association (const gunichar2 *schema)
977 GWin32AppInfoURLSchema *schema_rec;
978 GWin32AppInfoHandler *handler_rec;
979 GWin32AppInfoHandler *handler_rec_in_url;
980 gchar *schema_u8;
981 gchar *schema_folded;
982 GWin32RegistryKey *user_choice;
983 GWin32RegistryValueType val_type;
984 gunichar2 *program_id;
985 gsize program_id_size;
986 gunichar2 *program_command;
987 gunichar2 *proxy_id;
988 gunichar2 *proxy_command;
989 gchar *program_id_u8;
990 gchar *program_id_folded;
991 GWin32RegistryKey *program_key;
992 GWin32RegistryKey *proxy_key;
994 user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
995 schema, USER_CHOICE,
996 NULL);
998 if (user_choice == NULL)
999 return;
1001 if (!utf8_and_fold (schema, &schema_u8, &schema_folded))
1003 g_object_unref (user_choice);
1004 return;
1007 schema_rec = g_hash_table_lookup (urls, schema_folded);
1009 if (!g_win32_registry_key_get_value_w (user_choice,
1010 TRUE,
1011 L"Progid",
1012 &val_type,
1013 (void **) &program_id,
1014 &program_id_size,
1015 NULL))
1017 g_free (schema_u8);
1018 g_free (schema_folded);
1019 g_object_unref (user_choice);
1020 return;
1023 if (val_type != G_WIN32_REGISTRY_VALUE_STR)
1025 g_free (schema_u8);
1026 g_free (schema_folded);
1027 g_free (program_id);
1028 g_object_unref (user_choice);
1029 return;
1032 program_key = proxy_key = NULL;
1033 program_command = proxy_id = proxy_command = NULL;
1035 if (!follow_class_chain_to_handler (program_id,
1036 program_id_size,
1037 &program_command,
1038 &program_key,
1039 &proxy_id,
1040 &proxy_command,
1041 &proxy_key,
1042 &program_id_u8,
1043 &program_id_folded))
1045 g_free (schema_u8);
1046 g_free (schema_folded);
1047 g_free (program_id);
1048 g_object_unref (user_choice);
1049 return;
1052 handler_rec = g_hash_table_lookup (handlers, program_id_folded);
1054 if (handler_rec == NULL)
1056 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1058 handler_rec->proxy_key = proxy_key;
1059 handler_rec->key = program_key;
1060 handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
1061 handler_rec->handler_id_folded =
1062 g_strdup (program_id_folded);
1063 handler_rec->handler_command =
1064 program_command ? g_wcsdup (program_command, -1) : NULL;
1065 handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1066 handler_rec->proxy_command =
1067 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1068 extract_executable (proxy_command ? proxy_command : program_command,
1069 &handler_rec->executable,
1070 &handler_rec->executable_basename,
1071 &handler_rec->executable_folded,
1072 NULL);
1073 read_handler_icon (proxy_key, program_key, &handler_rec->icon);
1074 g_hash_table_insert (handlers,
1075 g_strdup (program_id_folded),
1076 handler_rec);
1078 else
1080 g_clear_object (&program_key);
1081 g_clear_object (&proxy_key);
1084 if (schema_rec == NULL)
1086 schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
1087 schema_rec->schema = g_wcsdup (schema, -1);
1088 schema_rec->schema_u8 = g_strdup (schema_u8);
1089 schema_rec->schema_folded = g_strdup (schema_folded);
1090 schema_rec->chosen_handler = g_object_ref (handler_rec);
1091 g_hash_table_insert (urls, g_strdup (schema_folded), schema_rec);
1094 if (schema_rec->chosen_handler == NULL)
1095 schema_rec->chosen_handler = g_object_ref (handler_rec);
1097 handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
1098 program_id_folded);
1100 if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
1101 g_hash_table_insert (schema_rec->handlers,
1102 g_strdup (program_id_folded),
1103 g_object_ref (handler_rec));
1105 g_free (schema_u8);
1106 g_free (schema_folded);
1107 g_free (program_id);
1108 g_free (program_id_u8);
1109 g_free (program_id_folded);
1110 g_free (program_command);
1111 g_free (proxy_id);
1112 g_free (proxy_command);
1113 g_object_unref (user_choice);
1116 static void
1117 get_file_ext (const gunichar2 *ext)
1119 GWin32AppInfoFileExtension *file_extn;
1120 gboolean file_ext_known;
1121 GWin32AppInfoHandler *handler_rec;
1122 GWin32AppInfoHandler *handler_rec_in_ext;
1123 gchar *ext_u8;
1124 gchar *ext_folded;
1125 GWin32RegistryKey *user_choice;
1126 GWin32RegistryKey *open_with_progids;
1127 GWin32RegistryValueType val_type;
1128 gsize program_id_size;
1129 gboolean found_handler;
1130 gunichar2 *program_id;
1131 gunichar2 *proxy_id;
1132 gchar *program_id_u8;
1133 gchar *program_id_folded;
1134 GWin32RegistryKey *program_key;
1135 GWin32RegistryKey *proxy_key;
1136 gunichar2 *program_command;
1137 gunichar2 *proxy_command;
1139 open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
1140 ext,
1141 OPEN_WITH_PROGIDS,
1142 NULL);
1144 user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, ext,
1145 USER_CHOICE, NULL);
1147 if (user_choice == NULL && open_with_progids == NULL)
1148 return;
1150 if (!utf8_and_fold (ext, &ext_u8, &ext_folded))
1152 g_clear_object (&user_choice);
1153 g_clear_object (&open_with_progids);
1154 return;
1157 file_extn = NULL;
1158 file_ext_known = g_hash_table_lookup_extended (extensions,
1159 ext_folded,
1160 NULL,
1161 (void **) &file_extn);
1163 if (!file_ext_known)
1164 file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1166 found_handler = FALSE;
1168 if (user_choice != NULL)
1170 if (g_win32_registry_key_get_value_w (user_choice,
1171 TRUE,
1172 L"Progid",
1173 &val_type,
1174 (void **) &program_id,
1175 &program_id_size,
1176 NULL))
1178 program_key = proxy_key = NULL;
1180 if (val_type == G_WIN32_REGISTRY_VALUE_STR &&
1181 follow_class_chain_to_handler (program_id,
1182 program_id_size,
1183 &program_command,
1184 &program_key,
1185 &proxy_id,
1186 &proxy_command,
1187 &proxy_key,
1188 &program_id_u8,
1189 &program_id_folded))
1191 handler_rec = g_hash_table_lookup (handlers,
1192 program_id_folded);
1194 if (handler_rec == NULL)
1196 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER,
1197 NULL);
1198 handler_rec->proxy_key = proxy_key;
1199 handler_rec->key = program_key;
1200 handler_rec->handler_id =
1201 g_wcsdup (program_id, program_id_size);
1202 handler_rec->handler_id_folded =
1203 g_strdup (program_id_folded);
1204 handler_rec->handler_command =
1205 program_command ? g_wcsdup (program_command, -1) : NULL;
1206 handler_rec->proxy_id =
1207 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1208 handler_rec->proxy_command =
1209 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1210 extract_executable (proxy_command ? proxy_command : program_command,
1211 &handler_rec->executable,
1212 &handler_rec->executable_basename,
1213 &handler_rec->executable_folded,
1214 NULL);
1215 read_handler_icon (proxy_key,
1216 program_key,
1217 &handler_rec->icon);
1218 g_hash_table_insert (handlers,
1219 g_strdup (program_id_folded),
1220 handler_rec);
1222 else
1224 g_clear_object (&program_key);
1225 g_clear_object (&proxy_key);
1228 found_handler = TRUE;
1230 handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
1231 program_id_folded);
1233 if (file_extn->chosen_handler == NULL)
1235 g_hash_table_insert (file_extn->handlers,
1236 g_strdup (program_id_folded),
1237 g_object_ref (handler_rec));
1239 else if (handler_rec_in_ext == NULL)
1241 if (file_extn->chosen_handler->handler_id_folded &&
1242 strcmp (file_extn->chosen_handler->handler_id_folded,
1243 program_id_folded) != 0)
1244 g_hash_table_insert (file_extn->handlers,
1245 g_strdup (program_id_folded),
1246 g_object_ref (handler_rec));
1249 g_free (program_id_u8);
1250 g_free (program_id_folded);
1251 g_free (program_command);
1252 g_free (proxy_id);
1253 g_free (proxy_command);
1256 g_free (program_id);
1259 g_object_unref (user_choice);
1262 if (open_with_progids != NULL)
1264 GWin32RegistryValueIter iter;
1266 if (g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
1268 gunichar2 *value_name;
1269 gunichar2 *value_data;
1270 gsize value_name_len;
1271 gsize value_data_size;
1272 GWin32RegistryValueType value_type;
1274 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1276 gsize value_name_size;
1277 program_key = proxy_key = NULL;
1279 if ((!g_win32_registry_value_iter_get_value_type (&iter,
1280 &value_type,
1281 NULL)) ||
1282 ((val_type != G_WIN32_REGISTRY_VALUE_STR) &&
1283 (val_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
1284 (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
1285 &value_name_len,
1286 NULL)) ||
1287 (value_name_len <= 0) ||
1288 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1289 (void **) &value_data,
1290 &value_data_size,
1291 NULL)) ||
1292 (value_data_size < sizeof (gunichar2)) ||
1293 (value_data[0] == L'\0'))
1294 continue;
1296 value_name_size = sizeof (gunichar2) * (value_name_len + 1);
1298 if (!follow_class_chain_to_handler (value_name,
1299 value_name_size,
1300 &program_command,
1301 &program_key,
1302 &proxy_id,
1303 &proxy_command,
1304 &proxy_key,
1305 &program_id_u8,
1306 &program_id_folded))
1307 continue;
1309 handler_rec = g_hash_table_lookup (handlers,
1310 program_id_folded);
1312 if (handler_rec == NULL)
1314 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1316 handler_rec->proxy_key = proxy_key;
1317 handler_rec->key = program_key;
1318 handler_rec->handler_id =
1319 g_wcsdup (value_name, value_name_size);
1320 handler_rec->handler_id_folded =
1321 g_strdup (program_id_folded);
1322 handler_rec->handler_command =
1323 program_command ? g_wcsdup (program_command, -1) : NULL;
1324 handler_rec->proxy_id =
1325 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1326 handler_rec->proxy_command =
1327 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1328 extract_executable (proxy_command ? proxy_command : program_command,
1329 &handler_rec->executable,
1330 &handler_rec->executable_basename,
1331 &handler_rec->executable_folded,
1332 NULL);
1333 read_handler_icon (proxy_key,
1334 program_key,
1335 &handler_rec->icon);
1336 g_hash_table_insert (handlers,
1337 g_strdup (program_id_folded),
1338 handler_rec);
1340 else
1342 g_clear_object (&program_key);
1343 g_clear_object (&proxy_key);
1346 found_handler = TRUE;
1348 handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
1349 program_id_folded);
1351 if (handler_rec_in_ext == NULL)
1353 if (file_extn->chosen_handler == NULL)
1354 g_hash_table_insert (file_extn->handlers,
1355 g_strdup (program_id_folded),
1356 g_object_ref (handler_rec));
1357 else if (file_extn->chosen_handler->handler_id_folded &&
1358 strcmp (file_extn->chosen_handler->handler_id_folded,
1359 program_id_folded) != 0)
1360 g_hash_table_insert (file_extn->handlers,
1361 g_strdup (program_id_folded),
1362 g_object_ref (handler_rec));
1365 g_free (program_id_u8);
1366 g_free (program_id_folded);
1367 g_free (program_command);
1368 g_free (proxy_id);
1369 g_free (proxy_command);
1372 g_win32_registry_value_iter_clear (&iter);
1375 g_object_unref (open_with_progids);
1378 if (!found_handler)
1380 if (!file_ext_known)
1381 g_object_unref (file_extn);
1383 else if (!file_ext_known)
1385 file_extn->extension = g_wcsdup (ext, -1);
1386 file_extn->extension_u8 = g_strdup (ext_u8);
1387 g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
1390 g_free (ext_u8);
1391 g_free (ext_folded);
1394 static void
1395 collect_capable_apps_from_clients (GPtrArray *capable_apps,
1396 GPtrArray *priority_capable_apps,
1397 gboolean user_registry)
1399 GWin32RegistryKey *clients;
1400 GWin32RegistrySubkeyIter clients_iter;
1402 gunichar2 *client_type_name;
1403 gsize client_type_name_len;
1406 if (user_registry)
1407 clients =
1408 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
1409 NULL);
1410 else
1411 clients =
1412 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
1413 NULL);
1415 if (clients == NULL)
1416 return;
1418 if (!g_win32_registry_subkey_iter_init (&clients_iter, clients, NULL))
1420 g_object_unref (clients);
1421 return;
1424 while (g_win32_registry_subkey_iter_next (&clients_iter, TRUE, NULL))
1426 GWin32RegistrySubkeyIter subkey_iter;
1427 GWin32RegistryKey *system_client_type;
1428 GWin32RegistryValueType default_type;
1429 gunichar2 *default_value;
1430 gunichar2 *client_name;
1431 gsize client_name_len;
1433 if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
1434 &client_type_name,
1435 &client_type_name_len,
1436 NULL))
1437 continue;
1439 system_client_type = g_win32_registry_key_get_child_w (clients,
1440 client_type_name,
1441 NULL);
1443 if (system_client_type == NULL)
1444 continue;
1446 if (g_win32_registry_key_get_value_w (system_client_type,
1447 TRUE,
1448 L"",
1449 &default_type,
1450 (gpointer *) &default_value,
1451 NULL,
1452 NULL))
1454 if (default_type != G_WIN32_REGISTRY_VALUE_STR ||
1455 default_value[0] == L'\0')
1456 g_clear_pointer (&default_value, g_free);
1459 if (!g_win32_registry_subkey_iter_init (&subkey_iter,
1460 system_client_type,
1461 NULL))
1463 g_clear_pointer (&default_value, g_free);
1464 g_object_unref (system_client_type);
1465 continue;
1468 while (g_win32_registry_subkey_iter_next (&subkey_iter, TRUE, NULL))
1470 GWin32RegistryKey *system_client;
1471 GWin32RegistryKey *system_client_assoc;
1472 gboolean add;
1473 gunichar2 *keyname;
1475 if (!g_win32_registry_subkey_iter_get_name_w (&subkey_iter,
1476 &client_name,
1477 &client_name_len,
1478 NULL))
1479 continue;
1481 system_client = g_win32_registry_key_get_child_w (system_client_type,
1482 client_name,
1483 NULL);
1485 if (system_client == NULL)
1486 continue;
1488 add = FALSE;
1490 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1491 L"Capabilities\\FileAssociations",
1492 NULL);
1494 if (system_client_assoc != NULL)
1496 add = TRUE;
1497 g_object_unref (system_client_assoc);
1499 else
1501 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1502 L"Capabilities\\UrlAssociations",
1503 NULL);
1505 if (system_client_assoc != NULL)
1507 add = TRUE;
1508 g_object_unref (system_client_assoc);
1512 if (add)
1514 keyname = g_wcsdup (g_win32_registry_key_get_path_w (system_client), -1);
1516 if (default_value && wcscmp (default_value, client_name) == 0)
1517 g_ptr_array_add (priority_capable_apps, keyname);
1518 else
1519 g_ptr_array_add (capable_apps, keyname);
1522 g_object_unref (system_client);
1525 g_win32_registry_subkey_iter_clear (&subkey_iter);
1526 g_clear_pointer (&default_value, g_free);
1527 g_object_unref (system_client_type);
1530 g_win32_registry_subkey_iter_clear (&clients_iter);
1531 g_object_unref (clients);
1534 static void
1535 collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
1536 gboolean user_registry)
1538 GWin32RegistryValueIter iter;
1540 gunichar2 *value_data;
1541 gsize value_data_size;
1542 GWin32RegistryValueType value_type;
1543 GWin32RegistryKey *registered_apps;
1545 if (user_registry)
1546 registered_apps =
1547 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
1548 NULL);
1549 else
1550 registered_apps =
1551 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
1552 NULL);
1554 if (!registered_apps)
1555 return;
1557 if (!g_win32_registry_value_iter_init (&iter, registered_apps, NULL))
1559 g_object_unref (registered_apps);
1560 return;
1563 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1565 gunichar2 possible_location[REG_PATH_MAX_SIZE + 1];
1566 GWin32RegistryKey *location = NULL;
1568 if ((!g_win32_registry_value_iter_get_value_type (&iter,
1569 &value_type,
1570 NULL)) ||
1571 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
1572 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1573 (void **) &value_data,
1574 &value_data_size,
1575 NULL)) ||
1576 (value_data_size < sizeof (gunichar2)) ||
1577 (value_data[0] == L'\0'))
1578 continue;
1580 if (build_registry_path (possible_location, sizeof (possible_location),
1581 HKCU, value_data, NULL))
1582 location = g_win32_registry_key_new_w (possible_location, NULL);
1584 if (location)
1586 gunichar2 *p = wcsrchr (possible_location, L'\\');
1588 if (p)
1589 *p = L'\0';
1591 g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
1592 g_object_unref (location);
1593 continue;
1596 if (!build_registry_path (possible_location, sizeof (possible_location),
1597 user_registry ? HKCU : HKLM, value_data, NULL))
1598 continue;
1600 location = g_win32_registry_key_new_w (possible_location, NULL);
1602 if (location)
1604 gunichar2 *p = wcsrchr (possible_location, L'\\');
1605 if (p)
1606 *p = L'\0';
1607 g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
1608 g_object_unref (location);
1612 g_win32_registry_value_iter_clear (&iter);
1613 g_object_unref (registered_apps);
1616 static void
1617 read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolean default_app)
1619 GWin32AppInfoApplication *app;
1620 gunichar2 *app_key_path;
1621 gunichar2 *canonical_name;
1622 gchar *canonical_name_u8;
1623 gchar *canonical_name_folded;
1624 GWin32RegistryKey *appkey;
1625 gunichar2 *fallback_friendly_name;
1626 GWin32RegistryValueType vtype;
1627 gboolean success;
1628 gunichar2 *friendly_name;
1629 gunichar2 *description;
1630 gunichar2 *narrow_application_name;
1631 gunichar2 *icon_source;
1632 GWin32RegistryKey *capabilities;
1633 GWin32RegistryKey *default_icon_key;
1634 GWin32RegistryKey *shell_open_command_key;
1635 gunichar2 *shell_open_command;
1636 gchar *app_executable;
1637 gchar *app_executable_basename;
1638 gchar *app_executable_folded;
1639 gchar *app_executable_folded_basename;
1640 GWin32RegistryKey *associations;
1642 app_key_path = g_wcsdup (input_app_key_path, -1);
1644 canonical_name = wcsrchr (app_key_path, L'\\');
1646 if (canonical_name == NULL)
1648 /* The key must have at least one '\\' */
1649 g_free (app_key_path);
1650 return;
1653 canonical_name += 1;
1655 if (!utf8_and_fold (canonical_name, &canonical_name_u8, &canonical_name_folded))
1657 g_free (app_key_path);
1658 return;
1661 appkey = g_win32_registry_key_new_w (app_key_path, NULL);
1663 if (appkey == NULL)
1665 g_free (canonical_name_u8);
1666 g_free (canonical_name_folded);
1667 g_free (app_key_path);
1668 return;
1671 capabilities =
1672 g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL);
1674 if (capabilities == NULL)
1676 /* Must have capabilities */
1677 g_free (canonical_name_u8);
1678 g_free (canonical_name_folded);
1679 g_free (app_key_path);
1680 return;
1683 shell_open_command_key =
1684 g_win32_registry_key_get_child_w (appkey,
1685 L"shell\\open\\command",
1686 NULL);
1688 if (shell_open_command_key == NULL)
1690 g_object_unref (capabilities);
1691 g_free (canonical_name_u8);
1692 g_free (canonical_name_folded);
1693 g_free (app_key_path);
1694 g_object_unref (appkey);
1695 return ;
1698 shell_open_command = NULL;
1700 success = g_win32_registry_key_get_value_w (shell_open_command_key,
1701 TRUE,
1702 L"",
1703 &vtype,
1704 (gpointer *) &shell_open_command,
1705 NULL,
1706 NULL);
1708 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1710 /* Must have a command */
1711 g_clear_pointer (&shell_open_command, g_free);
1712 g_object_unref (capabilities);
1713 g_free (canonical_name_u8);
1714 g_free (canonical_name_folded);
1715 g_free (app_key_path);
1716 g_object_unref (appkey);
1717 return;
1720 extract_executable (shell_open_command,
1721 &app_executable,
1722 &app_executable_basename,
1723 &app_executable_folded,
1724 &app_executable_folded_basename);
1726 app = g_hash_table_lookup (apps_by_id, canonical_name_folded);
1728 if (app == NULL)
1730 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
1732 app->canonical_name = g_wcsdup (canonical_name, -1);
1733 app->canonical_name_u8 = g_strdup (canonical_name_u8);
1734 app->canonical_name_folded =
1735 g_strdup (canonical_name_folded);
1737 app->command = g_wcsdup (shell_open_command, -1);
1738 app->command_u8 =
1739 g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
1740 app->executable = g_strdup (app_executable);
1741 app->executable_basename =
1742 &app->executable[app_executable_basename - app_executable];
1743 app->executable_folded =
1744 g_strdup (app_executable_folded);
1746 app->no_open_with = FALSE;
1748 app->user_specific = user_specific;
1749 app->default_app = default_app;
1751 g_hash_table_insert (apps_by_id,
1752 g_strdup (canonical_name_folded),
1753 app);
1756 fallback_friendly_name = NULL;
1757 success = g_win32_registry_key_get_value_w (appkey,
1758 TRUE,
1759 L"",
1760 &vtype,
1761 (void **) &fallback_friendly_name,
1762 NULL,
1763 NULL);
1765 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1766 g_clear_pointer (&fallback_friendly_name, g_free);
1768 if (fallback_friendly_name && app->pretty_name == NULL)
1770 app->pretty_name = g_wcsdup (fallback_friendly_name, -1);
1771 g_clear_pointer (&app->pretty_name_u8, g_free);
1772 app->pretty_name_u8 = g_utf16_to_utf8 (fallback_friendly_name,
1774 NULL,
1775 NULL,
1776 NULL);
1779 friendly_name = NULL;
1780 success = g_win32_registry_key_get_value_w (capabilities,
1781 TRUE,
1782 L"LocalizedString",
1783 &vtype,
1784 (void **) &friendly_name,
1785 NULL,
1786 NULL);
1788 if (success && (vtype != G_WIN32_REGISTRY_VALUE_STR || friendly_name[0] != L'@'))
1789 g_clear_pointer (&friendly_name, g_free);
1791 friendly_name = read_resource_string (friendly_name);
1793 if (friendly_name && app->localized_pretty_name == NULL)
1795 app->localized_pretty_name = g_wcsdup (friendly_name, -1);
1796 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
1797 app->localized_pretty_name_u8 = g_utf16_to_utf8 (friendly_name,
1799 NULL,
1800 NULL,
1801 NULL);
1804 description = NULL;
1805 success = g_win32_registry_key_get_value_w (capabilities,
1806 TRUE,
1807 L"ApplicationDescription",
1808 &vtype,
1809 (void **) &description,
1810 NULL,
1811 NULL);
1813 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1814 g_clear_pointer (&description, g_free);
1816 description = read_resource_string (description);
1818 if (description && app->description == NULL)
1820 app->description = g_wcsdup (description, -1);
1821 g_clear_pointer (&app->description_u8, g_free);
1822 app->description_u8 = g_utf16_to_utf8 (description, -1, NULL, NULL, NULL);
1825 default_icon_key = g_win32_registry_key_get_child_w (appkey,
1826 L"DefaultIcon",
1827 NULL);
1829 icon_source = NULL;
1831 if (default_icon_key != NULL)
1833 success = g_win32_registry_key_get_value_w (default_icon_key,
1834 TRUE,
1835 L"",
1836 &vtype,
1837 (void **) &icon_source,
1838 NULL,
1839 NULL);
1841 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1842 g_clear_pointer (&icon_source, g_free);
1844 g_object_unref (default_icon_key);
1847 if (icon_source == NULL)
1849 success = g_win32_registry_key_get_value_w (capabilities,
1850 TRUE,
1851 L"ApplicationIcon",
1852 &vtype,
1853 (void **) &icon_source,
1854 NULL,
1855 NULL);
1857 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1858 g_clear_pointer (&icon_source, g_free);
1861 if (icon_source && app->icon == NULL)
1863 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
1864 app->icon = g_themed_icon_new (name);
1865 g_free (name);
1868 narrow_application_name = NULL;
1869 success = g_win32_registry_key_get_value_w (capabilities,
1870 TRUE,
1871 L"ApplicationName",
1872 &vtype,
1873 (void **) &narrow_application_name,
1874 NULL,
1875 NULL);
1877 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1878 g_clear_pointer (&narrow_application_name, g_free);
1880 narrow_application_name = read_resource_string (narrow_application_name);
1882 /* TODO: do something with the narrow name. Maybe make a kind of sub-app?
1883 * Narrow name is a more precise name of the application in given context.
1884 * I.e. Thunderbird's name is "Thunderbird", whereas its narrow name is
1885 * "Thunderbird (news)" when registering it as a news client.
1886 * Maybe we should consider applications with different narrow names as
1887 * different applications altogether?
1890 associations = g_win32_registry_key_get_child_w (capabilities,
1891 L"FileAssociations",
1892 NULL);
1894 if (associations != NULL)
1896 GWin32RegistryValueIter iter;
1898 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
1900 gunichar2 *file_extension;
1901 gunichar2 *extension_handler;
1902 gsize file_extension_len;
1903 gsize extension_handler_size;
1904 GWin32RegistryValueType value_type;
1906 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1908 GWin32AppInfoHandler *handler_rec;
1909 GWin32AppInfoHandler *handler_rec_in_ext;
1910 GWin32AppInfoFileExtension *ext;
1911 gunichar2 *program_command;
1912 gunichar2 *proxy_id;
1913 gunichar2 *proxy_command;
1914 GWin32RegistryKey *program_key;
1915 GWin32RegistryKey *proxy_key;
1916 gchar *program_id_u8;
1917 gchar *program_id_folded;
1918 gchar *file_extension_u8;
1919 gchar *file_extension_folded;
1921 if ((!g_win32_registry_value_iter_get_value_type (&iter,
1922 &value_type,
1923 NULL)) ||
1924 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
1925 (!g_win32_registry_value_iter_get_name_w (&iter,
1926 &file_extension,
1927 &file_extension_len,
1928 NULL)) ||
1929 (file_extension_len <= 0) ||
1930 (file_extension[0] != L'.') ||
1931 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1932 (void **) &extension_handler,
1933 &extension_handler_size,
1934 NULL)) ||
1935 (extension_handler_size < sizeof (gunichar2)) ||
1936 (extension_handler[0] == L'\0'))
1937 continue;
1939 if (!follow_class_chain_to_handler (extension_handler,
1940 extension_handler_size,
1941 &program_command,
1942 &program_key,
1943 &proxy_id,
1944 &proxy_command,
1945 &proxy_key,
1946 &program_id_u8,
1947 &program_id_folded))
1948 continue;
1950 handler_rec = g_hash_table_lookup (handlers,
1951 program_id_folded);
1953 if (handler_rec == NULL)
1955 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1957 handler_rec->proxy_key = proxy_key;
1958 handler_rec->key = program_key;
1959 handler_rec->handler_id =
1960 g_wcsdup (extension_handler,extension_handler_size);
1961 handler_rec->handler_id_folded =
1962 g_strdup (program_id_folded);
1963 handler_rec->handler_command =
1964 program_command ? g_wcsdup (program_command, -1) : NULL;
1965 handler_rec->proxy_id =
1966 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
1967 handler_rec->proxy_command =
1968 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
1969 extract_executable (proxy_command ? proxy_command : program_command,
1970 &handler_rec->executable,
1971 &handler_rec->executable_basename,
1972 &handler_rec->executable_folded,
1973 NULL);
1974 read_handler_icon (proxy_key,
1975 program_key,
1976 &handler_rec->icon);
1977 g_hash_table_insert (handlers,
1978 g_strdup (program_id_folded),
1979 handler_rec);
1981 else
1983 g_clear_object (&program_key);
1984 g_clear_object (&proxy_key);
1987 if (utf8_and_fold (file_extension,
1988 &file_extension_u8,
1989 &file_extension_folded))
1991 ext = g_hash_table_lookup (extensions,
1992 file_extension_folded);
1994 if (ext == NULL)
1996 ext = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1998 ext->extension = g_wcsdup (file_extension, -1);
1999 ext->extension_u8 = g_strdup (file_extension_u8);
2000 g_hash_table_insert (extensions, g_strdup (file_extension_folded), ext);
2003 handler_rec_in_ext =
2004 g_hash_table_lookup (ext->handlers,
2005 program_id_folded);
2007 if (handler_rec_in_ext == NULL)
2009 if (ext->chosen_handler == NULL)
2010 g_hash_table_insert (ext->handlers,
2011 g_strdup (program_id_folded),
2012 g_object_ref (handler_rec));
2013 else if (ext->chosen_handler->handler_id_folded &&
2014 strcmp (ext->chosen_handler->handler_id_folded,
2015 program_id_folded) != 0)
2016 g_hash_table_insert (ext->handlers,
2017 g_strdup (program_id_folded),
2018 g_object_ref (handler_rec));
2021 handler_rec_in_ext =
2022 g_hash_table_lookup (app->supported_exts,
2023 file_extension_folded);
2025 if (handler_rec_in_ext == NULL)
2026 g_hash_table_insert (app->supported_exts,
2027 g_strdup (file_extension_folded),
2028 g_object_ref (handler_rec));
2030 g_free (file_extension_u8);
2031 g_free (file_extension_folded);
2034 g_free (program_id_u8);
2035 g_free (program_id_folded);
2036 g_free (program_command);
2037 g_free (proxy_id);
2038 g_free (proxy_command);
2041 g_win32_registry_value_iter_clear (&iter);
2044 g_object_unref (associations);
2047 associations = g_win32_registry_key_get_child_w (capabilities, L"URLAssociations", NULL);
2049 if (associations != NULL)
2051 GWin32RegistryValueIter iter;
2053 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
2055 gunichar2 *url_schema;
2056 gunichar2 *schema_handler;
2057 gsize url_schema_len;
2058 gsize schema_handler_size;
2059 GWin32RegistryValueType value_type;
2061 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2063 GWin32AppInfoHandler *handler_rec;
2064 GWin32AppInfoHandler *handler_rec_in_url;
2065 GWin32AppInfoURLSchema *schema;
2066 gunichar2 *program_command;
2067 gunichar2 *proxy_id;
2068 gunichar2 *proxy_command;
2069 GWin32RegistryKey *program_key;
2070 GWin32RegistryKey *proxy_key;
2071 gchar *program_id_u8;
2072 gchar *program_id_folded;
2073 gchar *schema_u8;
2074 gchar *schema_folded;
2076 if ((!g_win32_registry_value_iter_get_value_type (&iter,
2077 &value_type,
2078 NULL)) ||
2079 ((value_type != G_WIN32_REGISTRY_VALUE_STR) &&
2080 (value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
2081 (!g_win32_registry_value_iter_get_name_w (&iter,
2082 &url_schema,
2083 &url_schema_len,
2084 NULL)) ||
2085 (url_schema_len <= 0) ||
2086 (url_schema[0] == L'\0') ||
2087 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2088 (void **) &schema_handler,
2089 &schema_handler_size,
2090 NULL)) ||
2091 (schema_handler_size < sizeof (gunichar2)) ||
2092 (schema_handler[0] == L'\0'))
2093 continue;
2095 if (!follow_class_chain_to_handler (schema_handler,
2096 schema_handler_size,
2097 &program_command,
2098 &program_key,
2099 &proxy_id,
2100 &proxy_command,
2101 &proxy_key,
2102 &program_id_u8,
2103 &program_id_folded))
2104 continue;
2107 handler_rec = g_hash_table_lookup (handlers, program_id_folded);
2109 if (handler_rec == NULL)
2111 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2113 handler_rec->proxy_key = proxy_key;
2114 handler_rec->key = program_key;
2115 handler_rec->handler_id =
2116 g_wcsdup (schema_handler, schema_handler_size);
2117 handler_rec->handler_id_folded =
2118 g_strdup (program_id_folded);
2119 handler_rec->handler_command = program_command ?
2120 g_wcsdup (program_command, -1) : NULL;
2121 handler_rec->proxy_id =
2122 proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2123 handler_rec->proxy_command =
2124 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2125 extract_executable (proxy_command ? proxy_command : program_command,
2126 &handler_rec->executable,
2127 &handler_rec->executable_basename,
2128 &handler_rec->executable_folded,
2129 NULL);
2130 read_handler_icon (proxy_key,
2131 program_key,
2132 &handler_rec->icon);
2133 g_hash_table_insert (handlers,
2134 g_strdup (program_id_folded),
2135 handler_rec);
2137 else
2139 g_clear_object (&program_key);
2140 g_clear_object (&proxy_key);
2143 if (utf8_and_fold (url_schema,
2144 &schema_u8,
2145 &schema_folded))
2147 schema = g_hash_table_lookup (urls,
2148 schema_folded);
2150 if (schema == NULL)
2152 schema = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
2154 schema->schema = g_wcsdup (url_schema, -1);
2155 schema->schema_u8 = g_strdup (schema_u8);
2156 schema->schema_folded =
2157 g_strdup (schema_folded);
2158 g_hash_table_insert (urls,
2159 g_strdup (schema_folded),
2160 schema);
2163 handler_rec_in_url =
2164 g_hash_table_lookup (schema->handlers,
2165 program_id_folded);
2167 if (handler_rec_in_url == NULL)
2168 g_hash_table_insert (schema->handlers,
2169 g_strdup (program_id_folded),
2170 g_object_ref (handler_rec));
2172 handler_rec_in_url =
2173 g_hash_table_lookup (app->supported_urls,
2174 schema_folded);
2176 if (handler_rec_in_url == NULL)
2177 g_hash_table_insert (app->supported_urls,
2178 g_strdup (schema_folded),
2179 g_object_ref (handler_rec));
2181 g_free (schema_u8);
2182 g_free (schema_folded);
2185 g_free (program_id_u8);
2186 g_free (program_id_folded);
2187 g_free (program_command);
2188 g_free (proxy_id);
2189 g_free (proxy_command);
2192 g_win32_registry_value_iter_clear (&iter);
2195 g_object_unref (associations);
2198 g_clear_pointer (&app_executable, g_free);
2199 g_clear_pointer (&app_executable_folded, g_free);
2200 g_clear_pointer (&fallback_friendly_name, g_free);
2201 g_clear_pointer (&description, g_free);
2202 g_clear_pointer (&icon_source, g_free);
2203 g_clear_pointer (&narrow_application_name, g_free);
2204 g_clear_pointer (&shell_open_command, g_free);
2206 g_object_unref (appkey);
2207 g_object_unref (shell_open_command_key);
2208 g_object_unref (capabilities);
2209 g_free (canonical_name_u8);
2210 g_free (canonical_name_folded);
2211 g_free (app_key_path);
2214 static void
2215 read_urls (GWin32RegistryKey *url_associations)
2217 GWin32RegistrySubkeyIter url_iter;
2218 gunichar2 *url_schema;
2219 gsize url_schema_len;
2221 if (url_associations == NULL)
2222 return;
2224 if (!g_win32_registry_subkey_iter_init (&url_iter, url_associations, NULL))
2225 return;
2227 while (g_win32_registry_subkey_iter_next (&url_iter, TRUE, NULL))
2229 if (!g_win32_registry_subkey_iter_get_name_w (&url_iter,
2230 &url_schema,
2231 &url_schema_len,
2232 NULL))
2233 continue;
2235 get_url_association (url_schema);
2238 g_win32_registry_subkey_iter_clear (&url_iter);
2241 static void
2242 read_exeapps (void)
2244 GWin32RegistryKey *applications_key;
2245 GWin32RegistrySubkeyIter app_iter;
2246 gunichar2 *app_exe_basename;
2247 gsize app_exe_basename_len;
2249 applications_key =
2250 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
2252 if (applications_key == NULL)
2253 return;
2255 if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
2257 g_object_unref (applications_key);
2258 return;
2261 while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
2263 GWin32RegistryKey *incapable_app;
2264 gunichar2 *friendly_app_name;
2265 gboolean success;
2266 gboolean no_open_with;
2267 GWin32RegistryValueType vtype;
2268 GWin32RegistryKey *default_icon_key;
2269 gunichar2 *icon_source;
2270 GIcon *icon = NULL;
2271 gchar *appexe;
2272 gchar *appexe_basename;
2273 gchar *appexe_folded;
2274 gchar *appexe_folded_basename;
2275 GWin32AppInfoApplication *app;
2276 GWin32RegistryKey *shell_open_command_key;
2277 gunichar2 *shell_open_command;
2278 GWin32RegistryKey *supported_key;
2280 if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
2281 &app_exe_basename,
2282 &app_exe_basename_len,
2283 NULL))
2284 continue;
2286 incapable_app =
2287 g_win32_registry_key_get_child_w (applications_key,
2288 app_exe_basename,
2289 NULL);
2291 if (incapable_app == NULL)
2292 continue;
2294 extract_executable (app_exe_basename,
2295 &appexe,
2296 &appexe_basename,
2297 &appexe_folded,
2298 &appexe_folded_basename);
2300 shell_open_command_key =
2301 g_win32_registry_key_get_child_w (incapable_app,
2302 L"shell\\open\\command",
2303 NULL);
2305 shell_open_command = NULL;
2307 if (shell_open_command_key != NULL)
2309 success = g_win32_registry_key_get_value_w (shell_open_command_key,
2310 TRUE,
2311 L"",
2312 &vtype,
2313 (gpointer *) &shell_open_command,
2314 NULL,
2315 NULL);
2317 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2319 g_clear_pointer (&shell_open_command, g_free);
2322 g_object_unref (shell_open_command_key);
2325 friendly_app_name = NULL;
2326 success = g_win32_registry_key_get_value_w (incapable_app,
2327 TRUE,
2328 L"FriendlyAppName",
2329 &vtype,
2330 (void **) &friendly_app_name,
2331 NULL,
2332 NULL);
2334 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2335 g_clear_pointer (&friendly_app_name, g_free);
2337 friendly_app_name = read_resource_string (friendly_app_name);
2339 no_open_with = FALSE;
2340 success = g_win32_registry_key_get_value_w (incapable_app,
2341 TRUE,
2342 L"NoOpenWith",
2343 &vtype,
2344 NULL,
2345 NULL,
2346 NULL);
2348 if (success)
2349 no_open_with = TRUE;
2351 default_icon_key =
2352 g_win32_registry_key_get_child_w (incapable_app,
2353 L"DefaultIcon",
2354 NULL);
2356 icon_source = NULL;
2358 if (default_icon_key != NULL)
2360 success =
2361 g_win32_registry_key_get_value_w (default_icon_key,
2362 TRUE,
2363 L"",
2364 &vtype,
2365 (void **) &icon_source,
2366 NULL,
2367 NULL);
2369 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2370 g_clear_pointer (&icon_source, g_free);
2372 g_object_unref (default_icon_key);
2375 if (icon_source)
2377 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
2378 icon = g_themed_icon_new (name);
2379 g_free (name);
2382 app = g_hash_table_lookup (apps_by_exe, appexe_folded_basename);
2384 if (app == NULL)
2386 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
2388 app->command =
2389 shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL;
2391 if (shell_open_command)
2392 app->command_u8 = g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
2394 app->executable = g_strdup (appexe);
2395 app->executable_basename = &app->executable[appexe_basename - appexe];
2396 app->executable_folded = g_strdup (appexe_folded);
2398 app->no_open_with = no_open_with;
2400 if (friendly_app_name)
2402 app->localized_pretty_name = g_wcsdup (friendly_app_name, -1);
2403 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2404 app->localized_pretty_name_u8 =
2405 g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL);
2408 if (icon)
2409 app->icon = g_object_ref (icon);
2411 app->user_specific = FALSE;
2412 app->default_app = FALSE;
2414 g_hash_table_insert (apps_by_exe,
2415 g_strdup (appexe_folded_basename),
2416 app);
2419 supported_key =
2420 g_win32_registry_key_get_child_w (incapable_app,
2421 L"SupportedTypes",
2422 NULL);
2424 if (supported_key)
2426 GWin32RegistryValueIter sup_iter;
2427 if (g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
2429 gunichar2 *ext_name;
2430 gsize ext_name_len;
2432 while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
2434 gchar *ext_u8;
2435 gchar *ext_folded;
2436 GWin32AppInfoFileExtension *file_extn;
2437 gboolean file_ext_known;
2439 if ((!g_win32_registry_value_iter_get_name_w (&sup_iter,
2440 &ext_name,
2441 &ext_name_len,
2442 NULL)) ||
2443 (ext_name_len <= 0) ||
2444 (ext_name[0] != L'.') ||
2445 (!utf8_and_fold (ext_name,
2446 &ext_u8,
2447 &ext_folded)))
2448 continue;
2450 file_extn = NULL;
2451 file_ext_known =
2452 g_hash_table_lookup_extended (extensions,
2453 ext_folded,
2454 NULL,
2455 (void **) &file_extn);
2457 if (!file_ext_known)
2459 file_extn =
2460 g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
2461 file_extn->extension = g_wcsdup (ext_name, -1);
2462 file_extn->extension_u8 = g_strdup (ext_u8);
2463 g_hash_table_insert (extensions,
2464 g_strdup (ext_folded),
2465 file_extn);
2468 g_hash_table_insert (file_extn->other_apps,
2469 g_strdup (appexe_folded),
2470 g_object_ref (app));
2472 g_free (ext_u8);
2473 g_free (ext_folded);
2476 g_win32_registry_value_iter_clear (&sup_iter);
2479 g_object_unref (supported_key);
2483 g_free (appexe);
2484 g_free (appexe_folded);
2485 g_free (shell_open_command);
2486 g_free (friendly_app_name);
2487 g_free (icon_source);
2489 g_clear_object (&icon);
2490 g_clear_object (&incapable_app);
2493 g_win32_registry_subkey_iter_clear (&app_iter);
2494 g_object_unref (applications_key);
2498 static void
2499 read_exts (GWin32RegistryKey *file_exts)
2501 GWin32RegistrySubkeyIter ext_iter;
2502 gunichar2 *file_extension;
2503 gsize file_extension_len;
2505 if (file_exts == NULL)
2506 return;
2508 if (!g_win32_registry_subkey_iter_init (&ext_iter, file_exts, NULL))
2509 return;
2511 while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
2513 if (!g_win32_registry_subkey_iter_get_name_w (&ext_iter,
2514 &file_extension,
2515 &file_extension_len,
2516 NULL))
2517 continue;
2519 get_file_ext (file_extension);
2522 g_win32_registry_subkey_iter_clear (&ext_iter);
2525 static void
2526 read_class_extension (GWin32RegistryKey *classes_root,
2527 gunichar2 *class_name,
2528 gsize class_name_len)
2530 gchar *ext_u8;
2531 gchar *ext_folded;
2532 GWin32AppInfoFileExtension *file_extn;
2533 GWin32AppInfoHandler *handler_rec;
2534 GWin32AppInfoHandler *handler_rec_in_ext;
2535 GWin32RegistryKey *class_key;
2536 gsize program_id_size;
2537 gunichar2 *program_id;
2538 gunichar2 *proxy_id;
2539 GWin32RegistryKey *program_key;
2540 GWin32RegistryKey *proxy_key;
2541 gunichar2 *program_command;
2542 gunichar2 *proxy_command;
2544 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2546 if (class_key == NULL)
2547 return;
2549 program_id = class_name;
2550 program_id_size = (class_name_len + 1) * sizeof (gunichar2);
2551 program_key = proxy_key = NULL;
2552 program_command = proxy_command = NULL;
2554 if (!follow_class_chain_to_handler (program_id,
2555 program_id_size,
2556 &program_command,
2557 &program_key,
2558 &proxy_id,
2559 &proxy_command,
2560 &proxy_key,
2561 &ext_u8,
2562 &ext_folded))
2564 g_object_unref (class_key);
2565 return;
2569 file_extn = g_hash_table_lookup (extensions, ext_folded);
2570 handler_rec = g_hash_table_lookup (handlers, ext_folded);
2572 if (file_extn == NULL)
2574 file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
2575 file_extn->extension = g_wcsdup (class_name, -1);
2576 file_extn->extension_u8 = g_strdup (ext_u8);
2577 g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
2580 if (handler_rec == NULL)
2582 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2584 handler_rec->proxy_key = proxy_key;
2585 handler_rec->key = program_key;
2586 handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
2587 handler_rec->handler_id_folded = g_strdup (ext_folded);
2588 handler_rec->handler_command =
2589 program_command ? g_wcsdup (program_command, -1) : NULL;
2590 handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2591 handler_rec->proxy_command =
2592 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2593 extract_executable (proxy_command ? proxy_command : program_command,
2594 &handler_rec->executable,
2595 &handler_rec->executable_basename,
2596 &handler_rec->executable_folded,
2597 NULL);
2598 read_handler_icon (proxy_key, program_key, &handler_rec->icon);
2599 g_hash_table_insert (handlers,
2600 g_strdup (ext_folded),
2601 handler_rec);
2603 else
2605 g_clear_object (&program_key);
2606 g_clear_object (&proxy_key);
2609 handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
2610 ext_folded);
2612 if (file_extn->chosen_handler == NULL)
2613 g_hash_table_insert (file_extn->handlers,
2614 g_strdup (ext_folded),
2615 g_object_ref (handler_rec));
2616 else if (handler_rec_in_ext == NULL)
2618 if (file_extn->chosen_handler->handler_id_folded &&
2619 strcmp (file_extn->chosen_handler->handler_id_folded,
2620 ext_folded) != 0)
2621 g_hash_table_insert (file_extn->handlers,
2622 g_strdup (ext_folded),
2623 g_object_ref (handler_rec));
2626 g_free (program_command);
2627 g_free (proxy_id);
2628 g_free (proxy_command);
2629 g_free (ext_u8);
2630 g_free (ext_folded);
2631 g_object_unref (class_key);
2634 static void
2635 read_class_url (GWin32RegistryKey *classes_root,
2636 gunichar2 *class_name,
2637 gsize class_name_len)
2639 GWin32RegistryKey *class_key;
2640 gboolean success;
2641 GWin32RegistryValueType vtype;
2642 GWin32AppInfoURLSchema *schema_rec;
2643 GWin32AppInfoHandler *handler_rec;
2644 GWin32AppInfoHandler *handler_rec_in_url;
2645 gunichar2 *program_id;
2646 gsize program_id_size;
2647 gunichar2 *program_command;
2648 gunichar2 *proxy_id;
2649 gunichar2 *proxy_command;
2650 gchar *program_id_u8;
2651 gchar *program_id_folded;
2652 GWin32RegistryKey *program_key;
2653 GWin32RegistryKey *proxy_key;
2655 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2657 if (class_key == NULL)
2658 return;
2660 success = g_win32_registry_key_get_value_w (class_key,
2661 TRUE,
2662 L"URL Protocol",
2663 &vtype,
2664 NULL,
2665 NULL,
2666 NULL);
2668 if (!success ||
2669 vtype != G_WIN32_REGISTRY_VALUE_STR)
2671 g_object_unref (class_key);
2672 return;
2675 program_id = class_name;
2676 program_id_size = (class_name_len + 1) * sizeof (gunichar2);
2677 proxy_key = program_key = NULL;
2678 program_command = proxy_id = proxy_command = NULL;
2680 if (!follow_class_chain_to_handler (program_id,
2681 program_id_size,
2682 &program_command,
2683 &program_key,
2684 &proxy_id,
2685 &proxy_command,
2686 &proxy_key,
2687 &program_id_u8,
2688 &program_id_folded))
2690 g_object_unref (class_key);
2691 return;
2694 schema_rec = g_hash_table_lookup (urls, program_id_folded);
2695 handler_rec = g_hash_table_lookup (handlers, program_id_folded);
2697 if (handler_rec == NULL)
2699 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
2701 handler_rec->proxy_key = proxy_key;
2702 handler_rec->key = program_key;
2703 handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
2704 handler_rec->handler_id_folded =
2705 g_strdup (program_id_folded);
2706 handler_rec->handler_command =
2707 program_command ? g_wcsdup (program_command, -1) : NULL;
2708 handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
2709 handler_rec->proxy_command =
2710 proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
2711 extract_executable (proxy_command ? proxy_command : program_command,
2712 &handler_rec->executable,
2713 &handler_rec->executable_basename,
2714 &handler_rec->executable_folded,
2715 NULL);
2716 read_handler_icon (proxy_key, program_key, &handler_rec->icon);
2717 g_hash_table_insert (handlers,
2718 g_strdup (program_id_folded),
2719 handler_rec);
2721 else
2723 g_clear_object (&program_key);
2724 g_clear_object (&proxy_key);
2727 if (schema_rec == NULL)
2729 schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
2730 schema_rec->schema = g_wcsdup (class_name, -1);
2731 schema_rec->schema_u8 = g_strdup (program_id_u8);
2732 schema_rec->schema_folded = g_strdup (program_id_folded);
2733 schema_rec->chosen_handler = g_object_ref (handler_rec);
2734 g_hash_table_insert (urls,
2735 g_strdup (program_id_folded),
2736 schema_rec);
2739 if (schema_rec->chosen_handler == NULL)
2740 schema_rec->chosen_handler = g_object_ref (handler_rec);
2742 handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
2743 program_id_folded);
2745 if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
2746 g_hash_table_insert (schema_rec->handlers,
2747 g_strdup (program_id_folded),
2748 g_object_ref (handler_rec));
2750 g_free (program_id_u8);
2751 g_free (program_id_folded);
2752 g_free (program_command);
2753 g_free (proxy_id);
2754 g_free (proxy_command);
2755 g_object_unref (class_key);
2758 static void
2759 read_classes (GWin32RegistryKey *classes_root)
2761 GWin32RegistrySubkeyIter class_iter;
2762 gunichar2 *class_name;
2763 gsize class_name_len;
2765 if (classes_root == NULL)
2766 return;
2768 if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL))
2769 return;
2771 while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL))
2773 if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
2774 &class_name,
2775 &class_name_len,
2776 NULL)) ||
2777 (class_name_len <= 1))
2778 continue;
2780 if (class_name[0] == L'.')
2781 read_class_extension (classes_root, class_name, class_name_len);
2782 else
2784 gsize i;
2786 for (i = 0; i < class_name_len; i++)
2787 if (!iswalpha (class_name[i]))
2788 break;
2790 if (i == class_name_len)
2791 read_class_url (classes_root, class_name, class_name_len);
2795 g_win32_registry_subkey_iter_clear (&class_iter);
2798 static void
2799 link_chosen_handlers (void)
2801 GHashTableIter iter;
2802 GHashTableIter handler_iter;
2803 gchar *schema_folded;
2804 GWin32AppInfoURLSchema *schema;
2805 gchar *handler_id_folded;
2806 GWin32AppInfoHandler *handler;
2807 gchar *ext_folded;
2808 GWin32AppInfoFileExtension *ext;
2810 g_hash_table_iter_init (&iter, urls);
2812 while (g_hash_table_iter_next (&iter,
2813 (gpointer *) &schema_folded,
2814 (gpointer *) &schema))
2816 if (schema->chosen_handler != NULL)
2817 continue;
2819 g_hash_table_iter_init (&handler_iter, schema->handlers);
2821 while (g_hash_table_iter_next (&handler_iter,
2822 (gpointer *) &handler_id_folded,
2823 (gpointer *) &handler))
2825 gchar *proxy_id_folded;
2827 if (schema->chosen_handler != NULL)
2828 break;
2830 if (strcmp (handler_id_folded, schema_folded) != 0)
2831 continue;
2833 if (handler->proxy_command &&
2834 handler->proxy_id &&
2835 utf8_and_fold (handler->proxy_id,
2836 NULL,
2837 &proxy_id_folded))
2839 GWin32AppInfoHandler *proxy;
2841 proxy = g_hash_table_lookup (handlers, proxy_id_folded);
2843 if (proxy)
2845 schema->chosen_handler = g_object_ref (proxy);
2846 g_debug ("Linking schema %s to proxy handler %c ? \"%S\" : %S\n",
2847 schema->schema_u8,
2848 schema->chosen_handler->proxy_id ? 'P' : 'T',
2849 schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
2850 schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
2853 g_free (proxy_id_folded);
2856 if (schema->chosen_handler == NULL)
2858 schema->chosen_handler = g_object_ref (handler);
2859 g_debug ("Linking schema %s to handler %c ? \"%S\" : %S\n",
2860 schema->schema_u8,
2861 schema->chosen_handler->proxy_id ? 'P' : 'T',
2862 schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
2863 schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
2868 g_hash_table_iter_init (&iter, extensions);
2870 while (g_hash_table_iter_next (&iter,
2871 (gpointer *) &ext_folded,
2872 (gpointer *) &ext))
2874 if (ext->chosen_handler != NULL)
2875 continue;
2877 g_hash_table_iter_init (&handler_iter, ext->handlers);
2879 while (g_hash_table_iter_next (&handler_iter,
2880 (gpointer *) &handler_id_folded,
2881 (gpointer *) &handler))
2883 gchar *proxy_id_folded;
2885 if (ext->chosen_handler != NULL)
2886 break;
2888 if (strcmp (handler_id_folded, ext_folded) != 0)
2889 continue;
2891 if (handler->proxy_command &&
2892 handler->proxy_id &&
2893 utf8_and_fold (handler->proxy_id,
2894 NULL,
2895 &proxy_id_folded))
2897 GWin32AppInfoHandler *proxy;
2899 proxy = g_hash_table_lookup (handlers, proxy_id_folded);
2901 if (proxy)
2903 ext->chosen_handler = g_object_ref (proxy);
2904 g_debug ("Linking ext %s to proxy handler %c ? \"%S\" : %S\n",
2905 ext->extension_u8,
2906 ext->chosen_handler->proxy_id ? 'P' : 'T',
2907 ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
2908 ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
2911 g_free (proxy_id_folded);
2914 if (ext->chosen_handler == NULL)
2916 ext->chosen_handler = g_object_ref (handler);
2917 g_debug ("Linking ext %s to handler %c ? \"%S\" : %S\n",
2918 ext->extension_u8,
2919 ext->chosen_handler->proxy_id ? 'P' : 'T',
2920 ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
2921 ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
2927 static void
2928 link_handlers_to_registered_apps (void)
2930 GHashTableIter iter;
2931 GHashTableIter sup_iter;
2932 gchar *app_id_folded;
2933 GWin32AppInfoApplication *app;
2934 gchar *schema_folded;
2935 GWin32AppInfoURLSchema *schema;
2936 gchar *ext_folded;
2937 GWin32AppInfoFileExtension *ext;
2938 gsize unhandled_exts;
2940 g_hash_table_iter_init (&sup_iter, urls);
2941 while (g_hash_table_iter_next (&sup_iter,
2942 (gpointer *) &schema_folded,
2943 (gpointer *) &schema))
2945 if (schema->chosen_handler == NULL)
2946 g_debug ("WARNING: schema %s has no chosen handler\n", schema->schema_u8);
2948 unhandled_exts= 0;
2949 g_hash_table_iter_init (&sup_iter, extensions);
2950 while (g_hash_table_iter_next (&sup_iter,
2951 (gpointer *) &ext_folded,
2952 (gpointer *) &ext))
2954 if (ext->chosen_handler == NULL)
2956 g_debug ("WARNING: extension %s has no chosen handler\n",
2957 ext->extension_u8);
2958 unhandled_exts += 1;
2962 g_hash_table_iter_init (&iter, apps_by_id);
2963 while (g_hash_table_iter_next (&iter,
2964 (gpointer *) &app_id_folded,
2965 (gpointer *) &app))
2967 if (app->supported_urls)
2969 GWin32AppInfoHandler *handler;
2971 g_hash_table_iter_init (&sup_iter, app->supported_urls);
2972 while (g_hash_table_iter_next (&sup_iter,
2973 (gpointer *) &schema_folded,
2974 (gpointer *) &handler))
2976 schema = g_hash_table_lookup (urls, schema_folded);
2978 g_assert (schema != NULL);
2980 if (schema->chosen_handler != NULL &&
2981 schema->chosen_handler->app == NULL)
2983 schema->chosen_handler->app = g_object_ref (app);
2984 g_debug ("Linking %S", app->canonical_name);
2986 if (app->localized_pretty_name)
2987 g_debug (" '%S'", app->localized_pretty_name);
2988 else if (app->pretty_name)
2989 g_debug (" '%S'", app->pretty_name);
2990 else
2991 g_debug (" '%s'", app->executable);
2993 if (app->command)
2994 g_debug (" %S", app->command);
2996 g_debug ("\n to schema %s handler %c ? \"%S\" : %S\n",
2997 schema->schema_u8,
2998 schema->chosen_handler->proxy_id ? 'P' : 'T',
2999 schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id,
3000 schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command);
3004 g_hash_table_iter_init (&sup_iter, app->supported_urls);
3005 while (g_hash_table_iter_next (&sup_iter,
3006 (gpointer *) &schema_folded,
3007 (gpointer *) &handler))
3009 if (handler->app == NULL)
3011 handler->app = g_object_ref (app);
3012 g_debug ("Linking %S", app->canonical_name);
3014 if (app->localized_pretty_name)
3015 g_debug (" '%S'", app->localized_pretty_name);
3016 else if (app->pretty_name)
3017 g_debug (" '%S'", app->pretty_name);
3018 else
3019 g_debug (" '%s'", app->executable);
3021 if (app->command)
3022 g_debug (" %S", app->command);
3024 g_debug ("\n directly to schema handler to %c ? \"%S\" : %S\n",
3025 handler->proxy_id ? 'P' : 'T',
3026 handler->proxy_id ? handler->proxy_id : handler->handler_id,
3027 handler->proxy_command ? handler->proxy_command : handler->handler_command);
3032 if (app->supported_exts)
3034 GWin32AppInfoHandler *handler;
3036 g_hash_table_iter_init (&sup_iter, app->supported_exts);
3037 while (g_hash_table_iter_next (&sup_iter,
3038 (gpointer *) &ext_folded,
3039 (gpointer *) &handler))
3041 ext = g_hash_table_lookup (extensions, ext_folded);
3043 g_assert (ext != NULL);
3045 if (ext->chosen_handler != NULL &&
3046 ext->chosen_handler->app == NULL)
3048 ext->chosen_handler->app = g_object_ref (app);
3049 g_debug ("Linking %S", app->canonical_name);
3051 if (app->localized_pretty_name)
3052 g_debug (" '%S'", app->localized_pretty_name);
3053 else if (app->pretty_name)
3054 g_debug (" '%S'", app->pretty_name);
3055 else
3056 g_debug (" '%s'", app->executable);
3058 if (app->command)
3059 g_debug (" %S", app->command);
3061 g_debug ("\n to ext %s handler %c ? \"%S\" : %S\n",
3062 ext->extension_u8,
3063 ext->chosen_handler->proxy_id ? 'P' : 'T',
3064 ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id,
3065 ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command);
3069 g_hash_table_iter_init (&sup_iter, app->supported_exts);
3070 while (g_hash_table_iter_next (&sup_iter,
3071 (gpointer *) &ext_folded,
3072 (gpointer *) &handler))
3074 if (handler->app == NULL)
3076 handler->app = g_object_ref (app);
3077 g_debug ("Linking %S", app->canonical_name);
3079 if (app->localized_pretty_name)
3080 g_debug (" '%S'", app->localized_pretty_name);
3081 else if (app->pretty_name)
3082 g_debug (" '%S'", app->pretty_name);
3083 else
3084 g_debug (" '%s'", app->executable);
3086 if (app->command)
3087 g_debug (" %S", app->command);
3089 g_debug ("\n directly to ext handler %c ? \"%S\" : %S\n",
3090 handler->proxy_id ? 'P' : 'T',
3091 handler->proxy_id ? handler->proxy_id : handler->handler_id,
3092 handler->proxy_command ? handler->proxy_command : handler->handler_command);
3098 g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
3099 unhandled_exts= 0;
3100 g_hash_table_iter_init (&sup_iter, extensions);
3101 while (g_hash_table_iter_next (&sup_iter,
3102 (gpointer *) &ext_folded,
3103 (gpointer *) &ext))
3105 if (ext->chosen_handler == NULL)
3107 g_debug ("WARNING: extension %s has no chosen handler\n",
3108 ext->extension_u8);
3109 unhandled_exts += 1;
3112 g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
3115 static void
3116 link_handlers_to_unregistered_apps (void)
3118 GHashTableIter iter;
3119 GHashTableIter app_iter;
3120 GWin32AppInfoHandler *handler;
3121 gchar *handler_id_fc;
3122 GWin32AppInfoApplication *app;
3123 gchar *canonical_name_fc;
3124 gchar *appexe_fc_basename;
3126 g_hash_table_iter_init (&iter, handlers);
3127 while (g_hash_table_iter_next (&iter,
3128 (gpointer *) &handler_id_fc,
3129 (gpointer *) &handler))
3131 gchar *hndexe_fc_basename;
3133 if ((handler->app != NULL) ||
3134 (handler->executable_folded == NULL))
3135 continue;
3137 hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
3139 if (hndexe_fc_basename == NULL)
3140 continue;
3142 g_hash_table_iter_init (&app_iter, apps_by_id);
3144 while (g_hash_table_iter_next (&app_iter,
3145 (gpointer *) &canonical_name_fc,
3146 (gpointer *) &app))
3148 if (app->executable_folded == NULL)
3149 continue;
3151 if (strcmp (app->executable_folded,
3152 handler->executable_folded) != 0)
3153 continue;
3155 handler->app = app;
3156 break;
3159 if (handler->app != NULL)
3160 continue;
3162 g_hash_table_iter_init (&app_iter, apps_by_exe);
3164 while ((hndexe_fc_basename != NULL) &&
3165 (g_hash_table_iter_next (&app_iter,
3166 (gpointer *) &appexe_fc_basename,
3167 (gpointer *) &app)))
3169 /* Use basename because apps_by_exe only has basenames */
3170 if (strcmp (hndexe_fc_basename, appexe_fc_basename) != 0)
3171 continue;
3173 handler->app = app;
3174 break;
3177 g_free (hndexe_fc_basename);
3179 if (handler->app == NULL)
3180 g_debug ("WARNING: handler that runs %s has no corresponding app\n",
3181 handler->executable);
3186 static void
3187 update_registry_data (void)
3189 guint i;
3190 GPtrArray *capable_apps_keys;
3191 GPtrArray *user_capable_apps_keys;
3192 GPtrArray *priority_capable_apps_keys;
3193 GWin32RegistryKey *url_associations;
3194 GWin32RegistryKey *file_exts;
3195 GWin32RegistryKey *classes_root;
3196 DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, postproc_end;
3198 url_associations =
3199 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3200 NULL);
3201 file_exts =
3202 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3203 NULL);
3204 classes_root = g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT", NULL);
3206 capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3207 user_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3208 priority_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3210 g_clear_pointer (&apps_by_id, g_hash_table_destroy);
3211 g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
3212 g_clear_pointer (&urls, g_hash_table_destroy);
3213 g_clear_pointer (&extensions, g_hash_table_destroy);
3214 g_clear_pointer (&handlers, g_hash_table_destroy);
3216 collect_start = GetTickCount ();
3217 collect_capable_apps_from_clients (capable_apps_keys,
3218 priority_capable_apps_keys,
3219 FALSE);
3220 collect_capable_apps_from_clients (user_capable_apps_keys,
3221 priority_capable_apps_keys,
3222 TRUE);
3223 collect_capable_apps_from_registered_apps (user_capable_apps_keys,
3224 TRUE);
3225 collect_capable_apps_from_registered_apps (capable_apps_keys,
3226 FALSE);
3227 collect_end = GetTickCount ();
3229 apps_by_id =
3230 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3231 apps_by_exe =
3232 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3233 urls =
3234 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3235 extensions =
3236 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3237 handlers =
3238 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3239 alloc_end = GetTickCount ();
3241 for (i = 0; i < priority_capable_apps_keys->len; i++)
3242 read_capable_app (g_ptr_array_index (priority_capable_apps_keys, i),
3243 TRUE,
3244 TRUE);
3245 for (i = 0; i < user_capable_apps_keys->len; i++)
3246 read_capable_app (g_ptr_array_index (user_capable_apps_keys, i),
3247 TRUE,
3248 FALSE);
3249 for (i = 0; i < capable_apps_keys->len; i++)
3250 read_capable_app (g_ptr_array_index (capable_apps_keys, i),
3251 FALSE,
3252 FALSE);
3253 capable_end = GetTickCount ();
3255 read_urls (url_associations);
3256 url_end = GetTickCount ();
3257 read_exts (file_exts);
3258 ext_end = GetTickCount ();
3259 read_exeapps ();
3260 exeapp_end = GetTickCount ();
3261 read_classes (classes_root);
3262 classes_end = GetTickCount ();
3263 link_chosen_handlers ();
3264 link_handlers_to_registered_apps ();
3265 link_handlers_to_unregistered_apps ();
3266 postproc_end = GetTickCount ();
3268 g_debug ("Collecting capable appnames: %lums\n"
3269 "Allocating hashtables:...... %lums\n"
3270 "Reading capable apps: %lums\n"
3271 "Reading URL associations:... %lums\n"
3272 "Reading extension assocs: %lums\n"
3273 "Reading exe-only apps:...... %lums\n"
3274 "Reading classes: %lums\n"
3275 "Postprocessing:..............%lums\n"
3276 "TOTAL: %lums\n",
3277 collect_end - collect_start,
3278 alloc_end - collect_end,
3279 capable_end - alloc_end,
3280 url_end - capable_end,
3281 ext_end - url_end,
3282 exeapp_end - ext_end,
3283 classes_end - exeapp_end,
3284 postproc_end - classes_end,
3285 postproc_end - collect_start);
3287 g_clear_object (&classes_root);
3288 g_clear_object (&url_associations);
3289 g_clear_object (&file_exts);
3290 g_ptr_array_free (capable_apps_keys, TRUE);
3291 g_ptr_array_free (user_capable_apps_keys, TRUE);
3292 g_ptr_array_free (priority_capable_apps_keys, TRUE);
3294 return;
3297 static void
3298 watch_keys (void)
3300 if (url_associations_key)
3301 g_win32_registry_key_watch (url_associations_key,
3302 TRUE,
3303 G_WIN32_REGISTRY_WATCH_NAME |
3304 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3305 G_WIN32_REGISTRY_WATCH_VALUES,
3306 NULL,
3307 NULL,
3308 NULL);
3310 if (file_exts_key)
3311 g_win32_registry_key_watch (file_exts_key,
3312 TRUE,
3313 G_WIN32_REGISTRY_WATCH_NAME |
3314 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3315 G_WIN32_REGISTRY_WATCH_VALUES,
3316 NULL,
3317 NULL,
3318 NULL);
3320 if (user_clients_key)
3321 g_win32_registry_key_watch (user_clients_key,
3322 TRUE,
3323 G_WIN32_REGISTRY_WATCH_NAME |
3324 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3325 G_WIN32_REGISTRY_WATCH_VALUES,
3326 NULL,
3327 NULL,
3328 NULL);
3330 if (system_clients_key)
3331 g_win32_registry_key_watch (system_clients_key,
3332 TRUE,
3333 G_WIN32_REGISTRY_WATCH_NAME |
3334 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3335 G_WIN32_REGISTRY_WATCH_VALUES,
3336 NULL,
3337 NULL,
3338 NULL);
3340 if (applications_key)
3341 g_win32_registry_key_watch (applications_key,
3342 TRUE,
3343 G_WIN32_REGISTRY_WATCH_NAME |
3344 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3345 G_WIN32_REGISTRY_WATCH_VALUES,
3346 NULL,
3347 NULL,
3348 NULL);
3350 if (user_registered_apps_key)
3351 g_win32_registry_key_watch (user_registered_apps_key,
3352 TRUE,
3353 G_WIN32_REGISTRY_WATCH_NAME |
3354 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3355 G_WIN32_REGISTRY_WATCH_VALUES,
3356 NULL,
3357 NULL,
3358 NULL);
3360 if (system_registered_apps_key)
3361 g_win32_registry_key_watch (system_registered_apps_key,
3362 TRUE,
3363 G_WIN32_REGISTRY_WATCH_NAME |
3364 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3365 G_WIN32_REGISTRY_WATCH_VALUES,
3366 NULL,
3367 NULL,
3368 NULL);
3370 if (classes_root_key)
3371 g_win32_registry_key_watch (classes_root_key,
3372 FALSE,
3373 G_WIN32_REGISTRY_WATCH_NAME |
3374 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3375 G_WIN32_REGISTRY_WATCH_VALUES,
3376 NULL,
3377 NULL,
3378 NULL);
3382 static void
3383 g_win32_appinfo_init (void)
3385 static gsize initialized;
3387 if (g_once_init_enter (&initialized))
3389 url_associations_key =
3390 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3391 NULL);
3392 file_exts_key =
3393 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3394 NULL);
3395 user_clients_key =
3396 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
3397 NULL);
3398 system_clients_key =
3399 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
3400 NULL);
3401 applications_key =
3402 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications",
3403 NULL);
3404 user_registered_apps_key =
3405 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
3406 NULL);
3407 system_registered_apps_key =
3408 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
3409 NULL);
3410 classes_root_key =
3411 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT",
3412 NULL);
3414 watch_keys ();
3416 update_registry_data ();
3418 g_once_init_leave (&initialized, TRUE);
3421 if ((url_associations_key && g_win32_registry_key_has_changed (url_associations_key)) ||
3422 (file_exts_key && g_win32_registry_key_has_changed (file_exts_key)) ||
3423 (user_clients_key && g_win32_registry_key_has_changed (user_clients_key)) ||
3424 (system_clients_key && g_win32_registry_key_has_changed (system_clients_key)) ||
3425 (applications_key && g_win32_registry_key_has_changed (applications_key)) ||
3426 (user_registered_apps_key && g_win32_registry_key_has_changed (user_registered_apps_key)) ||
3427 (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
3428 (classes_root_key && g_win32_registry_key_has_changed (classes_root_key)))
3430 G_LOCK (gio_win32_appinfo);
3431 update_registry_data ();
3432 watch_keys ();
3433 G_UNLOCK (gio_win32_appinfo);
3438 static void g_win32_app_info_iface_init (GAppInfoIface *iface);
3440 struct _GWin32AppInfo
3442 GObject parent_instance;
3444 /*<private>*/
3445 gchar **supported_types;
3447 GWin32AppInfoApplication *app;
3449 GWin32AppInfoHandler *handler;
3451 guint startup_notify : 1;
3454 G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
3455 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
3456 g_win32_app_info_iface_init))
3459 static void
3460 g_win32_app_info_finalize (GObject *object)
3462 GWin32AppInfo *info;
3464 info = G_WIN32_APP_INFO (object);
3466 g_clear_pointer (&info->supported_types, g_free);
3467 g_clear_object (&info->app);
3468 g_clear_object (&info->handler);
3470 G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
3473 static void
3474 g_win32_app_info_class_init (GWin32AppInfoClass *klass)
3476 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3478 gobject_class->finalize = g_win32_app_info_finalize;
3481 static void
3482 g_win32_app_info_init (GWin32AppInfo *local)
3486 static GAppInfo *
3487 g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
3488 GWin32AppInfoHandler *handler)
3490 GWin32AppInfo *new_info;
3491 GHashTableIter iter;
3492 gpointer ext;
3493 int i;
3495 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3497 new_info->app = g_object_ref (app);
3499 g_win32_appinfo_init ();
3500 G_LOCK (gio_win32_appinfo);
3502 i = 0;
3503 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3505 while (g_hash_table_iter_next (&iter, &ext, NULL))
3507 if (ext)
3508 i += 1;
3511 new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3513 i = 0;
3514 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3516 while (g_hash_table_iter_next (&iter, &ext, NULL))
3518 if (!ext)
3519 continue;
3521 new_info->supported_types[i] = (gchar *) ext;
3522 i += 1;
3525 G_UNLOCK (gio_win32_appinfo);
3527 new_info->supported_types[i] = NULL;
3529 new_info->handler = handler ? g_object_ref (handler) : NULL;
3531 return G_APP_INFO (new_info);
3535 static GAppInfo *
3536 g_win32_app_info_dup (GAppInfo *appinfo)
3538 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3539 GWin32AppInfo *new_info;
3541 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3543 if (info->app)
3544 new_info->app = g_object_ref (info->app);
3546 if (info->handler)
3547 new_info->handler = g_object_ref (info->handler);
3549 new_info->startup_notify = info->startup_notify;
3551 if (info->supported_types)
3553 int i;
3555 for (i = 0; info->supported_types[i]; i++)
3556 break;
3558 new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3560 for (i = 0; info->supported_types[i]; i++)
3561 new_info->supported_types[i] = g_strdup (info->supported_types[i]);
3563 new_info->supported_types[i] = NULL;
3566 return G_APP_INFO (new_info);
3569 static gboolean
3570 g_win32_app_info_equal (GAppInfo *appinfo1,
3571 GAppInfo *appinfo2)
3573 GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
3574 GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
3576 if (info1->app == NULL ||
3577 info2->app == NULL)
3578 return info1 == info2;
3580 if (info1->app->canonical_name_folded != NULL &&
3581 info2->app->canonical_name_folded != NULL)
3582 return (strcmp (info1->app->canonical_name_folded,
3583 info2->app->canonical_name_folded)) == 0;
3585 if (info1->app->executable_folded != NULL &&
3586 info2->app->executable_folded != NULL)
3587 return (strcmp (info1->app->executable_folded,
3588 info2->app->executable_folded)) == 0;
3590 return info1->app == info2->app;
3593 static const char *
3594 g_win32_app_info_get_id (GAppInfo *appinfo)
3596 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3598 if (info->app == NULL)
3599 return NULL;
3601 if (info->app->canonical_name_u8)
3602 return info->app->canonical_name_u8;
3604 if (info->app->executable_basename)
3605 return info->app->executable_basename;
3607 return NULL;
3610 static const char *
3611 g_win32_app_info_get_name (GAppInfo *appinfo)
3613 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3615 if (info->app && info->app->canonical_name_u8)
3616 return info->app->canonical_name_u8;
3617 else
3618 return P_("Unnamed");
3621 static const char *
3622 g_win32_app_info_get_display_name (GAppInfo *appinfo)
3624 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3626 if (info->app)
3628 if (info->app->localized_pretty_name_u8)
3629 return info->app->localized_pretty_name_u8;
3630 else if (info->app->pretty_name_u8)
3631 return info->app->pretty_name_u8;
3634 return g_win32_app_info_get_name (appinfo);
3637 static const char *
3638 g_win32_app_info_get_description (GAppInfo *appinfo)
3640 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3642 if (info->app == NULL)
3643 return NULL;
3645 return info->app->description_u8;
3648 static const char *
3649 g_win32_app_info_get_executable (GAppInfo *appinfo)
3651 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3653 if (info->app == NULL)
3654 return NULL;
3656 return info->app->executable;
3659 static const char *
3660 g_win32_app_info_get_commandline (GAppInfo *appinfo)
3662 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3664 if (info->app == NULL)
3665 return NULL;
3667 return info->app->command_u8;
3670 static GIcon *
3671 g_win32_app_info_get_icon (GAppInfo *appinfo)
3673 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3675 if (info->app == NULL)
3676 return NULL;
3678 return info->app->icon;
3681 typedef struct _file_or_uri {
3682 gchar *uri;
3683 gchar *file;
3684 } file_or_uri;
3686 static char *
3687 expand_macro_single (char macro, file_or_uri *obj)
3689 char *result = NULL;
3691 switch (macro)
3693 case '*':
3694 case '0':
3695 case '1':
3696 case 'l':
3697 case 'd':
3698 case '2':
3699 case '3':
3700 case '4':
3701 case '5':
3702 case '6':
3703 case '7':
3704 case '8':
3705 case '9':
3706 /* TODO: handle 'l' and 'd' differently (longname and desktop name) */
3707 if (obj->uri)
3708 result = g_strdup (obj->uri);
3709 else if (obj->file)
3710 result = g_strdup (obj->file);
3711 break;
3712 case 'u':
3713 case 'U':
3714 if (obj->uri)
3715 result = g_shell_quote (obj->uri);
3716 break;
3717 case 'f':
3718 case 'F':
3719 if (obj->file)
3720 result = g_shell_quote (obj->file);
3721 break;
3724 return result;
3727 static gboolean
3728 expand_macro (char macro,
3729 GString *exec,
3730 GWin32AppInfo *info,
3731 GList **stat_obj_list,
3732 GList **obj_list)
3734 GList *objs = *obj_list;
3735 char *expanded;
3736 gboolean result = FALSE;
3738 g_return_val_if_fail (exec != NULL, FALSE);
3741 Legend: (from http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
3742 %* - replace with all parameters
3743 %~ - replace with all parameters starting with and following the second parameter
3744 %0 or %1 the first file parameter. For example "C:\\Users\\Eric\\Destop\\New Text Document.txt". Generally this should be in quotes and the applications command line parsing should accept quotes to disambiguate files with spaces in the name and different command line parameters (this is a security best practice and I believe mentioned in MSDN).
3745 %<n> (where N is 2 - 9), replace with the nth parameter
3746 %s - show command
3747 %h - hotkey value
3748 %i - IDList stored in a shared memory handle is passed here.
3749 %l - long file name form of the first parameter. Note win32 applications will be passed the long file name, win16 applications get the short file name. Specifying %L is preferred as it avoids the need to probe for the application type.
3750 %d - desktop absolute parsing name of the first parameter (for items that don't have file system paths)
3751 %v - for verbs that are none implies all, if there is no parameter passed this is the working directory
3752 %w - the working directory
3755 switch (macro)
3757 case '*':
3758 case '~':
3759 if (*stat_obj_list)
3761 gint i;
3762 GList *o;
3764 for (o = *stat_obj_list, i = 0;
3765 macro == '~' && o && i < 2;
3766 o = o->next, i++);
3768 for (; o; o = o->next)
3770 expanded = expand_macro_single (macro, o->data);
3772 if (expanded)
3774 if (o != *stat_obj_list)
3775 g_string_append (exec, " ");
3777 g_string_append (exec, expanded);
3778 g_free (expanded);
3782 objs = NULL;
3783 result = TRUE;
3785 break;
3786 case '0':
3787 case '1':
3788 case 'l':
3789 case 'd':
3790 if (*stat_obj_list)
3792 GList *o;
3794 o = *stat_obj_list;
3796 if (o)
3798 expanded = expand_macro_single (macro, o->data);
3800 if (expanded)
3802 if (o != *stat_obj_list)
3803 g_string_append (exec, " ");
3805 g_string_append (exec, expanded);
3806 g_free (expanded);
3810 if (objs)
3811 objs = objs->next;
3813 result = TRUE;
3815 break;
3816 case '2':
3817 case '3':
3818 case '4':
3819 case '5':
3820 case '6':
3821 case '7':
3822 case '8':
3823 case '9':
3824 if (*stat_obj_list)
3826 gint i;
3827 GList *o;
3828 gint n;
3830 switch (macro)
3832 case '2':
3833 n = 2;
3834 break;
3835 case '3':
3836 n = 3;
3837 break;
3838 case '4':
3839 n = 4;
3840 break;
3841 case '5':
3842 n = 5;
3843 break;
3844 case '6':
3845 n = 6;
3846 break;
3847 case '7':
3848 n = 7;
3849 break;
3850 case '8':
3851 n = 8;
3852 break;
3853 case '9':
3854 n = 9;
3855 break;
3858 for (o = *stat_obj_list, i = 0; o && i < n; o = o->next, i++);
3860 if (o)
3862 expanded = expand_macro_single (macro, o->data);
3864 if (expanded)
3866 if (o != *stat_obj_list)
3867 g_string_append (exec, " ");
3869 g_string_append (exec, expanded);
3870 g_free (expanded);
3873 result = TRUE;
3875 if (objs)
3876 objs = NULL;
3878 break;
3879 case 's':
3880 break;
3881 case 'h':
3882 break;
3883 case 'i':
3884 break;
3885 case 'v':
3886 break;
3887 case 'w':
3888 expanded = g_get_current_dir ();
3889 g_string_append (exec, expanded);
3890 g_free (expanded);
3891 break;
3892 case 'u':
3893 case 'f':
3894 if (objs)
3896 expanded = expand_macro_single (macro, objs->data);
3898 if (expanded)
3900 g_string_append (exec, expanded);
3901 g_free (expanded);
3903 objs = objs->next;
3904 result = TRUE;
3907 break;
3909 case 'U':
3910 case 'F':
3911 while (objs)
3913 expanded = expand_macro_single (macro, objs->data);
3915 if (expanded)
3917 g_string_append (exec, expanded);
3918 g_free (expanded);
3921 objs = objs->next;
3922 result = TRUE;
3924 if (objs != NULL && expanded)
3925 g_string_append_c (exec, ' ');
3928 break;
3930 case 'c':
3931 if (info->app && info->app->localized_pretty_name_u8)
3933 expanded = g_shell_quote (info->app->localized_pretty_name_u8);
3934 g_string_append (exec, expanded);
3935 g_free (expanded);
3937 break;
3939 case 'm': /* deprecated */
3940 case 'n': /* deprecated */
3941 case 'N': /* deprecated */
3942 /*case 'd': *//* deprecated */
3943 case 'D': /* deprecated */
3944 break;
3946 case '%':
3947 g_string_append_c (exec, '%');
3948 break;
3951 *obj_list = objs;
3953 return result;
3956 static gboolean
3957 expand_application_parameters (GWin32AppInfo *info,
3958 const gchar *exec_line,
3959 GList **objs,
3960 int *argc,
3961 char ***argv,
3962 GError **error)
3964 GList *obj_list = *objs;
3965 GList **stat_obj_list = objs;
3966 const char *p = exec_line;
3967 GString *expanded_exec;
3968 gboolean res;
3969 gchar *a_char;
3971 if (exec_line == NULL)
3973 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3974 P_("Application registry did not specify"
3975 " a shell\\open\\command"));
3976 return FALSE;
3979 expanded_exec = g_string_new (NULL);
3980 res = FALSE;
3982 while (*p)
3984 if (p[0] == '%' && p[1] != '\0')
3986 if (expand_macro (p[1],
3987 expanded_exec,
3988 info, stat_obj_list,
3989 objs))
3990 res = TRUE;
3992 p++;
3994 else
3995 g_string_append_c (expanded_exec, *p);
3997 p++;
4000 /* No file substitutions */
4001 if (obj_list == *objs && obj_list != NULL && !res)
4003 /* If there is no macro default to %f. This is also what KDE does */
4004 g_string_append_c (expanded_exec, ' ');
4005 expand_macro ('f', expanded_exec, info, stat_obj_list, objs);
4008 /* Replace '\\' with '/', because g_shell_parse_argv considers them
4009 * to be escape sequences.
4011 for (a_char = expanded_exec->str;
4012 a_char <= &expanded_exec->str[expanded_exec->len];
4013 a_char++)
4015 if (*a_char == '\\')
4016 *a_char = '/';
4019 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
4020 g_string_free (expanded_exec, TRUE);
4021 return res;
4025 static gchar *
4026 get_appath_for_exe (gunichar2 *exe_basename)
4028 GWin32RegistryKey *apppath_key = NULL;
4029 GWin32RegistryValueType val_type;
4030 gunichar2 *appath = NULL;
4031 gboolean got_value;
4032 gchar *result = NULL;
4034 apppath_key = _g_win32_registry_key_build_and_new_w (NULL, L"HKEY_LOCAL_MACHINE\\"
4035 L"\\SOFTWARE"
4036 L"\\Microsoft"
4037 L"\\Windows"
4038 L"\\CurrentVersion"
4039 L"\\App Paths\\",
4040 exe_basename, NULL);
4042 if (apppath_key == NULL)
4043 return NULL;
4045 got_value = g_win32_registry_key_get_value_w (apppath_key,
4046 TRUE,
4047 L"Path",
4048 &val_type,
4049 (void **) &appath,
4050 NULL,
4051 NULL);
4053 g_object_unref (apppath_key);
4055 if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
4056 result = g_utf16_to_utf8 (appath, -1, NULL,NULL, NULL);
4058 g_clear_pointer (&appath, g_free);
4060 return result;
4064 static gboolean
4065 g_win32_app_info_launch_internal (GWin32AppInfo *info,
4066 GList *objs,
4067 GAppLaunchContext *launch_context,
4068 GSpawnFlags spawn_flags,
4069 GError **error)
4071 gboolean completed = FALSE;
4072 char **argv, **envp;
4073 int argc;
4074 gchar *command;
4075 gchar *apppath;
4076 gunichar2 *exe_basename;
4078 g_return_val_if_fail (info != NULL, FALSE);
4079 g_return_val_if_fail (info->app != NULL, FALSE);
4081 argv = NULL;
4083 if (launch_context)
4084 envp = g_app_launch_context_get_environment (launch_context);
4085 else
4086 envp = g_get_environ ();
4088 command = NULL;
4089 exe_basename = NULL;
4091 if (info->handler)
4093 if (info->handler->handler_command)
4095 command = g_utf16_to_utf8 (info->handler->handler_command,
4097 NULL,
4098 NULL,
4099 NULL);
4100 exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
4102 NULL,
4103 NULL,
4104 NULL);
4106 else if (info->handler->proxy_command)
4108 command = g_utf16_to_utf8 (info->handler->proxy_command,
4110 NULL,
4111 NULL,
4112 NULL);
4113 exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
4115 NULL,
4116 NULL,
4117 NULL);
4121 if (command == NULL)
4123 command = g_strdup (info->app->command_u8);
4124 exe_basename = g_utf8_to_utf16 (info->app->executable_basename,
4126 NULL,
4127 NULL,
4128 NULL);
4131 apppath = get_appath_for_exe (exe_basename);
4133 g_free (exe_basename);
4135 if (apppath)
4137 gchar **p;
4138 gint p_index;
4140 for (p = envp, p_index = 0; p[0]; p++, p_index++)
4141 if ((p[0][0] == 'p' || p[0][0] == 'P') &&
4142 (p[0][1] == 'a' || p[0][1] == 'A') &&
4143 (p[0][2] == 't' || p[0][2] == 'T') &&
4144 (p[0][3] == 'h' || p[0][3] == 'H') &&
4145 (p[0][4] == '='))
4146 break;
4148 if (p[0] == NULL)
4150 gchar **new_envp;
4151 new_envp = g_new (char *, g_strv_length (envp) + 2);
4152 new_envp[0] = g_strdup_printf ("PATH=%s", apppath);
4154 for (p_index = 0; p_index <= g_strv_length (envp); p_index++)
4155 new_envp[1 + p_index] = envp[p_index];
4157 g_free (envp);
4158 envp = new_envp;
4160 else
4162 gchar *p_path;
4164 p_path = &p[0][5];
4166 if (p_path[0] != '\0')
4167 envp[p_index] = g_strdup_printf ("PATH=%s%c%s",
4168 apppath,
4169 G_SEARCHPATH_SEPARATOR,
4170 p_path);
4171 else
4172 envp[p_index] = g_strdup_printf ("PATH=%s", apppath);
4174 g_free (&p_path[-5]);
4180 GPid pid;
4182 if (!expand_application_parameters (info,
4183 command,
4184 &objs,
4185 &argc,
4186 &argv,
4187 error))
4188 goto out;
4190 if (!g_spawn_async (NULL,
4191 argv,
4192 envp,
4193 spawn_flags,
4194 NULL,
4195 NULL,
4196 &pid,
4197 error))
4198 goto out;
4200 if (launch_context != NULL)
4202 GVariantBuilder builder;
4203 GVariant *platform_data;
4205 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
4206 g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
4208 platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
4209 g_signal_emit_by_name (launch_context, "launched", info, platform_data);
4210 g_variant_unref (platform_data);
4213 g_strfreev (argv);
4214 argv = NULL;
4216 while (objs != NULL);
4218 completed = TRUE;
4220 out:
4221 g_strfreev (argv);
4222 g_strfreev (envp);
4223 g_free (command);
4225 return completed;
4228 static void
4229 free_file_or_uri (gpointer ptr)
4231 file_or_uri *obj = ptr;
4232 g_free (obj->file);
4233 g_free (obj->uri);
4234 g_free (obj);
4238 static gboolean
4239 g_win32_app_supports_uris (GWin32AppInfoApplication *app)
4241 gssize num_of_uris_supported;
4243 if (app == NULL)
4244 return FALSE;
4246 num_of_uris_supported = (gssize) g_hash_table_size (app->supported_urls);
4248 if (g_hash_table_lookup (app->supported_urls, "file"))
4249 num_of_uris_supported -= 1;
4251 return num_of_uris_supported > 0;
4255 static gboolean
4256 g_win32_app_info_supports_uris (GAppInfo *appinfo)
4258 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4260 if (info->app == NULL)
4261 return FALSE;
4263 return g_win32_app_supports_uris (info->app);
4267 static gboolean
4268 g_win32_app_info_supports_files (GAppInfo *appinfo)
4270 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4272 if (info->app == NULL)
4273 return FALSE;
4275 return g_hash_table_size (info->app->supported_exts) > 0;
4279 static gboolean
4280 g_win32_app_info_launch_uris (GAppInfo *appinfo,
4281 GList *uris,
4282 GAppLaunchContext *launch_context,
4283 GError **error)
4285 gboolean res = FALSE;
4286 gboolean do_files;
4287 GList *objs;
4289 do_files = g_win32_app_info_supports_files (appinfo);
4291 objs = NULL;
4292 while (uris)
4294 file_or_uri *obj;
4295 obj = g_new0 (file_or_uri, 1);
4297 if (do_files)
4299 GFile *file;
4300 gchar *path;
4302 file = g_file_new_for_uri (uris->data);
4303 path = g_file_get_path (file);
4304 obj->file = path;
4305 g_object_unref (file);
4308 obj->uri = g_strdup (uris->data);
4310 objs = g_list_prepend (objs, obj);
4311 uris = uris->next;
4314 objs = g_list_reverse (objs);
4316 res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4317 objs,
4318 launch_context,
4319 G_SPAWN_SEARCH_PATH,
4320 error);
4322 g_list_free_full (objs, free_file_or_uri);
4324 return res;
4327 static gboolean
4328 g_win32_app_info_launch (GAppInfo *appinfo,
4329 GList *files,
4330 GAppLaunchContext *launch_context,
4331 GError **error)
4333 gboolean res = FALSE;
4334 gboolean do_uris;
4335 GList *objs;
4337 do_uris = g_win32_app_info_supports_uris (appinfo);
4339 objs = NULL;
4340 while (files)
4342 file_or_uri *obj;
4343 obj = g_new0 (file_or_uri, 1);
4344 obj->file = g_file_get_path (G_FILE (files->data));
4346 if (do_uris)
4347 obj->uri = g_file_get_uri (G_FILE (files->data));
4349 objs = g_list_prepend (objs, obj);
4350 files = files->next;
4353 objs = g_list_reverse (objs);
4355 res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4356 objs,
4357 launch_context,
4358 G_SPAWN_SEARCH_PATH,
4359 error);
4361 g_list_free_full (objs, free_file_or_uri);
4363 return res;
4366 static const char **
4367 g_win32_app_info_get_supported_types (GAppInfo *appinfo)
4369 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4371 return (const char**) info->supported_types;
4374 GAppInfo *
4375 g_app_info_create_from_commandline (const char *commandline,
4376 const char *application_name,
4377 GAppInfoCreateFlags flags,
4378 GError **error)
4380 GWin32AppInfo *info;
4381 GWin32AppInfoApplication *app;
4383 g_return_val_if_fail (commandline, NULL);
4385 info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
4387 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
4389 if (application_name)
4391 app->canonical_name = g_utf8_to_utf16 (application_name,
4393 NULL,
4394 NULL,
4395 NULL);
4396 app->canonical_name_u8 = g_strdup (application_name);
4397 app->canonical_name_folded = g_utf8_casefold (application_name, -1);
4400 app->command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
4401 app->command_u8 = g_strdup (commandline);
4403 extract_executable (app->command,
4404 &app->executable,
4405 &app->executable_basename,
4406 &app->executable_folded,
4407 NULL);
4409 app->no_open_with = FALSE;
4410 app->user_specific = FALSE;
4411 app->default_app = FALSE;
4413 info->app = app;
4414 info->handler = NULL;
4416 return G_APP_INFO (info);
4419 /* GAppInfo interface init */
4421 static void
4422 g_win32_app_info_iface_init (GAppInfoIface *iface)
4424 iface->dup = g_win32_app_info_dup;
4425 iface->equal = g_win32_app_info_equal;
4426 iface->get_id = g_win32_app_info_get_id;
4427 iface->get_name = g_win32_app_info_get_name;
4428 iface->get_description = g_win32_app_info_get_description;
4429 iface->get_executable = g_win32_app_info_get_executable;
4430 iface->get_icon = g_win32_app_info_get_icon;
4431 iface->launch = g_win32_app_info_launch;
4432 iface->supports_uris = g_win32_app_info_supports_uris;
4433 iface->supports_files = g_win32_app_info_supports_files;
4434 iface->launch_uris = g_win32_app_info_launch_uris;
4435 /* iface->should_show = g_win32_app_info_should_show;*/
4436 /* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
4437 /* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
4438 /* iface->add_supports_type = g_win32_app_info_add_supports_type;*/
4439 /* iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
4440 /* iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
4441 /* iface->can_delete = g_win32_app_info_can_delete;*/
4442 /* iface->do_delete = g_win32_app_info_delete;*/
4443 iface->get_commandline = g_win32_app_info_get_commandline;
4444 iface->get_display_name = g_win32_app_info_get_display_name;
4445 /* iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
4446 iface->get_supported_types = g_win32_app_info_get_supported_types;
4449 GAppInfo *
4450 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
4452 GWin32AppInfoURLSchema *scheme;
4453 char *scheme_down;
4454 GAppInfo *result;
4456 scheme_down = g_utf8_casefold (uri_scheme, -1);
4458 if (!scheme_down)
4459 return NULL;
4461 if (strcmp (scheme_down, "file") == 0)
4463 g_free (scheme_down);
4464 return NULL;
4467 g_win32_appinfo_init ();
4468 G_LOCK (gio_win32_appinfo);
4470 scheme = g_hash_table_lookup (urls, scheme_down);
4471 g_free (scheme_down);
4473 if (scheme)
4474 g_object_ref (scheme);
4476 G_UNLOCK (gio_win32_appinfo);
4478 result = NULL;
4480 if (scheme != NULL &&
4481 scheme->chosen_handler != NULL &&
4482 scheme->chosen_handler->app != NULL)
4483 result = g_win32_app_info_new_from_app (scheme->chosen_handler->app,
4484 scheme->chosen_handler);
4486 if (scheme)
4487 g_object_unref (scheme);
4489 return result;
4492 GAppInfo *
4493 g_app_info_get_default_for_type (const char *content_type,
4494 gboolean must_support_uris)
4496 GWin32AppInfoFileExtension *ext;
4497 char *ext_down;
4498 GWin32AppInfoHandler *handler;
4499 GAppInfo *result;
4500 GWin32AppInfoApplication *app;
4501 GHashTableIter iter;
4503 ext_down = g_utf8_casefold (content_type, -1);
4505 if (!ext_down)
4506 return NULL;
4508 g_win32_appinfo_init ();
4509 G_LOCK (gio_win32_appinfo);
4511 /* Assuming that "content_type" is a file extension, not a MIME type */
4512 ext = g_hash_table_lookup (extensions, ext_down);
4513 g_free (ext_down);
4515 result = NULL;
4517 if (ext != NULL)
4518 g_object_ref (ext);
4520 G_UNLOCK (gio_win32_appinfo);
4522 if (ext != NULL)
4524 if (ext->chosen_handler != NULL &&
4525 ext->chosen_handler->app != NULL &&
4526 (!must_support_uris ||
4527 g_win32_app_supports_uris (ext->chosen_handler->app)))
4528 result = g_win32_app_info_new_from_app (ext->chosen_handler->app,
4529 ext->chosen_handler);
4530 else
4532 g_hash_table_iter_init (&iter, ext->handlers);
4534 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4536 if (handler->app &&
4537 (!must_support_uris ||
4538 g_win32_app_supports_uris (ext->chosen_handler->app)))
4540 result = g_win32_app_info_new_from_app (handler->app, handler);
4541 break;
4545 if (result == NULL)
4547 g_hash_table_iter_init (&iter, ext->other_apps);
4548 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
4550 if (!must_support_uris ||
4551 g_win32_app_supports_uris (ext->chosen_handler->app))
4553 result = g_win32_app_info_new_from_app (app, NULL);
4554 break;
4559 g_object_unref (ext);
4562 return result;
4565 GList *
4566 g_app_info_get_all (void)
4568 GHashTableIter iter;
4569 gpointer value;
4570 GList *infos;
4571 GList *apps;
4572 GList *apps_i;
4574 g_win32_appinfo_init ();
4575 G_LOCK (gio_win32_appinfo);
4577 apps = NULL;
4578 g_hash_table_iter_init (&iter, apps_by_id);
4579 while (g_hash_table_iter_next (&iter, NULL, &value))
4580 apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
4582 G_UNLOCK (gio_win32_appinfo);
4584 infos = NULL;
4585 for (apps_i = apps; apps_i; apps_i = apps_i->next)
4586 infos = g_list_prepend (infos,
4587 g_win32_app_info_new_from_app (apps_i->data, NULL));
4589 g_list_free_full (apps, g_object_unref);
4591 return infos;
4594 GList *
4595 g_app_info_get_all_for_type (const char *content_type)
4597 GWin32AppInfoFileExtension *ext;
4598 char *ext_down;
4599 GWin32AppInfoHandler *handler;
4600 GWin32AppInfoApplication *app;
4601 GHashTableIter iter;
4602 GList *result;
4604 ext_down = g_utf8_casefold (content_type, -1);
4606 if (!ext_down)
4607 return NULL;
4609 g_win32_appinfo_init ();
4610 G_LOCK (gio_win32_appinfo);
4612 /* Assuming that "content_type" is a file extension, not a MIME type */
4613 ext = g_hash_table_lookup (extensions, ext_down);
4614 g_free (ext_down);
4616 result = NULL;
4618 if (ext != NULL)
4619 g_object_ref (ext);
4621 G_UNLOCK (gio_win32_appinfo);
4623 if (ext == NULL)
4624 return NULL;
4626 if (ext->chosen_handler != NULL &&
4627 ext->chosen_handler->app != NULL)
4628 result = g_list_prepend (result,
4629 g_win32_app_info_new_from_app (ext->chosen_handler->app,
4630 ext->chosen_handler));
4632 g_hash_table_iter_init (&iter, ext->handlers);
4634 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4636 if (handler->app &&
4637 (ext->chosen_handler == NULL || ext->chosen_handler->app != handler->app))
4638 result = g_list_prepend (result,
4639 g_win32_app_info_new_from_app (handler->app,
4640 handler));
4643 g_hash_table_iter_init (&iter, ext->other_apps);
4645 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
4647 result = g_list_prepend (result, g_win32_app_info_new_from_app (app, NULL));
4650 g_object_unref (ext);
4652 result = g_list_reverse (result);
4654 return result;
4657 GList *
4658 g_app_info_get_fallback_for_type (const gchar *content_type)
4660 /* TODO: fix this once gcontenttype support is improved */
4661 return g_app_info_get_all_for_type (content_type);
4664 GList *
4665 g_app_info_get_recommended_for_type (const gchar *content_type)
4667 /* TODO: fix this once gcontenttype support is improved */
4668 return g_app_info_get_all_for_type (content_type);
4671 void
4672 g_app_info_reset_type_associations (const char *content_type)
4674 /* nothing to do */