| /* GIO - GLib Input, Output and Streaming Library |
| * |
| * Copyright (C) 2006-2007 Red Hat, Inc. |
| * Copyright (C) 2014 Руслан Ижбулатов |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General |
| * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| * |
| * Authors: Alexander Larsson <alexl@redhat.com> |
| * Руслан Ижбулатов <lrn1986@gmail.com> |
| */ |
| |
| #include "config.h" |
| |
| #include <string.h> |
| |
| #include "gcontenttype.h" |
| #include "gwin32appinfo.h" |
| #include "gappinfo.h" |
| #include "gioerror.h" |
| #include "gfile.h" |
| #include <glib/gstdio.h> |
| #include "glibintl.h" |
| #include <gio/gwin32registrykey.h> |
| |
| #include <windows.h> |
| |
| /* We need to watch 8 places: |
| * 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations |
| * (anything below that key) |
| * On change: re-enumerate subkeys, read their values. |
| * 1) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts |
| * (anything below that key) |
| * On change: re-enumerate subkeys |
| * 2) HKEY_CURRENT_USER\\Software\\Clients (anything below that key) |
| * On change: re-read the whole hierarchy of handlers |
| * 3) HKEY_LOCAL_MACHINE\\Software\\Clients (anything below that key) |
| * On change: re-read the whole hierarchy of handlers |
| * 4) HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications (values of that key) |
| * On change: re-read the value list of registered applications |
| * 5) HKEY_CURRENT_USER\\Software\\RegisteredApplications (values of that key) |
| * On change: re-read the value list of registered applications |
| * 6) HKEY_CLASSES_ROOT\\Applications (anything below that key) |
| * On change: re-read the whole hierarchy of apps |
| * 7) HKEY_CLASSES_ROOT (only its subkeys) |
| * On change: re-enumerate subkeys, try to filter out wrong names. |
| * |
| */ |
| |
| typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema; |
| typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension; |
| typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler; |
| typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication; |
| |
| typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass; |
| typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass; |
| typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass; |
| typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass; |
| |
| struct _GWin32AppInfoURLSchemaClass |
| { |
| GObjectClass parent_class; |
| }; |
| |
| struct _GWin32AppInfoFileExtensionClass |
| { |
| GObjectClass parent_class; |
| }; |
| |
| struct _GWin32AppInfoHandlerClass |
| { |
| GObjectClass parent_class; |
| }; |
| |
| struct _GWin32AppInfoApplicationClass |
| { |
| GObjectClass parent_class; |
| }; |
| |
| struct _GWin32AppInfoURLSchema { |
| GObject parent_instance; |
| |
| /* url schema (stuff before ':') */ |
| gunichar2 *schema; |
| |
| /* url schema (stuff before ':'), in UTF-8 */ |
| gchar *schema_u8; |
| |
| /* url schema (stuff before ':'), in UTF-8, folded */ |
| gchar *schema_folded; |
| |
| /* Handler currently selected for this schema */ |
| GWin32AppInfoHandler *chosen_handler; |
| |
| /* Maps folded handler IDs -> to other handlers for this schema */ |
| GHashTable *handlers; |
| }; |
| |
| struct _GWin32AppInfoHandler { |
| GObject parent_instance; |
| |
| /* Class name in HKCR */ |
| gunichar2 *handler_id; |
| |
| /* Handler registry key (HKCR\\handler_id). Can be used to watch this handler. */ |
| GWin32RegistryKey *key; |
| |
| /* Class name in HKCR, UTF-8, folded */ |
| gchar *handler_id_folded; |
| |
| /* shell/open/command default value for the class named by class_id */ |
| gunichar2 *handler_command; |
| |
| /* If handler_id class has no command, it might point to another class */ |
| gunichar2 *proxy_id; |
| |
| /* Proxy registry key (HKCR\\proxy_id). Can be used to watch handler's proxy. */ |
| GWin32RegistryKey *proxy_key; |
| |
| /* shell/open/command default value for the class named by proxy_id */ |
| gunichar2 *proxy_command; |
| |
| /* Executable of the program (for matching, in folded form; UTF-8) */ |
| gchar *executable_folded; |
| |
| /* Executable of the program (UTF-8) */ |
| gchar *executable; |
| |
| /* Pointer to a location within @executable */ |
| gchar *executable_basename; |
| |
| /* Icon of the application for this handler */ |
| GIcon *icon; |
| |
| /* The application that is linked to this handler. */ |
| GWin32AppInfoApplication *app; |
| }; |
| |
| struct _GWin32AppInfoFileExtension { |
| GObject parent_instance; |
| |
| /* File extension (with leading '.') */ |
| gunichar2 *extension; |
| |
| /* File extension (with leading '.'), in UTF-8 */ |
| gchar *extension_u8; |
| |
| /* handler currently selected for this extension */ |
| GWin32AppInfoHandler *chosen_handler; |
| |
| /* Maps folded handler IDs -> to other handlers for this extension */ |
| GHashTable *handlers; |
| |
| /* Maps folded app exename -> to apps that support this extension. |
| * ONLY for apps that are not reachable via handlers (i.e. apps from |
| * the HKCR/Applications, which have no handlers). */ |
| GHashTable *other_apps; |
| }; |
| |
| struct _GWin32AppInfoApplication { |
| GObject parent_instance; |
| |
| /* Canonical name (used for key names). Can be NULL. */ |
| gunichar2 *canonical_name; |
| |
| /* Canonical name (used for key names), in UTF-8. Can be NULL. */ |
| gchar *canonical_name_u8; |
| |
| /* Canonical name (used for key names), in UTF-8, folded. Can be NULL. */ |
| gchar *canonical_name_folded; |
| |
| /* Human-readable name in English. Can be NULL */ |
| gunichar2 *pretty_name; |
| |
| /* Human-readable name in English, UTF-8. Can be NULL */ |
| gchar *pretty_name_u8; |
| |
| /* Human-readable name in user's language. Can be NULL */ |
| gunichar2 *localized_pretty_name; |
| |
| /* Human-readable name in user's language, UTF-8. Can be NULL */ |
| gchar *localized_pretty_name_u8; |
| |
| /* Description, could be in user's language. Can be NULL */ |
| gunichar2 *description; |
| |
| /* Description, could be in user's language, UTF-8. Can be NULL */ |
| gchar *description_u8; |
| |
| /* shell/open/command for the application. Can be NULL (see executable). */ |
| gunichar2 *command; |
| |
| /* shell/open/command for the application. Can be NULL (see executable). */ |
| gchar *command_u8; |
| |
| /* Executable of the program (for matching, in folded form; |
| * UTF-8). Never NULL. */ |
| gchar *executable_folded; |
| |
| /* Executable of the program (UTF-8). Never NULL. */ |
| gchar *executable; |
| |
| /* Pointer to a location within @executable */ |
| gchar *executable_basename; |
| |
| /* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema, |
| * UTF-8, folded) -> a GWin32AppInfoHandler |
| * Schema can be used as a key in the urls hashmap. |
| */ |
| GHashTable *supported_urls; |
| |
| /* Explicitly supported extensions, hashmap from map-owned gchar ptr |
| * (.extension, UTF-8, folded) -> a GWin32AppInfoHandler |
| * Extension can be used as a key in the extensions hashmap. |
| */ |
| GHashTable *supported_exts; |
| |
| /* Icon of the application (remember, handler can have its own icon too) */ |
| GIcon *icon; |
| |
| /* Set to TRUE to prevent this app from appearing in lists of apps for |
| * opening files. This will not prevent it from appearing in lists of apps |
| * just for running, or lists of apps for opening exts/urls for which this |
| * app reports explicit support. |
| */ |
| gboolean no_open_with; |
| |
| /* Set to TRUE for applications from HKEY_CURRENT_USER. |
| * Give them priority over applications from HKEY_LOCAL_MACHINE, when all |
| * other things are equal. |
| */ |
| gboolean user_specific; |
| |
| /* Set to TRUE for applications that are machine-wide defaults (i.e. default |
| * browser) */ |
| gboolean default_app; |
| }; |
| |
| #define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ()) |
| #define G_WIN32_APPINFO_URL_SCHEMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema)) |
| |
| #define G_TYPE_WIN32_APPINFO_FILE_EXTENSION (g_win32_appinfo_file_extension_get_type ()) |
| #define G_WIN32_APPINFO_FILE_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension)) |
| |
| #define G_TYPE_WIN32_APPINFO_HANDLER (g_win32_appinfo_handler_get_type ()) |
| #define G_WIN32_APPINFO_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler)) |
| |
| #define G_TYPE_WIN32_APPINFO_APPLICATION (g_win32_appinfo_application_get_type ()) |
| #define G_WIN32_APPINFO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication)) |
| |
| GType g_win32_appinfo_url_schema_get_type (void) G_GNUC_CONST; |
| GType g_win32_appinfo_file_extension_get_type (void) G_GNUC_CONST; |
| GType g_win32_appinfo_handler_get_type (void) G_GNUC_CONST; |
| GType g_win32_appinfo_application_get_type (void) G_GNUC_CONST; |
| |
| G_DEFINE_TYPE (GWin32AppInfoURLSchema, g_win32_appinfo_url_schema, G_TYPE_OBJECT) |
| G_DEFINE_TYPE (GWin32AppInfoFileExtension, g_win32_appinfo_file_extension, G_TYPE_OBJECT) |
| G_DEFINE_TYPE (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT) |
| G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT) |
| |
| static void |
| g_win32_appinfo_url_schema_dispose (GObject *object) |
| { |
| GWin32AppInfoURLSchema *url = G_WIN32_APPINFO_URL_SCHEMA (object); |
| |
| g_clear_pointer (&url->schema, g_free); |
| g_clear_pointer (&url->schema_u8, g_free); |
| g_clear_pointer (&url->schema_folded, g_free); |
| g_clear_object (&url->chosen_handler); |
| g_clear_pointer (&url->handlers, g_hash_table_destroy); |
| G_OBJECT_CLASS (g_win32_appinfo_url_schema_parent_class)->dispose (object); |
| } |
| |
| |
| static void |
| g_win32_appinfo_handler_dispose (GObject *object) |
| { |
| GWin32AppInfoHandler *handler = G_WIN32_APPINFO_HANDLER (object); |
| |
| g_clear_pointer (&handler->handler_id, g_free); |
| g_clear_pointer (&handler->handler_id_folded, g_free); |
| g_clear_pointer (&handler->handler_command, g_free); |
| g_clear_pointer (&handler->proxy_id, g_free); |
| g_clear_pointer (&handler->proxy_command, g_free); |
| g_clear_pointer (&handler->executable_folded, g_free); |
| g_clear_pointer (&handler->executable, g_free); |
| g_clear_object (&handler->key); |
| g_clear_object (&handler->proxy_key); |
| g_clear_object (&handler->icon); |
| g_clear_object (&handler->app); |
| G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object); |
| } |
| |
| static void |
| g_win32_appinfo_file_extension_dispose (GObject *object) |
| { |
| GWin32AppInfoFileExtension *ext = G_WIN32_APPINFO_FILE_EXTENSION (object); |
| |
| g_clear_pointer (&ext->extension, g_free); |
| g_clear_pointer (&ext->extension_u8, g_free); |
| g_clear_object (&ext->chosen_handler); |
| g_clear_pointer (&ext->handlers, g_hash_table_destroy); |
| g_clear_pointer (&ext->other_apps, g_hash_table_destroy); |
| G_OBJECT_CLASS (g_win32_appinfo_file_extension_parent_class)->dispose (object); |
| } |
| |
| static void |
| g_win32_appinfo_application_dispose (GObject *object) |
| { |
| GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object); |
| |
| g_clear_pointer (&app->canonical_name_u8, g_free); |
| g_clear_pointer (&app->canonical_name_folded, g_free); |
| g_clear_pointer (&app->canonical_name, g_free); |
| g_clear_pointer (&app->pretty_name, g_free); |
| g_clear_pointer (&app->localized_pretty_name, g_free); |
| g_clear_pointer (&app->description, g_free); |
| g_clear_pointer (&app->command, g_free); |
| g_clear_pointer (&app->pretty_name_u8, g_free); |
| g_clear_pointer (&app->localized_pretty_name_u8, g_free); |
| g_clear_pointer (&app->description_u8, g_free); |
| g_clear_pointer (&app->command_u8, g_free); |
| g_clear_pointer (&app->executable_folded, g_free); |
| g_clear_pointer (&app->executable, g_free); |
| g_clear_pointer (&app->supported_urls, g_hash_table_destroy); |
| g_clear_pointer (&app->supported_exts, g_hash_table_destroy); |
| g_clear_object (&app->icon); |
| G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object); |
| } |
| |
| static void |
| g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| gobject_class->dispose = g_win32_appinfo_url_schema_dispose; |
| } |
| |
| static void |
| g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| gobject_class->dispose = g_win32_appinfo_file_extension_dispose; |
| } |
| |
| static void |
| g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| gobject_class->dispose = g_win32_appinfo_handler_dispose; |
| } |
| |
| static void |
| g_win32_appinfo_application_class_init (GWin32AppInfoApplicationClass *klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| gobject_class->dispose = g_win32_appinfo_application_dispose; |
| } |
| |
| static void |
| g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self) |
| { |
| self->handlers = g_hash_table_new_full (g_str_hash, |
| g_str_equal, |
| g_free, |
| g_object_unref); |
| } |
| |
| static void |
| g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self) |
| { |
| self->handlers = g_hash_table_new_full (g_str_hash, |
| g_str_equal, |
| g_free, |
| g_object_unref); |
| self->other_apps = g_hash_table_new_full (g_str_hash, |
| g_str_equal, |
| g_free, |
| g_object_unref); |
| } |
| |
| static void |
| g_win32_appinfo_handler_init (GWin32AppInfoHandler *self) |
| { |
| } |
| |
| static void |
| g_win32_appinfo_application_init (GWin32AppInfoApplication *self) |
| { |
| self->supported_urls = g_hash_table_new_full (g_str_hash, |
| g_str_equal, |
| g_free, |
| g_object_unref); |
| self->supported_exts = g_hash_table_new_full (g_str_hash, |
| g_str_equal, |
| g_free, |
| g_object_unref); |
| } |
| |
| G_LOCK_DEFINE_STATIC (gio_win32_appinfo); |
| |
| /* Map of owned ".ext" (with '.', UTF-8, folded) |
| * to GWin32AppInfoFileExtension ptr |
| */ |
| static GHashTable *extensions = NULL; |
| |
| /* Map of owned "schema" (without ':', UTF-8, folded) |
| * to GWin32AppInfoURLSchema ptr |
| */ |
| static GHashTable *urls = NULL; |
| |
| /* Map of owned "appID" (UTF-8, folded) to |
| * GWin32AppInfoApplication ptr |
| */ |
| static GHashTable *apps_by_id = NULL; |
| |
| /* Map of owned "app.exe" (UTF-8, folded) to |
| * GWin32AppInfoApplication ptr. |
| * This map and its values are separate from apps_by_id. The fact that an app |
| * with known ID has the same executable [base]name as an app in this map does |
| * not mean that they are the same application. |
| */ |
| static GHashTable *apps_by_exe = NULL; |
| |
| /* Map of owned "handler id" (UTF-8, folded) |
| * to GWin32AppInfoHandler ptr |
| */ |
| static GHashTable *handlers = NULL; |
| |
| /* Watch this whole subtree */ |
| static GWin32RegistryKey *url_associations_key; |
| |
| /* Watch this whole subtree */ |
| static GWin32RegistryKey *file_exts_key; |
| |
| /* Watch this whole subtree */ |
| static GWin32RegistryKey *user_clients_key; |
| |
| /* Watch this whole subtree */ |
| static GWin32RegistryKey *system_clients_key; |
| |
| /* Watch this key */ |
| static GWin32RegistryKey *user_registered_apps_key; |
| |
| /* Watch this key */ |
| static GWin32RegistryKey *system_registered_apps_key; |
| |
| /* Watch this whole subtree */ |
| static GWin32RegistryKey *applications_key; |
| |
| /* Watch this key */ |
| static GWin32RegistryKey *classes_root_key; |
| |
| static gunichar2 * |
| g_wcsdup (const gunichar2 *str, gssize str_size) |
| { |
| if (str_size == -1) |
| { |
| str_size = wcslen (str) + 1; |
| str_size *= sizeof (gunichar2); |
| } |
| return g_memdup (str, str_size); |
| } |
| |
| #define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\" |
| #define USER_CHOICE L"\\UserChoice" |
| #define OPEN_WITH_PROGIDS L"\\OpenWithProgids" |
| #define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\" |
| #define HKCR L"HKEY_CLASSES_ROOT\\" |
| #define HKCU L"HKEY_CURRENT_USER\\" |
| #define HKLM L"HKEY_LOCAL_MACHINE\\" |
| #define SHELL_OPEN_COMMAND L"\\shell\\open\\command" |
| #define REG_PATH_MAX 256 |
| #define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2)) |
| |
| static gunichar2 * |
| read_resource_string (gunichar2 *res) |
| { |
| gunichar2 *id_str; |
| gunichar2 *id_str_end; |
| gunichar2 *filename_str; |
| unsigned long id; |
| HMODULE resource_module; |
| gunichar2 *buffer; |
| int string_length; |
| int buffer_length; |
| |
| if (res == NULL || res[0] != L'@') |
| return res; |
| |
| id_str = wcsrchr (res, L'-'); |
| |
| if (id_str == NULL || id_str[-1] != L',') |
| return res; |
| |
| id_str += 1; |
| |
| id = wcstoul (id_str, &id_str_end, 10); |
| |
| if (id_str_end == id_str || id_str_end[0] != L'\0' || id == ULONG_MAX) |
| return res; |
| |
| filename_str = &res[1]; |
| id_str[-2] = L'\0'; |
| |
| resource_module = LoadLibraryExW (filename_str, |
| 0, |
| LOAD_LIBRARY_AS_DATAFILE | |
| LOAD_LIBRARY_AS_IMAGE_RESOURCE); |
| |
| g_free (res); |
| |
| if (resource_module == NULL) |
| return NULL; |
| |
| buffer_length = 256; |
| string_length = buffer_length - 1; |
| |
| while (TRUE) |
| { |
| buffer = g_malloc (buffer_length * sizeof (gunichar2)); |
| string_length = LoadStringW (resource_module, id, buffer, buffer_length); |
| |
| if (string_length != 0 && string_length == buffer_length - 1) |
| { |
| g_free (buffer); |
| buffer_length *= 2; |
| } |
| else |
| { |
| if (string_length == 0) |
| g_clear_pointer (&buffer, g_free); |
| |
| break; |
| } |
| } |
| |
| FreeLibrary (resource_module); |
| |
| if (buffer) |
| { |
| gunichar2 *result = g_wcsdup (buffer, -1); |
| g_free (buffer); |
| return result; |
| } |
| |
| return NULL; |
| } |
| |
| static void |
| read_handler_icon (GWin32RegistryKey *proxy_key, |
| GWin32RegistryKey *program_key, |
| GIcon **icon_out) |
| { |
| gint counter; |
| GWin32RegistryKey *key; |
| |
| *icon_out = NULL; |
| |
| for (counter = 0; counter < 2; counter++) |
| { |
| GWin32RegistryKey *icon_key; |
| |
| if (counter == 0) |
| key = proxy_key; |
| else |
| key = program_key; |
| |
| if (!key) |
| continue; |
| |
| icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL); |
| |
| if (icon_key != NULL) |
| { |
| GWin32RegistryValueType default_type; |
| gchar *default_value; |
| |
| if (g_win32_registry_key_get_value (icon_key, |
| TRUE, |
| "", |
| &default_type, |
| (gpointer *) &default_value, |
| NULL, |
| NULL)) |
| { |
| if (default_type == G_WIN32_REGISTRY_VALUE_STR || |
| default_value[0] != '\0') |
| *icon_out = g_themed_icon_new (default_value); |
| |
| g_clear_pointer (&default_value, g_free); |
| } |
| |
| g_object_unref (icon_key); |
| } |
| |
| if (*icon_out) |
| break; |
| } |
| } |
| |
| static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED; |
| static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_list components); |
| |
| static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED; |
| |
| /* output_size is in *bytes*, not gunichar2s! */ |
| static gboolean |
| build_registry_path (gunichar2 *output, gsize output_size, ...) |
| { |
| va_list ap; |
| gboolean result; |
| |
| va_start (ap, output_size); |
| |
| result = build_registry_pathv (output, output_size, ap); |
| |
| va_end (ap); |
| |
| return result; |
| } |
| |
| /* output_size is in *bytes*, not gunichar2s! */ |
| static gboolean |
| build_registry_pathv (gunichar2 *output, gsize output_size, va_list components) |
| { |
| va_list lentest; |
| gunichar2 *p; |
| gunichar2 *component; |
| gsize length; |
| |
| if (output == NULL) |
| return FALSE; |
| |
| G_VA_COPY (lentest, components); |
| |
| for (length = 0, component = va_arg (lentest, gunichar2 *); |
| component != NULL; |
| component = va_arg (lentest, gunichar2 *)) |
| { |
| length += wcslen (component); |
| } |
| |
| va_end (lentest); |
| |
| if ((length >= REG_PATH_MAX_SIZE) || |
| (length * sizeof (gunichar2) >= output_size)) |
| return FALSE; |
| |
| output[0] = L'\0'; |
| |
| for (p = output, component = va_arg (components, gunichar2 *); |
| component != NULL; |
| component = va_arg (components, gunichar2 *)) |
| { |
| length = wcslen (component); |
| wcscat (p, component); |
| p += length; |
| } |
| |
| return TRUE; |
| } |
| |
| |
| static GWin32RegistryKey * |
| _g_win32_registry_key_build_and_new_w (GError **error, ...) |
| { |
| va_list ap; |
| gunichar2 key_path[REG_PATH_MAX_SIZE + 1]; |
| GWin32RegistryKey *key; |
| |
| va_start (ap, error); |
| |
| key = NULL; |
| |
| if (build_registry_pathv (key_path, sizeof (key_path), ap)) |
| key = g_win32_registry_key_new_w (key_path, error); |
| |
| va_end (ap); |
| |
| return key; |
| } |
| |
| |
| static gboolean |
| utf8_and_fold (const gunichar2 *str, |
| gchar **str_u8, |
| gchar **str_u8_folded) |
| { |
| gchar *u8; |
| gchar *folded; |
| u8 = g_utf16_to_utf8 (str, -1, NULL, NULL, NULL); |
| |
| if (u8 == NULL) |
| return FALSE; |
| |
| folded = g_utf8_casefold (u8, -1); |
| |
| if (folded == NULL) |
| { |
| g_free (u8); |
| return FALSE; |
| } |
| |
| if (str_u8) |
| *str_u8 = u8; |
| else |
| g_free (u8); |
| |
| if (str_u8_folded) |
| *str_u8_folded = folded; |
| else |
| g_free (folded); |
| |
| return TRUE; |
| } |
| |
| |
| static gboolean |
| follow_class_chain_to_handler (const gunichar2 *program_id, |
| gsize program_id_size, |
| gunichar2 **program_command, |
| GWin32RegistryKey **program_key, |
| gunichar2 **proxy_id, |
| gunichar2 **proxy_command, |
| GWin32RegistryKey **proxy_key, |
| gchar **program_id_u8, |
| gchar **program_id_folded) |
| { |
| GWin32RegistryKey *key; |
| GWin32RegistryValueType val_type; |
| gsize proxy_id_size; |
| gboolean got_value; |
| |
| g_assert (program_id && program_command && proxy_id && proxy_command); |
| |
| *program_command = NULL; |
| *proxy_id = NULL; |
| *proxy_command = NULL; |
| |
| if (program_key) |
| *program_key = NULL; |
| |
| if (proxy_key) |
| *proxy_key = NULL; |
| |
| |
| key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, |
| SHELL_OPEN_COMMAND, NULL); |
| |
| if (key != NULL) |
| { |
| got_value = g_win32_registry_key_get_value_w (key, |
| TRUE, |
| L"", |
| &val_type, |
| (void **) program_command, |
| NULL, |
| NULL); |
| if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR) |
| { |
| if ((program_id_u8 != NULL || program_id_folded != NULL) && |
| !utf8_and_fold (program_id, program_id_u8, program_id_folded)) |
| { |
| g_object_unref (key); |
| g_free (program_command); |
| |
| return FALSE; |
| } |
| if (program_key == NULL) |
| g_object_unref (key); |
| else |
| *program_key = key; |
| |
| return TRUE; |
| } |
| else if (got_value) |
| g_clear_pointer (program_command, g_free); |
| |
| g_object_unref (key); |
| } |
| |
| key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, NULL); |
| |
| if (key == NULL) |
| return FALSE; |
| |
| got_value = g_win32_registry_key_get_value_w (key, |
| TRUE, |
| L"", |
| &val_type, |
| (void **) proxy_id, |
| &proxy_id_size, |
| NULL); |
| if (!got_value || |
| (val_type != G_WIN32_REGISTRY_VALUE_STR)) |
| { |
| g_object_unref (key); |
| g_clear_pointer (proxy_id, g_free); |
| return FALSE; |
| } |
| |
| if (proxy_key) |
| *proxy_key = key; |
| else |
| g_object_unref (key); |
| |
| key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id, |
| SHELL_OPEN_COMMAND, NULL); |
| |
| if (key == NULL) |
| { |
| g_clear_pointer (proxy_id, g_free); |
| if (proxy_key) |
| g_clear_object (proxy_key); |
| return FALSE; |
| } |
| |
| got_value = g_win32_registry_key_get_value_w (key, |
| TRUE, |
| L"", |
| &val_type, |
| (void **) proxy_command, |
| NULL, |
| NULL); |
| g_object_unref (key); |
| |
| if (!got_value || |
| val_type != G_WIN32_REGISTRY_VALUE_STR || |
| ((program_id_u8 != NULL || program_id_folded != NULL) && |
| !utf8_and_fold (program_id, program_id_u8, program_id_folded))) |
| { |
| g_clear_pointer (proxy_id, g_free); |
| g_clear_pointer (proxy_command, g_free); |
| if (proxy_key) |
| g_clear_object (proxy_key); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| |
| static void |
| extract_executable (gunichar2 *commandline, |
| gchar **ex_out, |
| gchar **ex_basename_out, |
| gchar **ex_folded_out, |
| gchar **ex_folded_basename_out) |
| { |
| gchar *ex; |
| gchar *ex_folded; |
| gunichar2 *p; |
| gboolean quoted; |
| size_t len; |
| size_t execlen; |
| gunichar2 *exepart; |
| gboolean found; |
| |
| quoted = FALSE; |
| execlen = 0; |
| found = FALSE; |
| len = wcslen (commandline); |
| p = commandline; |
| |
| while (p < &commandline[len]) |
| { |
| switch (p[0]) |
| { |
| case L'"': |
| quoted = !quoted; |
| break; |
| case L' ': |
| if (!quoted) |
| { |
| execlen = p - commandline; |
| p = &commandline[len]; |
| found = TRUE; |
| } |
| break; |
| default: |
| break; |
| } |
| p += 1; |
| } |
| |
| if (!found) |
| execlen = len; |
| |
| exepart = g_wcsdup (commandline, (execlen + 1) * sizeof (gunichar2)); |
| exepart[execlen] = L'\0'; |
| |
| p = &exepart[0]; |
| |
| while (execlen > 0 && exepart[0] == L'"' && exepart[execlen - 1] == L'"') |
| { |
| p = &exepart[1]; |
| exepart[execlen - 1] = L'\0'; |
| execlen -= 2; |
| } |
| |
| if (!utf8_and_fold (p, &ex, &ex_folded)) |
| /* Currently no code to handle this case. It shouldn't happen though... */ |
| g_assert_not_reached (); |
| |
| g_free (exepart); |
| |
| if (ex_out) |
| { |
| *ex_out = ex; |
| |
| if (ex_basename_out) |
| { |
| *ex_basename_out = &ex[strlen (ex) - 1]; |
| |
| while (*ex_basename_out > ex) |
| { |
| if ((*ex_basename_out)[0] == '/' || |
| (*ex_basename_out)[0] == '\\') |
| { |
| *ex_basename_out += 1; |
| break; |
| } |
| |
| *ex_basename_out -= 1; |
| } |
| } |
| } |
| else |
| { |
| g_free (ex); |
| } |
| |
| if (ex_folded_out) |
| { |
| *ex_folded_out = ex_folded; |
| |
| if (ex_folded_basename_out) |
| { |
| *ex_folded_basename_out = &ex_folded[strlen (ex_folded) - 1]; |
| |
| while (*ex_folded_basename_out > ex_folded) |
| { |
| if ((*ex_folded_basename_out)[0] == '/' || |
| (*ex_folded_basename_out)[0] == '\\') |
| { |
| *ex_folded_basename_out += 1; |
| break; |
| } |
| |
| *ex_folded_basename_out -= 1; |
| } |
| } |
| } |
| else |
| { |
| g_free (ex_folded); |
| } |
| } |
| |
| static void |
| get_url_association (const gunichar2 *schema) |
| { |
| GWin32AppInfoURLSchema *schema_rec; |
| GWin32AppInfoHandler *handler_rec; |
| GWin32AppInfoHandler *handler_rec_in_url; |
| gchar *schema_u8; |
| gchar *schema_folded; |
| GWin32RegistryKey *user_choice; |
| GWin32RegistryValueType val_type; |
| gunichar2 *program_id; |
| gsize program_id_size; |
| gunichar2 *program_command; |
| gunichar2 *proxy_id; |
| gunichar2 *proxy_command; |
| gchar *program_id_u8; |
| gchar *program_id_folded; |
| GWin32RegistryKey *program_key; |
| GWin32RegistryKey *proxy_key; |
| |
| user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS, |
| schema, USER_CHOICE, |
| NULL); |
| |
| if (user_choice == NULL) |
| return; |
| |
| if (!utf8_and_fold (schema, &schema_u8, &schema_folded)) |
| { |
| g_object_unref (user_choice); |
| return; |
| } |
| |
| schema_rec = g_hash_table_lookup (urls, schema_folded); |
| |
| if (!g_win32_registry_key_get_value_w (user_choice, |
| TRUE, |
| L"Progid", |
| &val_type, |
| (void **) &program_id, |
| &program_id_size, |
| NULL)) |
| { |
| g_free (schema_u8); |
| g_free (schema_folded); |
| g_object_unref (user_choice); |
| return; |
| } |
| |
| if (val_type != G_WIN32_REGISTRY_VALUE_STR) |
| { |
| g_free (schema_u8); |
| g_free (schema_folded); |
| g_free (program_id); |
| g_object_unref (user_choice); |
| return; |
| } |
| |
| program_key = proxy_key = NULL; |
| program_command = proxy_id = proxy_command = NULL; |
| |
| if (!follow_class_chain_to_handler (program_id, |
| program_id_size, |
| &program_command, |
| &program_key, |
| &proxy_id, |
| &proxy_command, |
| &proxy_key, |
| &program_id_u8, |
| &program_id_folded)) |
| { |
| g_free (schema_u8); |
| g_free (schema_folded); |
| g_free (program_id); |
| g_object_unref (user_choice); |
| return; |
| } |
| |
| handler_rec = g_hash_table_lookup (handlers, program_id_folded); |
| |
| if (handler_rec == NULL) |
| { |
| handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL); |
| |
| handler_rec->proxy_key = proxy_key; |
| handler_rec->key = program_key; |
| handler_rec->handler_id = g_wcsdup (program_id, program_id_size); |
| handler_rec->handler_id_folded = |
| g_strdup (program_id_folded); |
| handler_rec->handler_command = |
| program_command ? g_wcsdup (program_command, -1) : NULL; |
| handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL; |
| handler_rec->proxy_command = |
| proxy_command ? g_wcsdup (proxy_command, -1) : NULL; |
| extract_executable (proxy_command ? proxy_command : program_command, |
| &handler_rec->executable, |
| &handler_rec->executable_basename, |
| &handler_rec->executable_folded, |
| NULL); |
| read_handler_icon (proxy_key, program_key, &handler_rec->icon); |
| g_hash_table_insert (handlers, |
| g_strdup (program_id_folded), |
| handler_rec); |
| } |
| else |
| { |
| g_clear_object (&program_key); |
| g_clear_object (&proxy_key); |
| } |
| |
| if (schema_rec == NULL) |
| { |
| schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL); |
| schema_rec->schema = g_wcsdup (schema, -1); |
| schema_rec->schema_u8 = g_strdup (schema_u8); |
| schema_rec->schema_folded = g_strdup (schema_folded); |
| schema_rec->chosen_handler = g_object_ref (handler_rec); |
| g_hash_table_insert (urls, g_strdup (schema_folded), schema_rec); |
| } |
| |
| if (schema_rec->chosen_handler == NULL) |
| schema_rec->chosen_handler = g_object_ref (handler_rec); |
| |
| handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers, |
| program_id_folded); |
| |
| if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec) |
| g_hash_table_insert (schema_rec->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| |
| g_free (schema_u8); |
| g_free (schema_folded); |
| g_free (program_id); |
| g_free (program_id_u8); |
| g_free (program_id_folded); |
| g_free (program_command); |
| g_free (proxy_id); |
| g_free (proxy_command); |
| g_object_unref (user_choice); |
| } |
| |
| static void |
| get_file_ext (const gunichar2 *ext) |
| { |
| GWin32AppInfoFileExtension *file_extn; |
| gboolean file_ext_known; |
| GWin32AppInfoHandler *handler_rec; |
| GWin32AppInfoHandler *handler_rec_in_ext; |
| gchar *ext_u8; |
| gchar *ext_folded; |
| GWin32RegistryKey *user_choice; |
| GWin32RegistryKey *open_with_progids; |
| GWin32RegistryValueType val_type; |
| gsize program_id_size; |
| gboolean found_handler; |
| gunichar2 *program_id; |
| gunichar2 *proxy_id; |
| gchar *program_id_u8; |
| gchar *program_id_folded; |
| GWin32RegistryKey *program_key; |
| GWin32RegistryKey *proxy_key; |
| gunichar2 *program_command; |
| gunichar2 *proxy_command; |
| |
| open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, |
| ext, |
| OPEN_WITH_PROGIDS, |
| NULL); |
| |
| user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, ext, |
| USER_CHOICE, NULL); |
| |
| if (user_choice == NULL && open_with_progids == NULL) |
| return; |
| |
| if (!utf8_and_fold (ext, &ext_u8, &ext_folded)) |
| { |
| g_clear_object (&user_choice); |
| g_clear_object (&open_with_progids); |
| return; |
| } |
| |
| file_extn = NULL; |
| file_ext_known = g_hash_table_lookup_extended (extensions, |
| ext_folded, |
| NULL, |
| (void **) &file_extn); |
| |
| if (!file_ext_known) |
| file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL); |
| |
| found_handler = FALSE; |
| |
| if (user_choice != NULL) |
| { |
| if (g_win32_registry_key_get_value_w (user_choice, |
| TRUE, |
| L"Progid", |
| &val_type, |
| (void **) &program_id, |
| &program_id_size, |
| NULL)) |
| { |
| program_key = proxy_key = NULL; |
| |
| if (val_type == G_WIN32_REGISTRY_VALUE_STR && |
| follow_class_chain_to_handler (program_id, |
| program_id_size, |
| &program_command, |
| &program_key, |
| &proxy_id, |
| &proxy_command, |
| &proxy_key, |
| &program_id_u8, |
| &program_id_folded)) |
| { |
| handler_rec = g_hash_table_lookup (handlers, |
| program_id_folded); |
| |
| if (handler_rec == NULL) |
| { |
| handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, |
| NULL); |
| handler_rec->proxy_key = proxy_key; |
| handler_rec->key = program_key; |
| handler_rec->handler_id = |
| g_wcsdup (program_id, program_id_size); |
| handler_rec->handler_id_folded = |
| g_strdup (program_id_folded); |
| handler_rec->handler_command = |
| program_command ? g_wcsdup (program_command, -1) : NULL; |
| handler_rec->proxy_id = |
| proxy_id ? g_wcsdup (proxy_id, -1) : NULL; |
| handler_rec->proxy_command = |
| proxy_command ? g_wcsdup (proxy_command, -1) : NULL; |
| extract_executable (proxy_command ? proxy_command : program_command, |
| &handler_rec->executable, |
| &handler_rec->executable_basename, |
| &handler_rec->executable_folded, |
| NULL); |
| read_handler_icon (proxy_key, |
| program_key, |
| &handler_rec->icon); |
| g_hash_table_insert (handlers, |
| g_strdup (program_id_folded), |
| handler_rec); |
| } |
| else |
| { |
| g_clear_object (&program_key); |
| g_clear_object (&proxy_key); |
| } |
| |
| found_handler = TRUE; |
| |
| handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers, |
| program_id_folded); |
| |
| if (file_extn->chosen_handler == NULL) |
| { |
| g_hash_table_insert (file_extn->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| } |
| else if (handler_rec_in_ext == NULL) |
| { |
| if (file_extn->chosen_handler->handler_id_folded && |
| strcmp (file_extn->chosen_handler->handler_id_folded, |
| program_id_folded) != 0) |
| g_hash_table_insert (file_extn->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| } |
| |
| g_free (program_id_u8); |
| g_free (program_id_folded); |
| g_free (program_command); |
| g_free (proxy_id); |
| g_free (proxy_command); |
| } |
| |
| g_free (program_id); |
| } |
| |
| g_object_unref (user_choice); |
| } |
| |
| if (open_with_progids != NULL) |
| { |
| GWin32RegistryValueIter iter; |
| |
| if (g_win32_registry_value_iter_init (&iter, open_with_progids, NULL)) |
| { |
| gunichar2 *value_name; |
| gunichar2 *value_data; |
| gsize value_name_len; |
| gsize value_data_size; |
| GWin32RegistryValueType value_type; |
| |
| while (g_win32_registry_value_iter_next (&iter, TRUE, NULL)) |
| { |
| gsize value_name_size; |
| program_key = proxy_key = NULL; |
| |
| if ((!g_win32_registry_value_iter_get_value_type (&iter, |
| &value_type, |
| NULL)) || |
| ((val_type != G_WIN32_REGISTRY_VALUE_STR) && |
| (val_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) || |
| (!g_win32_registry_value_iter_get_name_w (&iter, &value_name, |
| &value_name_len, |
| NULL)) || |
| (value_name_len <= 0) || |
| (!g_win32_registry_value_iter_get_data_w (&iter, TRUE, |
| (void **) &value_data, |
| &value_data_size, |
| NULL)) || |
| (value_data_size < sizeof (gunichar2)) || |
| (value_data[0] == L'\0')) |
| continue; |
| |
| value_name_size = sizeof (gunichar2) * (value_name_len + 1); |
| |
| if (!follow_class_chain_to_handler (value_name, |
| value_name_size, |
| &program_command, |
| &program_key, |
| &proxy_id, |
| &proxy_command, |
| &proxy_key, |
| &program_id_u8, |
| &program_id_folded)) |
| continue; |
| |
| handler_rec = g_hash_table_lookup (handlers, |
| program_id_folded); |
| |
| if (handler_rec == NULL) |
| { |
| handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL); |
| |
| handler_rec->proxy_key = proxy_key; |
| handler_rec->key = program_key; |
| handler_rec->handler_id = |
| g_wcsdup (value_name, value_name_size); |
| handler_rec->handler_id_folded = |
| g_strdup (program_id_folded); |
| handler_rec->handler_command = |
| program_command ? g_wcsdup (program_command, -1) : NULL; |
| handler_rec->proxy_id = |
| proxy_id ? g_wcsdup (proxy_id, -1) : NULL; |
| handler_rec->proxy_command = |
| proxy_command ? g_wcsdup (proxy_command, -1) : NULL; |
| extract_executable (proxy_command ? proxy_command : program_command, |
| &handler_rec->executable, |
| &handler_rec->executable_basename, |
| &handler_rec->executable_folded, |
| NULL); |
| read_handler_icon (proxy_key, |
| program_key, |
| &handler_rec->icon); |
| g_hash_table_insert (handlers, |
| g_strdup (program_id_folded), |
| handler_rec); |
| } |
| else |
| { |
| g_clear_object (&program_key); |
| g_clear_object (&proxy_key); |
| } |
| |
| found_handler = TRUE; |
| |
| handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers, |
| program_id_folded); |
| |
| if (handler_rec_in_ext == NULL) |
| { |
| if (file_extn->chosen_handler == NULL) |
| g_hash_table_insert (file_extn->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| else if (file_extn->chosen_handler->handler_id_folded && |
| strcmp (file_extn->chosen_handler->handler_id_folded, |
| program_id_folded) != 0) |
| g_hash_table_insert (file_extn->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| } |
| |
| g_free (program_id_u8); |
| g_free (program_id_folded); |
| g_free (program_command); |
| g_free (proxy_id); |
| g_free (proxy_command); |
| } |
| |
| g_win32_registry_value_iter_clear (&iter); |
| } |
| |
| g_object_unref (open_with_progids); |
| } |
| |
| if (!found_handler) |
| { |
| if (!file_ext_known) |
| g_object_unref (file_extn); |
| } |
| else if (!file_ext_known) |
| { |
| file_extn->extension = g_wcsdup (ext, -1); |
| file_extn->extension_u8 = g_strdup (ext_u8); |
| g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn); |
| } |
| |
| g_free (ext_u8); |
| g_free (ext_folded); |
| } |
| |
| static void |
| collect_capable_apps_from_clients (GPtrArray *capable_apps, |
| GPtrArray *priority_capable_apps, |
| gboolean user_registry) |
| { |
| GWin32RegistryKey *clients; |
| GWin32RegistrySubkeyIter clients_iter; |
| |
| gunichar2 *client_type_name; |
| gsize client_type_name_len; |
| |
| |
| if (user_registry) |
| clients = |
| g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients", |
| NULL); |
| else |
| clients = |
| g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients", |
| NULL); |
| |
| if (clients == NULL) |
| return; |
| |
| if (!g_win32_registry_subkey_iter_init (&clients_iter, clients, NULL)) |
| { |
| g_object_unref (clients); |
| return; |
| } |
| |
| while (g_win32_registry_subkey_iter_next (&clients_iter, TRUE, NULL)) |
| { |
| GWin32RegistrySubkeyIter subkey_iter; |
| GWin32RegistryKey *system_client_type; |
| GWin32RegistryValueType default_type; |
| gunichar2 *default_value; |
| gunichar2 *client_name; |
| gsize client_name_len; |
| |
| if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter, |
| &client_type_name, |
| &client_type_name_len, |
| NULL)) |
| continue; |
| |
| system_client_type = g_win32_registry_key_get_child_w (clients, |
| client_type_name, |
| NULL); |
| |
| if (system_client_type == NULL) |
| continue; |
| |
| if (g_win32_registry_key_get_value_w (system_client_type, |
| TRUE, |
| L"", |
| &default_type, |
| (gpointer *) &default_value, |
| NULL, |
| NULL)) |
| { |
| if (default_type != G_WIN32_REGISTRY_VALUE_STR || |
| default_value[0] == L'\0') |
| g_clear_pointer (&default_value, g_free); |
| } |
| |
| if (!g_win32_registry_subkey_iter_init (&subkey_iter, |
| system_client_type, |
| NULL)) |
| { |
| g_clear_pointer (&default_value, g_free); |
| g_object_unref (system_client_type); |
| continue; |
| } |
| |
| while (g_win32_registry_subkey_iter_next (&subkey_iter, TRUE, NULL)) |
| { |
| GWin32RegistryKey *system_client; |
| GWin32RegistryKey *system_client_assoc; |
| gboolean add; |
| gunichar2 *keyname; |
| |
| if (!g_win32_registry_subkey_iter_get_name_w (&subkey_iter, |
| &client_name, |
| &client_name_len, |
| NULL)) |
| continue; |
| |
| system_client = g_win32_registry_key_get_child_w (system_client_type, |
| client_name, |
| NULL); |
| |
| if (system_client == NULL) |
| continue; |
| |
| add = FALSE; |
| |
| system_client_assoc = g_win32_registry_key_get_child_w (system_client, |
| L"Capabilities\\FileAssociations", |
| NULL); |
| |
| if (system_client_assoc != NULL) |
| { |
| add = TRUE; |
| g_object_unref (system_client_assoc); |
| } |
| else |
| { |
| system_client_assoc = g_win32_registry_key_get_child_w (system_client, |
| L"Capabilities\\UrlAssociations", |
| NULL); |
| |
| if (system_client_assoc != NULL) |
| { |
| add = TRUE; |
| g_object_unref (system_client_assoc); |
| } |
| } |
| |
| if (add) |
| { |
| keyname = g_wcsdup (g_win32_registry_key_get_path_w (system_client), -1); |
| |
| if (default_value && wcscmp (default_value, client_name) == 0) |
| g_ptr_array_add (priority_capable_apps, keyname); |
| else |
| g_ptr_array_add (capable_apps, keyname); |
| } |
| |
| g_object_unref (system_client); |
| } |
| |
| g_win32_registry_subkey_iter_clear (&subkey_iter); |
| g_clear_pointer (&default_value, g_free); |
| g_object_unref (system_client_type); |
| } |
| |
| g_win32_registry_subkey_iter_clear (&clients_iter); |
| g_object_unref (clients); |
| } |
| |
| static void |
| collect_capable_apps_from_registered_apps (GPtrArray *capable_apps, |
| gboolean user_registry) |
| { |
| GWin32RegistryValueIter iter; |
| |
| gunichar2 *value_data; |
| gsize value_data_size; |
| GWin32RegistryValueType value_type; |
| GWin32RegistryKey *registered_apps; |
| |
| if (user_registry) |
| registered_apps = |
| g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications", |
| NULL); |
| else |
| registered_apps = |
| g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications", |
| NULL); |
| |
| if (!registered_apps) |
| return; |
| |
| if (!g_win32_registry_value_iter_init (&iter, registered_apps, NULL)) |
| { |
| g_object_unref (registered_apps); |
| return; |
| } |
| |
| while (g_win32_registry_value_iter_next (&iter, TRUE, NULL)) |
| { |
| gunichar2 possible_location[REG_PATH_MAX_SIZE + 1]; |
| GWin32RegistryKey *location = NULL; |
| |
| if ((!g_win32_registry_value_iter_get_value_type (&iter, |
| &value_type, |
| NULL)) || |
| (value_type != G_WIN32_REGISTRY_VALUE_STR) || |
| (!g_win32_registry_value_iter_get_data_w (&iter, TRUE, |
| (void **) &value_data, |
| &value_data_size, |
| NULL)) || |
| (value_data_size < sizeof (gunichar2)) || |
| (value_data[0] == L'\0')) |
| continue; |
| |
| if (build_registry_path (possible_location, sizeof (possible_location), |
| HKCU, value_data, NULL)) |
| location = g_win32_registry_key_new_w (possible_location, NULL); |
| |
| if (location) |
| { |
| gunichar2 *p = wcsrchr (possible_location, L'\\'); |
| |
| if (p) |
| *p = L'\0'; |
| |
| g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1)); |
| g_object_unref (location); |
| continue; |
| } |
| |
| if (!build_registry_path (possible_location, sizeof (possible_location), |
| user_registry ? HKCU : HKLM, value_data, NULL)) |
| continue; |
| |
| location = g_win32_registry_key_new_w (possible_location, NULL); |
| |
| if (location) |
| { |
| gunichar2 *p = wcsrchr (possible_location, L'\\'); |
| if (p) |
| *p = L'\0'; |
| g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1)); |
| g_object_unref (location); |
| } |
| } |
| |
| g_win32_registry_value_iter_clear (&iter); |
| g_object_unref (registered_apps); |
| } |
| |
| static void |
| read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolean default_app) |
| { |
| GWin32AppInfoApplication *app; |
| gunichar2 *app_key_path; |
| gunichar2 *canonical_name; |
| gchar *canonical_name_u8; |
| gchar *canonical_name_folded; |
| GWin32RegistryKey *appkey; |
| gunichar2 *fallback_friendly_name; |
| GWin32RegistryValueType vtype; |
| gboolean success; |
| gunichar2 *friendly_name; |
| gunichar2 *description; |
| gunichar2 *narrow_application_name; |
| gunichar2 *icon_source; |
| GWin32RegistryKey *capabilities; |
| GWin32RegistryKey *default_icon_key; |
| GWin32RegistryKey *shell_open_command_key; |
| gunichar2 *shell_open_command; |
| gchar *app_executable; |
| gchar *app_executable_basename; |
| gchar *app_executable_folded; |
| gchar *app_executable_folded_basename; |
| GWin32RegistryKey *associations; |
| |
| app_key_path = g_wcsdup (input_app_key_path, -1); |
| |
| canonical_name = wcsrchr (app_key_path, L'\\'); |
| |
| if (canonical_name == NULL) |
| { |
| /* The key must have at least one '\\' */ |
| g_free (app_key_path); |
| return; |
| } |
| |
| canonical_name += 1; |
| |
| if (!utf8_and_fold (canonical_name, &canonical_name_u8, &canonical_name_folded)) |
| { |
| g_free (app_key_path); |
| return; |
| } |
| |
| appkey = g_win32_registry_key_new_w (app_key_path, NULL); |
| |
| if (appkey == NULL) |
| { |
| g_free (canonical_name_u8); |
| g_free (canonical_name_folded); |
| g_free (app_key_path); |
| return; |
| } |
| |
| capabilities = |
| g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL); |
| |
| if (capabilities == NULL) |
| { |
| /* Must have capabilities */ |
| g_free (canonical_name_u8); |
| g_free (canonical_name_folded); |
| g_free (app_key_path); |
| return; |
| } |
| |
| shell_open_command_key = |
| g_win32_registry_key_get_child_w (appkey, |
| L"shell\\open\\command", |
| NULL); |
| |
| if (shell_open_command_key == NULL) |
| { |
| g_object_unref (capabilities); |
| g_free (canonical_name_u8); |
| g_free (canonical_name_folded); |
| g_free (app_key_path); |
| g_object_unref (appkey); |
| return ; |
| } |
| |
| shell_open_command = NULL; |
| |
| success = g_win32_registry_key_get_value_w (shell_open_command_key, |
| TRUE, |
| L"", |
| &vtype, |
| (gpointer *) &shell_open_command, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| { |
| /* Must have a command */ |
| g_clear_pointer (&shell_open_command, g_free); |
| g_object_unref (capabilities); |
| g_free (canonical_name_u8); |
| g_free (canonical_name_folded); |
| g_free (app_key_path); |
| g_object_unref (appkey); |
| return; |
| } |
| |
| extract_executable (shell_open_command, |
| &app_executable, |
| &app_executable_basename, |
| &app_executable_folded, |
| &app_executable_folded_basename); |
| |
| app = g_hash_table_lookup (apps_by_id, canonical_name_folded); |
| |
| if (app == NULL) |
| { |
| app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL); |
| |
| app->canonical_name = g_wcsdup (canonical_name, -1); |
| app->canonical_name_u8 = g_strdup (canonical_name_u8); |
| app->canonical_name_folded = |
| g_strdup (canonical_name_folded); |
| |
| app->command = g_wcsdup (shell_open_command, -1); |
| app->command_u8 = |
| g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL); |
| app->executable = g_strdup (app_executable); |
| app->executable_basename = |
| &app->executable[app_executable_basename - app_executable]; |
| app->executable_folded = |
| g_strdup (app_executable_folded); |
| |
| app->no_open_with = FALSE; |
| |
| app->user_specific = user_specific; |
| app->default_app = default_app; |
| |
| g_hash_table_insert (apps_by_id, |
| g_strdup (canonical_name_folded), |
| app); |
| } |
| |
| fallback_friendly_name = NULL; |
| success = g_win32_registry_key_get_value_w (appkey, |
| TRUE, |
| L"", |
| &vtype, |
| (void **) &fallback_friendly_name, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| g_clear_pointer (&fallback_friendly_name, g_free); |
| |
| if (fallback_friendly_name && app->pretty_name == NULL) |
| { |
| app->pretty_name = g_wcsdup (fallback_friendly_name, -1); |
| g_clear_pointer (&app->pretty_name_u8, g_free); |
| app->pretty_name_u8 = g_utf16_to_utf8 (fallback_friendly_name, |
| -1, |
| NULL, |
| NULL, |
| NULL); |
| } |
| |
| friendly_name = NULL; |
| success = g_win32_registry_key_get_value_w (capabilities, |
| TRUE, |
| L"LocalizedString", |
| &vtype, |
| (void **) &friendly_name, |
| NULL, |
| NULL); |
| |
| if (success && (vtype != G_WIN32_REGISTRY_VALUE_STR || friendly_name[0] != L'@')) |
| g_clear_pointer (&friendly_name, g_free); |
| |
| friendly_name = read_resource_string (friendly_name); |
| |
| if (friendly_name && app->localized_pretty_name == NULL) |
| { |
| app->localized_pretty_name = g_wcsdup (friendly_name, -1); |
| g_clear_pointer (&app->localized_pretty_name_u8, g_free); |
| app->localized_pretty_name_u8 = g_utf16_to_utf8 (friendly_name, |
| -1, |
| NULL, |
| NULL, |
| NULL); |
| } |
| |
| description = NULL; |
| success = g_win32_registry_key_get_value_w (capabilities, |
| TRUE, |
| L"ApplicationDescription", |
| &vtype, |
| (void **) &description, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| g_clear_pointer (&description, g_free); |
| |
| description = read_resource_string (description); |
| |
| if (description && app->description == NULL) |
| { |
| app->description = g_wcsdup (description, -1); |
| g_clear_pointer (&app->description_u8, g_free); |
| app->description_u8 = g_utf16_to_utf8 (description, -1, NULL, NULL, NULL); |
| } |
| |
| default_icon_key = g_win32_registry_key_get_child_w (appkey, |
| L"DefaultIcon", |
| NULL); |
| |
| icon_source = NULL; |
| |
| if (default_icon_key != NULL) |
| { |
| success = g_win32_registry_key_get_value_w (default_icon_key, |
| TRUE, |
| L"", |
| &vtype, |
| (void **) &icon_source, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| g_clear_pointer (&icon_source, g_free); |
| |
| g_object_unref (default_icon_key); |
| } |
| |
| if (icon_source == NULL) |
| { |
| success = g_win32_registry_key_get_value_w (capabilities, |
| TRUE, |
| L"ApplicationIcon", |
| &vtype, |
| (void **) &icon_source, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| g_clear_pointer (&icon_source, g_free); |
| } |
| |
| if (icon_source && app->icon == NULL) |
| { |
| gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL); |
| app->icon = g_themed_icon_new (name); |
| g_free (name); |
| } |
| |
| narrow_application_name = NULL; |
| success = g_win32_registry_key_get_value_w (capabilities, |
| TRUE, |
| L"ApplicationName", |
| &vtype, |
| (void **) &narrow_application_name, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| g_clear_pointer (&narrow_application_name, g_free); |
| |
| narrow_application_name = read_resource_string (narrow_application_name); |
| |
| /* TODO: do something with the narrow name. Maybe make a kind of sub-app? |
| * Narrow name is a more precise name of the application in given context. |
| * I.e. Thunderbird's name is "Thunderbird", whereas its narrow name is |
| * "Thunderbird (news)" when registering it as a news client. |
| * Maybe we should consider applications with different narrow names as |
| * different applications altogether? |
| */ |
| |
| associations = g_win32_registry_key_get_child_w (capabilities, |
| L"FileAssociations", |
| NULL); |
| |
| if (associations != NULL) |
| { |
| GWin32RegistryValueIter iter; |
| |
| if (g_win32_registry_value_iter_init (&iter, associations, NULL)) |
| { |
| gunichar2 *file_extension; |
| gunichar2 *extension_handler; |
| gsize file_extension_len; |
| gsize extension_handler_size; |
| GWin32RegistryValueType value_type; |
| |
| while (g_win32_registry_value_iter_next (&iter, TRUE, NULL)) |
| { |
| GWin32AppInfoHandler *handler_rec; |
| GWin32AppInfoHandler *handler_rec_in_ext; |
| GWin32AppInfoFileExtension *ext; |
| gunichar2 *program_command; |
| gunichar2 *proxy_id; |
| gunichar2 *proxy_command; |
| GWin32RegistryKey *program_key; |
| GWin32RegistryKey *proxy_key; |
| gchar *program_id_u8; |
| gchar *program_id_folded; |
| gchar *file_extension_u8; |
| gchar *file_extension_folded; |
| |
| if ((!g_win32_registry_value_iter_get_value_type (&iter, |
| &value_type, |
| NULL)) || |
| (value_type != G_WIN32_REGISTRY_VALUE_STR) || |
| (!g_win32_registry_value_iter_get_name_w (&iter, |
| &file_extension, |
| &file_extension_len, |
| NULL)) || |
| (file_extension_len <= 0) || |
| (file_extension[0] != L'.') || |
| (!g_win32_registry_value_iter_get_data_w (&iter, TRUE, |
| (void **) &extension_handler, |
| &extension_handler_size, |
| NULL)) || |
| (extension_handler_size < sizeof (gunichar2)) || |
| (extension_handler[0] == L'\0')) |
| continue; |
| |
| if (!follow_class_chain_to_handler (extension_handler, |
| extension_handler_size, |
| &program_command, |
| &program_key, |
| &proxy_id, |
| &proxy_command, |
| &proxy_key, |
| &program_id_u8, |
| &program_id_folded)) |
| continue; |
| |
| handler_rec = g_hash_table_lookup (handlers, |
| program_id_folded); |
| |
| if (handler_rec == NULL) |
| { |
| handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL); |
| |
| handler_rec->proxy_key = proxy_key; |
| handler_rec->key = program_key; |
| handler_rec->handler_id = |
| g_wcsdup (extension_handler,extension_handler_size); |
| handler_rec->handler_id_folded = |
| g_strdup (program_id_folded); |
| handler_rec->handler_command = |
| program_command ? g_wcsdup (program_command, -1) : NULL; |
| handler_rec->proxy_id = |
| proxy_id ? g_wcsdup (proxy_id, -1) : NULL; |
| handler_rec->proxy_command = |
| proxy_command ? g_wcsdup (proxy_command, -1) : NULL; |
| extract_executable (proxy_command ? proxy_command : program_command, |
| &handler_rec->executable, |
| &handler_rec->executable_basename, |
| &handler_rec->executable_folded, |
| NULL); |
| read_handler_icon (proxy_key, |
| program_key, |
| &handler_rec->icon); |
| g_hash_table_insert (handlers, |
| g_strdup (program_id_folded), |
| handler_rec); |
| } |
| else |
| { |
| g_clear_object (&program_key); |
| g_clear_object (&proxy_key); |
| } |
| |
| if (utf8_and_fold (file_extension, |
| &file_extension_u8, |
| &file_extension_folded)) |
| { |
| ext = g_hash_table_lookup (extensions, |
| file_extension_folded); |
| |
| if (ext == NULL) |
| { |
| ext = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL); |
| |
| ext->extension = g_wcsdup (file_extension, -1); |
| ext->extension_u8 = g_strdup (file_extension_u8); |
| g_hash_table_insert (extensions, g_strdup (file_extension_folded), ext); |
| } |
| |
| handler_rec_in_ext = |
| g_hash_table_lookup (ext->handlers, |
| program_id_folded); |
| |
| if (handler_rec_in_ext == NULL) |
| { |
| if (ext->chosen_handler == NULL) |
| g_hash_table_insert (ext->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| else if (ext->chosen_handler->handler_id_folded && |
| strcmp (ext->chosen_handler->handler_id_folded, |
| program_id_folded) != 0) |
| g_hash_table_insert (ext->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| } |
| |
| handler_rec_in_ext = |
| g_hash_table_lookup (app->supported_exts, |
| file_extension_folded); |
| |
| if (handler_rec_in_ext == NULL) |
| g_hash_table_insert (app->supported_exts, |
| g_strdup (file_extension_folded), |
| g_object_ref (handler_rec)); |
| |
| g_free (file_extension_u8); |
| g_free (file_extension_folded); |
| } |
| |
| g_free (program_id_u8); |
| g_free (program_id_folded); |
| g_free (program_command); |
| g_free (proxy_id); |
| g_free (proxy_command); |
| } |
| |
| g_win32_registry_value_iter_clear (&iter); |
| } |
| |
| g_object_unref (associations); |
| } |
| |
| associations = g_win32_registry_key_get_child_w (capabilities, L"URLAssociations", NULL); |
| |
| if (associations != NULL) |
| { |
| GWin32RegistryValueIter iter; |
| |
| if (g_win32_registry_value_iter_init (&iter, associations, NULL)) |
| { |
| gunichar2 *url_schema; |
| gunichar2 *schema_handler; |
| gsize url_schema_len; |
| gsize schema_handler_size; |
| GWin32RegistryValueType value_type; |
| |
| while (g_win32_registry_value_iter_next (&iter, TRUE, NULL)) |
| { |
| GWin32AppInfoHandler *handler_rec; |
| GWin32AppInfoHandler *handler_rec_in_url; |
| GWin32AppInfoURLSchema *schema; |
| gunichar2 *program_command; |
| gunichar2 *proxy_id; |
| gunichar2 *proxy_command; |
| GWin32RegistryKey *program_key; |
| GWin32RegistryKey *proxy_key; |
| gchar *program_id_u8; |
| gchar *program_id_folded; |
| gchar *schema_u8; |
| gchar *schema_folded; |
| |
| if ((!g_win32_registry_value_iter_get_value_type (&iter, |
| &value_type, |
| NULL)) || |
| ((value_type != G_WIN32_REGISTRY_VALUE_STR) && |
| (value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) || |
| (!g_win32_registry_value_iter_get_name_w (&iter, |
| &url_schema, |
| &url_schema_len, |
| NULL)) || |
| (url_schema_len <= 0) || |
| (url_schema[0] == L'\0') || |
| (!g_win32_registry_value_iter_get_data_w (&iter, TRUE, |
| (void **) &schema_handler, |
| &schema_handler_size, |
| NULL)) || |
| (schema_handler_size < sizeof (gunichar2)) || |
| (schema_handler[0] == L'\0')) |
| continue; |
| |
| if (!follow_class_chain_to_handler (schema_handler, |
| schema_handler_size, |
| &program_command, |
| &program_key, |
| &proxy_id, |
| &proxy_command, |
| &proxy_key, |
| &program_id_u8, |
| &program_id_folded)) |
| continue; |
| |
| |
| handler_rec = g_hash_table_lookup (handlers, program_id_folded); |
| |
| if (handler_rec == NULL) |
| { |
| handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL); |
| |
| handler_rec->proxy_key = proxy_key; |
| handler_rec->key = program_key; |
| handler_rec->handler_id = |
| g_wcsdup (schema_handler, schema_handler_size); |
| handler_rec->handler_id_folded = |
| g_strdup (program_id_folded); |
| handler_rec->handler_command = program_command ? |
| g_wcsdup (program_command, -1) : NULL; |
| handler_rec->proxy_id = |
| proxy_id ? g_wcsdup (proxy_id, -1) : NULL; |
| handler_rec->proxy_command = |
| proxy_command ? g_wcsdup (proxy_command, -1) : NULL; |
| extract_executable (proxy_command ? proxy_command : program_command, |
| &handler_rec->executable, |
| &handler_rec->executable_basename, |
| &handler_rec->executable_folded, |
| NULL); |
| read_handler_icon (proxy_key, |
| program_key, |
| &handler_rec->icon); |
| g_hash_table_insert (handlers, |
| g_strdup (program_id_folded), |
| handler_rec); |
| } |
| else |
| { |
| g_clear_object (&program_key); |
| g_clear_object (&proxy_key); |
| } |
| |
| if (utf8_and_fold (url_schema, |
| &schema_u8, |
| &schema_folded)) |
| { |
| schema = g_hash_table_lookup (urls, |
| schema_folded); |
| |
| if (schema == NULL) |
| { |
| schema = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL); |
| |
| schema->schema = g_wcsdup (url_schema, -1); |
| schema->schema_u8 = g_strdup (schema_u8); |
| schema->schema_folded = |
| g_strdup (schema_folded); |
| g_hash_table_insert (urls, |
| g_strdup (schema_folded), |
| schema); |
| } |
| |
| handler_rec_in_url = |
| g_hash_table_lookup (schema->handlers, |
| program_id_folded); |
| |
| if (handler_rec_in_url == NULL) |
| g_hash_table_insert (schema->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| |
| handler_rec_in_url = |
| g_hash_table_lookup (app->supported_urls, |
| schema_folded); |
| |
| if (handler_rec_in_url == NULL) |
| g_hash_table_insert (app->supported_urls, |
| g_strdup (schema_folded), |
| g_object_ref (handler_rec)); |
| |
| g_free (schema_u8); |
| g_free (schema_folded); |
| } |
| |
| g_free (program_id_u8); |
| g_free (program_id_folded); |
| g_free (program_command); |
| g_free (proxy_id); |
| g_free (proxy_command); |
| } |
| |
| g_win32_registry_value_iter_clear (&iter); |
| } |
| |
| g_object_unref (associations); |
| } |
| |
| g_clear_pointer (&app_executable, g_free); |
| g_clear_pointer (&app_executable_folded, g_free); |
| g_clear_pointer (&fallback_friendly_name, g_free); |
| g_clear_pointer (&description, g_free); |
| g_clear_pointer (&icon_source, g_free); |
| g_clear_pointer (&narrow_application_name, g_free); |
| g_clear_pointer (&shell_open_command, g_free); |
| |
| g_object_unref (appkey); |
| g_object_unref (shell_open_command_key); |
| g_object_unref (capabilities); |
| g_free (canonical_name_u8); |
| g_free (canonical_name_folded); |
| g_free (app_key_path); |
| } |
| |
| static void |
| read_urls (GWin32RegistryKey *url_associations) |
| { |
| GWin32RegistrySubkeyIter url_iter; |
| gunichar2 *url_schema; |
| gsize url_schema_len; |
| |
| if (url_associations == NULL) |
| return; |
| |
| if (!g_win32_registry_subkey_iter_init (&url_iter, url_associations, NULL)) |
| return; |
| |
| while (g_win32_registry_subkey_iter_next (&url_iter, TRUE, NULL)) |
| { |
| if (!g_win32_registry_subkey_iter_get_name_w (&url_iter, |
| &url_schema, |
| &url_schema_len, |
| NULL)) |
| continue; |
| |
| get_url_association (url_schema); |
| } |
| |
| g_win32_registry_subkey_iter_clear (&url_iter); |
| } |
| |
| static void |
| read_exeapps (void) |
| { |
| GWin32RegistryKey *applications_key; |
| GWin32RegistrySubkeyIter app_iter; |
| gunichar2 *app_exe_basename; |
| gsize app_exe_basename_len; |
| |
| applications_key = |
| g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL); |
| |
| if (applications_key == NULL) |
| return; |
| |
| if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL)) |
| { |
| g_object_unref (applications_key); |
| return; |
| } |
| |
| while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL)) |
| { |
| GWin32RegistryKey *incapable_app; |
| gunichar2 *friendly_app_name; |
| gboolean success; |
| gboolean no_open_with; |
| GWin32RegistryValueType vtype; |
| GWin32RegistryKey *default_icon_key; |
| gunichar2 *icon_source; |
| GIcon *icon = NULL; |
| gchar *appexe; |
| gchar *appexe_basename; |
| gchar *appexe_folded; |
| gchar *appexe_folded_basename; |
| GWin32AppInfoApplication *app; |
| GWin32RegistryKey *shell_open_command_key; |
| gunichar2 *shell_open_command; |
| GWin32RegistryKey *supported_key; |
| |
| if (!g_win32_registry_subkey_iter_get_name_w (&app_iter, |
| &app_exe_basename, |
| &app_exe_basename_len, |
| NULL)) |
| continue; |
| |
| incapable_app = |
| g_win32_registry_key_get_child_w (applications_key, |
| app_exe_basename, |
| NULL); |
| |
| if (incapable_app == NULL) |
| continue; |
| |
| extract_executable (app_exe_basename, |
| &appexe, |
| &appexe_basename, |
| &appexe_folded, |
| &appexe_folded_basename); |
| |
| shell_open_command_key = |
| g_win32_registry_key_get_child_w (incapable_app, |
| L"shell\\open\\command", |
| NULL); |
| |
| shell_open_command = NULL; |
| |
| if (shell_open_command_key != NULL) |
| { |
| success = g_win32_registry_key_get_value_w (shell_open_command_key, |
| TRUE, |
| L"", |
| &vtype, |
| (gpointer *) &shell_open_command, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| { |
| g_clear_pointer (&shell_open_command, g_free); |
| } |
| |
| g_object_unref (shell_open_command_key); |
| } |
| |
| friendly_app_name = NULL; |
| success = g_win32_registry_key_get_value_w (incapable_app, |
| TRUE, |
| L"FriendlyAppName", |
| &vtype, |
| (void **) &friendly_app_name, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| g_clear_pointer (&friendly_app_name, g_free); |
| |
| friendly_app_name = read_resource_string (friendly_app_name); |
| |
| no_open_with = FALSE; |
| success = g_win32_registry_key_get_value_w (incapable_app, |
| TRUE, |
| L"NoOpenWith", |
| &vtype, |
| NULL, |
| NULL, |
| NULL); |
| |
| if (success) |
| no_open_with = TRUE; |
| |
| default_icon_key = |
| g_win32_registry_key_get_child_w (incapable_app, |
| L"DefaultIcon", |
| NULL); |
| |
| icon_source = NULL; |
| |
| if (default_icon_key != NULL) |
| { |
| success = |
| g_win32_registry_key_get_value_w (default_icon_key, |
| TRUE, |
| L"", |
| &vtype, |
| (void **) &icon_source, |
| NULL, |
| NULL); |
| |
| if (success && vtype != G_WIN32_REGISTRY_VALUE_STR) |
| g_clear_pointer (&icon_source, g_free); |
| |
| g_object_unref (default_icon_key); |
| } |
| |
| if (icon_source) |
| { |
| gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL); |
| icon = g_themed_icon_new (name); |
| g_free (name); |
| } |
| |
| app = g_hash_table_lookup (apps_by_exe, appexe_folded_basename); |
| |
| if (app == NULL) |
| { |
| app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL); |
| |
| app->command = |
| shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL; |
| |
| if (shell_open_command) |
| app->command_u8 = g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL); |
| |
| app->executable = g_strdup (appexe); |
| app->executable_basename = &app->executable[appexe_basename - appexe]; |
| app->executable_folded = g_strdup (appexe_folded); |
| |
| app->no_open_with = no_open_with; |
| |
| if (friendly_app_name) |
| { |
| app->localized_pretty_name = g_wcsdup (friendly_app_name, -1); |
| g_clear_pointer (&app->localized_pretty_name_u8, g_free); |
| app->localized_pretty_name_u8 = |
| g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL); |
| } |
| |
| if (icon) |
| app->icon = g_object_ref (icon); |
| |
| app->user_specific = FALSE; |
| app->default_app = FALSE; |
| |
| g_hash_table_insert (apps_by_exe, |
| g_strdup (appexe_folded_basename), |
| app); |
| } |
| |
| supported_key = |
| g_win32_registry_key_get_child_w (incapable_app, |
| L"SupportedTypes", |
| NULL); |
| |
| if (supported_key) |
| { |
| GWin32RegistryValueIter sup_iter; |
| if (g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL)) |
| { |
| gunichar2 *ext_name; |
| gsize ext_name_len; |
| |
| while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL)) |
| { |
| gchar *ext_u8; |
| gchar *ext_folded; |
| GWin32AppInfoFileExtension *file_extn; |
| gboolean file_ext_known; |
| |
| if ((!g_win32_registry_value_iter_get_name_w (&sup_iter, |
| &ext_name, |
| &ext_name_len, |
| NULL)) || |
| (ext_name_len <= 0) || |
| (ext_name[0] != L'.') || |
| (!utf8_and_fold (ext_name, |
| &ext_u8, |
| &ext_folded))) |
| continue; |
| |
| file_extn = NULL; |
| file_ext_known = |
| g_hash_table_lookup_extended (extensions, |
| ext_folded, |
| NULL, |
| (void **) &file_extn); |
| |
| if (!file_ext_known) |
| { |
| file_extn = |
| g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL); |
| file_extn->extension = g_wcsdup (ext_name, -1); |
| file_extn->extension_u8 = g_strdup (ext_u8); |
| g_hash_table_insert (extensions, |
| g_strdup (ext_folded), |
| file_extn); |
| } |
| |
| g_hash_table_insert (file_extn->other_apps, |
| g_strdup (appexe_folded), |
| g_object_ref (app)); |
| |
| g_free (ext_u8); |
| g_free (ext_folded); |
| } |
| |
| g_win32_registry_value_iter_clear (&sup_iter); |
| } |
| |
| g_object_unref (supported_key); |
| } |
| |
| |
| g_free (appexe); |
| g_free (appexe_folded); |
| g_free (shell_open_command); |
| g_free (friendly_app_name); |
| g_free (icon_source); |
| |
| g_clear_object (&icon); |
| g_clear_object (&incapable_app); |
| } |
| |
| g_win32_registry_subkey_iter_clear (&app_iter); |
| g_object_unref (applications_key); |
| } |
| |
| |
| static void |
| read_exts (GWin32RegistryKey *file_exts) |
| { |
| GWin32RegistrySubkeyIter ext_iter; |
| gunichar2 *file_extension; |
| gsize file_extension_len; |
| |
| if (file_exts == NULL) |
| return; |
| |
| if (!g_win32_registry_subkey_iter_init (&ext_iter, file_exts, NULL)) |
| return; |
| |
| while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL)) |
| { |
| if (!g_win32_registry_subkey_iter_get_name_w (&ext_iter, |
| &file_extension, |
| &file_extension_len, |
| NULL)) |
| continue; |
| |
| get_file_ext (file_extension); |
| } |
| |
| g_win32_registry_subkey_iter_clear (&ext_iter); |
| } |
| |
| static void |
| read_class_extension (GWin32RegistryKey *classes_root, |
| gunichar2 *class_name, |
| gsize class_name_len) |
| { |
| gchar *ext_u8; |
| gchar *ext_folded; |
| GWin32AppInfoFileExtension *file_extn; |
| GWin32AppInfoHandler *handler_rec; |
| GWin32AppInfoHandler *handler_rec_in_ext; |
| GWin32RegistryKey *class_key; |
| gsize program_id_size; |
| gunichar2 *program_id; |
| gunichar2 *proxy_id; |
| GWin32RegistryKey *program_key; |
| GWin32RegistryKey *proxy_key; |
| gunichar2 *program_command; |
| gunichar2 *proxy_command; |
| |
| class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL); |
| |
| if (class_key == NULL) |
| return; |
| |
| program_id = class_name; |
| program_id_size = (class_name_len + 1) * sizeof (gunichar2); |
| program_key = proxy_key = NULL; |
| program_command = proxy_command = NULL; |
| |
| if (!follow_class_chain_to_handler (program_id, |
| program_id_size, |
| &program_command, |
| &program_key, |
| &proxy_id, |
| &proxy_command, |
| &proxy_key, |
| &ext_u8, |
| &ext_folded)) |
| { |
| g_object_unref (class_key); |
| return; |
| } |
| |
| |
| file_extn = g_hash_table_lookup (extensions, ext_folded); |
| handler_rec = g_hash_table_lookup (handlers, ext_folded); |
| |
| if (file_extn == NULL) |
| { |
| file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL); |
| file_extn->extension = g_wcsdup (class_name, -1); |
| file_extn->extension_u8 = g_strdup (ext_u8); |
| g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn); |
| } |
| |
| if (handler_rec == NULL) |
| { |
| handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL); |
| |
| handler_rec->proxy_key = proxy_key; |
| handler_rec->key = program_key; |
| handler_rec->handler_id = g_wcsdup (program_id, program_id_size); |
| handler_rec->handler_id_folded = g_strdup (ext_folded); |
| handler_rec->handler_command = |
| program_command ? g_wcsdup (program_command, -1) : NULL; |
| handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL; |
| handler_rec->proxy_command = |
| proxy_command ? g_wcsdup (proxy_command, -1) : NULL; |
| extract_executable (proxy_command ? proxy_command : program_command, |
| &handler_rec->executable, |
| &handler_rec->executable_basename, |
| &handler_rec->executable_folded, |
| NULL); |
| read_handler_icon (proxy_key, program_key, &handler_rec->icon); |
| g_hash_table_insert (handlers, |
| g_strdup (ext_folded), |
| handler_rec); |
| } |
| else |
| { |
| g_clear_object (&program_key); |
| g_clear_object (&proxy_key); |
| } |
| |
| handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers, |
| ext_folded); |
| |
| if (file_extn->chosen_handler == NULL) |
| g_hash_table_insert (file_extn->handlers, |
| g_strdup (ext_folded), |
| g_object_ref (handler_rec)); |
| else if (handler_rec_in_ext == NULL) |
| { |
| if (file_extn->chosen_handler->handler_id_folded && |
| strcmp (file_extn->chosen_handler->handler_id_folded, |
| ext_folded) != 0) |
| g_hash_table_insert (file_extn->handlers, |
| g_strdup (ext_folded), |
| g_object_ref (handler_rec)); |
| } |
| |
| g_free (program_command); |
| g_free (proxy_id); |
| g_free (proxy_command); |
| g_free (ext_u8); |
| g_free (ext_folded); |
| g_object_unref (class_key); |
| } |
| |
| static void |
| read_class_url (GWin32RegistryKey *classes_root, |
| gunichar2 *class_name, |
| gsize class_name_len) |
| { |
| GWin32RegistryKey *class_key; |
| gboolean success; |
| GWin32RegistryValueType vtype; |
| GWin32AppInfoURLSchema *schema_rec; |
| GWin32AppInfoHandler *handler_rec; |
| GWin32AppInfoHandler *handler_rec_in_url; |
| gunichar2 *program_id; |
| gsize program_id_size; |
| gunichar2 *program_command; |
| gunichar2 *proxy_id; |
| gunichar2 *proxy_command; |
| gchar *program_id_u8; |
| gchar *program_id_folded; |
| GWin32RegistryKey *program_key; |
| GWin32RegistryKey *proxy_key; |
| |
| class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL); |
| |
| if (class_key == NULL) |
| return; |
| |
| success = g_win32_registry_key_get_value_w (class_key, |
| TRUE, |
| L"URL Protocol", |
| &vtype, |
| NULL, |
| NULL, |
| NULL); |
| |
| if (!success || |
| vtype != G_WIN32_REGISTRY_VALUE_STR) |
| { |
| g_object_unref (class_key); |
| return; |
| } |
| |
| program_id = class_name; |
| program_id_size = (class_name_len + 1) * sizeof (gunichar2); |
| proxy_key = program_key = NULL; |
| program_command = proxy_id = proxy_command = NULL; |
| |
| if (!follow_class_chain_to_handler (program_id, |
| program_id_size, |
| &program_command, |
| &program_key, |
| &proxy_id, |
| &proxy_command, |
| &proxy_key, |
| &program_id_u8, |
| &program_id_folded)) |
| { |
| g_object_unref (class_key); |
| return; |
| } |
| |
| schema_rec = g_hash_table_lookup (urls, program_id_folded); |
| handler_rec = g_hash_table_lookup (handlers, program_id_folded); |
| |
| if (handler_rec == NULL) |
| { |
| handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL); |
| |
| handler_rec->proxy_key = proxy_key; |
| handler_rec->key = program_key; |
| handler_rec->handler_id = g_wcsdup (program_id, program_id_size); |
| handler_rec->handler_id_folded = |
| g_strdup (program_id_folded); |
| handler_rec->handler_command = |
| program_command ? g_wcsdup (program_command, -1) : NULL; |
| handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL; |
| handler_rec->proxy_command = |
| proxy_command ? g_wcsdup (proxy_command, -1) : NULL; |
| extract_executable (proxy_command ? proxy_command : program_command, |
| &handler_rec->executable, |
| &handler_rec->executable_basename, |
| &handler_rec->executable_folded, |
| NULL); |
| read_handler_icon (proxy_key, program_key, &handler_rec->icon); |
| g_hash_table_insert (handlers, |
| g_strdup (program_id_folded), |
| handler_rec); |
| } |
| else |
| { |
| g_clear_object (&program_key); |
| g_clear_object (&proxy_key); |
| } |
| |
| if (schema_rec == NULL) |
| { |
| schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL); |
| schema_rec->schema = g_wcsdup (class_name, -1); |
| schema_rec->schema_u8 = g_strdup (program_id_u8); |
| schema_rec->schema_folded = g_strdup (program_id_folded); |
| schema_rec->chosen_handler = g_object_ref (handler_rec); |
| g_hash_table_insert (urls, |
| g_strdup (program_id_folded), |
| schema_rec); |
| } |
| |
| if (schema_rec->chosen_handler == NULL) |
| schema_rec->chosen_handler = g_object_ref (handler_rec); |
| |
| handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers, |
| program_id_folded); |
| |
| if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec) |
| g_hash_table_insert (schema_rec->handlers, |
| g_strdup (program_id_folded), |
| g_object_ref (handler_rec)); |
| |
| g_free (program_id_u8); |
| g_free (program_id_folded); |
| g_free (program_command); |
| g_free (proxy_id); |
| g_free (proxy_command); |
| g_object_unref (class_key); |
| } |
| |
| static void |
| read_classes (GWin32RegistryKey *classes_root) |
| { |
| GWin32RegistrySubkeyIter class_iter; |
| gunichar2 *class_name; |
| gsize class_name_len; |
| |
| if (classes_root == NULL) |
| return; |
| |
| if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL)) |
| return; |
| |
| while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL)) |
| { |
| if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter, |
| &class_name, |
| &class_name_len, |
| NULL)) || |
| (class_name_len <= 1)) |
| continue; |
| |
| if (class_name[0] == L'.') |
| read_class_extension (classes_root, class_name, class_name_len); |
| else |
| { |
| gsize i; |
| |
| for (i = 0; i < class_name_len; i++) |
| if (!iswalpha (class_name[i])) |
| break; |
| |
| if (i == class_name_len) |
| read_class_url (classes_root, class_name, class_name_len); |
| } |
| } |
| |
| g_win32_registry_subkey_iter_clear (&class_iter); |
| } |
| |
| static void |
| link_chosen_handlers (void) |
| { |
| GHashTableIter iter; |
| GHashTableIter handler_iter; |
| gchar *schema_folded; |
| GWin32AppInfoURLSchema *schema; |
| gchar *handler_id_folded; |
| GWin32AppInfoHandler *handler; |
| gchar *ext_folded; |
| GWin32AppInfoFileExtension *ext; |
| |
| g_hash_table_iter_init (&iter, urls); |
| |
| while (g_hash_table_iter_next (&iter, |
| (gpointer *) &schema_folded, |
| (gpointer *) &schema)) |
| { |
| if (schema->chosen_handler != NULL) |
| continue; |
| |
| g_hash_table_iter_init (&handler_iter, schema->handlers); |
| |
| while (g_hash_table_iter_next (&handler_iter, |
| (gpointer *) &handler_id_folded, |
| (gpointer *) &handler)) |
| { |
| gchar *proxy_id_folded; |
| |
| if (schema->chosen_handler != NULL) |
| break; |
| |
| if (strcmp (handler_id_folded, schema_folded) != 0) |
| continue; |
| |
| if (handler->proxy_command && |
| handler->proxy_id && |
| utf8_and_fold (handler->proxy_id, |
| NULL, |
| &proxy_id_folded)) |
| { |
| GWin32AppInfoHandler *proxy; |
| |
| proxy = g_hash_table_lookup (handlers, proxy_id_folded); |
| |
| if (proxy) |
| { |
| schema->chosen_handler = g_object_ref (proxy); |
| g_debug ("Linking schema %s to proxy handler %c ? \"%S\" : %S\n", |
| schema->schema_u8, |
| schema->chosen_handler->proxy_id ? 'P' : 'T', |
| schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id, |
| schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command); |
| } |
| |
| g_free (proxy_id_folded); |
| } |
| |
| if (schema->chosen_handler == NULL) |
| { |
| schema->chosen_handler = g_object_ref (handler); |
| g_debug ("Linking schema %s to handler %c ? \"%S\" : %S\n", |
| schema->schema_u8, |
| schema->chosen_handler->proxy_id ? 'P' : 'T', |
| schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id, |
| schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command); |
| } |
| } |
| } |
| |
| g_hash_table_iter_init (&iter, extensions); |
| |
| while (g_hash_table_iter_next (&iter, |
| (gpointer *) &ext_folded, |
| (gpointer *) &ext)) |
| { |
| if (ext->chosen_handler != NULL) |
| continue; |
| |
| g_hash_table_iter_init (&handler_iter, ext->handlers); |
| |
| while (g_hash_table_iter_next (&handler_iter, |
| (gpointer *) &handler_id_folded, |
| (gpointer *) &handler)) |
| { |
| gchar *proxy_id_folded; |
| |
| if (ext->chosen_handler != NULL) |
| break; |
| |
| if (strcmp (handler_id_folded, ext_folded) != 0) |
| continue; |
| |
| if (handler->proxy_command && |
| handler->proxy_id && |
| utf8_and_fold (handler->proxy_id, |
| NULL, |
| &proxy_id_folded)) |
| { |
| GWin32AppInfoHandler *proxy; |
| |
| proxy = g_hash_table_lookup (handlers, proxy_id_folded); |
| |
| if (proxy) |
| { |
| ext->chosen_handler = g_object_ref (proxy); |
| g_debug ("Linking ext %s to proxy handler %c ? \"%S\" : %S\n", |
| ext->extension_u8, |
| ext->chosen_handler->proxy_id ? 'P' : 'T', |
| ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id, |
| ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command); |
| } |
| |
| g_free (proxy_id_folded); |
| } |
| |
| if (ext->chosen_handler == NULL) |
| { |
| ext->chosen_handler = g_object_ref (handler); |
| g_debug ("Linking ext %s to handler %c ? \"%S\" : %S\n", |
| ext->extension_u8, |
| ext->chosen_handler->proxy_id ? 'P' : 'T', |
| ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id, |
| ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command); |
| } |
| } |
| } |
| } |
| |
| static void |
| link_handlers_to_registered_apps (void) |
| { |
| GHashTableIter iter; |
| GHashTableIter sup_iter; |
| gchar *app_id_folded; |
| GWin32AppInfoApplication *app; |
| gchar *schema_folded; |
| GWin32AppInfoURLSchema *schema; |
| gchar *ext_folded; |
| GWin32AppInfoFileExtension *ext; |
| gsize unhandled_exts; |
| |
| g_hash_table_iter_init (&sup_iter, urls); |
| while (g_hash_table_iter_next (&sup_iter, |
| (gpointer *) &schema_folded, |
| (gpointer *) &schema)) |
| { |
| if (schema->chosen_handler == NULL) |
| g_debug ("WARNING: schema %s has no chosen handler\n", schema->schema_u8); |
| } |
| unhandled_exts= 0; |
| g_hash_table_iter_init (&sup_iter, extensions); |
| while (g_hash_table_iter_next (&sup_iter, |
| (gpointer *) &ext_folded, |
| (gpointer *) &ext)) |
| { |
| if (ext->chosen_handler == NULL) |
| { |
| g_debug ("WARNING: extension %s has no chosen handler\n", |
| ext->extension_u8); |
| unhandled_exts += 1; |
| } |
| } |
| |
| g_hash_table_iter_init (&iter, apps_by_id); |
| while (g_hash_table_iter_next (&iter, |
| (gpointer *) &app_id_folded, |
| (gpointer *) &app)) |
| { |
| if (app->supported_urls) |
| { |
| GWin32AppInfoHandler *handler; |
| |
| g_hash_table_iter_init (&sup_iter, app->supported_urls); |
| while (g_hash_table_iter_next (&sup_iter, |
| (gpointer *) &schema_folded, |
| (gpointer *) &handler)) |
| { |
| schema = g_hash_table_lookup (urls, schema_folded); |
| |
| g_assert (schema != NULL); |
| |
| if (schema->chosen_handler != NULL && |
| schema->chosen_handler->app == NULL) |
| { |
| schema->chosen_handler->app = g_object_ref (app); |
| g_debug ("Linking %S", app->canonical_name); |
| |
| if (app->localized_pretty_name) |
| g_debug (" '%S'", app->localized_pretty_name); |
| else if (app->pretty_name) |
| g_debug (" '%S'", app->pretty_name); |
| else |
| g_debug (" '%s'", app->executable); |
| |
| if (app->command) |
| g_debug (" %S", app->command); |
| |
| g_debug ("\n to schema %s handler %c ? \"%S\" : %S\n", |
| schema->schema_u8, |
| schema->chosen_handler->proxy_id ? 'P' : 'T', |
| schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id : schema->chosen_handler->handler_id, |
| schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command : schema->chosen_handler->handler_command); |
| } |
| } |
| |
| g_hash_table_iter_init (&sup_iter, app->supported_urls); |
| while (g_hash_table_iter_next (&sup_iter, |
| (gpointer *) &schema_folded, |
| (gpointer *) &handler)) |
| { |
| if (handler->app == NULL) |
| { |
| handler->app = g_object_ref (app); |
| g_debug ("Linking %S", app->canonical_name); |
| |
| if (app->localized_pretty_name) |
| g_debug (" '%S'", app->localized_pretty_name); |
| else if (app->pretty_name) |
| g_debug (" '%S'", app->pretty_name); |
| else |
| g_debug (" '%s'", app->executable); |
| |
| if (app->command) |
| g_debug (" %S", app->command); |
| |
| g_debug ("\n directly to schema handler to %c ? \"%S\" : %S\n", |
| handler->proxy_id ? 'P' : 'T', |
| handler->proxy_id ? handler->proxy_id : handler->handler_id, |
| handler->proxy_command ? handler->proxy_command : handler->handler_command); |
| } |
| } |
| } |
| |
| if (app->supported_exts) |
| { |
| GWin32AppInfoHandler *handler; |
| |
| g_hash_table_iter_init (&sup_iter, app->supported_exts); |
| while (g_hash_table_iter_next (&sup_iter, |
| (gpointer *) &ext_folded, |
| (gpointer *) &handler)) |
| { |
| ext = g_hash_table_lookup (extensions, ext_folded); |
| |
| g_assert (ext != NULL); |
| |
| if (ext->chosen_handler != NULL && |
| ext->chosen_handler->app == NULL) |
| { |
| ext->chosen_handler->app = g_object_ref (app); |
| g_debug ("Linking %S", app->canonical_name); |
| |
| if (app->localized_pretty_name) |
| g_debug (" '%S'", app->localized_pretty_name); |
| else if (app->pretty_name) |
| g_debug (" '%S'", app->pretty_name); |
| else |
| g_debug (" '%s'", app->executable); |
| |
| if (app->command) |
| g_debug (" %S", app->command); |
| |
| g_debug ("\n to ext %s handler %c ? \"%S\" : %S\n", |
| ext->extension_u8, |
| ext->chosen_handler->proxy_id ? 'P' : 'T', |
| ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id : ext->chosen_handler->handler_id, |
| ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command : ext->chosen_handler->handler_command); |
| } |
| } |
| |
| g_hash_table_iter_init (&sup_iter, app->supported_exts); |
| while (g_hash_table_iter_next (&sup_iter, |
| (gpointer *) &ext_folded, |
| (gpointer *) &handler)) |
| { |
| if (handler->app == NULL) |
| { |
| handler->app = g_object_ref (app); |
| g_debug ("Linking %S", app->canonical_name); |
| |
| if (app->localized_pretty_name) |
| g_debug (" '%S'", app->localized_pretty_name); |
| else if (app->pretty_name) |
| g_debug (" '%S'", app->pretty_name); |
| else |
| g_debug (" '%s'", app->executable); |
| |
| if (app->command) |
| g_debug (" %S", app->command); |
| |
| g_debug ("\n directly to ext handler %c ? \"%S\" : %S\n", |
| handler->proxy_id ? 'P' : 'T', |
| handler->proxy_id ? handler->proxy_id : handler->handler_id, |
| handler->proxy_command ? handler->proxy_command : handler->handler_command); |
| } |
| } |
| } |
| } |
| |
| g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts); |
| unhandled_exts= 0; |
| g_hash_table_iter_init (&sup_iter, extensions); |
| while (g_hash_table_iter_next (&sup_iter, |
| (gpointer *) &ext_folded, |
| (gpointer *) &ext)) |
| { |
| if (ext->chosen_handler == NULL) |
| { |
| g_debug ("WARNING: extension %s has no chosen handler\n", |
| ext->extension_u8); |
| unhandled_exts += 1; |
| } |
| } |
| g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts); |
| } |
| |
| static void |
| link_handlers_to_unregistered_apps (void) |
| { |
| GHashTableIter iter; |
| GHashTableIter app_iter; |
| GWin32AppInfoHandler *handler; |
| gchar *handler_id_fc; |
| GWin32AppInfoApplication *app; |
| gchar *canonical_name_fc; |
| gchar *appexe_fc_basename; |
| |
| g_hash_table_iter_init (&iter, handlers); |
| while (g_hash_table_iter_next (&iter, |
| (gpointer *) &handler_id_fc, |
| (gpointer *) &handler)) |
| { |
| gchar *hndexe_fc_basename; |
| |
| if ((handler->app != NULL) || |
| (handler->executable_folded == NULL)) |
| continue; |
| |
| hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1); |
| |
| if (hndexe_fc_basename == NULL) |
| continue; |
| |
| g_hash_table_iter_init (&app_iter, apps_by_id); |
| |
| while (g_hash_table_iter_next (&app_iter, |
| (gpointer *) &canonical_name_fc, |
| (gpointer *) &app)) |
| { |
| if (app->executable_folded == NULL) |
| continue; |
| |
| if (strcmp (app->executable_folded, |
| handler->executable_folded) != 0) |
| continue; |
| |
| handler->app = app; |
| break; |
| } |
| |
| if (handler->app != NULL) |
| continue; |
| |
| g_hash_table_iter_init (&app_iter, apps_by_exe); |
| |
| while ((hndexe_fc_basename != NULL) && |
| (g_hash_table_iter_next (&app_iter, |
| (gpointer *) &appexe_fc_basename, |
| (gpointer *) &app))) |
| { |
| /* Use basename because apps_by_exe only has basenames */ |
| if (strcmp (hndexe_fc_basename, appexe_fc_basename) != 0) |
| continue; |
| |
| handler->app = app; |
| break; |
| } |
| |
| g_free (hndexe_fc_basename); |
| |
| if (handler->app == NULL) |
| g_debug ("WARNING: handler that runs %s has no corresponding app\n", |
| handler->executable); |
| } |
| } |
| |
| |
| static void |
| update_registry_data (void) |
| { |
| guint i; |
| GPtrArray *capable_apps_keys; |
| GPtrArray *user_capable_apps_keys; |
| GPtrArray *priority_capable_apps_keys; |
| GWin32RegistryKey *url_associations; |
| GWin32RegistryKey *file_exts; |
| GWin32Registr
|