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