[glib] GWin32AppInfo rewrite
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] GWin32AppInfo rewrite
- Date: Fri, 5 Jun 2015 20:20:35 +0000 (UTC)
commit 4d800e4d86db6825dd3c508c83352b9a4cd24350
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date: Fri Aug 29 08:53:35 2014 +0000
GWin32AppInfo rewrite
- On first call scan the registry, collect information about URI protocols,
file extensions, applications and handlers, store that as a set of
interconnected structures in several hash tables
- Watch the registry keys, re-scan the registry when any one of them changes.
https://bugzilla.gnome.org/show_bug.cgi?id=666831
gio/gwin32appinfo.c | 4875 +++++++++++++++++++++++++++++++++++++++++++++------
gio/gwin32appinfo.h | 4 +-
2 files changed, 4376 insertions(+), 503 deletions(-)
---
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index f1697c8..5b94157 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -1,6 +1,7 @@
/* 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
@@ -15,7 +16,8 @@
* 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/>.
*
- * Author: Alexander Larsson <alexl redhat com>
+ * Authors: Alexander Larsson <alexl redhat com>
+ * Руслан Ижбулатов <lrn1986 gmail com>
*/
#include "config.h"
@@ -29,41 +31,3429 @@
#include "gfile.h"
#include <glib/gstdio.h>
#include "glibintl.h"
+#include <gio/gwin32registrykey.h>
#include <windows.h>
-#include <shlwapi.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;
+
+ 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);
+ }
-#ifndef ASSOCF_INIT_BYEXENAME
-#define ASSOCF_INIT_BYEXENAME 0x00000002
-#endif
+ 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);
+ }
-/* These were wrong in MingW */
-#define REAL_ASSOCSTR_COMMAND 1
-#define REAL_ASSOCSTR_EXECUTABLE 2
-#define REAL_ASSOCSTR_FRIENDLYDOCNAME 3
-#define REAL_ASSOCSTR_FRIENDLYAPPNAME 4
+ 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 ("%u 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 ("%u 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;
+ GWin32RegistryKey *classes_root;
+ DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end,
postproc_end;
+
+ url_associations =
+ g_win32_registry_key_new_w
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
+ NULL);
+ file_exts =
+ g_win32_registry_key_new_w
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
+ NULL);
+ classes_root = g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT", NULL);
+
+ capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
+ user_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
+ priority_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
+
+ g_clear_pointer (&apps_by_id, g_hash_table_destroy);
+ g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
+ g_clear_pointer (&urls, g_hash_table_destroy);
+ g_clear_pointer (&extensions, g_hash_table_destroy);
+ g_clear_pointer (&handlers, g_hash_table_destroy);
+
+ collect_start = GetTickCount ();
+ collect_capable_apps_from_clients (capable_apps_keys,
+ priority_capable_apps_keys,
+ FALSE);
+ collect_capable_apps_from_clients (user_capable_apps_keys,
+ priority_capable_apps_keys,
+ TRUE);
+ collect_capable_apps_from_registered_apps (user_capable_apps_keys,
+ TRUE);
+ collect_capable_apps_from_registered_apps (capable_apps_keys,
+ FALSE);
+ collect_end = GetTickCount ();
+
+ apps_by_id =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ apps_by_exe =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ urls =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ extensions =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ handlers =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ alloc_end = GetTickCount ();
+
+ for (i = 0; i < priority_capable_apps_keys->len; i++)
+ read_capable_app (g_ptr_array_index (priority_capable_apps_keys, i),
+ TRUE,
+ TRUE);
+ for (i = 0; i < user_capable_apps_keys->len; i++)
+ read_capable_app (g_ptr_array_index (user_capable_apps_keys, i),
+ TRUE,
+ FALSE);
+ for (i = 0; i < capable_apps_keys->len; i++)
+ read_capable_app (g_ptr_array_index (capable_apps_keys, i),
+ FALSE,
+ FALSE);
+ capable_end = GetTickCount ();
+
+ read_urls (url_associations);
+ url_end = GetTickCount ();
+ read_exts (file_exts);
+ ext_end = GetTickCount ();
+ read_exeapps ();
+ exeapp_end = GetTickCount ();
+ read_classes (classes_root);
+ classes_end = GetTickCount ();
+ link_chosen_handlers ();
+ link_handlers_to_registered_apps ();
+ link_handlers_to_unregistered_apps ();
+ postproc_end = GetTickCount ();
+
+ g_debug ("Collecting capable appnames: %lums\n"
+ "Allocating hashtables:...... %lums\n"
+ "Reading capable apps: %lums\n"
+ "Reading URL associations:... %lums\n"
+ "Reading extension assocs: %lums\n"
+ "Reading exe-only apps:...... %lums\n"
+ "Reading classes: %lums\n"
+ "Postprocessing:..............%lums\n"
+ "TOTAL: %lums\n",
+ collect_end - collect_start,
+ alloc_end - collect_end,
+ capable_end - alloc_end,
+ url_end - capable_end,
+ ext_end - url_end,
+ exeapp_end - ext_end,
+ classes_end - exeapp_end,
+ postproc_end - classes_end,
+ postproc_end - collect_start);
+
+ g_clear_object (&classes_root);
+ g_clear_object (&url_associations);
+ g_clear_object (&file_exts);
+ g_ptr_array_free (capable_apps_keys, TRUE);
+ g_ptr_array_free (user_capable_apps_keys, TRUE);
+ g_ptr_array_free (priority_capable_apps_keys, TRUE);
+
+ return;
+}
+
+static void
+watch_keys (void)
+{
+ if (url_associations_key)
+ g_win32_registry_key_watch (url_associations_key,
+ TRUE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+
+ if (file_exts_key)
+ g_win32_registry_key_watch (file_exts_key,
+ TRUE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+
+ if (user_clients_key)
+ g_win32_registry_key_watch (user_clients_key,
+ TRUE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+
+ if (system_clients_key)
+ g_win32_registry_key_watch (system_clients_key,
+ TRUE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+
+ if (applications_key)
+ g_win32_registry_key_watch (applications_key,
+ TRUE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+
+ if (user_registered_apps_key)
+ g_win32_registry_key_watch (user_registered_apps_key,
+ TRUE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+
+ if (system_registered_apps_key)
+ g_win32_registry_key_watch (system_registered_apps_key,
+ TRUE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+
+ if (classes_root_key)
+ g_win32_registry_key_watch (classes_root_key,
+ FALSE,
+ G_WIN32_REGISTRY_WATCH_NAME |
+ G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+ G_WIN32_REGISTRY_WATCH_VALUES,
+ NULL,
+ NULL,
+ NULL);
+}
+
+
+static void
+g_win32_appinfo_init (void)
+{
+ static gboolean initialized = FALSE;
+
+ if (g_once_init_enter (&initialized))
+ {
+ url_associations_key =
+ g_win32_registry_key_new_w
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
+ NULL);
+ file_exts_key =
+ g_win32_registry_key_new_w
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
+ NULL);
+ user_clients_key =
+ g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
+ NULL);
+ system_clients_key =
+ g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
+ NULL);
+ applications_key =
+ g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications",
+ NULL);
+ user_registered_apps_key =
+ g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
+ NULL);
+ system_registered_apps_key =
+ g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
+ NULL);
+ classes_root_key =
+ g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT",
+ NULL);
+
+ watch_keys ();
+
+ update_registry_data ();
+
+ g_once_init_leave (&initialized, TRUE);
+ }
+
+ if ((url_associations_key && g_win32_registry_key_has_changed (url_associations_key)) ||
+ (file_exts_key && g_win32_registry_key_has_changed (file_exts_key)) ||
+ (user_clients_key && g_win32_registry_key_has_changed (user_clients_key)) ||
+ (system_clients_key && g_win32_registry_key_has_changed (system_clients_key)) ||
+ (applications_key && g_win32_registry_key_has_changed (applications_key)) ||
+ (user_registered_apps_key && g_win32_registry_key_has_changed (user_registered_apps_key)) ||
+ (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
+ (classes_root_key && g_win32_registry_key_has_changed (classes_root_key)))
+ {
+ G_LOCK (gio_win32_appinfo);
+ update_registry_data ();
+ watch_keys ();
+ G_UNLOCK (gio_win32_appinfo);
+ }
+}
-#ifndef AssocQueryString
-#pragma message("AssocQueryString not available with SDK used")
-#endif
static void g_win32_app_info_iface_init (GAppInfoIface *iface);
struct _GWin32AppInfo
{
GObject parent_instance;
- wchar_t *id;
- char *id_utf8;
- gboolean id_is_exename;
- char *executable;
- char *name;
- gboolean no_open_with;
+
+ /*<private>*/
+ gchar **supported_types;
+
+ GWin32AppInfoApplication *app;
+
+ GWin32AppInfoHandler *handler;
+
+ guint startup_notify : 1;
};
G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
- g_win32_app_info_iface_init))
+ G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
+ g_win32_app_info_iface_init))
static void
@@ -73,10 +3463,9 @@ g_win32_app_info_finalize (GObject *object)
info = G_WIN32_APP_INFO (object);
- g_free (info->id);
- g_free (info->id_utf8);
- g_free (info->name);
- g_free (info->executable);
+ g_clear_pointer (&info->supported_types, g_free);
+ g_clear_object (&info->app);
+ g_clear_object (&info->handler);
G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
}
@@ -85,7 +3474,7 @@ static void
g_win32_app_info_class_init (GWin32AppInfoClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
+
gobject_class->finalize = g_win32_app_info_finalize;
}
@@ -95,96 +3484,85 @@ g_win32_app_info_init (GWin32AppInfo *local)
}
static GAppInfo *
-g_desktop_app_info_new_from_id (wchar_t *id /* takes ownership */,
- gboolean id_is_exename)
-{
-#ifdef AssocQueryString
- ASSOCF flags;
-#endif
- wchar_t buffer[1024];
- DWORD buffer_size;
- GWin32AppInfo *info;
- HKEY app_key;
-
- info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
- info->id = id; /* Takes ownership */
- info->id_utf8 = g_utf16_to_utf8 (id, -1, NULL, NULL, NULL);
- info->id_is_exename = id_is_exename;
-
-#ifdef AssocQueryString
- flags = 0;
- if (id_is_exename)
- flags |= ASSOCF_INIT_BYEXENAME;
-
- buffer_size = 1024;
- if (AssocQueryStringW(flags,
- REAL_ASSOCSTR_EXECUTABLE,
- id,
- NULL,
- buffer,
- &buffer_size) == S_OK)
- info->executable = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
-
- buffer_size = 1024;
- if (AssocQueryStringW(flags,
- REAL_ASSOCSTR_FRIENDLYAPPNAME,
- id,
- NULL,
- buffer,
- &buffer_size) == S_OK)
- info->name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
-#endif
-
- if (info->name == NULL)
- {
- /* TODO: Should look up name from executable resources */
- if (info->executable)
- info->name = g_path_get_basename (info->executable);
- else
- info->name = g_strdup (info->id_utf8);
+g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
+ GWin32AppInfoHandler *handler)
+{
+ GWin32AppInfo *new_info;
+ GHashTableIter iter;
+ gpointer ext;
+ int i;
+
+ new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
+
+ new_info->app = g_object_ref (app);
+
+ g_win32_appinfo_init ();
+ G_LOCK (gio_win32_appinfo);
+
+ i = 0;
+ g_hash_table_iter_init (&iter, new_info->app->supported_exts);
+
+ while (g_hash_table_iter_next (&iter, &ext, NULL))
+ {
+ if (ext)
+ i += 1;
}
-#ifdef AssocQueryString
- if (AssocQueryKeyW(flags,
- ASSOCKEY_APP,
- info->id,
- NULL,
- &app_key) == S_OK)
+ new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
+
+ i = 0;
+ g_hash_table_iter_init (&iter, new_info->app->supported_exts);
+
+ while (g_hash_table_iter_next (&iter, &ext, NULL))
{
- if (RegQueryValueExW (app_key, L"NoOpenWith", 0,
- NULL, NULL, NULL) == ERROR_SUCCESS)
- info->no_open_with = TRUE;
- RegCloseKey (app_key);
+ if (!ext)
+ continue;
+
+ new_info->supported_types[i] = (gchar *) ext;
+ i += 1;
}
-#endif
-
- return G_APP_INFO (info);
-}
-static wchar_t *
-dup_wstring (wchar_t *str)
-{
- gsize len;
- for (len = 0; str[len] != 0; len++)
- ;
- return (wchar_t *)g_memdup (str, (len + 1) * 2);
+ G_UNLOCK (gio_win32_appinfo);
+
+ new_info->supported_types[i] = NULL;
+
+ new_info->handler = g_object_ref (handler);
+
+ return G_APP_INFO (new_info);
}
+
static GAppInfo *
g_win32_app_info_dup (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
GWin32AppInfo *new_info;
-
+
new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
- new_info->id = dup_wstring (info->id);
- new_info->id_utf8 = g_strdup (info->id_utf8);
- new_info->id_is_exename = info->id_is_exename;
- new_info->name = g_strdup (info->name);
- new_info->executable = g_strdup (info->executable);
- new_info->no_open_with = info->no_open_with;
-
+ if (info->app)
+ new_info->app = g_object_ref (info->app);
+
+ if (info->handler)
+ new_info->handler = g_object_ref (info->handler);
+
+ new_info->startup_notify = info->startup_notify;
+
+ if (info->supported_types)
+ {
+ int i;
+
+ for (i = 0; info->supported_types[i]; i++)
+ break;
+
+ new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
+
+ for (i = 0; info->supported_types[i]; i++)
+ new_info->supported_types[i] = g_strdup (info->supported_types[i]);
+
+ new_info->supported_types[i] = NULL;
+ }
+
return G_APP_INFO (new_info);
}
@@ -195,11 +3573,21 @@ g_win32_app_info_equal (GAppInfo *appinfo1,
GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
- if (info1->executable == NULL ||
- info2->executable == NULL)
- return FALSE;
-
- return strcmp (info1->executable, info2->executable) == 0;
+ if (info1->app == NULL ||
+ info2->app == NULL)
+ return info1 == info2;
+
+ if (info1->app->canonical_name_folded != NULL &&
+ info2->app->canonical_name_folded != NULL)
+ return (strcmp (info1->app->canonical_name_folded,
+ info2->app->canonical_name_folded)) == 0;
+
+ if (info1->app->executable_folded != NULL &&
+ info2->app->executable_folded != NULL)
+ return (strcmp (info1->app->executable_folded,
+ info2->app->executable_folded)) == 0;
+
+ return info1->app == info2->app;
}
static const char *
@@ -207,7 +3595,16 @@ g_win32_app_info_get_id (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
- return info->id_utf8;
+ if (info->app == NULL)
+ return NULL;
+
+ if (info->app->canonical_name_u8)
+ return info->app->canonical_name_u8;
+
+ if (info->app->executable_basename)
+ return info->app->executable_basename;
+
+ return NULL;
}
static const char *
@@ -215,182 +3612,811 @@ g_win32_app_info_get_name (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
- if (info->name == NULL)
- return _("Unnamed");
-
- return info->name;
+ if (info->app && info->app->canonical_name_u8)
+ return info->app->canonical_name_u8;
+ else
+ return P_("Unnamed");
+}
+
+static const char *
+g_win32_app_info_get_display_name (GAppInfo *appinfo)
+{
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+ if (info->app)
+ {
+ if (info->app->localized_pretty_name_u8)
+ return info->app->localized_pretty_name_u8;
+ else if (info->app->pretty_name_u8)
+ return info->app->pretty_name_u8;
+ }
+
+ return g_win32_app_info_get_name (appinfo);
}
static const char *
g_win32_app_info_get_description (GAppInfo *appinfo)
{
- /* Win32 has no app descriptions */
- return NULL;
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+ if (info->app == NULL)
+ return NULL;
+
+ return info->app->description_u8;
}
static const char *
g_win32_app_info_get_executable (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
-
- return info->executable;
+
+ if (info->app == NULL)
+ return NULL;
+
+ return info->app->executable;
+}
+
+static const char *
+g_win32_app_info_get_commandline (GAppInfo *appinfo)
+{
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+ if (info->app == NULL)
+ return NULL;
+
+ return info->app->command_u8;
}
static GIcon *
g_win32_app_info_get_icon (GAppInfo *appinfo)
{
- /* GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo); */
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
- /* TODO: How to handle icons */
- return NULL;
+ if (info->app == NULL)
+ return NULL;
+
+ return info->app->icon;
+}
+
+typedef struct _file_or_uri {
+ gchar *uri;
+ gchar *file;
+} file_or_uri;
+
+static char *
+expand_macro_single (char macro, file_or_uri *obj)
+{
+ char *result = NULL;
+
+ switch (macro)
+ {
+ case '*':
+ case '0':
+ case '1':
+ case 'l':
+ case 'd':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* TODO: handle 'l' and 'd' differently (longname and desktop name) */
+ if (obj->uri)
+ result = g_strdup (obj->uri);
+ else if (obj->file)
+ result = g_strdup (obj->file);
+ break;
+ case 'u':
+ case 'U':
+ if (obj->uri)
+ result = g_shell_quote (obj->uri);
+ break;
+ case 'f':
+ case 'F':
+ if (obj->file)
+ result = g_shell_quote (obj->file);
+ break;
+ }
+
+ return result;
}
static gboolean
-g_win32_app_info_launch_locations (GAppInfo *appinfo,
- GList *locations,
- GAppLaunchContext *launch_context,
- GError **error)
+expand_macro (char macro,
+ GString *exec,
+ GWin32AppInfo *info,
+ GList **stat_obj_list,
+ GList **obj_list)
{
- GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
-#ifdef AssocQueryString
- ASSOCF flags;
-#endif
- HKEY class_key;
- SHELLEXECUTEINFOW exec_info = {0};
- GList *l;
-
- /* TODO: What might startup_id mean on win32? */
-#ifdef AssocQueryString
- flags = 0;
- if (info->id_is_exename)
- flags |= ASSOCF_INIT_BYEXENAME;
-
- if (AssocQueryKeyW (flags,
- ASSOCKEY_SHELLEXECCLASS,
- info->id,
- NULL,
- &class_key) != S_OK)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Can't find application"));
+ GList *objs = *obj_list;
+ char *expanded;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (exec != NULL, FALSE);
+
+/*
+Legend: (from http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
+%* - replace with all parameters
+%~ - replace with all parameters starting with and following the second parameter
+%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).
+%<n> (where N is 2 - 9), replace with the nth parameter
+%s - show command
+%h - hotkey value
+%i - IDList stored in a shared memory handle is passed here.
+%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.
+%d - desktop absolute parsing name of the first parameter (for items that don't have file system paths)
+%v - for verbs that are none implies all, if there is no parameter passed this is the working directory
+%w - the working directory
+*/
+
+ switch (macro)
+ {
+ case '*':
+ case '~':
+ if (*stat_obj_list)
+ {
+ gint i;
+ GList *o;
+
+ for (o = *stat_obj_list, i = 0;
+ macro == '~' && o && i < 2;
+ o = o->next, i++);
+
+ for (; o; o = o->next)
+ {
+ expanded = expand_macro_single (macro, o->data);
+
+ if (expanded)
+ {
+ if (o != *stat_obj_list)
+ g_string_append (exec, " ");
+
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ }
+
+ objs = NULL;
+ result = TRUE;
+ }
+ break;
+ case '0':
+ case '1':
+ case 'l':
+ case 'd':
+ if (*stat_obj_list)
+ {
+ GList *o;
+
+ o = *stat_obj_list;
+
+ if (o)
+ {
+ expanded = expand_macro_single (macro, o->data);
+
+ if (expanded)
+ {
+ if (o != *stat_obj_list)
+ g_string_append (exec, " ");
+
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ }
+
+ if (objs)
+ objs = objs->next;
+
+ result = TRUE;
+ }
+ break;
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (*stat_obj_list)
+ {
+ gint i;
+ GList *o;
+ gint n;
+
+ switch (macro)
+ {
+ case '2':
+ n = 2;
+ break;
+ case '3':
+ n = 3;
+ break;
+ case '4':
+ n = 4;
+ break;
+ case '5':
+ n = 5;
+ break;
+ case '6':
+ n = 6;
+ break;
+ case '7':
+ n = 7;
+ break;
+ case '8':
+ n = 8;
+ break;
+ case '9':
+ n = 9;
+ break;
+ }
+
+ for (o = *stat_obj_list, i = 0; o && i < n; o = o->next, i++);
+
+ if (o)
+ {
+ expanded = expand_macro_single (macro, o->data);
+
+ if (expanded)
+ {
+ if (o != *stat_obj_list)
+ g_string_append (exec, " ");
+
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ }
+ result = TRUE;
+
+ if (objs)
+ objs = NULL;
+ }
+ break;
+ case 's':
+ break;
+ case 'h':
+ break;
+ case 'i':
+ break;
+ case 'v':
+ break;
+ case 'w':
+ expanded = g_get_current_dir ();
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ break;
+ case 'u':
+ case 'f':
+ if (objs)
+ {
+ expanded = expand_macro_single (macro, objs->data);
+
+ if (expanded)
+ {
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ objs = objs->next;
+ result = TRUE;
+ }
+
+ break;
+
+ case 'U':
+ case 'F':
+ while (objs)
+ {
+ expanded = expand_macro_single (macro, objs->data);
+
+ if (expanded)
+ {
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+
+ objs = objs->next;
+ result = TRUE;
+
+ if (objs != NULL && expanded)
+ g_string_append_c (exec, ' ');
+ }
+
+ break;
+
+ case 'c':
+ if (info->app && info->app->localized_pretty_name_u8)
+ {
+ expanded = g_shell_quote (info->app->localized_pretty_name_u8);
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ break;
+
+ case 'm': /* deprecated */
+ case 'n': /* deprecated */
+ case 'N': /* deprecated */
+ /*case 'd': *//* deprecated */
+ case 'D': /* deprecated */
+ break;
+
+ case '%':
+ g_string_append_c (exec, '%');
+ break;
+ }
+
+ *obj_list = objs;
+
+ return result;
+}
+
+static gboolean
+expand_application_parameters (GWin32AppInfo *info,
+ const gchar *exec_line,
+ GList **objs,
+ int *argc,
+ char ***argv,
+ GError **error)
+{
+ GList *obj_list = *objs;
+ GList **stat_obj_list = objs;
+ const char *p = exec_line;
+ GString *expanded_exec;
+ gboolean res;
+ gchar *a_char;
+
+ if (exec_line == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ P_("Application registry did not specify"
+ " a shell\\open\\command"));
return FALSE;
}
-#endif
- /* FIXME: Need to do something with
- * g_app_launch_context_get_environment()... ShellExecuteExW()
- * doesn't have any way to pass an environment though. We need to
- * either (a) update environment, ShellExecuteExW(), revert
- * environment; or (b) find an API to figure out what app
- * ShellExecuteExW() would launch, and then use g_spawn_async()
- * instead.
+ expanded_exec = g_string_new (NULL);
+ res = FALSE;
+
+ while (*p)
+ {
+ if (p[0] == '%' && p[1] != '\0')
+ {
+ if (expand_macro (p[1],
+ expanded_exec,
+ info, stat_obj_list,
+ objs))
+ res = TRUE;
+
+ p++;
+ }
+ else
+ g_string_append_c (expanded_exec, *p);
+
+ p++;
+ }
+
+ /* No file substitutions */
+ if (obj_list == *objs && obj_list != NULL && !res)
+ {
+ /* If there is no macro default to %f. This is also what KDE does */
+ g_string_append_c (expanded_exec, ' ');
+ expand_macro ('f', expanded_exec, info, stat_obj_list, objs);
+ }
+
+ /* Replace '\\' with '/', because g_shell_parse_argv considers them
+ * to be escape sequences.
*/
+ for (a_char = expanded_exec->str;
+ a_char <= &expanded_exec->str[expanded_exec->len];
+ a_char++)
+ {
+ if (*a_char == '\\')
+ *a_char = '/';
+ }
- for (l = locations; l != NULL; l = l->next)
- {
- wchar_t *wloc = g_utf8_to_utf16 (l->data, -1, NULL, NULL, NULL);
-
- memset (&exec_info, 0, sizeof (exec_info));
- exec_info.cbSize = sizeof (exec_info);
- exec_info.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_CLASSKEY;
- exec_info.lpFile = wloc;
- exec_info.nShow = SW_SHOWNORMAL;
- exec_info.hkeyClass = class_key;
-
- if (!ShellExecuteExW (&exec_info))
- {
- char *message_utf8 = g_win32_error_message (GetLastError ());
-
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Error launching application: %s"),
message_utf8);
- g_free (message_utf8);
-
- g_free (wloc);
- RegCloseKey (class_key);
- return FALSE;
- }
-
- g_free (wloc);
- }
-
- RegCloseKey (class_key);
-
- return TRUE;
+ res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
+ g_string_free (expanded_exec, TRUE);
+ return res;
}
-static gboolean
-g_win32_app_info_supports_uris (GAppInfo *appinfo)
+
+static gchar *
+get_appath_for_exe (gunichar2 *exe_basename)
{
- /* TODO: is there some way to determine this on windows? */
- return TRUE;
+ GWin32RegistryKey *apppath_key = NULL;
+ GWin32RegistryValueType val_type;
+ gunichar2 *appath = NULL;
+ gboolean got_value;
+ gchar *result = NULL;
+
+ apppath_key = _g_win32_registry_key_build_and_new_w (NULL, L"HKEY_LOCAL_MACHINE\\"
+ L"\\SOFTWARE"
+ L"\\Microsoft"
+ L"\\Windows"
+ L"\\CurrentVersion"
+ L"\\App Paths\\",
+ exe_basename, NULL);
+
+ if (apppath_key == NULL)
+ return NULL;
+
+ got_value = g_win32_registry_key_get_value_w (apppath_key,
+ TRUE,
+ L"Path",
+ &val_type,
+ (void **) &appath,
+ NULL,
+ NULL);
+
+ g_object_unref (apppath_key);
+
+ if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
+ result = g_utf16_to_utf8 (appath, -1, NULL,NULL, NULL);
+
+ g_clear_pointer (&appath, g_free);
+
+ return result;
}
+
static gboolean
-g_win32_app_info_supports_files (GAppInfo *appinfo)
+g_win32_app_info_launch_internal (GWin32AppInfo *info,
+ GList *objs,
+ GAppLaunchContext *launch_context,
+ GSpawnFlags spawn_flags,
+ GError **error)
{
- return TRUE;
+ gboolean completed = FALSE;
+ char **argv, **envp;
+ int argc;
+ gchar *command;
+ gchar *apppath;
+ gunichar2 *exe_basename;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (info->app != NULL, FALSE);
+
+ argv = NULL;
+
+ if (launch_context)
+ envp = g_app_launch_context_get_environment (launch_context);
+ else
+ envp = g_get_environ ();
+
+ command = NULL;
+ exe_basename = NULL;
+
+ if (info->handler)
+ {
+ if (info->handler->handler_command)
+ {
+ command = g_utf16_to_utf8 (info->handler->handler_command,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ }
+ else if (info->handler->proxy_command)
+ {
+ command = g_utf16_to_utf8 (info->handler->proxy_command,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ }
+ }
+
+ if (command == NULL)
+ {
+ command = g_strdup (info->app->command_u8);
+ exe_basename = g_utf8_to_utf16 (info->app->executable_basename,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+ apppath = get_appath_for_exe (exe_basename);
+
+ g_free (exe_basename);
+
+ if (apppath)
+ {
+ gchar **p;
+ gint p_index;
+
+ for (p = envp, p_index = 0; p[0]; p++, p_index++)
+ if ((p[0][0] == 'p' || p[0][0] == 'P') &&
+ (p[0][1] == 'a' || p[0][1] == 'A') &&
+ (p[0][2] == 't' || p[0][2] == 'T') &&
+ (p[0][3] == 'h' || p[0][3] == 'H') &&
+ (p[0][4] == '='))
+ break;
+
+ if (p[0] == NULL)
+ {
+ gchar **new_envp;
+ new_envp = g_new (char *, g_strv_length (envp) + 2);
+ new_envp[0] = g_strdup_printf ("PATH=%s", apppath);
+
+ for (p_index = 0; p_index <= g_strv_length (envp); p_index++)
+ new_envp[1 + p_index] = envp[p_index];
+
+ g_free (envp);
+ envp = new_envp;
+ }
+ else
+ {
+ gchar *p_path;
+
+ p_path = &p[0][5];
+
+ if (p_path[0] != '\0')
+ envp[p_index] = g_strdup_printf ("PATH=%s%c%s",
+ apppath,
+ G_SEARCHPATH_SEPARATOR,
+ p_path);
+ else
+ envp[p_index] = g_strdup_printf ("PATH=%s", apppath);
+
+ g_free (&p_path[-5]);
+ }
+ }
+
+ do
+ {
+ GPid pid;
+
+ if (!expand_application_parameters (info,
+ command,
+ &objs,
+ &argc,
+ &argv,
+ error))
+ goto out;
+
+ if (!g_spawn_async (NULL,
+ argv,
+ envp,
+ spawn_flags,
+ NULL,
+ NULL,
+ &pid,
+ error))
+ goto out;
+
+ if (launch_context != NULL)
+ {
+ GVariantBuilder builder;
+ GVariant *platform_data;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
+
+ platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
+ g_signal_emit_by_name (launch_context, "launched", info, platform_data);
+ g_variant_unref (platform_data);
+ }
+
+ g_strfreev (argv);
+ argv = NULL;
+ }
+ while (objs != NULL);
+
+ completed = TRUE;
+
+ out:
+ g_strfreev (argv);
+ g_strfreev (envp);
+ g_free (command);
+
+ return completed;
+}
+
+static void
+free_file_or_uri (gpointer ptr)
+{
+ file_or_uri *obj = ptr;
+ g_free (obj->file);
+ g_free (obj->uri);
+ g_free (obj);
}
+
static gboolean
-g_win32_app_info_launch (GAppInfo *appinfo,
- GList *files,
- GAppLaunchContext *launch_context,
- GError **error)
+g_win32_app_supports_uris (GWin32AppInfoApplication *app)
{
- gboolean success;
- GList *paths = g_list_copy_deep (files, (GCopyFunc) g_file_get_path,
- NULL);
+ gssize num_of_uris_supported;
- success = g_win32_app_info_launch_locations (appinfo, paths,
- launch_context, error);
+ if (app == NULL)
+ return FALSE;
+
+ num_of_uris_supported = (gssize) g_hash_table_size (app->supported_urls);
- g_list_free_full (paths, g_free);
+ if (g_hash_table_lookup (app->supported_urls, "file"))
+ num_of_uris_supported -= 1;
- return success;
+ return num_of_uris_supported > 0;
}
+
static gboolean
-g_win32_app_info_launch_uris (GAppInfo *appinfo,
- GList *uris,
- GAppLaunchContext *launch_context,
- GError **error)
+g_win32_app_info_supports_uris (GAppInfo *appinfo)
{
- return g_win32_app_info_launch_locations (appinfo, uris,
- launch_context, error);
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+ if (info->app == NULL)
+ return FALSE;
+
+ return g_win32_app_supports_uris (info->app);
}
+
static gboolean
-g_win32_app_info_should_show (GAppInfo *appinfo)
+g_win32_app_info_supports_files (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
- if (info->no_open_with)
+ if (info->app == NULL)
return FALSE;
-
- return TRUE;
+
+ return g_hash_table_size (info->app->supported_exts) > 0;
}
+
static gboolean
-g_win32_app_info_set_as_default_for_type (GAppInfo *appinfo,
- const char *content_type,
- GError **error)
+g_win32_app_info_launch_uris (GAppInfo *appinfo,
+ GList *uris,
+ GAppLaunchContext *launch_context,
+ GError **error)
{
- g_set_error_literal (error, G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("association changes not supported on win32"));
- return FALSE;
+ gboolean res = FALSE;
+ gboolean do_files;
+ GList *objs;
+
+ do_files = g_win32_app_info_supports_files (appinfo);
+
+ objs = NULL;
+ while (uris)
+ {
+ file_or_uri *obj;
+ obj = g_new0 (file_or_uri, 1);
+
+ if (do_files)
+ {
+ GFile *file;
+ gchar *path;
+
+ file = g_file_new_for_uri (uris->data);
+ path = g_file_get_path (file);
+ obj->file = path;
+ g_object_unref (file);
+ }
+
+ obj->uri = g_strdup (uris->data);
+
+ objs = g_list_prepend (objs, obj);
+ uris = uris->next;
+ }
+
+ objs = g_list_reverse (objs);
+
+ res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
+ objs,
+ launch_context,
+ G_SPAWN_SEARCH_PATH,
+ error);
+
+ g_list_free_full (objs, free_file_or_uri);
+
+ return res;
+}
+
+static gboolean
+g_win32_app_info_launch (GAppInfo *appinfo,
+ GList *files,
+ GAppLaunchContext *launch_context,
+ GError **error)
+{
+ gboolean res = FALSE;
+ gboolean do_uris;
+ GList *objs;
+
+ do_uris = g_win32_app_info_supports_uris (appinfo);
+
+ objs = NULL;
+ while (files)
+ {
+ file_or_uri *obj;
+ obj = g_new0 (file_or_uri, 1);
+ obj->file = g_file_get_path (G_FILE (files->data));
+
+ if (do_uris)
+ obj->uri = g_file_get_uri (G_FILE (files->data));
+
+ objs = g_list_prepend (objs, obj);
+ files = files->next;
+ }
+
+ objs = g_list_reverse (objs);
+
+ res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
+ objs,
+ launch_context,
+ G_SPAWN_SEARCH_PATH,
+ error);
+
+ g_list_free_full (objs, free_file_or_uri);
+
+ return res;
+}
+
+static const char **
+g_win32_app_info_get_supported_types (GAppInfo *appinfo)
+{
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+ return (const char**) info->supported_types;
}
GAppInfo *
g_app_info_create_from_commandline (const char *commandline,
- const char *application_name,
- GAppInfoCreateFlags flags,
- GError **error)
+ const char *application_name,
+ GAppInfoCreateFlags flags,
+ GError **error)
{
- g_set_error_literal (error, G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Association creation not supported on win32"));
- return NULL;
+ GWin32AppInfo *info;
+ GWin32AppInfoApplication *app;
+
+ g_return_val_if_fail (commandline, NULL);
+
+ info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
+
+ app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
+
+ if (application_name)
+ {
+ app->canonical_name = g_utf8_to_utf16 (application_name,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ app->canonical_name_u8 = g_strdup (application_name);
+ app->canonical_name_folded = g_utf8_casefold (application_name, -1);
+ }
+
+ app->command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
+ app->command_u8 = g_strdup (commandline);
+
+ extract_executable (app->command,
+ &app->executable,
+ &app->executable_basename,
+ &app->executable_folded,
+ NULL);
+
+ app->no_open_with = FALSE;
+ app->user_specific = FALSE;
+ app->default_app = FALSE;
+
+ info->app = app;
+ info->handler = NULL;
+
+ return G_APP_INFO (info);
}
+/* GAppInfo interface init */
static void
g_win32_app_info_iface_init (GAppInfoIface *iface)
@@ -406,316 +4432,161 @@ g_win32_app_info_iface_init (GAppInfoIface *iface)
iface->supports_uris = g_win32_app_info_supports_uris;
iface->supports_files = g_win32_app_info_supports_files;
iface->launch_uris = g_win32_app_info_launch_uris;
- iface->should_show = g_win32_app_info_should_show;
- iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;
-}
-
-static void
-enumerate_open_with_list (HKEY dir_key,
- GList **prognames)
-{
- DWORD index;
- wchar_t name[256];
- DWORD name_len, nbytes;
- wchar_t data[256];
- wchar_t *data_alloc;
- DWORD type;
-
- /* Must also look inside for a,b,c, + MRUList */
- index = 0;
- name_len = 256;
- nbytes = sizeof (data) - 2;
- while (RegEnumValueW (dir_key,
- index,
- name,
- &name_len,
- 0,
- &type,
- (LPBYTE)data,
- &nbytes) == ERROR_SUCCESS)
- {
- data[nbytes/2] = '\0';
- if (type == REG_SZ &&
- /* Ignore things like MRUList, just look at 'a', 'b', 'c', etc */
- name_len == 1)
- {
- data_alloc = (wchar_t *)g_memdup (data, nbytes + 2);
- data_alloc[nbytes/2] = 0;
- *prognames = g_list_prepend (*prognames, data_alloc);
- }
- index++;
- name_len = 256;
- nbytes = sizeof (data) - 2;
- }
-
- index = 0;
- name_len = 256;
- while (RegEnumKeyExW (dir_key,
- index,
- name,
- &name_len,
- NULL,
- NULL,
- NULL,
- NULL) == ERROR_SUCCESS)
- {
- *prognames = g_list_prepend (*prognames, g_memdup (name, (name_len + 1) * 2));
- index++;
- name_len = 256;
- }
+/* iface->should_show = g_win32_app_info_should_show;*/
+/* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
+/* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
+/* iface->add_supports_type = g_win32_app_info_add_supports_type;*/
+/* iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
+/* iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
+/* iface->can_delete = g_win32_app_info_can_delete;*/
+/* iface->do_delete = g_win32_app_info_delete;*/
+ iface->get_commandline = g_win32_app_info_get_commandline;
+ iface->get_display_name = g_win32_app_info_get_display_name;
+/* iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
+ iface->get_supported_types = g_win32_app_info_get_supported_types;
}
-static void
-enumerate_open_with_progids (HKEY dir_key,
- GList **progids)
+GAppInfo *
+g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
{
- DWORD index;
- wchar_t name[256];
- DWORD name_len, type;
+ GWin32AppInfoURLSchema *scheme;
+ char *scheme_down;
+ GAppInfo *result;
- index = 0;
- name_len = 256;
- while (RegEnumValueW (dir_key,
- index,
- name,
- &name_len,
- 0,
- &type,
- NULL,
- 0) == ERROR_SUCCESS)
- {
- *progids = g_list_prepend (*progids, g_memdup (name, (name_len + 1) * 2));
- index++;
- name_len = 256;
- }
-}
+ scheme_down = g_utf8_casefold (uri_scheme, -1);
-static void
-enumerate_open_with_root (HKEY dir_key,
- GList **progids,
- GList **prognames)
-{
- HKEY reg_key = NULL;
-
- if (RegOpenKeyExW (dir_key, L"OpenWithList", 0,
- KEY_READ, ®_key) == ERROR_SUCCESS)
- {
- enumerate_open_with_list (reg_key, prognames);
- RegCloseKey (reg_key);
- }
-
- if (RegOpenKeyExW (dir_key, L"OpenWithProgids", 0,
- KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS)
- {
- enumerate_open_with_progids (reg_key, progids);
- RegCloseKey (reg_key);
- }
-}
+ if (!scheme_down)
+ return NULL;
-static gboolean
-app_info_in_list (GAppInfo *info,
- GList *list)
-{
- while (list != NULL)
+ if (strcmp (scheme_down, "file") == 0)
{
- if (g_app_info_equal (info, list->data))
- return TRUE;
- list = list->next;
+ g_free (scheme_down);
+ return NULL;
}
- return FALSE;
-}
-GList *
-g_app_info_get_all_for_type (const char *content_type)
-{
- GList *progids = NULL;
- GList *prognames = NULL;
- HKEY reg_key, sys_file_assoc_key, reg_key2;
- wchar_t percieved_type[128];
- DWORD nchars, key_type;
- wchar_t *wc_key;
- GList *l;
- GList *infos;
+ g_win32_appinfo_init ();
+ G_LOCK (gio_win32_appinfo);
- wc_key = g_utf8_to_utf16 (content_type, -1, NULL, NULL, NULL);
- if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
- KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS)
- {
- enumerate_open_with_root (reg_key, &progids, &prognames);
-
- nchars = sizeof (percieved_type) / sizeof(wchar_t);
- if (RegQueryValueExW (reg_key, L"PerceivedType", 0,
- &key_type, (LPBYTE) percieved_type, &nchars) == ERROR_SUCCESS)
- {
- if (key_type == REG_SZ &&
- RegOpenKeyExW (HKEY_CLASSES_ROOT, L"SystemFileAssociations", 0,
- KEY_QUERY_VALUE, &sys_file_assoc_key) == ERROR_SUCCESS)
- {
- if (RegOpenKeyExW (sys_file_assoc_key, percieved_type, 0,
- KEY_QUERY_VALUE, ®_key2) == ERROR_SUCCESS)
- {
- enumerate_open_with_root (reg_key2, &progids, &prognames);
- RegCloseKey (reg_key2);
- }
-
- RegCloseKey (sys_file_assoc_key);
- }
- }
- RegCloseKey (reg_key);
- }
-
- if (RegOpenKeyExW (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
0,
- KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS)
- {
- if (RegOpenKeyExW (reg_key, wc_key, 0,
- KEY_QUERY_VALUE, ®_key2) == ERROR_SUCCESS)
- {
- enumerate_open_with_root (reg_key2, &progids, &prognames);
- RegCloseKey (reg_key2);
- }
-
- RegCloseKey (reg_key);
- }
+ scheme = g_hash_table_lookup (urls, scheme_down);
+ g_free (scheme_down);
- infos = NULL;
- for (l = prognames; l != NULL; l = l->next)
- {
- GAppInfo *info;
+ if (scheme)
+ g_object_ref (scheme);
- /* l->data ownership is taken */
- info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, TRUE);
- if (app_info_in_list (info, infos))
- g_object_unref (info);
- else
- infos = g_list_prepend (infos, info);
- }
- g_list_free (prognames);
+ G_UNLOCK (gio_win32_appinfo);
- for (l = progids; l != NULL; l = l->next)
- {
- GAppInfo *info;
+ result = NULL;
- /* l->data ownership is taken */
- info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, FALSE);
- if (app_info_in_list (info, infos))
- g_object_unref (info);
- else
- infos = g_list_prepend (infos, info);
- }
- g_list_free (progids);
-
- g_free (wc_key);
- return g_list_reverse (infos);
-}
+ if (scheme != NULL &&
+ scheme->chosen_handler != NULL &&
+ scheme->chosen_handler->app != NULL)
+ result = g_win32_app_info_new_from_app (scheme->chosen_handler->app,
+ scheme->chosen_handler);
-GList *
-g_app_info_get_recommended_for_type (const char *content_type)
-{
- /* FIXME: this should generate a list of applications that are registered
- * as direct handlers for the given content type, without using MIME subclassing.
- * See g_app_info_get_recommended_for_type() in gdesktopappinfo.c for a reference
- * UNIX implementation.
- */
- return g_app_info_get_all_for_type (content_type);
-}
+ if (scheme)
+ g_object_unref (scheme);
-GList *
-g_app_info_get_fallback_for_type (const char *content_type)
-{
- /* FIXME: this should generate a list of applications that are registered
- * as handlers for a superclass of the given content type, but are not
- * direct handlers for the content type itself. See g_app_info_get_fallback_for_type()
- * in gdesktopappinfo.c for a reference UNIX implementation.
- */
- return g_app_info_get_all_for_type (content_type);
-}
-
-/*
- * The windows api (AssocQueryString) doesn't distinguish between uri schemes
- * and file type extensions here, so we use the same implementation for both
- * g_app_info_get_default_for_type and g_app_info_get_default_for_uri_scheme
- */
-static GAppInfo *
-get_default_for_association (const char *association)
-{
- wchar_t *wtype;
- wchar_t buffer[1024];
- DWORD buffer_size;
-
- wtype = g_utf8_to_utf16 (association, -1, NULL, NULL, NULL);
-
- /* Verify that we have some sort of app registered for this type */
-#ifdef AssocQueryString
- buffer_size = 1024;
- if (AssocQueryStringW (0,
- REAL_ASSOCSTR_COMMAND,
- wtype,
- NULL,
- buffer,
- &buffer_size) == S_OK)
- /* Takes ownership of wtype */
- return g_desktop_app_info_new_from_id (wtype, FALSE);
-#endif
-
- g_free (wtype);
- return NULL;
+ return result;
}
GAppInfo *
g_app_info_get_default_for_type (const char *content_type,
gboolean must_support_uris)
{
- return get_default_for_association (content_type);
-}
+ GWin32AppInfoFileExtension *ext;
+ char *ext_down;
+ GWin32AppInfoHandler *handler;
+ GAppInfo *result;
+ GWin32AppInfoApplication *app;
+ GHashTableIter iter;
-GAppInfo *
-g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
-{
- return get_default_for_association (uri_scheme);
+ ext_down = g_utf8_casefold (content_type, -1);
+
+ if (!ext_down)
+ return NULL;
+
+ g_win32_appinfo_init ();
+ G_LOCK (gio_win32_appinfo);
+
+ /* Assuming that "content_type" is a file extension, not a MIME type */
+ ext = g_hash_table_lookup (extensions, ext_down);
+ g_free (ext_down);
+
+ result = NULL;
+
+ if (ext != NULL)
+ g_object_ref (ext);
+
+ G_UNLOCK (gio_win32_appinfo);
+
+ if (ext != NULL)
+ {
+ if (ext->chosen_handler != NULL &&
+ ext->chosen_handler->app != NULL &&
+ (!must_support_uris ||
+ g_win32_app_supports_uris (ext->chosen_handler->app)))
+ result = g_win32_app_info_new_from_app (ext->chosen_handler->app,
+ ext->chosen_handler);
+ else
+ {
+ g_hash_table_iter_init (&iter, ext->handlers);
+
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
+ {
+ if (handler->app &&
+ (!must_support_uris ||
+ g_win32_app_supports_uris (ext->chosen_handler->app)))
+ {
+ result = g_win32_app_info_new_from_app (handler->app, handler);
+ break;
+ }
+ }
+
+ if (result == NULL)
+ {
+ g_hash_table_iter_init (&iter, ext->other_apps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
+ {
+ if (!must_support_uris ||
+ g_win32_app_supports_uris (ext->chosen_handler->app))
+ {
+ result = g_win32_app_info_new_from_app (app, NULL);
+ break;
+ }
+ }
+ }
+ }
+ g_object_unref (ext);
+ }
+
+ return result;
}
GList *
g_app_info_get_all (void)
{
- DWORD index;
- wchar_t name[256];
- DWORD name_len;
- HKEY reg_key;
+ GHashTableIter iter;
+ gpointer value;
GList *infos;
- GAppInfo *info;
+ GList *apps;
+ GList *apps_i;
- if (RegOpenKeyExW (HKEY_CLASSES_ROOT, L"Applications", 0,
- KEY_READ, ®_key) != ERROR_SUCCESS)
- return NULL;
+ g_win32_appinfo_init ();
+ G_LOCK (gio_win32_appinfo);
+
+ apps = NULL;
+ g_hash_table_iter_init (&iter, apps_by_id);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
+
+ G_UNLOCK (gio_win32_appinfo);
infos = NULL;
- index = 0;
- name_len = 256;
- while (RegEnumKeyExW (reg_key,
- index,
- name,
- &name_len,
- NULL,
- NULL,
- NULL,
- NULL) == ERROR_SUCCESS)
- {
- wchar_t *name_dup = g_memdup (name, (name_len+1)*2);
- /* name_dup ownership is taken */
- info = g_desktop_app_info_new_from_id (name_dup, TRUE);
- infos = g_list_prepend (infos, info);
-
- index++;
- name_len = 256;
- }
-
- RegCloseKey (reg_key);
-
- return g_list_reverse (infos);
-}
-
-void
-g_app_info_reset_type_associations (const char *content_type)
-{
- /* nothing to do */
+ for (apps_i = apps; apps_i; apps_i = apps_i->next)
+ infos = g_list_prepend (infos,
+ g_win32_app_info_new_from_app (apps_i->data, NULL));
+
+ g_list_free_full (apps, g_object_unref);
+
+ return infos;
}
diff --git a/gio/gwin32appinfo.h b/gio/gwin32appinfo.h
index 5414f68..16c1181 100644
--- a/gio/gwin32appinfo.h
+++ b/gio/gwin32appinfo.h
@@ -1,6 +1,7 @@
/* 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
@@ -15,7 +16,8 @@
* 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/>.
*
- * Author: Alexander Larsson <alexl redhat com>
+ * Authors: Alexander Larsson <alexl redhat com>
+ * Руслан Ижбулатов <lrn1986 gmail com>
*/
#ifndef __G_WIN32_APP_INFO_H__
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]