summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2021-10-29 10:30:56 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2021-10-29 10:30:56 +0900
commit12500068fcfd930af96d75506201fda8d829220b (patch)
tree84c49eeeae1de699480669d815d01ad1f5c888d3 /gio
parent71ffb6083fde389da869fa671affd9da39c40984 (diff)
downloadglib-12500068fcfd930af96d75506201fda8d829220b.tar.gz
glib-12500068fcfd930af96d75506201fda8d829220b.tar.bz2
glib-12500068fcfd930af96d75506201fda8d829220b.zip
Imported Upstream version 2.67.0upstream/2.67.0
Diffstat (limited to 'gio')
-rw-r--r--gio/gcredentials.c2
-rw-r--r--gio/gdatainputstream.c25
-rw-r--r--gio/gdbus-2.0/codegen/codegen.py2
-rw-r--r--gio/gdbusaddress.c38
-rw-r--r--gio/gdbusconnection.c16
-rw-r--r--gio/gdbusinterfaceskeleton.c3
-rw-r--r--gio/gdbusmethodinvocation.h35
-rw-r--r--gio/gdbusprivate.c5
-rw-r--r--gio/gdbusserver.c5
-rw-r--r--gio/gdesktopappinfo.c79
-rw-r--r--gio/gfile.c122
-rw-r--r--gio/gfile.h6
-rw-r--r--gio/gio-tool-info.c15
-rw-r--r--gio/giomodule.c24
-rw-r--r--gio/giowin32-private.c20
-rw-r--r--gio/gkeyfilesettingsbackend.c33
-rw-r--r--gio/glocalfileinfo.c79
-rw-r--r--gio/glocalfileoutputstream.c101
-rw-r--r--gio/gresource.c9
-rw-r--r--gio/gsettingsschema.c11
-rw-r--r--gio/gsocket.c20
-rw-r--r--gio/gsocketclient.c258
-rw-r--r--gio/gsubprocesslauncher-private.h1
-rw-r--r--gio/gsubprocesslauncher.c116
-rw-r--r--gio/gsubprocesslauncher.h3
-rw-r--r--gio/gtlscertificate.c132
-rw-r--r--gio/gtlscertificate.h5
-rw-r--r--gio/gtlspassword.c10
-rw-r--r--gio/gwin32appinfo.c3849
-rw-r--r--gio/gwin32registrykey.c60
-rw-r--r--gio/gwin32registrykey.h4
-rw-r--r--gio/meson.build4
-rw-r--r--gio/tests/async-close-output-stream.c6
-rw-r--r--gio/tests/desktop-app-info.c6
-rw-r--r--gio/tests/desktop-files/usr/applications/invalid-desktop.desktop5
-rw-r--r--gio/tests/file.c190
-rw-r--r--gio/tests/gdbus-auth.c2
-rw-r--r--gio/tests/gdbus-close-pending.c2
-rw-r--r--gio/tests/gdbus-connection-loss.c2
-rw-r--r--gio/tests/gdbus-connection-slow.c4
-rw-r--r--gio/tests/gdbus-connection.c4
-rw-r--r--gio/tests/gdbus-example-objectmanager-server.c2
-rw-r--r--gio/tests/gdbus-example-server.c2
-rw-r--r--gio/tests/gdbus-exit-on-close.c2
-rw-r--r--gio/tests/gdbus-export.c5
-rw-r--r--gio/tests/gdbus-overflow.c2
-rw-r--r--gio/tests/gdbus-peer.c394
-rw-r--r--gio/tests/gdbus-test-codegen.c37
-rw-r--r--gio/tests/gdbus-tests.c6
-rw-r--r--gio/tests/gsettings.c170
-rw-r--r--gio/tests/gsocketclient-slow.c39
-rw-r--r--gio/tests/gsubprocess.c32
-rw-r--r--gio/tests/gtesttlsbackend.c25
-rw-r--r--gio/tests/tls-certificate.c36
-rw-r--r--gio/tests/tls-interaction.c55
-rw-r--r--gio/win32/gwin32fsmonitorutils.c2
-rw-r--r--gio/win32/gwinhttpfile.c9
57 files changed, 2815 insertions, 3316 deletions
diff --git a/gio/gcredentials.c b/gio/gcredentials.c
index 146829e7c..9423f9ef9 100644
--- a/gio/gcredentials.c
+++ b/gio/gcredentials.c
@@ -225,7 +225,7 @@ g_credentials_to_string (GCredentials *credentials)
{
GString *ret;
#if G_CREDENTIALS_USE_APPLE_XUCRED
- __typeof__(credentials->native.cr_ngroups) i;
+ glib_typeof (credentials->native.cr_ngroups) i;
#endif
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
diff --git a/gio/gdatainputstream.c b/gio/gdatainputstream.c
index 2cdcbda19..2e7750cb5 100644
--- a/gio/gdatainputstream.c
+++ b/gio/gdatainputstream.c
@@ -27,7 +27,6 @@
#include "gioenumtypes.h"
#include "gioerror.h"
#include "glibintl.h"
-#include "gstrfuncsprivate.h"
#include <string.h>
@@ -857,7 +856,7 @@ static gssize
scan_for_chars (GDataInputStream *stream,
gsize *checked_out,
const char *stop_chars,
- gsize stop_chars_len)
+ gssize stop_chars_len)
{
GBufferedInputStream *bstream;
const char *buffer;
@@ -953,7 +952,7 @@ typedef struct
gsize checked;
gchar *stop_chars;
- gsize stop_chars_len;
+ gssize stop_chars_len;
gsize length;
} GDataInputStreamReadData;
@@ -1079,17 +1078,12 @@ g_data_input_stream_read_async (GDataInputStream *stream,
{
GDataInputStreamReadData *data;
GTask *task;
- gsize stop_chars_len_unsigned;
data = g_slice_new0 (GDataInputStreamReadData);
-
- if (stop_chars_len < 0)
- stop_chars_len_unsigned = strlen (stop_chars);
- else
- stop_chars_len_unsigned = (gsize) stop_chars_len;
-
- data->stop_chars = g_memdup2 (stop_chars, stop_chars_len_unsigned);
- data->stop_chars_len = stop_chars_len_unsigned;
+ if (stop_chars_len == -1)
+ stop_chars_len = strlen (stop_chars);
+ data->stop_chars = g_memdup (stop_chars, stop_chars_len);
+ data->stop_chars_len = stop_chars_len;
data->last_saw_cr = FALSE;
task = g_task_new (stream, cancellable, callback, user_data);
@@ -1344,20 +1338,17 @@ g_data_input_stream_read_upto (GDataInputStream *stream,
gssize found_pos;
gssize res;
char *data_until;
- gsize stop_chars_len_unsigned;
g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL);
if (stop_chars_len < 0)
- stop_chars_len_unsigned = strlen (stop_chars);
- else
- stop_chars_len_unsigned = (gsize) stop_chars_len;
+ stop_chars_len = strlen (stop_chars);
bstream = G_BUFFERED_INPUT_STREAM (stream);
checked = 0;
- while ((found_pos = scan_for_chars (stream, &checked, stop_chars, stop_chars_len_unsigned)) == -1)
+ while ((found_pos = scan_for_chars (stream, &checked, stop_chars, stop_chars_len)) == -1)
{
if (g_buffered_input_stream_get_available (bstream) ==
g_buffered_input_stream_get_buffer_size (bstream))
diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py
index 5234b3807..cda047173 100644
--- a/gio/gdbus-2.0/codegen/codegen.py
+++ b/gio/gdbus-2.0/codegen/codegen.py
@@ -1522,7 +1522,7 @@ class CodeGenerator:
' *\n'
' * If a signal handler returns %%TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call %s_complete_%s() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %%G_DBUS_ERROR_UNKNOWN_METHOD error is returned.\n'
' *\n'
- ' * Returns: %%TRUE if the invocation was handled, %%FALSE to let other signal handlers run.\n'
+ ' * Returns: %%G_DBUS_METHOD_INVOCATION_HANDLED or %%TRUE if the invocation was handled, %%G_DBUS_METHOD_INVOCATION_UNHANDLED or %%FALSE to let other signal handlers run.\n'
%(i.name, m.name, i.name_lower, m.name_lower), False))
self.write_gtkdoc_deprecated_and_since_and_close(m, self.outfile, 2)
if m.unix_fd:
diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c
index 0044cd3c6..3dd3cc84b 100644
--- a/gio/gdbusaddress.c
+++ b/gio/gdbusaddress.c
@@ -30,7 +30,6 @@
#include "gdbusaddress.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
-#include "glib-private.h"
#include "gnetworkaddress.h"
#include "gsocketclient.h"
#include "giostream.h"
@@ -1280,7 +1279,6 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
GCancellable *cancellable,
GError **error)
{
- gboolean has_elevated_privileges = GLIB_PRIVATE_CALL (g_check_setuid) ();
gchar *ret, *s = NULL;
const gchar *starter_bus;
GError *local_error;
@@ -1319,16 +1317,10 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
_g_dbus_debug_print_unlock ();
}
- /* Don’t load the addresses from the environment if running as setuid, as they
- * come from an unprivileged caller. */
switch (bus_type)
{
case G_BUS_TYPE_SYSTEM:
- if (has_elevated_privileges)
- ret = NULL;
- else
- ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
-
+ ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
if (ret == NULL)
{
ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
@@ -1336,33 +1328,7 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
break;
case G_BUS_TYPE_SESSION:
- if (has_elevated_privileges)
- {
-#ifdef G_OS_UNIX
- if (geteuid () == getuid ())
- {
- /* Ideally we shouldn't do this, because setgid and
- * filesystem capabilities are also elevated privileges
- * with which we should not be trusting environment variables
- * from the caller. Unfortunately, there are programs with
- * elevated privileges that rely on the session bus being
- * available. We already prevent the really dangerous
- * transports like autolaunch: and unixexec: when our
- * privileges are elevated, so this can only make us connect
- * to the wrong AF_UNIX or TCP socket. */
- ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
- }
- else
-#endif
- {
- ret = NULL;
- }
- }
- else
- {
- ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
- }
-
+ ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
if (ret == NULL)
{
ret = get_session_address_platform_specific (&local_error);
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 75b818069..54aaf43d9 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -110,7 +110,6 @@
#include "gasyncinitable.h"
#include "giostream.h"
#include "gasyncresult.h"
-#include "gstrfuncsprivate.h"
#include "gtask.h"
#include "gmarshal-internal.h"
@@ -121,13 +120,6 @@
#include "glibintl.h"
-#define G_DBUS_CONNECTION_FLAGS_ALL \
- (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | \
- G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | \
- G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS | \
- G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | \
- G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING)
-
/**
* SECTION:gdbusconnection
* @short_description: D-Bus Connections
@@ -2713,7 +2705,6 @@ g_dbus_connection_new (GIOStream *stream,
_g_dbus_initialize ();
g_return_if_fail (G_IS_IO_STREAM (stream));
- g_return_if_fail ((flags & ~G_DBUS_CONNECTION_FLAGS_ALL) == 0);
g_async_initable_new_async (G_TYPE_DBUS_CONNECTION,
G_PRIORITY_DEFAULT,
@@ -2801,7 +2792,6 @@ g_dbus_connection_new_sync (GIOStream *stream,
{
_g_dbus_initialize ();
g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL);
- g_return_val_if_fail ((flags & ~G_DBUS_CONNECTION_FLAGS_ALL) == 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_initable_new (G_TYPE_DBUS_CONNECTION,
cancellable,
@@ -2859,7 +2849,6 @@ g_dbus_connection_new_for_address (const gchar *address,
_g_dbus_initialize ();
g_return_if_fail (address != NULL);
- g_return_if_fail ((flags & ~G_DBUS_CONNECTION_FLAGS_ALL) == 0);
g_async_initable_new_async (G_TYPE_DBUS_CONNECTION,
G_PRIORITY_DEFAULT,
@@ -2947,7 +2936,6 @@ g_dbus_connection_new_for_address_sync (const gchar *address,
_g_dbus_initialize ();
g_return_val_if_fail (address != NULL, NULL);
- g_return_val_if_fail ((flags & ~G_DBUS_CONNECTION_FLAGS_ALL) == 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_initable_new (G_TYPE_DBUS_CONNECTION,
cancellable,
@@ -4021,7 +4009,7 @@ _g_dbus_interface_vtable_copy (const GDBusInterfaceVTable *vtable)
/* Don't waste memory by copying padding - remember to update this
* when changing struct _GDBusInterfaceVTable in gdbusconnection.h
*/
- return g_memdup2 ((gconstpointer) vtable, 3 * sizeof (gpointer));
+ return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer));
}
static void
@@ -4038,7 +4026,7 @@ _g_dbus_subtree_vtable_copy (const GDBusSubtreeVTable *vtable)
/* Don't waste memory by copying padding - remember to update this
* when changing struct _GDBusSubtreeVTable in gdbusconnection.h
*/
- return g_memdup2 ((gconstpointer) vtable, 3 * sizeof (gpointer));
+ return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer));
}
static void
diff --git a/gio/gdbusinterfaceskeleton.c b/gio/gdbusinterfaceskeleton.c
index a0cd25087..a0125c541 100644
--- a/gio/gdbusinterfaceskeleton.c
+++ b/gio/gdbusinterfaceskeleton.c
@@ -28,7 +28,6 @@
#include "gdbusmethodinvocation.h"
#include "gdbusconnection.h"
#include "gmarshal-internal.h"
-#include "gstrfuncsprivate.h"
#include "gtask.h"
#include "gioerror.h"
@@ -703,7 +702,7 @@ add_connection_locked (GDBusInterfaceSkeleton *interface_,
* properly before building the hooked_vtable, so we create it
* once at the last minute.
*/
- interface_->priv->hooked_vtable = g_memdup2 (g_dbus_interface_skeleton_get_vtable (interface_), sizeof (GDBusInterfaceVTable));
+ interface_->priv->hooked_vtable = g_memdup (g_dbus_interface_skeleton_get_vtable (interface_), sizeof (GDBusInterfaceVTable));
interface_->priv->hooked_vtable->method_call = skeleton_intercept_handle_method_call;
}
diff --git a/gio/gdbusmethodinvocation.h b/gio/gdbusmethodinvocation.h
index 061256ffe..775070a2c 100644
--- a/gio/gdbusmethodinvocation.h
+++ b/gio/gdbusmethodinvocation.h
@@ -33,6 +33,41 @@ G_BEGIN_DECLS
#define G_DBUS_METHOD_INVOCATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_METHOD_INVOCATION, GDBusMethodInvocation))
#define G_IS_DBUS_METHOD_INVOCATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_METHOD_INVOCATION))
+/**
+ * G_DBUS_METHOD_INVOCATION_HANDLED:
+ *
+ * The value returned by handlers of the signals generated by
+ * the `gdbus-codegen` tool to indicate that a method call has been
+ * handled by an implementation. It is equal to %TRUE, but using
+ * this macro is sometimes more readable.
+ *
+ * In code that needs to be backwards-compatible with older GLib,
+ * use %TRUE instead, often written like this:
+ *
+ * |[
+ * g_dbus_method_invocation_return_error (invocation, ...);
+ * return TRUE; // handled
+ * ]|
+ *
+ * Since: 2.68
+ */
+#define G_DBUS_METHOD_INVOCATION_HANDLED TRUE GLIB_AVAILABLE_MACRO_IN_2_68
+
+/**
+ * G_DBUS_METHOD_INVOCATION_UNHANDLED:
+ *
+ * The value returned by handlers of the signals generated by
+ * the `gdbus-codegen` tool to indicate that a method call has not been
+ * handled by an implementation. It is equal to %FALSE, but using
+ * this macro is sometimes more readable.
+ *
+ * In code that needs to be backwards-compatible with older GLib,
+ * use %FALSE instead.
+ *
+ * Since: 2.68
+ */
+#define G_DBUS_METHOD_INVOCATION_UNHANDLED FALSE GLIB_AVAILABLE_MACRO_IN_2_68
+
GLIB_AVAILABLE_IN_ALL
GType g_dbus_method_invocation_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c
index 2551e4791..5c980b40b 100644
--- a/gio/gdbusprivate.c
+++ b/gio/gdbusprivate.c
@@ -1085,11 +1085,8 @@ write_message_continue_writing (MessageToWriteData *data)
else
{
#ifdef G_OS_UNIX
- if (data->total_written == 0 && fd_list != NULL)
+ if (fd_list != NULL)
{
- /* We were trying to write byte 0 of the message, which needs
- * the fd list to be attached to it, but this connection doesn't
- * support doing that. */
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c
index 64bac73ae..4a8dab0ea 100644
--- a/gio/gdbusserver.c
+++ b/gio/gdbusserver.c
@@ -57,10 +57,6 @@
#include "glibintl.h"
-#define G_DBUS_SERVER_FLAGS_ALL \
- (G_DBUS_SERVER_FLAGS_RUN_IN_THREAD | \
- G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS)
-
/**
* SECTION:gdbusserver
* @short_description: Helper for accepting connections
@@ -516,7 +512,6 @@ g_dbus_server_new_sync (const gchar *address,
g_return_val_if_fail (address != NULL, NULL);
g_return_val_if_fail (g_dbus_is_guid (guid), NULL);
- g_return_val_if_fail ((flags & ~G_DBUS_SERVER_FLAGS_ALL) == 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
server = g_initable_new (G_TYPE_DBUS_SERVER,
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index 2b139a01c..b779b309b 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -305,55 +305,6 @@ desktop_file_dir_app_name_is_masked (DesktopFileDir *dir,
return FALSE;
}
-/* Not much to go on from https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
- * so validate it as a non-empty alphanumeric ASCII string with `-` and `_` allowed.
- *
- * Validation is important as the desktop IDs are used to construct filenames,
- * and may be set by an unprivileged caller if running in a setuid program. */
-static gboolean
-validate_xdg_desktop (const gchar *desktop)
-{
- gsize i;
-
- for (i = 0; desktop[i] != '\0'; i++)
- if (desktop[i] != '-' && desktop[i] != '_' &&
- !g_ascii_isalnum (desktop[i]))
- return FALSE;
-
- if (i == 0)
- return FALSE;
-
- return TRUE;
-}
-
-static char **
-get_valid_current_desktops (const char *value)
-{
- char **tmp;
- gsize i;
- GPtrArray *valid_desktops;
-
- if (value == NULL)
- value = g_getenv ("XDG_CURRENT_DESKTOP");
- if (value == NULL)
- value = "";
-
- tmp = g_strsplit (value, G_SEARCHPATH_SEPARATOR_S, 0);
- valid_desktops = g_ptr_array_new_full (g_strv_length (tmp) + 1, g_free);
- for (i = 0; tmp[i]; i++)
- {
- if (validate_xdg_desktop (tmp[i]))
- g_ptr_array_add (valid_desktops, tmp[i]);
- else
- g_free (tmp[i]);
- }
- g_ptr_array_add (valid_desktops, NULL);
- g_free (tmp);
- tmp = (char **) g_ptr_array_steal (valid_desktops, NULL);
- g_ptr_array_unref (valid_desktops);
- return tmp;
-}
-
static const gchar * const *
get_lowercase_current_desktops (void)
{
@@ -361,15 +312,23 @@ get_lowercase_current_desktops (void)
if (g_once_init_enter (&result))
{
- char **tmp = get_valid_current_desktops (NULL);
- gsize i, j;
+ const gchar *envvar;
+ gchar **tmp;
+
+ envvar = g_getenv ("XDG_CURRENT_DESKTOP");
- for (i = 0; tmp[i]; i++)
+ if (envvar)
{
- /* Convert to lowercase. */
- for (j = 0; tmp[i][j]; j++)
- tmp[i][j] = g_ascii_tolower (tmp[i][j]);
+ gint i, j;
+
+ tmp = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0);
+
+ for (i = 0; tmp[i]; i++)
+ for (j = 0; tmp[i][j]; j++)
+ tmp[i][j] = g_ascii_tolower (tmp[i][j]);
}
+ else
+ tmp = g_new0 (gchar *, 0 + 1);
g_once_init_leave (&result, tmp);
}
@@ -384,7 +343,15 @@ get_current_desktops (const gchar *value)
if (g_once_init_enter (&result))
{
- char **tmp = get_valid_current_desktops (value);
+ gchar **tmp;
+
+ if (!value)
+ value = g_getenv ("XDG_CURRENT_DESKTOP");
+
+ if (!value)
+ value = "";
+
+ tmp = g_strsplit (value, ":", 0);
g_once_init_leave (&result, tmp);
}
diff --git a/gio/gfile.c b/gio/gfile.c
index c16912b89..f92c07612 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -60,7 +60,6 @@
#include "gasyncresult.h"
#include "gioerror.h"
#include "glibintl.h"
-#include "gstrfuncsprivate.h"
/**
@@ -2696,14 +2695,36 @@ should_copy (GFileAttributeInfo *info,
return info->flags & G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE;
}
-static gboolean
-build_attribute_list_for_copy (GFile *file,
- GFileCopyFlags flags,
- char **out_attributes,
- GCancellable *cancellable,
- GError **error)
+/**
+ * g_file_build_attribute_list_for_copy:
+ * @file: a #GFile to copy attributes to
+ * @flags: a set of #GFileCopyFlags
+ * @cancellable: (nullable): optional #GCancellable object,
+ * %NULL to ignore
+ * @error: a #GError, %NULL to ignore
+ *
+ * Prepares the file attribute query string for copying to @file.
+ *
+ * This function prepares an attribute query string to be
+ * passed to g_file_query_info() to get a list of attributes
+ * normally copied with the file (see g_file_copy_attributes()
+ * for the detailed description). This function is used by the
+ * implementation of g_file_copy_attributes() and is useful
+ * when one needs to query and set the attributes in two
+ * stages (e.g., for recursive move of a directory).
+ *
+ * Returns: an attribute query string for g_file_query_info(),
+ * or %NULL if an error occurs.
+ *
+ * Since: 2.68
+ */
+char *
+g_file_build_attribute_list_for_copy (GFile *file,
+ GFileCopyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
{
- gboolean ret = FALSE;
+ char *ret = NULL;
GFileAttributeInfoList *attributes = NULL, *namespaces = NULL;
GString *s = NULL;
gboolean first;
@@ -2711,6 +2732,10 @@ build_attribute_list_for_copy (GFile *file,
gboolean copy_all_attributes;
gboolean skip_perms;
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
copy_all_attributes = flags & G_FILE_COPY_ALL_METADATA;
skip_perms = (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) != 0;
@@ -2764,8 +2789,7 @@ build_attribute_list_for_copy (GFile *file,
}
}
- ret = TRUE;
- *out_attributes = g_string_free (s, FALSE);
+ ret = g_string_free (s, FALSE);
s = NULL;
out:
if (s)
@@ -2811,8 +2835,9 @@ g_file_copy_attributes (GFile *source,
GFileInfo *info;
gboolean source_nofollow_symlinks;
- if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read,
- cancellable, error))
+ attrs_to_read = g_file_build_attribute_list_for_copy (destination, flags,
+ cancellable, error);
+ if (!attrs_to_read)
return FALSE;
source_nofollow_symlinks = flags & G_FILE_COPY_NOFOLLOW_SYMLINKS;
@@ -3158,6 +3183,7 @@ file_copy_fallback (GFile *source,
char *attrs_to_read;
gboolean do_set_attributes = FALSE;
GFileCreateFlags create_flags;
+ GError *tmp_error = NULL;
/* need to know the file type */
info = g_file_query_info (source,
@@ -3199,47 +3225,43 @@ file_copy_fallback (GFile *source,
goto out;
in = G_INPUT_STREAM (file_in);
- if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read,
- cancellable, error))
+ attrs_to_read = g_file_build_attribute_list_for_copy (destination, flags,
+ cancellable, error);
+ if (!attrs_to_read)
goto out;
- if (attrs_to_read != NULL)
- {
- GError *tmp_error = NULL;
+ /* Ok, ditch the previous lightweight info (on Unix we just
+ * called lstat()); at this point we gather all the information
+ * we need about the source from the opened file descriptor.
+ */
+ g_object_unref (info);
- /* Ok, ditch the previous lightweight info (on Unix we just
- * called lstat()); at this point we gather all the information
- * we need about the source from the opened file descriptor.
+ info = g_file_input_stream_query_info (file_in, attrs_to_read,
+ cancellable, &tmp_error);
+ if (!info)
+ {
+ /* Not all gvfs backends implement query_info_on_read(), we
+ * can just fall back to the pathname again.
+ * https://bugzilla.gnome.org/706254
*/
- g_object_unref (info);
-
- info = g_file_input_stream_query_info (file_in, attrs_to_read,
- cancellable, &tmp_error);
- if (!info)
+ if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
{
- /* Not all gvfs backends implement query_info_on_read(), we
- * can just fall back to the pathname again.
- * https://bugzilla.gnome.org/706254
- */
- if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
- {
- g_clear_error (&tmp_error);
- info = g_file_query_info (source, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable, error);
- }
- else
- {
- g_free (attrs_to_read);
- g_propagate_error (error, tmp_error);
- goto out;
- }
+ g_clear_error (&tmp_error);
+ info = g_file_query_info (source, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable, error);
+ }
+ else
+ {
+ g_free (attrs_to_read);
+ g_propagate_error (error, tmp_error);
+ goto out;
}
- g_free (attrs_to_read);
- if (!info)
- goto out;
-
- do_set_attributes = TRUE;
}
+ g_free (attrs_to_read);
+ if (!info)
+ goto out;
+
+ do_set_attributes = TRUE;
/* In the local file path, we pass down the source info which
* includes things like unix::mode, to ensure that the target file
@@ -7558,7 +7580,6 @@ replace_contents_close_callback (GObject *obj,
/* Ignore errors here, we're only reading anyway */
g_output_stream_close_finish (stream, close_res, NULL);
- g_object_unref (stream);
if (!data->failed)
{
@@ -7640,6 +7661,7 @@ replace_contents_open_callback (GObject *obj,
g_task_get_cancellable (data->task),
replace_contents_write_callback,
data);
+ g_object_unref (stream); /* ownership is transferred to the write_async() call above */
}
else
{
@@ -7872,7 +7894,7 @@ measure_disk_usage_progress (gboolean reporting,
g_main_context_invoke_full (g_task_get_context (task),
g_task_get_priority (task),
measure_disk_usage_invoke_progress,
- g_memdup2 (&progress, sizeof progress),
+ g_memdup (&progress, sizeof progress),
g_free);
}
@@ -7890,7 +7912,7 @@ measure_disk_usage_thread (GTask *task,
data->progress_callback ? measure_disk_usage_progress : NULL, task,
&result.disk_usage, &result.num_dirs, &result.num_files,
&error))
- g_task_return_pointer (task, g_memdup2 (&result, sizeof result), g_free);
+ g_task_return_pointer (task, g_memdup (&result, sizeof result), g_free);
else
g_task_return_error (task, error);
}
@@ -7914,7 +7936,7 @@ g_file_real_measure_disk_usage_async (GFile *file,
task = g_task_new (file, cancellable, callback, user_data);
g_task_set_source_tag (task, g_file_real_measure_disk_usage_async);
- g_task_set_task_data (task, g_memdup2 (&data, sizeof data), g_free);
+ g_task_set_task_data (task, g_memdup (&data, sizeof data), g_free);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, measure_disk_usage_thread);
diff --git a/gio/gfile.h b/gio/gfile.h
index 8b6d08385..4cff1a372 100644
--- a/gio/gfile.h
+++ b/gio/gfile.h
@@ -1094,6 +1094,12 @@ gboolean g_file_eject_mountable_with_operation_finish (GFile
GAsyncResult *result,
GError **error);
+GLIB_AVAILABLE_IN_2_68
+char * g_file_build_attribute_list_for_copy (GFile *file,
+ GFileCopyFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+
GLIB_AVAILABLE_IN_ALL
gboolean g_file_copy_attributes (GFile *source,
GFile *destination,
diff --git a/gio/gio-tool-info.c b/gio/gio-tool-info.c
index a06263545..7cf568370 100644
--- a/gio/gio-tool-info.c
+++ b/gio/gio-tool-info.c
@@ -182,8 +182,7 @@ show_info (GFile *file, GFileInfo *info)
gchar *root_string = NULL;
gchar *mount;
gchar *fs;
- const gchar *options;
- gchar *options_string = NULL;
+ gchar *options;
device = g_strescape (g_unix_mount_get_device_path (entry), NULL);
root = g_unix_mount_get_root_path (entry);
@@ -195,22 +194,16 @@ show_info (GFile *file, GFileInfo *info)
}
mount = g_strescape (g_unix_mount_get_mount_path (entry), NULL);
fs = g_strescape (g_unix_mount_get_fs_type (entry), NULL);
-
- options = g_unix_mount_get_options (entry);
- if (options != NULL)
- {
- options_string = g_strescape (options, NULL);
- }
+ options = g_strescape (g_unix_mount_get_options (entry), NULL);
g_print (_("unix mount: %s%s %s %s %s\n"), device,
- root_string ? root_string : "", mount, fs,
- options_string ? options_string : "");
+ root_string ? root_string : "", mount, fs, options);
g_free (device);
g_free (root_string);
g_free (mount);
g_free (fs);
- g_free (options_string);
+ g_free (options);
g_unix_mount_free (entry);
}
diff --git a/gio/giomodule.c b/gio/giomodule.c
index aaf46364c..dc4d6d3b3 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -30,7 +30,6 @@
#include "giomodule.h"
#include "giomodule-priv.h"
-#include "glib-private.h"
#include "glocalfilemonitor.h"
#include "gnativevolumemonitor.h"
#include "gproxyresolver.h"
@@ -807,9 +806,6 @@ _g_io_module_get_default_type (const gchar *extension_point,
return G_TYPE_INVALID;
}
- /* It’s OK to query the environment here, even when running as setuid, because
- * it only allows a choice between existing already-loaded modules. No new
- * code is loaded based on the environment variable value. */
use_this = envvar ? g_getenv (envvar) : NULL;
if (g_strcmp0 (use_this, "help") == 0)
{
@@ -959,9 +955,6 @@ _g_io_module_get_default (const gchar *extension_point,
return NULL;
}
- /* It’s OK to query the environment here, even when running as setuid, because
- * it only allows a choice between existing already-loaded modules. No new
- * code is loaded based on the environment variable value. */
use_this = envvar ? g_getenv (envvar) : NULL;
if (g_strcmp0 (use_this, "help") == 0)
{
@@ -1157,16 +1150,8 @@ static gchar *
get_gio_module_dir (void)
{
gchar *module_dir;
- gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
-
- /* If running as setuid, loading modules from an arbitrary directory
- * controlled by the unprivileged user who is running the program could allow
- * for execution of arbitrary code (in constructors in modules).
- * Don’t allow it.
- *
- * If a setuid program somehow needs to load additional GIO modules, it should
- * explicitly call g_io_modules_scan_all_in_directory(). */
- module_dir = !is_setuid ? g_strdup (g_getenv ("GIO_MODULE_DIR")) : NULL;
+
+ module_dir = g_strdup (g_getenv ("GIO_MODULE_DIR"));
if (module_dir == NULL)
{
#ifdef G_OS_WIN32
@@ -1198,14 +1183,13 @@ _g_io_modules_ensure_loaded (void)
if (!loaded_dirs)
{
- gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
gchar *module_dir;
loaded_dirs = TRUE;
scope = g_io_module_scope_new (G_IO_MODULE_SCOPE_BLOCK_DUPLICATES);
- /* First load any overrides, extras (but not if running as setuid!) */
- module_path = !is_setuid ? g_getenv ("GIO_EXTRA_MODULES") : NULL;
+ /* First load any overrides, extras */
+ module_path = g_getenv ("GIO_EXTRA_MODULES");
if (module_path)
{
gchar **paths;
diff --git a/gio/giowin32-private.c b/gio/giowin32-private.c
index 47e840805..7120ae0ea 100644
--- a/gio/giowin32-private.c
+++ b/gio/giowin32-private.c
@@ -16,12 +16,11 @@
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "gstrfuncsprivate.h"
-static gsize
+static gssize
g_utf16_len (const gunichar2 *str)
{
- gsize result;
+ gssize result;
for (result = 0; str[0] != 0; str++, result++)
;
@@ -32,20 +31,17 @@ g_utf16_len (const gunichar2 *str)
static gunichar2 *
g_wcsdup (const gunichar2 *str, gssize str_len)
{
- gsize str_len_unsigned;
- gsize str_size;
+ gssize str_size;
g_return_val_if_fail (str != NULL, NULL);
- if (str_len < 0)
- str_len_unsigned = g_utf16_len (str);
- else
- str_len_unsigned = (gsize) str_len;
+ if (str_len == -1)
+ str_len = g_utf16_len (str);
- g_assert (str_len_unsigned <= G_MAXSIZE / sizeof (gunichar2) - 1);
- str_size = (str_len_unsigned + 1) * sizeof (gunichar2);
+ g_assert (str_len <= G_MAXSIZE / sizeof (gunichar2) - 1);
+ str_size = (str_len + 1) * sizeof (gunichar2);
- return g_memdup2 (str, str_size);
+ return g_memdup (str, str_size);
}
static const gunichar2 *
diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index de216e615..cd5765afd 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -33,7 +33,6 @@
#include "gfilemonitor.h"
#include "gsimplepermission.h"
#include "gsettingsbackendinternal.h"
-#include "gstrfuncsprivate.h"
#include "giomodule-priv.h"
#include "gportalsupport.h"
@@ -146,8 +145,8 @@ convert_path (GKeyfileSettingsBackend *kfsb,
gchar **group,
gchar **basename)
{
- gsize key_len = strlen (key);
- const gchar *last_slash;
+ gint key_len = strlen (key);
+ gint i;
if (key_len < kfsb->prefix_len ||
memcmp (key, kfsb->prefix, kfsb->prefix_len) != 0)
@@ -156,48 +155,38 @@ convert_path (GKeyfileSettingsBackend *kfsb,
key_len -= kfsb->prefix_len;
key += kfsb->prefix_len;
- last_slash = strrchr (key, '/');
-
- /* Disallow empty group names or key names */
- if (key_len == 0 ||
- (last_slash != NULL &&
- (*(last_slash + 1) == '\0' ||
- last_slash == key)))
- return FALSE;
+ for (i = key_len; i >= 0; i--)
+ if (key[i] == '/')
+ break;
if (kfsb->root_group)
{
/* if a root_group was specified, make sure the user hasn't given
* a path that ghosts that group name
*/
- if (last_slash != NULL && (last_slash - key) == kfsb->root_group_len && memcmp (key, kfsb->root_group, last_slash - key) == 0)
+ if (i == kfsb->root_group_len && memcmp (key, kfsb->root_group, i) == 0)
return FALSE;
}
else
{
/* if no root_group was given, ensure that the user gave a path */
- if (last_slash == NULL)
+ if (i == -1)
return FALSE;
}
if (group)
{
- if (last_slash != NULL)
+ if (i >= 0)
{
- *group = g_memdup2 (key, (last_slash - key) + 1);
- (*group)[(last_slash - key)] = '\0';
+ *group = g_memdup (key, i + 1);
+ (*group)[i] = '\0';
}
else
*group = g_strdup (kfsb->root_group);
}
if (basename)
- {
- if (last_slash != NULL)
- *basename = g_memdup2 (last_slash + 1, key_len - (last_slash - key));
- else
- *basename = g_strdup (key);
- }
+ *basename = g_memdup (key + i + 1, key_len - i);
return TRUE;
}
diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c
index a4abef089..90fcb3336 100644
--- a/gio/glocalfileinfo.c
+++ b/gio/glocalfileinfo.c
@@ -1547,45 +1547,15 @@ win32_get_file_user_info (const gchar *filename,
/* support for '.hidden' files */
G_LOCK_DEFINE_STATIC (hidden_cache);
static GHashTable *hidden_cache;
-static GSource *hidden_cache_source = NULL; /* Under the hidden_cache lock */
-static guint hidden_cache_ttl_secs = 5;
-static guint hidden_cache_ttl_jitter_secs = 2;
-
-typedef struct
-{
- GHashTable *hidden_files;
- gint64 timestamp_secs;
-} HiddenCacheData;
static gboolean
remove_from_hidden_cache (gpointer user_data)
{
- HiddenCacheData *data;
- GHashTableIter iter;
- gboolean retval;
- gint64 timestamp_secs;
-
G_LOCK (hidden_cache);
- timestamp_secs = g_source_get_time (hidden_cache_source) / G_USEC_PER_SEC;
-
- g_hash_table_iter_init (&iter, hidden_cache);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
- {
- if (timestamp_secs > data->timestamp_secs + hidden_cache_ttl_secs)
- g_hash_table_iter_remove (&iter);
- }
-
- if (g_hash_table_size (hidden_cache) == 0)
- {
- g_clear_pointer (&hidden_cache_source, g_source_unref);
- retval = G_SOURCE_REMOVE;
- }
- else
- retval = G_SOURCE_CONTINUE;
-
+ g_hash_table_remove (hidden_cache, user_data);
G_UNLOCK (hidden_cache);
- return retval;
+ return FALSE;
}
static GHashTable *
@@ -1623,19 +1593,16 @@ read_hidden_file (const gchar *dirname)
}
static void
-free_hidden_file_data (gpointer user_data)
+maybe_unref_hash_table (gpointer data)
{
- HiddenCacheData *data = user_data;
-
- g_clear_pointer (&data->hidden_files, g_hash_table_unref);
- g_free (data);
+ if (data != NULL)
+ g_hash_table_unref (data);
}
static gboolean
file_is_hidden (const gchar *path,
const gchar *basename)
{
- HiddenCacheData *data;
gboolean result;
gchar *dirname;
gpointer table;
@@ -1646,38 +1613,28 @@ file_is_hidden (const gchar *path,
if G_UNLIKELY (hidden_cache == NULL)
hidden_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, free_hidden_file_data);
+ g_free, maybe_unref_hash_table);
if (!g_hash_table_lookup_extended (hidden_cache, dirname,
- NULL, (gpointer *) &data))
+ NULL, &table))
{
gchar *mydirname;
-
- data = g_new0 (HiddenCacheData, 1);
- data->hidden_files = table = read_hidden_file (dirname);
- data->timestamp_secs = g_get_monotonic_time () / G_USEC_PER_SEC;
+ GSource *remove_from_cache_source;
g_hash_table_insert (hidden_cache,
mydirname = g_strdup (dirname),
- data);
+ table = read_hidden_file (dirname));
- if (!hidden_cache_source)
- {
- hidden_cache_source =
- g_timeout_source_new_seconds (hidden_cache_ttl_secs +
- hidden_cache_ttl_jitter_secs);
- g_source_set_priority (hidden_cache_source, G_PRIORITY_DEFAULT);
- g_source_set_name (hidden_cache_source,
- "[gio] remove_from_hidden_cache");
- g_source_set_callback (hidden_cache_source,
- remove_from_hidden_cache,
- NULL, NULL);
- g_source_attach (hidden_cache_source,
- GLIB_PRIVATE_CALL (g_get_worker_context) ());
- }
+ remove_from_cache_source = g_timeout_source_new_seconds (5);
+ g_source_set_priority (remove_from_cache_source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (remove_from_cache_source,
+ remove_from_hidden_cache,
+ mydirname,
+ NULL);
+ g_source_attach (remove_from_cache_source,
+ GLIB_PRIVATE_CALL (g_get_worker_context) ());
+ g_source_unref (remove_from_cache_source);
}
- else
- table = data->hidden_files;
result = table != NULL && g_hash_table_contains (table, basename);
diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c
index 4c512ea81..f34c3e439 100644
--- a/gio/glocalfileoutputstream.c
+++ b/gio/glocalfileoutputstream.c
@@ -63,12 +63,6 @@
#define O_BINARY 0
#endif
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#else
-#define HAVE_O_CLOEXEC 1
-#endif
-
struct _GLocalFileOutputStreamPrivate {
char *tmp_filename;
char *original_filename;
@@ -856,12 +850,11 @@ handle_overwrite_open (const char *filename,
int res;
int mode;
int errsv;
- gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION);
mode = mode_from_flags_or_info (flags, reference_info);
/* We only need read access to the original file if we are creating a backup.
- * We also add O_CREAT to avoid a race if the file was just removed */
+ * We also add O_CREATE to avoid a race if the file was just removed */
if (create_backup || readable)
open_flags = O_RDWR | O_CREAT | O_BINARY;
else
@@ -884,22 +877,16 @@ handle_overwrite_open (const char *filename,
/* Could be a symlink, or it could be a regular ELOOP error,
* but then the next open will fail too. */
is_symlink = TRUE;
- if (!replace_destination_set)
- fd = g_open (filename, open_flags, mode);
+ fd = g_open (filename, open_flags, mode);
}
-#else /* if !O_NOFOLLOW */
+#else
+ fd = g_open (filename, open_flags, mode);
+ errsv = errno;
/* This is racy, but we do it as soon as possible to minimize the race */
is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK);
-
- if (!is_symlink || !replace_destination_set)
- {
- fd = g_open (filename, open_flags, mode);
- errsv = errno;
- }
#endif
- if (fd == -1 &&
- (!is_symlink || !replace_destination_set))
+ if (fd == -1)
{
char *display_name = g_filename_display_name (filename);
g_set_error (error, G_IO_ERROR,
@@ -910,30 +897,15 @@ handle_overwrite_open (const char *filename,
return -1;
}
- if (!is_symlink)
- {
- res = g_local_file_fstat (fd,
- G_LOCAL_FILE_STAT_FIELD_TYPE |
- G_LOCAL_FILE_STAT_FIELD_MODE |
- G_LOCAL_FILE_STAT_FIELD_UID |
- G_LOCAL_FILE_STAT_FIELD_GID |
- G_LOCAL_FILE_STAT_FIELD_MTIME |
- G_LOCAL_FILE_STAT_FIELD_NLINK,
- G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat);
- errsv = errno;
- }
- else
- {
- res = g_local_file_lstat (filename,
- G_LOCAL_FILE_STAT_FIELD_TYPE |
- G_LOCAL_FILE_STAT_FIELD_MODE |
- G_LOCAL_FILE_STAT_FIELD_UID |
- G_LOCAL_FILE_STAT_FIELD_GID |
- G_LOCAL_FILE_STAT_FIELD_MTIME |
- G_LOCAL_FILE_STAT_FIELD_NLINK,
- G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat);
- errsv = errno;
- }
+ res = g_local_file_fstat (fd,
+ G_LOCAL_FILE_STAT_FIELD_TYPE |
+ G_LOCAL_FILE_STAT_FIELD_MODE |
+ G_LOCAL_FILE_STAT_FIELD_UID |
+ G_LOCAL_FILE_STAT_FIELD_GID |
+ G_LOCAL_FILE_STAT_FIELD_MTIME |
+ G_LOCAL_FILE_STAT_FIELD_NLINK,
+ G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat);
+ errsv = errno;
if (res != 0)
{
@@ -950,27 +922,16 @@ handle_overwrite_open (const char *filename,
if (!S_ISREG (_g_stat_mode (&original_stat)))
{
if (S_ISDIR (_g_stat_mode (&original_stat)))
- {
- g_set_error_literal (error,
- G_IO_ERROR,
- G_IO_ERROR_IS_DIRECTORY,
- _("Target file is a directory"));
- goto err_out;
- }
- else if (!is_symlink ||
-#ifdef S_ISLNK
- !S_ISLNK (_g_stat_mode (&original_stat))
-#else
- FALSE
-#endif
- )
- {
- g_set_error_literal (error,
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ _("Target file is a directory"));
+ else
+ g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_REGULAR_FILE,
_("Target file is not a regular file"));
- goto err_out;
- }
+ goto err_out;
}
if (etag != NULL)
@@ -999,7 +960,7 @@ handle_overwrite_open (const char *filename,
* to a backup file and rewrite the contents of the file.
*/
- if (replace_destination_set ||
+ if ((flags & G_FILE_CREATE_REPLACE_DESTINATION) ||
(!(_g_stat_nlink (&original_stat) > 1) && !is_symlink))
{
char *dirname, *tmp_filename;
@@ -1018,7 +979,7 @@ handle_overwrite_open (const char *filename,
/* try to keep permissions (unless replacing) */
- if (!replace_destination_set &&
+ if ( ! (flags & G_FILE_CREATE_REPLACE_DESTINATION) &&
(
#ifdef HAVE_FCHOWN
fchown (tmpfd, _g_stat_uid (&original_stat), _g_stat_gid (&original_stat)) == -1 ||
@@ -1053,8 +1014,7 @@ handle_overwrite_open (const char *filename,
}
}
- if (fd >= 0)
- g_close (fd, NULL);
+ g_close (fd, NULL);
*temp_filename = tmp_filename;
return tmpfd;
}
@@ -1160,7 +1120,7 @@ handle_overwrite_open (const char *filename,
}
}
- if (replace_destination_set)
+ if (flags & G_FILE_CREATE_REPLACE_DESTINATION)
{
g_close (fd, NULL);
@@ -1245,7 +1205,7 @@ _g_local_file_output_stream_replace (const char *filename,
sync_on_close = FALSE;
/* If the file doesn't exist, create it */
- open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
+ open_flags = O_CREAT | O_EXCL | O_BINARY;
if (readable)
open_flags |= O_RDWR;
else
@@ -1275,11 +1235,8 @@ _g_local_file_output_stream_replace (const char *filename,
set_error_from_open_errno (filename, error);
return NULL;
}
-#if !defined(HAVE_O_CLOEXEC) && defined(F_SETFD)
- else
- fcntl (fd, F_SETFD, FD_CLOEXEC);
-#endif
-
+
+
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
stream->priv->fd = fd;
stream->priv->sync_on_close = sync_on_close;
diff --git a/gio/gresource.c b/gio/gresource.c
index 13632d971..b7222b8eb 100644
--- a/gio/gresource.c
+++ b/gio/gresource.c
@@ -32,8 +32,6 @@
#include <gio/gzlibdecompressor.h>
#include <gio/gconverterinputstream.h>
-#include "glib-private.h"
-
struct _GResource
{
int ref_count;
@@ -161,7 +159,7 @@ G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref)
* replace resources in the program or library, without recompiling, for debugging or quick hacking and testing
* purposes. Since GLib 2.50, it is possible to use the `G_RESOURCE_OVERLAYS` environment variable to selectively overlay
* resources with replacements from the filesystem. It is a %G_SEARCHPATH_SEPARATOR-separated list of substitutions to perform
- * during resource lookups. It is ignored when running in a setuid process.
+ * during resource lookups.
*
* A substitution has the form
*
@@ -332,13 +330,10 @@ g_resource_find_overlay (const gchar *path,
if (g_once_init_enter (&overlay_dirs))
{
- gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
const gchar * const *result;
const gchar *envvar;
- /* Don’t load overlays if setuid, as they could allow reading privileged
- * files. */
- envvar = !is_setuid ? g_getenv ("G_RESOURCE_OVERLAYS") : NULL;
+ envvar = g_getenv ("G_RESOURCE_OVERLAYS");
if (envvar != NULL)
{
gchar **parts;
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index 1282c10a1..0b94f76f6 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -18,10 +18,8 @@
#include "config.h"
-#include "glib-private.h"
#include "gsettingsschema-internal.h"
#include "gsettings.h"
-#include "gstrfuncsprivate.h"
#include "gvdb/gvdb-reader.h"
#include "strinfo.c"
@@ -345,7 +343,6 @@ initialise_schema_sources (void)
*/
if G_UNLIKELY (g_once_init_enter (&initialised))
{
- gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
const gchar * const *dirs;
const gchar *path;
gchar **extra_schema_dirs;
@@ -360,9 +357,7 @@ initialise_schema_sources (void)
try_prepend_data_dir (g_get_user_data_dir ());
- /* Disallow loading extra schemas if running as setuid, as that could
- * allow reading privileged files. */
- if (!is_setuid && (path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
+ if ((path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
{
extra_schema_dirs = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; extra_schema_dirs[i]; i++);
@@ -1072,9 +1067,9 @@ g_settings_schema_list_children (GSettingsSchema *schema)
if (g_str_has_suffix (key, "/"))
{
- gsize length = strlen (key);
+ gint length = strlen (key);
- strv[j] = g_memdup2 (key, length);
+ strv[j] = g_memdup (key, length);
strv[j][length - 1] = '\0';
j++;
}
diff --git a/gio/gsocket.c b/gio/gsocket.c
index 5a2468e0a..0f8f9259f 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -75,7 +75,6 @@
#include "gcredentialsprivate.h"
#include "glibintl.h"
#include "gioprivate.h"
-#include "gstrfuncsprivate.h"
/**
* SECTION:gsocket
@@ -170,7 +169,7 @@ static gboolean g_socket_datagram_based_condition_wait (GDatagramBased
GError **error);
static GSocketAddress *
-cache_recv_address (GSocket *socket, struct sockaddr *native, size_t native_len);
+cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len);
static gssize
g_socket_receive_message_with_timeout (GSocket *socket,
@@ -256,7 +255,7 @@ struct _GSocketPrivate
struct {
GSocketAddress *addr;
struct sockaddr *native;
- gsize native_len;
+ gint native_len;
guint64 last_used;
} recv_addr_cache[RECV_ADDR_CACHE_SIZE];
};
@@ -5264,14 +5263,14 @@ g_socket_send_messages_with_timeout (GSocket *socket,
}
static GSocketAddress *
-cache_recv_address (GSocket *socket, struct sockaddr *native, size_t native_len)
+cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len)
{
GSocketAddress *saddr;
gint i;
guint64 oldest_time = G_MAXUINT64;
gint oldest_index = 0;
- if (native_len == 0)
+ if (native_len <= 0)
return NULL;
saddr = NULL;
@@ -5279,7 +5278,7 @@ cache_recv_address (GSocket *socket, struct sockaddr *native, size_t native_len)
{
GSocketAddress *tmp = socket->priv->recv_addr_cache[i].addr;
gpointer tmp_native = socket->priv->recv_addr_cache[i].native;
- gsize tmp_native_len = socket->priv->recv_addr_cache[i].native_len;
+ gint tmp_native_len = socket->priv->recv_addr_cache[i].native_len;
if (!tmp)
continue;
@@ -5309,7 +5308,7 @@ cache_recv_address (GSocket *socket, struct sockaddr *native, size_t native_len)
g_free (socket->priv->recv_addr_cache[oldest_index].native);
}
- socket->priv->recv_addr_cache[oldest_index].native = g_memdup2 (native, native_len);
+ socket->priv->recv_addr_cache[oldest_index].native = g_memdup (native, native_len);
socket->priv->recv_addr_cache[oldest_index].native_len = native_len;
socket->priv->recv_addr_cache[oldest_index].addr = g_object_ref (saddr);
socket->priv->recv_addr_cache[oldest_index].last_used = g_get_monotonic_time ();
@@ -5457,9 +5456,6 @@ g_socket_receive_message_with_timeout (GSocket *socket,
/* do it */
while (1)
{
- /* addrlen has to be of type int because that’s how WSARecvFrom() is defined */
- G_STATIC_ASSERT (sizeof addr <= G_MAXINT);
-
addrlen = sizeof addr;
if (address)
result = WSARecvFrom (socket->priv->fd,
@@ -5479,10 +5475,10 @@ g_socket_receive_message_with_timeout (GSocket *socket,
if (errsv == WSAEINTR)
continue;
- win32_unset_event_mask (socket, FD_READ);
-
if (errsv == WSAEWOULDBLOCK)
{
+ win32_unset_event_mask (socket, FD_READ);
+
if (timeout_us != 0)
{
if (!block_on_timeout (socket, G_IO_IN, timeout_us,
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index c9943309c..ce3c186fb 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -24,6 +24,10 @@
#include "config.h"
#include "gsocketclient.h"
+#ifndef G_OS_WIN32
+#include <netinet/in.h>
+#endif
+
#include <stdlib.h>
#include <string.h>
@@ -39,6 +43,7 @@
#include <gio/gioerror.h>
#include <gio/gsocket.h>
#include <gio/gnetworkaddress.h>
+#include <gio/gnetworking.h>
#include <gio/gnetworkservice.h>
#include <gio/gproxy.h>
#include <gio/gproxyresolver.h>
@@ -142,6 +147,10 @@ create_socket (GSocketClient *client,
if (client->priv->local_address)
{
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ g_socket_set_option (socket, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, 1, NULL);
+#endif
+
if (!g_socket_bind (socket,
client->priv->local_address,
FALSE,
@@ -819,7 +828,7 @@ g_socket_client_class_init (GSocketClientClass *class)
* multiple times (or not at all) for a given connectable (in
* particular, if @client ends up attempting to connect to more than
* one address). However, if @client emits the #GSocketClient::event
- * signal at all for a given connectable, that it will always emit
+ * signal at all for a given connectable, then it will always emit
* it with %G_SOCKET_CLIENT_COMPLETE when it is done.
*
* Note that there may be additional #GSocketClientEvent values in
@@ -936,7 +945,7 @@ g_socket_client_class_init (GSocketClientClass *class)
static void
g_socket_client_emit_event (GSocketClient *client,
- GSocketClientEvent event,
+ GSocketClientEvent event,
GSocketConnectable *connectable,
GIOStream *connection)
{
@@ -944,6 +953,72 @@ g_socket_client_emit_event (GSocketClient *client,
event, connectable, connection);
}
+/* Originally, GSocketClient returned whatever error occured last. Turns
+ * out this doesn't work well in practice. Consider the following case:
+ * DNS returns an IPv4 and IPv6 address. First we'll connect() to the
+ * IPv4 address, and say that succeeds, but TLS is enabled and the TLS
+ * handshake fails. Then we try the IPv6 address and receive ENETUNREACH
+ * because IPv6 isn't supported. We wind up returning NETWORK_UNREACHABLE
+ * even though the address can be pinged and a TLS error would be more
+ * appropriate. So instead, we now try to return the error corresponding
+ * to the latest attempted GSocketClientEvent in the connection process.
+ * TLS errors take precedence over proxy errors, which take precedence
+ * over connect() errors, which take precedence over DNS errors.
+ *
+ * Note that the example above considers a sync codepath, but this is an
+ * issue for the async codepath too, where events and errors may occur
+ * in confusing orders.
+ */
+typedef struct
+{
+ GError *tmp_error;
+ GError *best_error;
+ GSocketClientEvent best_error_event;
+} SocketClientErrorInfo;
+
+static SocketClientErrorInfo *
+socket_client_error_info_new (void)
+{
+ return g_new0 (SocketClientErrorInfo, 1);
+}
+
+static void
+socket_client_error_info_free (SocketClientErrorInfo *info)
+{
+ g_assert (info->tmp_error == NULL);
+ g_clear_error (&info->best_error);
+ g_free (info);
+}
+
+static void
+consider_tmp_error (SocketClientErrorInfo *info,
+ GSocketClientEvent event)
+{
+ if (info->tmp_error == NULL)
+ return;
+
+ /* If we ever add more GSocketClientEvents in the future, then we'll
+ * no longer be able to use >= for this comparison, because future
+ * events will compare greater than G_SOCKET_CLIENT_COMPLETE. Until
+ * then, this is convenient. Note G_SOCKET_CLIENT_RESOLVING is 0 so we
+ * need to use >= here or those errors would never be set. That means
+ * if we get two errors on the same GSocketClientEvent, we wind up
+ * preferring the last one, which is fine.
+ */
+ g_assert (event <= G_SOCKET_CLIENT_COMPLETE);
+ if (event >= info->best_error_event)
+ {
+ g_clear_error (&info->best_error);
+ info->best_error = info->tmp_error;
+ info->tmp_error = NULL;
+ info->best_error_event = event;
+ }
+ else
+ {
+ g_clear_error (&info->tmp_error);
+ }
+}
+
/**
* g_socket_client_connect:
* @client: a #GSocketClient.
@@ -982,9 +1057,10 @@ g_socket_client_connect (GSocketClient *client,
{
GIOStream *connection = NULL;
GSocketAddressEnumerator *enumerator = NULL;
- GError *last_error, *tmp_error;
+ SocketClientErrorInfo *error_info;
+ gboolean ever_resolved = FALSE;
- last_error = NULL;
+ error_info = socket_client_error_info_new ();
if (can_use_proxy (client))
{
@@ -1009,44 +1085,38 @@ g_socket_client_connect (GSocketClient *client,
if (g_cancellable_is_cancelled (cancellable))
{
- g_clear_error (error);
- g_clear_error (&last_error);
- g_cancellable_set_error_if_cancelled (cancellable, error);
+ g_clear_error (&error_info->best_error);
+ g_cancellable_set_error_if_cancelled (cancellable, &error_info->best_error);
break;
}
- tmp_error = NULL;
- g_socket_client_emit_event (client, G_SOCKET_CLIENT_RESOLVING,
- connectable, NULL);
+ if (!ever_resolved)
+ {
+ g_socket_client_emit_event (client, G_SOCKET_CLIENT_RESOLVING,
+ connectable, NULL);
+ }
address = g_socket_address_enumerator_next (enumerator, cancellable,
- &tmp_error);
+ &error_info->tmp_error);
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_RESOLVING);
+ if (!ever_resolved)
+ {
+ g_socket_client_emit_event (client, G_SOCKET_CLIENT_RESOLVED,
+ connectable, NULL);
+ ever_resolved = TRUE;
+ }
if (address == NULL)
{
- if (tmp_error)
- {
- g_clear_error (&last_error);
- g_propagate_error (error, tmp_error);
- }
- else if (last_error)
- {
- g_propagate_error (error, last_error);
- }
- else
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unknown error on connect"));
+ /* Enumeration is finished. */
+ g_assert (&error_info->best_error != NULL);
break;
}
- g_socket_client_emit_event (client, G_SOCKET_CLIENT_RESOLVED,
- connectable, NULL);
using_proxy = (G_IS_PROXY_ADDRESS (address) &&
client->priv->enable_proxy);
- /* clear error from previous attempt */
- g_clear_error (&last_error);
-
- socket = create_socket (client, address, &last_error);
+ socket = create_socket (client, address, &error_info->tmp_error);
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_CONNECTING);
if (socket == NULL)
{
g_object_unref (address);
@@ -1058,14 +1128,15 @@ g_socket_client_connect (GSocketClient *client,
g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTING, connectable, connection);
if (g_socket_connection_connect (G_SOCKET_CONNECTION (connection),
- address, cancellable, &last_error))
+ address, cancellable, &error_info->tmp_error))
{
g_socket_connection_set_cached_remote_address ((GSocketConnection*)connection, NULL);
g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTED, connectable, connection);
}
else
{
- clarify_connect_error (last_error, connectable, address);
+ clarify_connect_error (error_info->tmp_error, connectable, address);
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_CONNECTING);
g_object_unref (connection);
connection = NULL;
}
@@ -1086,9 +1157,10 @@ g_socket_client_connect (GSocketClient *client,
g_critical ("Trying to proxy over non-TCP connection, this is "
"most likely a bug in GLib IO library.");
- g_set_error_literal (&last_error,
+ g_set_error_literal (&error_info->tmp_error,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Proxying over a non-TCP connection is not supported."));
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_PROXY_NEGOTIATING);
g_object_unref (connection);
connection = NULL;
@@ -1106,7 +1178,9 @@ g_socket_client_connect (GSocketClient *client,
connection,
proxy_addr,
cancellable,
- &last_error);
+ &error_info->tmp_error);
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_PROXY_NEGOTIATING);
+
g_object_unref (connection);
connection = proxy_connection;
g_object_unref (proxy);
@@ -1116,9 +1190,10 @@ g_socket_client_connect (GSocketClient *client,
}
else
{
- g_set_error (&last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ g_set_error (&error_info->tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Proxy protocol “%s” is not supported."),
protocol);
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_PROXY_NEGOTIATING);
g_object_unref (connection);
connection = NULL;
}
@@ -1128,7 +1203,7 @@ g_socket_client_connect (GSocketClient *client,
{
GIOStream *tlsconn;
- tlsconn = g_tls_client_connection_new (connection, connectable, &last_error);
+ tlsconn = g_tls_client_connection_new (connection, connectable, &error_info->tmp_error);
g_object_unref (connection);
connection = tlsconn;
@@ -1138,16 +1213,21 @@ g_socket_client_connect (GSocketClient *client,
client->priv->tls_validation_flags);
g_socket_client_emit_event (client, G_SOCKET_CLIENT_TLS_HANDSHAKING, connectable, connection);
if (g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn),
- cancellable, &last_error))
+ cancellable, &error_info->tmp_error))
{
g_socket_client_emit_event (client, G_SOCKET_CLIENT_TLS_HANDSHAKED, connectable, connection);
}
else
{
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_TLS_HANDSHAKING);
g_object_unref (tlsconn);
connection = NULL;
}
}
+ else
+ {
+ consider_tmp_error (error_info, G_SOCKET_CLIENT_TLS_HANDSHAKING);
+ }
}
if (connection && !G_IS_SOCKET_CONNECTION (connection))
@@ -1164,6 +1244,10 @@ g_socket_client_connect (GSocketClient *client,
}
g_object_unref (enumerator);
+ if (!connection)
+ g_propagate_error (error, g_steal_pointer (&error_info->best_error));
+ socket_client_error_info_free (error_info);
+
g_socket_client_emit_event (client, G_SOCKET_CLIENT_COMPLETE, connectable, connection);
return G_SOCKET_CONNECTION (connection);
}
@@ -1341,7 +1425,7 @@ typedef struct
GSList *connection_attempts;
GSList *successful_connections;
- GError *last_error;
+ SocketClientErrorInfo *error_info;
gboolean enumerated_at_least_once;
gboolean enumeration_completed;
@@ -1361,7 +1445,7 @@ g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data)
g_slist_free_full (data->connection_attempts, connection_attempt_unref);
g_slist_free_full (data->successful_connections, connection_attempt_unref);
- g_clear_error (&data->last_error);
+ g_clear_pointer (&data->error_info, socket_client_error_info_free);
g_slice_free (GSocketClientAsyncConnectData, data);
}
@@ -1484,14 +1568,6 @@ g_socket_client_enumerator_callback (GObject *object,
gpointer user_data);
static void
-set_last_error (GSocketClientAsyncConnectData *data,
- GError *error)
-{
- g_clear_error (&data->last_error);
- data->last_error = error;
-}
-
-static void
enumerator_next_async (GSocketClientAsyncConnectData *data,
gboolean add_task_ref)
{
@@ -1500,7 +1576,8 @@ enumerator_next_async (GSocketClientAsyncConnectData *data,
if (add_task_ref)
g_object_ref (data->task);
- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL);
+ if (!data->enumerated_at_least_once)
+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL);
g_debug ("GSocketClient: Starting new address enumeration");
g_socket_address_enumerator_next_async (data->enumerator,
data->enumeration_cancellable,
@@ -1520,7 +1597,7 @@ g_socket_client_tls_handshake_callback (GObject *object,
if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (object),
result,
- &data->last_error))
+ &data->error_info->tmp_error))
{
g_object_unref (attempt->connection);
attempt->connection = G_IO_STREAM (object);
@@ -1533,7 +1610,9 @@ g_socket_client_tls_handshake_callback (GObject *object,
{
g_object_unref (object);
connection_attempt_unref (attempt);
- g_debug ("GSocketClient: TLS handshake failed: %s", data->last_error->message);
+
+ g_debug ("GSocketClient: TLS handshake failed: %s", data->error_info->tmp_error->message);
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_TLS_HANDSHAKING);
try_next_connection_or_finish (data, TRUE);
}
}
@@ -1553,7 +1632,7 @@ g_socket_client_tls_handshake (ConnectionAttempt *attempt)
g_debug ("GSocketClient: Starting TLS handshake");
tlsconn = g_tls_client_connection_new (attempt->connection,
data->connectable,
- &data->last_error);
+ &data->error_info->tmp_error);
if (tlsconn)
{
g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
@@ -1568,6 +1647,8 @@ g_socket_client_tls_handshake (ConnectionAttempt *attempt)
else
{
connection_attempt_unref (attempt);
+
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_TLS_HANDSHAKING);
try_next_connection_or_finish (data, TRUE);
}
}
@@ -1583,19 +1664,19 @@ g_socket_client_proxy_connect_callback (GObject *object,
g_object_unref (attempt->connection);
attempt->connection = g_proxy_connect_finish (G_PROXY (object),
result,
- &data->last_error);
+ &data->error_info->tmp_error);
if (attempt->connection)
{
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_PROXY_NEGOTIATED, data->connectable, attempt->connection);
+ g_socket_client_tls_handshake (attempt);
}
else
{
connection_attempt_unref (attempt);
+
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_PROXY_NEGOTIATING);
try_next_connection_or_finish (data, TRUE);
- return;
}
-
- g_socket_client_tls_handshake (attempt);
}
static void
@@ -1663,9 +1744,10 @@ try_next_successful_connection (GSocketClientAsyncConnectData *data)
g_critical ("Trying to proxy over non-TCP connection, this is "
"most likely a bug in GLib IO library.");
- g_set_error_literal (&data->last_error,
+ g_set_error_literal (&data->error_info->tmp_error,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Proxying over a non-TCP connection is not supported."));
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_PROXY_NEGOTIATING);
}
else if (g_hash_table_contains (data->client->priv->app_proxies, protocol))
{
@@ -1692,11 +1774,10 @@ try_next_successful_connection (GSocketClientAsyncConnectData *data)
}
else
{
- g_clear_error (&data->last_error);
-
- g_set_error (&data->last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ g_set_error (&data->error_info->tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Proxy protocol “%s” is not supported."),
protocol);
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_PROXY_NEGOTIATING);
}
data->connection_in_progress = FALSE;
@@ -1727,7 +1808,7 @@ try_next_connection_or_finish (GSocketClientAsyncConnectData *data,
return;
}
- complete_connection_with_error (data, data->last_error);
+ complete_connection_with_error (data, g_steal_pointer (&data->error_info->best_error));
}
static void
@@ -1737,7 +1818,6 @@ g_socket_client_connected_callback (GObject *source,
{
ConnectionAttempt *attempt = user_data;
GSocketClientAsyncConnectData *data = attempt->data;
- GError *error = NULL;
if (task_completed_or_cancelled (data) || g_cancellable_is_cancelled (attempt->cancellable))
{
@@ -1753,20 +1833,20 @@ g_socket_client_connected_callback (GObject *source,
}
if (!g_socket_connection_connect_finish (G_SOCKET_CONNECTION (source),
- result, &error))
+ result, &data->error_info->tmp_error))
{
if (!g_cancellable_is_cancelled (attempt->cancellable))
{
- clarify_connect_error (error, data->connectable, attempt->address);
- set_last_error (data, error);
- g_debug ("GSocketClient: Connection attempt failed: %s", error->message);
+ clarify_connect_error (data->error_info->tmp_error, data->connectable, attempt->address);
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_CONNECTING);
+ g_debug ("GSocketClient: Connection attempt failed: %s", data->error_info->tmp_error->message);
connection_attempt_remove (attempt);
connection_attempt_unref (attempt);
try_next_connection_or_finish (data, FALSE);
}
else /* Silently ignore cancelled attempts */
{
- g_clear_error (&error);
+ g_clear_error (&data->error_info->tmp_error);
g_object_unref (data->task);
connection_attempt_unref (attempt);
}
@@ -1824,7 +1904,6 @@ g_socket_client_enumerator_callback (GObject *object,
GSocketAddress *address = NULL;
GSocket *socket;
ConnectionAttempt *attempt;
- GError *error = NULL;
if (task_completed_or_cancelled (data))
{
@@ -1833,7 +1912,7 @@ g_socket_client_enumerator_callback (GObject *object,
}
address = g_socket_address_enumerator_next_finish (data->enumerator,
- result, &error);
+ result, &data->error_info->tmp_error);
if (address == NULL)
{
if (G_UNLIKELY (data->enumeration_completed))
@@ -1842,7 +1921,7 @@ g_socket_client_enumerator_callback (GObject *object,
data->enumeration_completed = TRUE;
g_debug ("GSocketClient: Address enumeration completed (out of addresses)");
- /* As per API docs: We only care about error if its the first call,
+ /* As per API docs: We only care about error if it's the first call,
after that the enumerator is done.
Note that we don't care about cancellation errors because
@@ -1853,20 +1932,11 @@ g_socket_client_enumerator_callback (GObject *object,
if ((data->enumerated_at_least_once && !data->connection_attempts && !data->connection_in_progress) ||
!data->enumerated_at_least_once)
{
- g_debug ("GSocketClient: Address enumeration failed: %s", error ? error->message : NULL);
- if (data->last_error)
- {
- g_clear_error (&error);
- error = data->last_error;
- data->last_error = NULL;
- }
- else if (!error)
- {
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unknown error on connect"));
- }
-
- complete_connection_with_error (data, error);
+ g_debug ("GSocketClient: Address enumeration failed: %s",
+ data->error_info->tmp_error ? data->error_info->tmp_error->message : NULL);
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_RESOLVING);
+ g_assert (data->error_info->best_error);
+ complete_connection_with_error (data, g_steal_pointer (&data->error_info->best_error));
}
/* Enumeration should never trigger again, drop our ref */
@@ -1874,17 +1944,19 @@ g_socket_client_enumerator_callback (GObject *object,
return;
}
- data->enumerated_at_least_once = TRUE;
g_debug ("GSocketClient: Address enumeration succeeded");
- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVED,
- data->connectable, NULL);
-
- g_clear_error (&data->last_error);
+ if (!data->enumerated_at_least_once)
+ {
+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVED,
+ data->connectable, NULL);
+ data->enumerated_at_least_once = TRUE;
+ }
- socket = create_socket (data->client, address, &data->last_error);
+ socket = create_socket (data->client, address, &data->error_info->tmp_error);
if (socket == NULL)
{
g_object_unref (address);
+ consider_tmp_error (data->error_info, G_SOCKET_CLIENT_CONNECTING);
enumerator_next_async (data, FALSE);
return;
}
@@ -1927,6 +1999,15 @@ g_socket_client_enumerator_callback (GObject *object,
*
* This is the asynchronous version of g_socket_client_connect().
*
+ * You may wish to prefer the asynchronous version even in synchronous
+ * command line programs because, since 2.60, it implements
+ * [RFC 8305](https://tools.ietf.org/html/rfc8305) "Happy Eyeballs"
+ * recommendations to work around long connection timeouts in networks
+ * where IPv6 is broken by performing an IPv4 connection simultaneously
+ * without waiting for IPv6 to time out, which is not supported by the
+ * synchronous call. (This is not an API guarantee, and may change in
+ * the future.)
+ *
* When the operation is finished @callback will be
* called. You can then call g_socket_client_connect_finish() to get
* the result of the operation.
@@ -1947,6 +2028,7 @@ g_socket_client_connect_async (GSocketClient *client,
data = g_slice_new0 (GSocketClientAsyncConnectData);
data->client = client;
data->connectable = g_object_ref (connectable);
+ data->error_info = socket_client_error_info_new ();
if (can_use_proxy (client))
{
diff --git a/gio/gsubprocesslauncher-private.h b/gio/gsubprocesslauncher-private.h
index 64374a02d..95431a0ab 100644
--- a/gio/gsubprocesslauncher-private.h
+++ b/gio/gsubprocesslauncher-private.h
@@ -44,6 +44,7 @@ struct _GSubprocessLauncher
GArray *basic_fd_assignments;
GArray *needdup_fd_assignments;
+ gboolean closed_fd;
GSpawnChildSetupFunc child_setup_func;
gpointer child_setup_user_data;
diff --git a/gio/gsubprocesslauncher.c b/gio/gsubprocesslauncher.c
index 8c2f1c179..c27960949 100644
--- a/gio/gsubprocesslauncher.c
+++ b/gio/gsubprocesslauncher.c
@@ -46,6 +46,7 @@
#include "gioenumtypes.h"
#include "gsubprocess.h"
#include "ginitable.h"
+#include "gioerror.h"
#ifdef G_OS_UNIX
#include <unistd.h>
@@ -131,47 +132,27 @@ g_subprocess_launcher_set_property (GObject *object, guint prop_id,
}
static void
-g_subprocess_launcher_finalize (GObject *object)
+g_subprocess_launcher_dispose (GObject *object)
{
GSubprocessLauncher *self = G_SUBPROCESS_LAUNCHER (object);
#ifdef G_OS_UNIX
- guint i;
-
- g_free (self->stdin_path);
- g_free (self->stdout_path);
- g_free (self->stderr_path);
-
- if (self->stdin_fd != -1)
- close (self->stdin_fd);
+ g_clear_pointer (&self->stdin_path, g_free);
+ g_clear_pointer (&self->stdout_path, g_free);
+ g_clear_pointer (&self->stderr_path, g_free);
- if (self->stdout_fd != -1)
- close (self->stdout_fd);
-
- if (self->stderr_fd != -1)
- close (self->stderr_fd);
-
- if (self->basic_fd_assignments)
- {
- for (i = 0; i < self->basic_fd_assignments->len; i++)
- (void) close (g_array_index (self->basic_fd_assignments, int, i));
- g_array_unref (self->basic_fd_assignments);
- }
- if (self->needdup_fd_assignments)
- {
- for (i = 0; i < self->needdup_fd_assignments->len; i += 2)
- (void) close (g_array_index (self->needdup_fd_assignments, int, i));
- g_array_unref (self->needdup_fd_assignments);
- }
+ g_subprocess_launcher_close (self);
if (self->child_setup_destroy_notify)
(* self->child_setup_destroy_notify) (self->child_setup_user_data);
+ self->child_setup_destroy_notify = NULL;
+ self->child_setup_user_data = NULL;
#endif
- g_strfreev (self->envp);
- g_free (self->cwd);
+ g_clear_pointer (&self->envp, g_strfreev);
+ g_clear_pointer (&self->cwd, g_free);
- G_OBJECT_CLASS (g_subprocess_launcher_parent_class)->finalize (object);
+ G_OBJECT_CLASS (g_subprocess_launcher_parent_class)->dispose (object);
}
static void
@@ -194,7 +175,7 @@ g_subprocess_launcher_class_init (GSubprocessLauncherClass *class)
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = g_subprocess_launcher_set_property;
- gobject_class->finalize = g_subprocess_launcher_finalize;
+ gobject_class->dispose = g_subprocess_launcher_dispose;
g_object_class_install_property (gobject_class, 1,
g_param_spec_flags ("flags", "Flags", "GSubprocessFlags for launched processes",
@@ -634,13 +615,69 @@ g_subprocess_launcher_take_fd (GSubprocessLauncher *self,
{
if (source_fd == target_fd)
{
- g_array_append_val (self->basic_fd_assignments, source_fd);
+ if (self->basic_fd_assignments)
+ g_array_append_val (self->basic_fd_assignments, source_fd);
}
else
{
- g_array_append_val (self->needdup_fd_assignments, source_fd);
- g_array_append_val (self->needdup_fd_assignments, target_fd);
+ if (self->needdup_fd_assignments)
+ {
+ g_array_append_val (self->needdup_fd_assignments, source_fd);
+ g_array_append_val (self->needdup_fd_assignments, target_fd);
+ }
+ }
+}
+
+/**
+ * g_subprocess_launcher_close:
+ * @self: a #GSubprocessLauncher
+ *
+ * Closes all the file descriptors previously passed to the object with
+ * g_subprocess_launcher_take_fd(), g_subprocess_launcher_take_stderr_fd(), etc.
+ *
+ * After calling this method, any subsequent calls to g_subprocess_launcher_spawn() or g_subprocess_launcher_spawnv() will
+ * return %G_IO_ERROR_CLOSED. This method is idempotent if
+ * called more than once.
+ *
+ * This function is called automatically when the #GSubprocessLauncher
+ * is disposed, but is provided separately so that garbage collected
+ * language bindings can call it earlier to guarantee when FDs are closed.
+ *
+ * Since: 2.68
+ */
+void
+g_subprocess_launcher_close (GSubprocessLauncher *self)
+{
+ guint i;
+
+ g_return_if_fail (G_IS_SUBPROCESS_LAUNCHER (self));
+
+ if (self->stdin_fd != -1)
+ close (self->stdin_fd);
+ self->stdin_fd = -1;
+
+ if (self->stdout_fd != -1)
+ close (self->stdout_fd);
+ self->stdout_fd = -1;
+
+ if (self->stderr_fd != -1)
+ close (self->stderr_fd);
+ self->stderr_fd = -1;
+
+ if (self->basic_fd_assignments)
+ {
+ for (i = 0; i < self->basic_fd_assignments->len; i++)
+ (void) close (g_array_index (self->basic_fd_assignments, int, i));
+ g_clear_pointer (&self->basic_fd_assignments, g_array_unref);
}
+ if (self->needdup_fd_assignments)
+ {
+ for (i = 0; i < self->needdup_fd_assignments->len; i += 2)
+ (void) close (g_array_index (self->needdup_fd_assignments, int, i));
+ g_clear_pointer (&self->needdup_fd_assignments, g_array_unref);
+ }
+
+ self->closed_fd = TRUE;
}
/**
@@ -745,6 +782,17 @@ g_subprocess_launcher_spawnv (GSubprocessLauncher *launcher,
g_return_val_if_fail (argv != NULL && argv[0] != NULL && argv[0][0] != '\0', NULL);
+#ifdef G_OS_UNIX
+ if (launcher->closed_fd)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_CLOSED,
+ "Can't spawn a new child because a passed file descriptor has been closed.");
+ return NULL;
+ }
+#endif
+
subprocess = g_object_new (G_TYPE_SUBPROCESS,
"argv", argv,
"flags", launcher->flags,
diff --git a/gio/gsubprocesslauncher.h b/gio/gsubprocesslauncher.h
index 05d83f131..0654c2b70 100644
--- a/gio/gsubprocesslauncher.h
+++ b/gio/gsubprocesslauncher.h
@@ -103,6 +103,9 @@ void g_subprocess_launcher_take_fd (GSubpro
gint source_fd,
gint target_fd);
+GLIB_AVAILABLE_IN_2_68
+void g_subprocess_launcher_close (GSubprocessLauncher *self);
+
/* Child setup, only available on UNIX */
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_child_setup (GSubprocessLauncher *self,
diff --git a/gio/gtlscertificate.c b/gio/gtlscertificate.c
index 72de5eb1f..0668d4905 100644
--- a/gio/gtlscertificate.c
+++ b/gio/gtlscertificate.c
@@ -60,7 +60,9 @@ enum
PROP_CERTIFICATE_PEM,
PROP_PRIVATE_KEY,
PROP_PRIVATE_KEY_PEM,
- PROP_ISSUER
+ PROP_ISSUER,
+ PROP_PKCS11_URI,
+ PROP_PRIVATE_KEY_PKCS11_URI,
};
static void
@@ -74,7 +76,16 @@ g_tls_certificate_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ switch (prop_id)
+ {
+ case PROP_PKCS11_URI:
+ case PROP_PRIVATE_KEY_PKCS11_URI:
+ /* Subclasses must override this property but this allows older backends to not fatally error */
+ g_value_set_static_string (value, NULL);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
}
static void
@@ -83,7 +94,15 @@ g_tls_certificate_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ switch (prop_id)
+ {
+ case PROP_PKCS11_URI:
+ case PROP_PRIVATE_KEY_PKCS11_URI:
+ /* Subclasses must override this property but this allows older backends to not fatally error */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
}
static void
@@ -193,6 +212,42 @@ g_tls_certificate_class_init (GTlsCertificateClass *class)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GTlsCertificate:pkcs11-uri: (nullable)
+ *
+ * A URI referencing the PKCS \#11 objects containing an X.509 certificate
+ * and optionally a private key.
+ *
+ * If %NULL the certificate is either not backed by PKCS \#11 or the
+ * #GTlsBackend does not support PKCS \#11.
+ *
+ * Since: 2.68
+ */
+ g_object_class_install_property (gobject_class, PROP_PKCS11_URI,
+ g_param_spec_string ("pkcs11-uri",
+ P_("PKCS #11 URI"),
+ P_("The PKCS #11 URI"),
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GTlsCertificate:private-key-pkcs11-uri: (nullable)
+ *
+ * A URI referencing a PKCS \#11 object containing a private key.
+ *
+ * Since: 2.68
+ */
+ g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY_PKCS11_URI,
+ g_param_spec_string ("private-key-pkcs11-uri",
+ P_("PKCS #11 URI"),
+ P_("The PKCS #11 URI for a private key"),
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
}
static GTlsCertificate *
@@ -592,6 +647,77 @@ g_tls_certificate_new_from_files (const gchar *cert_file,
}
/**
+ * g_tls_certificate_new_from_pkcs11_uris:
+ * @pkcs11_uri: A PKCS \#11 URI
+ * @private_key_pkcs11_uri: (nullable): A PKCS \#11 URI
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Creates a #GTlsCertificate from a PKCS \#11 URI.
+ *
+ * An example @pkcs11_uri would be `pkcs11:model=Model;manufacturer=Manufacture;serial=1;token=My%20Client%20Certificate;id=%01`
+ *
+ * Where the token’s layout is:
+ *
+ * ```
+ * Object 0:
+ * URL: pkcs11:model=Model;manufacturer=Manufacture;serial=1;token=My%20Client%20Certificate;id=%01;object=private%20key;type=private
+ * Type: Private key (RSA-2048)
+ * ID: 01
+ *
+ * Object 1:
+ * URL: pkcs11:model=Model;manufacturer=Manufacture;serial=1;token=My%20Client%20Certificate;id=%01;object=Certificate%20for%20Authentication;type=cert
+ * Type: X.509 Certificate (RSA-2048)
+ * ID: 01
+ * ```
+ *
+ * In this case the certificate and private key would both be detected and used as expected.
+ * @pkcs_uri may also just reference an X.509 certificate object and then optionally
+ * @private_key_pkcs11_uri allows using a private key exposed under a different URI.
+ *
+ * Note that the private key is not accessed until usage and may fail or require a PIN later.
+ *
+ * Returns: (transfer full): the new certificate, or %NULL on error
+ *
+ * Since: 2.68
+ */
+GTlsCertificate *
+g_tls_certificate_new_from_pkcs11_uris (const gchar *pkcs11_uri,
+ const gchar *private_key_pkcs11_uri,
+ GError **error)
+{
+ GObject *cert;
+ GTlsBackend *backend;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ g_return_val_if_fail (pkcs11_uri, NULL);
+
+ backend = g_tls_backend_get_default ();
+
+ cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+ NULL, error,
+ "pkcs11-uri", pkcs11_uri,
+ "private-key-pkcs11-uri", private_key_pkcs11_uri,
+ NULL);
+
+ if (cert != NULL)
+ {
+ gchar *objects_uri;
+
+ /* Old implementations might not override this property */
+ g_object_get (cert, "pkcs11-uri", &objects_uri, NULL);
+ if (objects_uri == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("This GTlsBackend does not support creating PKCS #11 certificates"));
+ g_object_unref (cert);
+ return NULL;
+ }
+ g_free (objects_uri);
+ }
+
+ return G_TLS_CERTIFICATE (cert);
+}
+
+/**
* g_tls_certificate_list_new_from_file:
* @file: (type filename): file containing PEM-encoded certificates to import
* @error: #GError for error reporting, or %NULL to ignore.
diff --git a/gio/gtlscertificate.h b/gio/gtlscertificate.h
index a064543c4..ead4f015e 100644
--- a/gio/gtlscertificate.h
+++ b/gio/gtlscertificate.h
@@ -71,6 +71,11 @@ GLIB_AVAILABLE_IN_ALL
GTlsCertificate *g_tls_certificate_new_from_files (const gchar *cert_file,
const gchar *key_file,
GError **error);
+GLIB_AVAILABLE_IN_2_68
+GTlsCertificate *g_tls_certificate_new_from_pkcs11_uris (const gchar *pkcs11_uri,
+ const gchar *private_key_pkcs11_uri,
+ GError **error);
+
GLIB_AVAILABLE_IN_ALL
GList *g_tls_certificate_list_new_from_file (const gchar *file,
GError **error);
diff --git a/gio/gtlspassword.c b/gio/gtlspassword.c
index bd86a6dfe..1e437a7b6 100644
--- a/gio/gtlspassword.c
+++ b/gio/gtlspassword.c
@@ -23,7 +23,6 @@
#include "glibintl.h"
#include "gioenumtypes.h"
-#include "gstrfuncsprivate.h"
#include "gtlspassword.h"
#include <string.h>
@@ -288,14 +287,9 @@ g_tls_password_set_value (GTlsPassword *password,
g_return_if_fail (G_IS_TLS_PASSWORD (password));
if (length < 0)
- {
- /* FIXME: g_tls_password_set_value_full() doesn’t support unsigned gsize */
- gsize length_unsigned = strlen ((gchar *) value);
- g_return_if_fail (length_unsigned <= G_MAXSSIZE);
- length = (gssize) length_unsigned;
- }
+ length = strlen ((gchar *)value);
- g_tls_password_set_value_full (password, g_memdup2 (value, (gsize) length), length, g_free);
+ g_tls_password_set_value_full (password, g_memdup (value, length), length, g_free);
}
/**
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index ec3e2b70c..612373a6a 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -35,6 +35,9 @@
#include <windows.h>
+#include <glib/gstdioprivate.h>
+#include "glib-private.h"
+
/* We need to watch 8 places:
* 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
* (anything below that key)
@@ -55,15 +58,73 @@
* 7) HKEY_CLASSES_ROOT (only its subkeys)
* On change: re-enumerate subkeys, try to filter out wrong names.
*
+ *
+ * About verbs. A registry key (the name of that key is known as ProgID)
+ * can contain a "shell" subkey, which can then contain a number of verb
+ * subkeys (the most common being the "open" verb), and each of these
+ * contains a "command" subkey, which has a default string value that
+ * is the command to be run.
+ * Most ProgIDs are in HKEY_CLASSES_ROOT, but some are nested deeper in
+ * the registry (such as HKEY_CURRENT_USER\\Software\\<softwarename>).
+ *
+ * Verb selection works like this (according to https://docs.microsoft.com/en-us/windows/win32/shell/context ):
+ * 1) If "open" verb is available, that verb is used.
+ * 2) If the Shell subkey has a default string value, and if a verb subkey
+ * with that name exists, that verb is used.
+ * 3) The first subkey found in the list of verb subkeys is used.
+ * 4) The "openwith" verb is used
+ *
+ * Testing suggests that Windows never reaches the point 4 in any realistic
+ * circumstances. If a "command" subkey is missing for a verb, or if it has
+ * an empty string as its default value, the app launch fails
+ * (the "openwith" verb is not used, even if it's present).
+ * If the command is present, but is not valid (runs nonexisting executable,
+ * for example), then other verbs are not checked.
+ * It seems that when the documentation said "openwith verb", it meant
+ * that Windows invokes the default "Open with..." dialog (it does not
+ * look at the "openwith" verb subkey, even if it's there).
+ * If a verb subkey that is supposed to be used is present, but it lacks
+ * a command subkey, an error message is shown and nothing else happens.
*/
+#define _verb_idx(array,index) ((GWin32AppInfoShellVerb *) g_ptr_array_index (array, index))
+
+#define _lookup_by_verb(array, verb, dst, itemtype) do { \
+ gsize _index; \
+ itemtype *_v; \
+ for (_index = 0; array && _index < array->len; _index++) \
+ { \
+ _v = (itemtype *) g_ptr_array_index (array, _index); \
+ if (_wcsicmp (_v->verb_name, (verb)) == 0) \
+ { \
+ *(dst) = _v; \
+ break; \
+ } \
+ } \
+ if (array == NULL || _index >= array->len) \
+ *(dst) = NULL; \
+} while (0)
+
+#define _verb_lookup(array, verb, dst) _lookup_by_verb (array, verb, dst, GWin32AppInfoShellVerb)
+
+/* Because with subcommands a verb would have
+ * a name like "foo\\bar", but the key its command
+ * should be looked for is "shell\\foo\\shell\\bar\\command"
+ */
+typedef struct _reg_verb {
+ gunichar2 *name;
+ gunichar2 *shellpath;
+} reg_verb;
+
typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
+typedef struct _GWin32AppInfoShellVerb GWin32AppInfoShellVerb;
typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
+typedef struct _GWin32AppInfoShellVerbClass GWin32AppInfoShellVerbClass;
typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
@@ -87,6 +148,11 @@ struct _GWin32AppInfoApplicationClass
GObjectClass parent_class;
};
+struct _GWin32AppInfoShellVerbClass
+{
+ GObjectClass parent_class;
+};
+
struct _GWin32AppInfoURLSchema {
GObject parent_instance;
@@ -97,45 +163,57 @@ struct _GWin32AppInfoURLSchema {
gchar *schema_u8;
/* url schema (stuff before ':'), in UTF-8, folded */
- gchar *schema_folded;
+ gchar *schema_u8_folded;
- /* Handler currently selected for this schema */
+ /* Handler currently selected for this schema. Can be NULL. */
GWin32AppInfoHandler *chosen_handler;
- /* Maps folded handler IDs -> to other handlers for this schema */
+ /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this schema.
+ * Includes the chosen handler, if any.
+ */
GHashTable *handlers;
};
struct _GWin32AppInfoHandler {
GObject parent_instance;
- /* Class name in HKCR */
+ /* Usually a class name in HKCR */
gunichar2 *handler_id;
- /* Handler registry key (HKCR\\handler_id). Can be used to watch this handler. */
+ /* Registry object obtained by opening @handler_id. Can be used to watch this handler. */
GWin32RegistryKey *key;
- /* Class name in HKCR, UTF-8, folded */
+ /* @handler_id, in UTF-8, folded */
gchar *handler_id_folded;
- /* shell/open/command default value for the class named by class_id */
- gunichar2 *handler_command;
+ /* Icon of the application for this handler */
+ GIcon *icon;
+
+ /* Verbs that this handler supports */
+ GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
+};
- /* If handler_id class has no command, it might point to another class */
- gunichar2 *proxy_id;
+struct _GWin32AppInfoShellVerb {
+ GObject parent_instance;
- /* Proxy registry key (HKCR\\proxy_id). Can be used to watch handler's proxy. */
- GWin32RegistryKey *proxy_key;
+ /* The verb that is used to invoke this handler. */
+ gunichar2 *verb_name;
- /* shell/open/command default value for the class named by proxy_id */
- gunichar2 *proxy_command;
+ /* User-friendly (localized) verb name. */
+ gchar *verb_displayname;
- /* Executable of the program (for matching, in folded form; UTF-8) */
- gchar *executable_folded;
+ /* shell/verb/command */
+ gunichar2 *command;
+
+ /* Same as @command, but in UTF-8 */
+ gchar *command_utf8;
/* Executable of the program (UTF-8) */
gchar *executable;
+ /* Executable of the program (for matching, in folded form; UTF-8) */
+ gchar *executable_folded;
+
/* Pointer to a location within @executable */
gchar *executable_basename;
@@ -146,10 +224,7 @@ struct _GWin32AppInfoHandler {
*/
gchar *dll_function;
- /* Icon of the application for this handler */
- GIcon *icon;
-
- /* The application that is linked to this handler. */
+ /* The application that is linked to this verb. */
GWin32AppInfoApplication *app;
};
@@ -162,28 +237,34 @@ struct _GWin32AppInfoFileExtension {
/* File extension (with leading '.'), in UTF-8 */
gchar *extension_u8;
- /* handler currently selected for this extension */
+ /* handler currently selected for this extension. Can be NULL. */
GWin32AppInfoHandler *chosen_handler;
- /* Maps folded handler IDs -> to other handlers for this extension */
+ /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this extension.
+ * Includes the chosen handler, if any.
+ */
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. */
+ /* Canonical name (used for key names).
+ * For applications tracked by id this is the root registry
+ * key path for the application.
+ * For applications tracked by executable name this is the
+ * basename of the executable.
+ * For fake applications this is the full filename of the
+ * executable (as far as it can be inferred from a command line,
+ * meaning that it can also be a basename, if that's
+ * all that a commandline happen to give us).
+ */
gunichar2 *canonical_name;
- /* Canonical name (used for key names), in UTF-8. Can be NULL. */
+ /* @canonical_name, in UTF-8 */
gchar *canonical_name_u8;
- /* Canonical name (used for key names), in UTF-8, folded. Can be NULL. */
+ /* @canonical_name, in UTF-8, folded */
gchar *canonical_name_folded;
/* Human-readable name in English. Can be NULL */
@@ -204,37 +285,17 @@ struct _GWin32AppInfoApplication {
/* 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;
-
- /* If not NULL, then @executable and its derived fields contain the name
- * of a DLL file (without the name of the function that rundll32.exe should
- * invoke), and this field contains the name of the function to be invoked.
- * The application is then invoked as 'rundll32.exe "dll_path",dll_function other_arguments...'.
- */
- gchar *dll_function;
+ /* Verbs that this application supports */
+ GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
/* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
- * UTF-8, folded) -> a GWin32AppInfoHandler
+ * UTF-8, folded) -> to 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, UTF-8, folded) -> to a GWin32AppInfoHandler
* Extension can be used as a key in the extensions hashmap.
*/
GHashTable *supported_exts;
@@ -272,13 +333,18 @@ struct _GWin32AppInfoApplication {
#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))
+#define G_TYPE_WIN32_APPINFO_SHELL_VERB (g_win32_appinfo_shell_verb_get_type ())
+#define G_WIN32_APPINFO_SHELL_VERB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_SHELL_VERB, GWin32AppInfoShellVerb))
+
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_shell_verb_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 (GWin32AppInfoShellVerb, g_win32_appinfo_shell_verb, 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)
@@ -289,7 +355,7 @@ g_win32_appinfo_url_schema_dispose (GObject *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_pointer (&url->schema_u8_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);
@@ -303,16 +369,9 @@ g_win32_appinfo_handler_dispose (GObject *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_pointer (&handler->dll_function, g_free);
g_clear_object (&handler->key);
- g_clear_object (&handler->proxy_key);
g_clear_object (&handler->icon);
- g_clear_object (&handler->app);
+ g_clear_pointer (&handler->verbs, g_ptr_array_unref);
G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
}
@@ -325,11 +384,26 @@ g_win32_appinfo_file_extension_dispose (GObject *object)
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_shell_verb_dispose (GObject *object)
+{
+ GWin32AppInfoShellVerb *shverb = G_WIN32_APPINFO_SHELL_VERB (object);
+
+ g_clear_pointer (&shverb->verb_name, g_free);
+ g_clear_pointer (&shverb->verb_displayname, g_free);
+ g_clear_pointer (&shverb->command, g_free);
+ g_clear_pointer (&shverb->command_utf8, g_free);
+ g_clear_pointer (&shverb->executable_folded, g_free);
+ g_clear_pointer (&shverb->executable, g_free);
+ g_clear_pointer (&shverb->dll_function, g_free);
+ g_clear_object (&shverb->app);
+ G_OBJECT_CLASS (g_win32_appinfo_shell_verb_parent_class)->dispose (object);
+}
+
+static void
g_win32_appinfo_application_dispose (GObject *object)
{
GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object);
@@ -340,20 +414,28 @@ g_win32_appinfo_application_dispose (GObject *object)
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->dll_function, 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_clear_pointer (&app->verbs, g_ptr_array_unref);
G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
}
+static const gchar *
+g_win32_appinfo_application_get_some_name (GWin32AppInfoApplication *app)
+{
+ if (app->localized_pretty_name_u8)
+ return app->localized_pretty_name_u8;
+
+ if (app->pretty_name_u8)
+ return app->pretty_name_u8;
+
+ return app->canonical_name_u8;
+}
+
static void
g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
{
@@ -371,6 +453,14 @@ g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klas
}
static void
+g_win32_appinfo_shell_verb_class_init (GWin32AppInfoShellVerbClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = g_win32_appinfo_shell_verb_dispose;
+}
+
+static void
g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
@@ -396,21 +486,23 @@ g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
}
static void
+g_win32_appinfo_shell_verb_init (GWin32AppInfoShellVerb *self)
+{
+}
+
+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)
{
+ self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
}
static void
@@ -424,6 +516,7 @@ g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
g_str_equal,
g_free,
g_object_unref);
+ self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
}
G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
@@ -439,20 +532,27 @@ static GHashTable *extensions = NULL;
static GHashTable *urls = NULL;
/* Map of owned "appID" (UTF-8, folded) to
- * GWin32AppInfoApplication ptr
+ * a GWin32AppInfoApplication
*/
static GHashTable *apps_by_id = NULL;
/* Map of owned "app.exe" (UTF-8, folded) to
- * GWin32AppInfoApplication ptr.
+ * a GWin32AppInfoApplication.
* 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 "path:\to\app.exe" (UTF-8, folded) to
+ * a GWin32AppInfoApplication.
+ * The app objects in this map are fake - they are linked to
+ * handlers that do not have any apps associated with them.
+ */
+static GHashTable *fake_apps = NULL;
+
/* Map of owned "handler id" (UTF-8, folded)
- * to GWin32AppInfoHandler ptr
+ * to a GWin32AppInfoHandler
*/
static GHashTable *handlers = NULL;
@@ -487,7 +587,6 @@ static GWin32RegistryKey *classes_root_key;
#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))
@@ -498,56 +597,92 @@ static GWin32RegistryKey *classes_root_key;
#include "giowin32-private.c"
static void
-read_handler_icon (GWin32RegistryKey *proxy_key,
- GWin32RegistryKey *program_key,
+read_handler_icon (GWin32RegistryKey *key,
GIcon **icon_out)
{
- gint counter;
- GWin32RegistryKey *key;
+ GWin32RegistryKey *icon_key;
+ GWin32RegistryValueType default_type;
+ gchar *default_value;
+
+ g_assert (icon_out);
*icon_out = NULL;
- for (counter = 0; counter < 2; counter++)
- {
- GWin32RegistryKey *icon_key;
+ icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
- if (counter == 0)
- key = proxy_key;
- else
- key = program_key;
+ if (icon_key == NULL)
+ return;
- if (!key)
- continue;
+ if (g_win32_registry_key_get_value (icon_key,
+ NULL,
+ 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);
- icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
+ g_clear_pointer (&default_value, g_free);
+ }
- if (icon_key != NULL)
- {
- GWin32RegistryValueType default_type;
- gchar *default_value;
+ g_object_unref (icon_key);
+}
- if (g_win32_registry_key_get_value (icon_key,
- NULL,
- 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);
+static void
+reg_verb_free (gpointer p)
+{
+ if (p == NULL)
+ return;
- g_clear_pointer (&default_value, g_free);
- }
+ g_free (((reg_verb *) p)->name);
+ g_free (((reg_verb *) p)->shellpath);
+ g_free (p);
+}
- g_object_unref (icon_key);
- }
+#define is_open(x) ( \
+ ((x)[0] == L'o' || (x)[0] == L'O') && \
+ ((x)[1] == L'p' || (x)[1] == L'P') && \
+ ((x)[2] == L'e' || (x)[2] == L'E') && \
+ ((x)[3] == L'n' || (x)[3] == L'N') && \
+ ((x)[4] == L'\0') \
+)
+
+/* default verb (if any) comes first,
+ * then "open", then the rest of the verbs
+ * are sorted alphabetically
+ */
+static gint
+compare_verbs (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ const reg_verb *ca = (const reg_verb *) a;
+ const reg_verb *cb = (const reg_verb *) b;
+ const gunichar2 *def = (const gunichar2 *) user_data;
+ gboolean is_open_ca;
+ gboolean is_open_cb;
- if (*icon_out)
- break;
+ if (def != NULL)
+ {
+ if (_wcsicmp (ca->name, def) == 0)
+ return 1;
+ else if (_wcsicmp (cb->name, def) == 0)
+ return -1;
}
+
+ is_open_ca = is_open (ca->name);
+ is_open_cb = is_open (cb->name);
+
+ if (is_open_ca && !is_open_cb)
+ return 1;
+ else if (is_open_ca && !is_open_cb)
+ return -1;
+
+ return _wcsicmp (ca->name, cb->name);
}
static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
@@ -555,6 +690,64 @@ static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_l
static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
+/* Called by process_verbs_commands.
+ * @verb is a verb name
+ * @command_line is the commandline of that verb
+ * @command_line_utf8 is the UTF-8 version of @command_line
+ * @verb_displayname is the prettier display name of the verb (might be NULL)
+ * @verb_is_preferred is TRUE if the verb is the preferred one
+ * @invent_new_verb_name is TRUE when the verb should be added
+ * even if a verb with such
+ * name already exists (in which case
+ * a new name is invented), unless
+ * the existing verb runs exactly the same
+ * commandline.
+ */
+typedef void (*verb_command_func) (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name);
+
+static gunichar2 * decide_which_id_to_use (const gunichar2 *program_id,
+ GWin32RegistryKey **return_key,
+ gchar **return_handler_id_u8,
+ gchar **return_handler_id_u8_folded);
+
+static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *schema,
+ const gchar *schema_u8,
+ const gchar *schema_u8_folded);
+
+static GWin32AppInfoHandler * get_handler_object (const gchar *handler_id_u8_folded,
+ GWin32RegistryKey *handler_key,
+ const gunichar2 *handler_id);
+
+static GWin32AppInfoFileExtension *get_ext_object (const gunichar2 *ext,
+ const gchar *ext_u8,
+ const gchar *ext_u8_folded);
+
+
+static void process_verbs_commands (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ verb_command_func handler,
+ gpointer handler_data1,
+ gpointer handler_data2);
+
+static void handler_add_verb (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name);
+
/* output_size is in *bytes*, not gunichar2s! */
static gboolean
build_registry_path (gunichar2 *output, gsize output_size, ...)
@@ -632,589 +825,830 @@ _g_win32_registry_key_build_and_new_w (GError **error, ...)
return key;
}
-/* Slow and dirty validator for UTF-16 strings */
-static gboolean
-g_utf16_validate (const gunichar2 *str,
- glong len)
-{
- gchar *tmp;
-
- if (str == NULL)
- return FALSE;
-
- tmp = g_utf16_to_utf8 (str, len, NULL, NULL, NULL);
-
- if (tmp == NULL)
- return FALSE;
-
- g_free (tmp);
-
- return TRUE;
-}
-
-/* Does a UTF-16 validity check on *proxy_command and/or *program_command.
- * Fails if that check doesn't pass.
+/* Gets the list of shell verbs (a GList of reg_verb, put into @verbs)
+ * from the @program_id_key.
+ * If one of the verbs should be preferred,
+ * a pointer to this verb (in the GList) will be
+ * put into @preferred_verb.
+ * Does not automatically assume that the first verb
+ * is preferred (when no other preferences exist).
+ * @verbname_prefix is prefixed to the name of the verb
+ * (this is used for subcommands) and is initially an
+ * empty string.
+ * @verbshell_prefix is the subkey of @program_id_key
+ * that contains the verbs. It is "Shell" initially,
+ * but grows with recursive invocations (for subcommands).
+ * Returns TRUE on success, FALSE on failure.
*/
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)
+get_verbs (GWin32RegistryKey *program_id_key,
+ const reg_verb **preferred_verb,
+ GList **verbs,
+ const gunichar2 *verbname_prefix,
+ const gunichar2 *verbshell_prefix)
{
+ GWin32RegistrySubkeyIter iter;
GWin32RegistryKey *key;
GWin32RegistryValueType val_type;
- gsize proxy_id_size;
- gboolean got_value;
-
- g_assert (program_id && program_command && proxy_id && proxy_command);
+ gunichar2 *default_verb;
+ gsize verbshell_prefix_len;
+ gsize verbname_prefix_len;
+ GList *i;
- *program_command = NULL;
- *proxy_id = NULL;
- *proxy_command = NULL;
+ g_assert (program_id_key && verbs && preferred_verb);
- if (program_key)
- *program_key = NULL;
-
- if (proxy_key)
- *proxy_key = NULL;
+ *verbs = NULL;
+ *preferred_verb = NULL;
+ key = g_win32_registry_key_get_child_w (program_id_key,
+ verbshell_prefix,
+ NULL);
- key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id,
- SHELL_OPEN_COMMAND, NULL);
+ if (key == NULL)
+ return FALSE;
- if (key != NULL)
+ if (!g_win32_registry_subkey_iter_init (&iter, key, NULL))
{
- got_value = g_win32_registry_key_get_value_w (key,
- NULL,
- 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) &&
- !g_utf16_to_utf8_and_fold (program_id, -1, program_id_u8, program_id_folded)) ||
- !g_utf16_validate (*program_command, -1))
- {
- 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);
+
+ return FALSE;
}
- key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, NULL);
+ verbshell_prefix_len = g_utf16_len (verbshell_prefix);
+ verbname_prefix_len = g_utf16_len (verbname_prefix);
- if (key == NULL)
- return FALSE;
+ while (g_win32_registry_subkey_iter_next (&iter, TRUE, NULL))
+ {
+ const gunichar2 *name;
+ gsize name_len;
+ GWin32RegistryKey *subkey;
+ gboolean has_subcommands;
+ const reg_verb *tmp;
+ GWin32RegistryValueType subc_type;
+ reg_verb *rverb;
+ const gunichar2 *shell = L"Shell";
+ const gsize shell_len = g_utf16_len (shell);
+
+ if (!g_win32_registry_subkey_iter_get_name_w (&iter, &name, &name_len, NULL))
+ continue;
- got_value = g_win32_registry_key_get_value_w (key,
- NULL,
- TRUE,
- L"",
- &val_type,
- (void **) proxy_id,
- &proxy_id_size,
- NULL);
- g_object_unref (key);
+ subkey = g_win32_registry_key_get_child_w (key,
+ name,
+ NULL);
+
+ g_assert (subkey != NULL);
+ /* The key we're looking at is "<some_root>/Shell/<this_key>",
+ * where "Shell" is verbshell_prefix.
+ * If it has a value named 'Subcommands' (doesn't matter what its data is),
+ * it means that this key has its own Shell subkey, the subkeys
+ * of which are shell commands (i.e. <some_root>/Shell/<this_key>/Shell/<some_other_keys>).
+ * To handle that, create new, extended nameprefix and shellprefix,
+ * and call the function recursively.
+ * name prefix "" -> "<this_key_name>\\"
+ * shell prefix "Shell" -> "Shell\\<this_key_name>\\Shell"
+ * The root, program_id_key, remains the same in all invocations.
+ * Essentially, we're flattening the command tree into a list.
+ */
+ has_subcommands = FALSE;
+ if (g_win32_registry_key_get_value_w (subkey,
+ NULL,
+ TRUE,
+ L"Subcommands",
+ &subc_type,
+ NULL,
+ NULL,
+ NULL) &&
+ subc_type == G_WIN32_REGISTRY_VALUE_STR)
+ {
+ gunichar2 *new_nameprefix = g_malloc ((verbname_prefix_len + name_len + 1 + 1) * sizeof (gunichar2));
+ gunichar2 *new_shellprefix = g_malloc ((verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1) * sizeof (gunichar2));
+ memcpy (&new_shellprefix[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
+ new_shellprefix[verbshell_prefix_len] = L'\\';
+ memcpy (&new_shellprefix[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
+ new_shellprefix[verbshell_prefix_len + 1 + name_len] = L'\\';
+ memcpy (&new_shellprefix[verbshell_prefix_len + 1 + name_len + 1], shell, shell_len * sizeof (gunichar2));
+ new_shellprefix[verbshell_prefix_len + 1 + name_len + 1 + shell_len] = 0;
+
+ memcpy (&new_nameprefix[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
+ memcpy (&new_nameprefix[verbname_prefix_len], name, (name_len) * sizeof (gunichar2));
+ new_nameprefix[verbname_prefix_len + name_len] = L'\\';
+ new_nameprefix[verbname_prefix_len + name_len + 1] = 0;
+ has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix);
+ g_free (new_shellprefix);
+ g_free (new_nameprefix);
+ }
- if (!got_value ||
- (val_type != G_WIN32_REGISTRY_VALUE_STR))
- {
- g_clear_pointer (proxy_id, g_free);
+ g_clear_object (&subkey);
- return FALSE;
+ /* Presence of subcommands means that this key itself is not a command-key */
+ if (has_subcommands)
+ continue;
+
+ /* We don't look at the command sub-key and its value (the actual command line) here.
+ * We save the registry path instead, and use it later in process_verbs_commands().
+ * The name of the verb is also saved.
+ * verbname_prefix is prefixed to the verb name (it's either an empty string
+ * or already ends with a '\\', so no extra separators needed).
+ * verbshell_prefix is prefixed to the verb key path (this one needs a separator,
+ * because it never has one - all verbshell prefixes end with "Shell", not "Shell\\")
+ */
+ rverb = g_new0 (reg_verb, 1);
+ rverb->name = g_malloc ((verbname_prefix_len + name_len + 1) * sizeof (gunichar2));
+ memcpy (&rverb->name[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
+ memcpy (&rverb->name[verbname_prefix_len], name, name_len * sizeof (gunichar2));
+ rverb->name[verbname_prefix_len + name_len] = 0;
+ rverb->shellpath = g_malloc ((verbshell_prefix_len + 1 + name_len + 1) * sizeof (gunichar2));
+ memcpy (&rverb->shellpath[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
+ memcpy (&rverb->shellpath[verbshell_prefix_len], L"\\", sizeof (gunichar2));
+ memcpy (&rverb->shellpath[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
+ rverb->shellpath[verbshell_prefix_len + 1 + name_len] = 0;
+ *verbs = g_list_append (*verbs, rverb);
}
- key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id,
- SHELL_OPEN_COMMAND, NULL);
+ g_win32_registry_subkey_iter_clear (&iter);
- if (key == NULL)
+ if (*verbs == NULL)
{
- g_clear_pointer (proxy_id, g_free);
+ g_object_unref (key);
return FALSE;
}
- got_value = g_win32_registry_key_get_value_w (key,
- NULL,
- TRUE,
- L"",
- &val_type,
- (void **) proxy_command,
- NULL,
- NULL);
+ default_verb = NULL;
- if (proxy_key)
- *proxy_key = key;
- else
- g_object_unref (key);
+ if (g_win32_registry_key_get_value_w (key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &default_verb,
+ NULL,
+ NULL) &&
+ (val_type != G_WIN32_REGISTRY_VALUE_STR ||
+ g_utf16_len (default_verb) <= 0))
+ g_clear_pointer (&default_verb, g_free);
+
+ g_object_unref (key);
- if (!got_value ||
- val_type != G_WIN32_REGISTRY_VALUE_STR ||
- ((program_id_u8 != NULL || program_id_folded != NULL) &&
- !g_utf16_to_utf8_and_fold (program_id, -1, program_id_u8, program_id_folded)) ||
- !g_utf16_validate (*proxy_command, -1))
+ /* Only sort at the top level */
+ if (verbname_prefix[0] == 0)
{
- g_clear_pointer (proxy_id, g_free);
- g_clear_pointer (proxy_command, g_free);
- if (proxy_key)
- g_clear_object (proxy_key);
- return FALSE;
+ *verbs = g_list_sort_with_data (*verbs, compare_verbs, default_verb);
+
+ for (i = *verbs; default_verb && *preferred_verb == NULL && i; i = i->next)
+ if (_wcsicmp (default_verb, ((const reg_verb *) i->data)->name) == 0)
+ *preferred_verb = (const reg_verb *) i->data;
}
+ g_clear_pointer (&default_verb, g_free);
+
return TRUE;
}
+/* Grabs a URL association (from HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
+ * or from an application with Capabilities, or just a schema subkey in HKCR).
+ * @program_id is a ProgID of the handler for the URL.
+ * @schema is the schema for the URL.
+ * @schema_u8 and @schema_u8_folded are UTF-8 and folded UTF-8
+ * respectively.
+ * @app is the app to which the URL handler belongs (can be NULL).
+ * @is_user_choice is TRUE if this association is clearly preferred
+ */
static void
-get_url_association (const gunichar2 *schema)
+get_url_association (const gunichar2 *program_id,
+ const gunichar2 *schema,
+ const gchar *schema_u8,
+ const gchar *schema_u8_folded,
+ GWin32AppInfoApplication *app,
+ gboolean is_user_choice)
{
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)
+ gunichar2 *handler_id;
+ GList *verbs;
+ const reg_verb *preferred_verb;
+ gchar *handler_id_u8;
+ gchar *handler_id_u8_folded;
+ GWin32RegistryKey *handler_key;
+
+ if ((handler_id = decide_which_id_to_use (program_id,
+ &handler_key,
+ &handler_id_u8,
+ &handler_id_u8_folded)) == NULL)
return;
- if (!g_utf16_to_utf8_and_fold (schema, -1, &schema_u8, &schema_folded))
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
{
- g_object_unref (user_choice);
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_object (&handler_key);
+
return;
}
- schema_rec = g_hash_table_lookup (urls, schema_folded);
+ schema_rec = get_schema_object (schema,
+ schema_u8,
+ schema_u8_folded);
- if (!g_win32_registry_key_get_value_w (user_choice,
- NULL,
- 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;
- }
+ handler_rec = get_handler_object (handler_id_u8_folded,
+ handler_key,
+ handler_id);
+
+ if (is_user_choice || schema_rec->chosen_handler == NULL)
+ g_set_object (&schema_rec->chosen_handler, handler_rec);
+
+ g_hash_table_insert (schema_rec->handlers,
+ g_strdup (handler_id_u8_folded),
+ g_object_ref (handler_rec));
+
+ g_clear_object (&handler_key);
- if (val_type != G_WIN32_REGISTRY_VALUE_STR)
+ if (app)
+ g_hash_table_insert (app->supported_urls,
+ g_strdup (schema_rec->schema_u8_folded),
+ g_object_ref (handler_rec));
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_add_verb,
+ handler_rec,
+ app);
+
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_pointer (&handler_id, g_free);
+}
+
+/* Grabs a file extension association (from HKCR\.ext or similar).
+ * @program_id is a ProgID of the handler for the extension.
+ * @file_extension is the extension (with the leading '.')
+ * @app is the app to which the extension handler belongs (can be NULL).
+ * @is_user_choice is TRUE if this is clearly the preferred association
+ */
+static void
+get_file_ext (const gunichar2 *program_id,
+ const gunichar2 *file_extension,
+ GWin32AppInfoApplication *app,
+ gboolean is_user_choice)
+{
+ GWin32AppInfoHandler *handler_rec;
+ gunichar2 *handler_id;
+ const reg_verb *preferred_verb;
+ GList *verbs;
+ gchar *handler_id_u8;
+ gchar *handler_id_u8_folded;
+ GWin32RegistryKey *handler_key;
+ GWin32AppInfoFileExtension *file_extn;
+ gchar *file_extension_u8;
+ gchar *file_extension_u8_folded;
+
+ if ((handler_id = decide_which_id_to_use (program_id,
+ &handler_key,
+ &handler_id_u8,
+ &handler_id_u8_folded)) == NULL)
+ return;
+
+ if (!g_utf16_to_utf8_and_fold (file_extension,
+ -1,
+ &file_extension_u8,
+ &file_extension_u8_folded))
{
- g_free (schema_u8);
- g_free (schema_folded);
- g_free (program_id);
- g_object_unref (user_choice);
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_object (&handler_key);
+
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))
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
{
- g_free (schema_u8);
- g_free (schema_folded);
- g_free (program_id);
- g_object_unref (user_choice);
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_object (&handler_key);
+ g_clear_pointer (&file_extension_u8, g_free);
+ g_clear_pointer (&file_extension_u8_folded, g_free);
+
return;
}
- handler_rec = g_hash_table_lookup (handlers, program_id_folded);
+ file_extn = get_ext_object (file_extension, file_extension_u8, file_extension_u8_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;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
- 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);
- }
+ handler_rec = get_handler_object (handler_id_u8_folded,
+ handler_key,
+ handler_id);
- 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 (is_user_choice || file_extn->chosen_handler == NULL)
+ g_set_object (&file_extn->chosen_handler, handler_rec);
- if (schema_rec->chosen_handler == NULL)
- schema_rec->chosen_handler = g_object_ref (handler_rec);
+ g_hash_table_insert (file_extn->handlers,
+ g_strdup (handler_id_u8_folded),
+ 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),
+ if (app)
+ g_hash_table_insert (app->supported_exts,
+ g_strdup (file_extension_u8_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);
+ g_clear_pointer (&file_extension_u8, g_free);
+ g_clear_pointer (&file_extension_u8_folded, g_free);
+ g_clear_object (&handler_key);
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_add_verb,
+ handler_rec,
+ app);
+
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
}
-static void
-get_file_ext (const gunichar2 *ext)
+/* Returns either a @program_id or the string from
+ * the default value of the program_id key (which is a name
+ * of a proxy class), or NULL.
+ * Does not check that proxy represents a valid
+ * record, just checks that it exists.
+ * Can return the class key (HKCR/program_id or HKCR/proxy_id).
+ * Can convert returned value to UTF-8 and fold it.
+ */
+static gunichar2 *
+decide_which_id_to_use (const gunichar2 *program_id,
+ GWin32RegistryKey **return_key,
+ gchar **return_handler_id_u8,
+ gchar **return_handler_id_u8_folded)
{
- 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;
+ GWin32RegistryKey *key;
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);
+ gunichar2 *return_id;
+ gboolean got_value;
+ gchar *handler_id_u8;
+ gchar *handler_id_u8_folded;
+ g_assert (program_id);
- user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, ext,
- USER_CHOICE, NULL);
+ if (return_key)
+ *return_key = NULL;
- if (user_choice == NULL && open_with_progids == NULL)
- return;
+ /* Check the proxy first */
+ key = g_win32_registry_key_get_child_w (classes_root_key, program_id, NULL);
- if (!g_utf16_to_utf8_and_fold (ext, -1, &ext_u8, &ext_folded))
+ if (key == NULL)
+ return NULL;
+
+ proxy_id = NULL;
+ got_value = g_win32_registry_key_get_value_w (key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &proxy_id,
+ NULL,
+ NULL);
+ if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&proxy_id, g_free);
+
+ return_id = NULL;
+
+ if (proxy_id)
{
- g_clear_object (&user_choice);
- g_clear_object (&open_with_progids);
- return;
+ GWin32RegistryKey *proxy_key;
+ proxy_key = g_win32_registry_key_get_child_w (classes_root_key, proxy_id, NULL);
+
+ if (proxy_key)
+ {
+ if (return_key)
+ *return_key = g_steal_pointer (&proxy_key);
+ g_clear_object (&proxy_key);
+
+ return_id = g_steal_pointer (&proxy_id);
+ }
+
+ g_clear_pointer (&proxy_id, g_free);
}
- file_extn = NULL;
- file_ext_known = g_hash_table_lookup_extended (extensions,
- ext_folded,
- NULL,
- (void **) &file_extn);
+ if ((return_handler_id_u8 ||
+ return_handler_id_u8_folded) &&
+ !g_utf16_to_utf8_and_fold (return_id == NULL ? program_id : return_id,
+ -1,
+ &handler_id_u8,
+ &handler_id_u8_folded))
+ {
+ g_clear_object (&key);
+ if (return_key)
+ g_clear_object (return_key);
+ g_clear_pointer (&return_id, g_free);
- if (!file_ext_known)
- file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
+ return NULL;
+ }
+
+ if (return_handler_id_u8)
+ *return_handler_id_u8 = g_steal_pointer (&handler_id_u8);
+ if (return_handler_id_u8_folded)
+ *return_handler_id_u8_folded = g_steal_pointer (&handler_id_u8_folded);
- found_handler = FALSE;
+ if (return_id == NULL && return_key)
+ *return_key = g_steal_pointer (&key);
+ g_clear_object (&key);
- if (user_choice != NULL)
+ if (return_id == NULL)
+ return g_wcsdup (program_id, -1);
+
+ return return_id;
+}
+
+/* Grabs the command for each verb from @verbs,
+ * and invokes @handler for it. Consumes @verbs.
+ * @path_to_progid and @progid are concatenated to
+ * produce a path to the key where Shell/verb/command
+ * subkeys are looked up.
+ * @preferred_verb, if not NULL, will be used to inform
+ * the @handler that a verb is preferred.
+ * @autoprefer_first_verb will automatically make the first
+ * verb to be preferred, if @preferred_verb is NULL.
+ * @handler_data1 and @handler_data2 are passed to @handler as-is.
+ */
+static void
+process_verbs_commands (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ verb_command_func handler,
+ gpointer handler_data1,
+ gpointer handler_data2)
+{
+ GList *i;
+ gboolean got_value;
+
+ g_assert (handler != NULL);
+ g_assert (verbs != NULL);
+ g_assert (progid != NULL);
+
+ for (i = verbs; i; i = i->next)
{
- if (g_win32_registry_key_get_value_w (user_choice,
- NULL,
- TRUE,
- L"Progid",
- &val_type,
- (void **) &program_id,
- &program_id_size,
- NULL))
+ const reg_verb *verb = (const reg_verb *) i->data;
+ GWin32RegistryKey *key;
+ GWin32RegistryKey *verb_key;
+ gunichar2 *command_value;
+ gchar *command_value_utf8;
+ GWin32RegistryValueType val_type;
+ gunichar2 *verb_displayname;
+ gchar *verb_displayname_u8;
+
+ key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
+ L"\\", verb->shellpath, L"\\command", NULL);
+
+ if (key == 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);
+ g_debug ("%S%S\\shell\\%S does not have a \"command\" subkey",
+ path_to_progid, progid, verb->shellpath);
+ continue;
+ }
- 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;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
- 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);
- }
+ command_value = NULL;
+ got_value = g_win32_registry_key_get_value_w (key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &command_value,
+ NULL,
+ NULL);
+ g_clear_object (&key);
- found_handler = TRUE;
+ if (!got_value ||
+ val_type != G_WIN32_REGISTRY_VALUE_STR ||
+ (command_value_utf8 = g_utf16_to_utf8 (command_value,
+ -1,
+ NULL,
+ NULL,
+ NULL)) == NULL)
+ {
+ g_clear_pointer (&command_value, g_free);
+ continue;
+ }
- handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
- program_id_folded);
+ verb_displayname = NULL;
+ verb_displayname_u8 = NULL;
+ verb_key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
+ L"\\", verb->shellpath, 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 (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));
- }
+ if (verb_key)
+ {
+ gsize verb_displayname_len;
+
+ got_value = g_win32_registry_key_get_value_w (verb_key,
+ g_win32_registry_get_os_dirs_w (),
+ TRUE,
+ L"MUIVerb",
+ &val_type,
+ (void **) &verb_displayname,
+ &verb_displayname_len,
+ NULL);
+
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR &&
+ verb_displayname_len > sizeof (gunichar2))
+ verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
+
+ g_clear_pointer (&verb_displayname, g_free);
+
+ if (verb_displayname_u8 == NULL)
+ {
+ got_value = g_win32_registry_key_get_value_w (verb_key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &verb_displayname,
+ &verb_displayname_len,
+ NULL);
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR &&
+ verb_displayname_len > sizeof (gunichar2))
+ verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
}
- g_free (program_id);
+ g_clear_pointer (&verb_displayname, g_free);
+ g_clear_object (&verb_key);
}
- g_object_unref (user_choice);
+ handler (handler_data1, handler_data2, verb->name, command_value, command_value_utf8,
+ verb_displayname_u8,
+ (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) ||
+ (!preferred_verb && autoprefer_first_verb && i == verbs),
+ FALSE);
+
+ g_clear_pointer (&command_value, g_free);
+ g_clear_pointer (&command_value_utf8, g_free);
+ g_clear_pointer (&verb_displayname_u8, g_free);
}
- if (open_with_progids != NULL)
- {
- GWin32RegistryValueIter iter;
+ g_list_free_full (verbs, reg_verb_free);
+}
- 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;
+/* Looks up a schema object identified by
+ * @schema_u8_folded in the urls hash table.
+ * If such object doesn't exist,
+ * creates it and puts it into the urls hash table.
+ * Returns the object.
+ */
+static GWin32AppInfoURLSchema *
+get_schema_object (const gunichar2 *schema,
+ const gchar *schema_u8,
+ const gchar *schema_u8_folded)
+{
+ GWin32AppInfoURLSchema *schema_rec;
- while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
- {
- gsize value_name_size;
- program_key = proxy_key = NULL;
+ schema_rec = g_hash_table_lookup (urls, schema_u8_folded);
- 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;
+ if (schema_rec != NULL)
+ return schema_rec;
- 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;
+ 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_u8_folded = g_strdup (schema_u8_folded);
+ g_hash_table_insert (urls, g_strdup (schema_rec->schema_u8_folded), schema_rec);
- handler_rec = g_hash_table_lookup (handlers,
- program_id_folded);
+ return schema_rec;
+}
- 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;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
- 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);
- }
+/* Looks up a handler object identified by
+ * @handler_id_u8_folded in the handlers hash table.
+ * If such object doesn't exist,
+ * creates it and puts it into the handlers hash table.
+ * Returns the object.
+ */
+static GWin32AppInfoHandler *
+get_handler_object (const gchar *handler_id_u8_folded,
+ GWin32RegistryKey *handler_key,
+ const gunichar2 *handler_id)
+{
+ GWin32AppInfoHandler *handler_rec;
- found_handler = TRUE;
+ handler_rec = g_hash_table_lookup (handlers, handler_id_u8_folded);
- handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
- program_id_folded);
+ if (handler_rec != NULL)
+ return handler_rec;
- 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));
- }
+ handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
+ handler_rec->key = g_object_ref (handler_key);
+ handler_rec->handler_id = g_wcsdup (handler_id, -1);
+ handler_rec->handler_id_folded = g_strdup (handler_id_u8_folded);
+ read_handler_icon (handler_key, &handler_rec->icon);
+ g_hash_table_insert (handlers, g_strdup (handler_id_u8_folded), handler_rec);
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
- }
+ return handler_rec;
+}
- g_win32_registry_value_iter_clear (&iter);
- }
+static void
+handler_add_verb (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name)
+{
+ GWin32AppInfoHandler *handler_rec = (GWin32AppInfoHandler *) handler_data1;
+ GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
+ GWin32AppInfoShellVerb *shverb;
- g_object_unref (open_with_progids);
- }
+ _verb_lookup (handler_rec->verbs, verb, &shverb);
- if (!found_handler)
- {
- if (!file_ext_known)
- g_object_unref (file_extn);
- }
- else if (!file_ext_known)
+ if (shverb != NULL)
+ return;
+
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ shverb->verb_name = g_wcsdup (verb, -1);
+ shverb->verb_displayname = g_strdup (verb_displayname);
+ shverb->command = g_wcsdup (command_line, -1);
+ shverb->command_utf8 = g_strdup (command_line_utf8);
+ if (app_rec)
+ shverb->app = g_object_ref (app_rec);
+
+ _g_win32_extract_executable (shverb->command,
+ &shverb->executable,
+ &shverb->executable_basename,
+ &shverb->executable_folded,
+ NULL,
+ &shverb->dll_function);
+
+ if (shverb->dll_function != NULL)
+ _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
+
+ if (!verb_is_preferred)
+ g_ptr_array_add (handler_rec->verbs, shverb);
+ else
+ g_ptr_array_insert (handler_rec->verbs, 0, shverb);
+}
+
+/* Tries to generate a new name for a verb that looks
+ * like "verb (%x)", where %x is an integer in range of [0;255).
+ * On success puts new verb (and new verb displayname) into
+ * @new_verb and @new_displayname and return TRUE.
+ * On failure puts NULL into both and returns FALSE.
+ */
+static gboolean
+generate_new_verb_name (GPtrArray *verbs,
+ const gunichar2 *verb,
+ const gchar *verb_displayname,
+ gunichar2 **new_verb,
+ gchar **new_displayname)
+{
+ gsize counter;
+ GWin32AppInfoShellVerb *shverb;
+ gsize orig_len = g_utf16_len (verb);
+ gsize new_verb_name_len = orig_len + strlen (" ()") + 2 + 1;
+ gunichar2 *new_verb_name = g_malloc (new_verb_name_len * sizeof (gunichar2));
+
+ *new_verb = NULL;
+ *new_displayname = NULL;
+
+ memcpy (new_verb_name, verb, orig_len * sizeof (gunichar2));
+ for (counter = 0; counter < 255; counter++)
+ {
+ _snwprintf (&new_verb_name[orig_len], new_verb_name_len, L" (%x)", counter);
+ _verb_lookup (verbs, new_verb_name, &shverb);
+
+ if (shverb == NULL)
+ {
+ *new_verb = new_verb_name;
+ if (verb_displayname != NULL)
+ *new_displayname = g_strdup_printf ("%s (%zx)", verb_displayname, counter);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+app_add_verb (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name)
+{
+ gunichar2 *new_verb = NULL;
+ gchar *new_displayname = NULL;
+ GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
+ GWin32AppInfoShellVerb *shverb;
+
+ _verb_lookup (app_rec->verbs, verb, &shverb);
+
+ /* Special logic for fake apps - do our best to
+ * collate all possible verbs in the app,
+ * including the verbs that have the same name but
+ * different commandlines, in which case a new
+ * verb name has to be invented.
+ */
+ if (shverb != NULL)
{
- 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);
+ gsize vi;
+
+ if (!invent_new_verb_name)
+ return;
+
+ for (vi = 0; vi < app_rec->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *app_verb;
+
+ app_verb = _verb_idx (app_rec->verbs, vi);
+
+ if (_wcsicmp (command_line, app_verb->command) == 0)
+ break;
+ }
+
+ if (vi < app_rec->verbs->len ||
+ !generate_new_verb_name (app_rec->verbs,
+ verb,
+ verb_displayname,
+ &new_verb,
+ &new_displayname))
+ return;
}
- g_free (ext_u8);
- g_free (ext_folded);
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ if (new_verb == NULL)
+ shverb->verb_name = g_wcsdup (verb, -1);
+ else
+ shverb->verb_name = g_steal_pointer (&new_verb);
+ if (new_displayname == NULL)
+ shverb->verb_displayname = g_strdup (verb_displayname);
+ else
+ shverb->verb_displayname = g_steal_pointer (&new_displayname);
+
+ shverb->command = g_wcsdup (command_line, -1);
+ shverb->command_utf8 = g_strdup (command_line_utf8);
+ shverb->app = g_object_ref (app_rec);
+
+ _g_win32_extract_executable (shverb->command,
+ &shverb->executable,
+ &shverb->executable_basename,
+ &shverb->executable_folded,
+ NULL,
+ &shverb->dll_function);
+
+ if (shverb->dll_function != NULL)
+ _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
+
+ if (!verb_is_preferred)
+ g_ptr_array_add (app_rec->verbs, shverb);
+ else
+ g_ptr_array_insert (app_rec->verbs, 0, shverb);
+}
+
+/* Looks up a file extension object identified by
+ * @ext_u8_folded in the extensions hash table.
+ * If such object doesn't exist,
+ * creates it and puts it into the extensions hash table.
+ * Returns the object.
+ */
+static GWin32AppInfoFileExtension *
+get_ext_object (const gunichar2 *ext,
+ const gchar *ext_u8,
+ const gchar *ext_u8_folded)
+{
+ GWin32AppInfoFileExtension *file_extn;
+
+ if (g_hash_table_lookup_extended (extensions,
+ ext_u8_folded,
+ NULL,
+ (void **) &file_extn))
+ return file_extn;
+
+ file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
+ file_extn->extension = g_wcsdup (ext, -1);
+ file_extn->extension_u8 = g_strdup (ext_u8);
+ g_hash_table_insert (extensions, g_strdup (ext_u8_folded), file_extn);
+
+ return file_extn;
}
+/* Iterates over HKCU\\Software\\Clients or HKLM\\Software\\Clients,
+ * (depending on @user_registry being TRUE or FALSE),
+ * collecting applications listed there.
+ * Puts the path to the client key for each client into @priority_capable_apps
+ * (only for clients with file or URL associations).
+ */
static void
collect_capable_apps_from_clients (GPtrArray *capable_apps,
GPtrArray *priority_capable_apps,
@@ -1223,7 +1657,7 @@ collect_capable_apps_from_clients (GPtrArray *capable_apps,
GWin32RegistryKey *clients;
GWin32RegistrySubkeyIter clients_iter;
- gunichar2 *client_type_name;
+ const gunichar2 *client_type_name;
gsize client_type_name_len;
@@ -1251,7 +1685,7 @@ collect_capable_apps_from_clients (GPtrArray *capable_apps,
GWin32RegistryKey *system_client_type;
GWin32RegistryValueType default_type;
gunichar2 *default_value = NULL;
- gunichar2 *client_name;
+ const gunichar2 *client_name;
gsize client_name_len;
if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
@@ -1356,11 +1790,17 @@ collect_capable_apps_from_clients (GPtrArray *capable_apps,
g_object_unref (clients);
}
+/* Iterates over HKCU\\Software\\RegisteredApplications or HKLM\\Software\\RegisteredApplications,
+ * (depending on @user_registry being TRUE or FALSE),
+ * collecting applications listed there.
+ * Puts the path to the app key for each app into @capable_apps.
+ */
static void
collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
gboolean user_registry)
{
GWin32RegistryValueIter iter;
+ const gunichar2 *reg_path;
gunichar2 *value_data;
gsize value_data_size;
@@ -1368,13 +1808,12 @@ collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
GWin32RegistryKey *registered_apps;
if (user_registry)
- registered_apps =
- g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
- NULL);
+ reg_path = L"HKEY_CURRENT_USER\\Software\\RegisteredApplications";
else
- registered_apps =
- g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
- NULL);
+ reg_path = L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications";
+
+ registered_apps =
+ g_win32_registry_key_new_w (reg_path, NULL);
if (!registered_apps)
return;
@@ -1382,13 +1821,15 @@ collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
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;
+ GWin32RegistryKey *location;
+ gunichar2 *p;
if ((!g_win32_registry_value_iter_get_value_type (&iter,
&value_type,
@@ -1402,51 +1843,81 @@ collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
(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)
+ if (location == NULL)
+ continue;
+
+ p = wcsrchr (possible_location, L'\\');
+
+ if (p)
{
- gunichar2 *p = wcsrchr (possible_location, L'\\');
- if (p)
- *p = L'\0';
+ *p = L'\0';
g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
- g_object_unref (location);
}
+
+ g_object_unref (location);
}
g_win32_registry_value_iter_clear (&iter);
g_object_unref (registered_apps);
}
+/* Looks up an app object identified by
+ * @canonical_name_folded in the @app_hashmap.
+ * If such object doesn't exist,
+ * creates it and puts it into the @app_hashmap.
+ * Returns the object.
+ */
+static GWin32AppInfoApplication *
+get_app_object (GHashTable *app_hashmap,
+ const gunichar2 *canonical_name,
+ const gchar *canonical_name_u8,
+ const gchar *canonical_name_folded,
+ gboolean user_specific,
+ gboolean default_app)
+{
+ GWin32AppInfoApplication *app;
+
+ app = g_hash_table_lookup (app_hashmap, canonical_name_folded);
+
+ if (app != NULL)
+ return app;
+
+ 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->no_open_with = FALSE;
+ app->user_specific = user_specific;
+ app->default_app = default_app;
+ g_hash_table_insert (app_hashmap,
+ g_strdup (canonical_name_folded),
+ app);
+
+ return app;
+}
+
+/* Grabs an application that has Capabilities.
+ * @app_key_path is the path to the application key
+ * (which must have a "Capabilities" subkey).
+ * @default_app is TRUE if the app has priority
+ */
static void
-read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolean default_app)
+read_capable_app (const gunichar2 *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;
+ gchar *canonical_name_u8 = NULL;
+ gchar *canonical_name_folded = NULL;
+ gchar *app_key_path_u8 = NULL;
+ gchar *app_key_path_u8_folded = NULL;
+ GWin32RegistryKey *appkey = NULL;
gunichar2 *fallback_friendly_name;
GWin32RegistryValueType vtype;
gboolean success;
@@ -1456,136 +1927,49 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
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;
- gchar *app_dll_function;
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 (!g_utf16_to_utf8_and_fold (canonical_name, -1, &canonical_name_u8, &canonical_name_folded))
+ const reg_verb *preferred_verb;
+ GList *verbs = NULL;
+
+ appkey = NULL;
+ capabilities = NULL;
+
+ if (!g_utf16_to_utf8_and_fold (app_key_path,
+ -1,
+ &canonical_name_u8,
+ &canonical_name_folded) ||
+ !g_utf16_to_utf8_and_fold (app_key_path,
+ -1,
+ &app_key_path_u8,
+ &app_key_path_u8_folded) ||
+ (appkey = g_win32_registry_key_new_w (app_key_path, NULL)) == NULL ||
+ (capabilities = g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL)) == NULL ||
+ !get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell"))
{
- g_free (app_key_path);
- return;
- }
-
- appkey = g_win32_registry_key_new_w (app_key_path, NULL);
+ g_clear_pointer (&canonical_name_u8, g_free);
+ g_clear_pointer (&canonical_name_folded, g_free);
+ g_clear_object (&appkey);
+ g_clear_pointer (&app_key_path_u8, g_free);
+ g_clear_pointer (&app_key_path_u8_folded, g_free);
- 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,
- NULL,
- TRUE,
- L"",
- &vtype,
- (gpointer *) &shell_open_command,
- NULL,
- NULL);
-
- if (success &&
- (vtype != G_WIN32_REGISTRY_VALUE_STR ||
- !g_utf16_validate (shell_open_command, -1)))
- {
- /* 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;
- }
-
- _g_win32_extract_executable (shell_open_command,
- &app_executable,
- &app_executable_basename,
- &app_executable_folded,
- &app_executable_folded_basename,
- &app_dll_function);
- if (app_dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (shell_open_command);
-
- 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;
-
- app->dll_function = g_strdup (app_dll_function);
-
- g_hash_table_insert (apps_by_id,
- g_strdup (canonical_name_folded),
- app);
- }
+ app = get_app_object (apps_by_id,
+ app_key_path,
+ canonical_name_u8,
+ canonical_name_folded,
+ user_specific,
+ default_app);
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ L"", /* [ab]use the fact that two strings are simply concatenated */
+ g_win32_registry_key_get_path_w (capabilities),
+ FALSE,
+ app_add_verb,
+ app,
+ app);
fallback_friendly_name = NULL;
success = g_win32_registry_key_get_value_w (appkey,
@@ -1600,7 +1984,8 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&fallback_friendly_name, g_free);
- if (fallback_friendly_name && app->pretty_name == NULL)
+ 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);
@@ -1621,10 +2006,12 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ if (success &&
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&friendly_name, g_free);
- if (friendly_name && app->localized_pretty_name == NULL)
+ 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);
@@ -1672,7 +2059,8 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ if (success &&
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&icon_source, g_free);
g_object_unref (default_icon_key);
@@ -1689,11 +2077,13 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ if (success &&
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&icon_source, g_free);
}
- if (icon_source && app->icon == NULL)
+ 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);
@@ -1713,13 +2103,17 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&narrow_application_name, g_free);
- /* 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?
- */
+ if (narrow_application_name &&
+ app->localized_pretty_name == NULL)
+ {
+ app->localized_pretty_name = g_wcsdup (narrow_application_name, -1);
+ g_clear_pointer (&app->localized_pretty_name_u8, g_free);
+ app->localized_pretty_name_u8 = g_utf16_to_utf8 (narrow_application_name,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ }
associations = g_win32_registry_key_get_child_w (capabilities,
L"FileAssociations",
@@ -1739,19 +2133,6 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
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)) ||
@@ -1770,110 +2151,7 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
(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;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
- 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 (g_utf16_to_utf8_and_fold (file_extension,
- -1,
- &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);
+ get_file_ext (extension_handler, file_extension, app, FALSE);
}
g_win32_registry_value_iter_clear (&iter);
@@ -1898,18 +2176,8 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
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;
+ gchar *schema_u8_folded;
if ((!g_win32_registry_value_iter_get_value_type (&iter,
&value_type,
@@ -1930,105 +2198,16 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
(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;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
- 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 (g_utf16_to_utf8_and_fold (url_schema,
+ url_schema_len,
+ &schema_u8,
+ &schema_u8_folded))
+ get_url_association (schema_handler, url_schema, schema_u8, schema_u8_folded, app, FALSE);
- if (g_utf16_to_utf8_and_fold (url_schema,
- -1,
- &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_clear_pointer (&schema_u8, g_free);
+ g_clear_pointer (&schema_u8_folded, g_free);
}
g_win32_registry_value_iter_clear (&iter);
@@ -2037,29 +2216,26 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
g_object_unref (associations);
}
- g_clear_pointer (&app_executable, g_free);
- g_clear_pointer (&app_executable_folded, g_free);
- g_clear_pointer (&app_dll_function, 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);
+ g_clear_pointer (&app_key_path_u8, g_free);
+ g_clear_pointer (&app_key_path_u8_folded, g_free);
+ g_clear_pointer (&canonical_name_u8, g_free);
+ g_clear_pointer (&canonical_name_folded, g_free);
}
+/* Iterates over subkeys in HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
+ * and calls get_url_association() for each one that has a user-chosen handler.
+ */
static void
read_urls (GWin32RegistryKey *url_associations)
{
GWin32RegistrySubkeyIter url_iter;
- gunichar2 *url_schema;
- gsize url_schema_len;
if (url_associations == NULL)
return;
@@ -2069,302 +2245,259 @@ read_urls (GWin32RegistryKey *url_associations)
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);
+ gchar *schema_u8 = NULL;
+ gchar *schema_u8_folded = NULL;
+ const gunichar2 *url_schema = NULL;
+ gunichar2 *program_id = NULL;
+ GWin32RegistryKey *user_choice = NULL;
+ gsize url_schema_len;
+ GWin32RegistryValueType val_type;
+
+ if (g_win32_registry_subkey_iter_get_name_w (&url_iter,
+ &url_schema,
+ &url_schema_len,
+ NULL) &&
+ g_utf16_to_utf8_and_fold (url_schema,
+ url_schema_len,
+ &schema_u8,
+ &schema_u8_folded) &&
+ (user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
+ url_schema, USER_CHOICE,
+ NULL)) != NULL &&
+ g_win32_registry_key_get_value_w (user_choice,
+ NULL,
+ TRUE,
+ L"Progid",
+ &val_type,
+ (void **) &program_id,
+ NULL,
+ NULL) &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR)
+ get_url_association (program_id, url_schema, schema_u8, schema_u8_folded, NULL, TRUE);
+
+ g_clear_pointer (&program_id, g_free);
+ g_clear_pointer (&user_choice, g_object_unref);
+ g_clear_pointer (&schema_u8, g_free);
+ g_clear_pointer (&schema_u8_folded, g_free);
}
g_win32_registry_subkey_iter_clear (&url_iter);
}
+/* Reads an application that is only registered by the basename of its
+ * executable (and doesn't have Capabilities subkey).
+ * @incapable_app is the registry key for the app.
+ * @app_exe_basename is the basename of its executable.
+ */
static void
-read_exeapps (void)
+read_incapable_app (GWin32RegistryKey *incapable_app,
+ const gunichar2 *app_exe_basename,
+ const gchar *app_exe_basename_u8,
+ const gchar *app_exe_basename_u8_folded)
{
- GWin32RegistryKey *applications_key;
- GWin32RegistrySubkeyIter app_iter;
-
- applications_key =
- g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
+ GWin32RegistryValueIter sup_iter;
+ GWin32AppInfoApplication *app;
+ GList *verbs;
+ const reg_verb *preferred_verb;
+ gunichar2 *friendly_app_name;
+ gboolean success;
+ GWin32RegistryValueType vtype;
+ gboolean no_open_with;
+ GWin32RegistryKey *default_icon_key;
+ gunichar2 *icon_source;
+ GIcon *icon = NULL;
+ GWin32RegistryKey *supported_key;
- if (applications_key == NULL)
+ if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell"))
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))
- {
- gunichar2 *app_exe_basename;
- gsize app_exe_basename_len;
- 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) ||
- !g_utf16_validate (app_exe_basename, app_exe_basename_len))
- continue;
-
- incapable_app =
- g_win32_registry_key_get_child_w (applications_key,
- app_exe_basename,
- NULL);
-
- if (incapable_app == NULL)
- continue;
-
- _g_win32_extract_executable (app_exe_basename,
- &appexe,
- &appexe_basename,
- &appexe_folded,
- &appexe_folded_basename,
- NULL);
-
- 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,
- NULL,
- TRUE,
- L"",
- &vtype,
- (gpointer *) &shell_open_command,
- NULL,
- NULL);
-
- if (success &&
- (vtype != G_WIN32_REGISTRY_VALUE_STR ||
- !g_utf16_validate (shell_open_command, -1)))
- {
- 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,
- g_win32_registry_get_os_dirs_w (),
- 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);
-
- no_open_with = FALSE;
- success = g_win32_registry_key_get_value_w (incapable_app,
- NULL,
- 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,
- NULL,
+ app = get_app_object (apps_by_exe,
+ app_exe_basename,
+ app_exe_basename_u8,
+ app_exe_basename_u8_folded,
+ FALSE,
+ FALSE);
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ L"HKEY_CLASSES_ROOT\\Applications\\",
+ app_exe_basename,
+ TRUE,
+ app_add_verb,
+ app,
+ app);
+
+ friendly_app_name = NULL;
+ success = g_win32_registry_key_get_value_w (incapable_app,
+ g_win32_registry_get_os_dirs_w (),
TRUE,
- L"",
+ L"FriendlyAppName",
&vtype,
- (void **) &icon_source,
+ (void **) &friendly_app_name,
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
- g_clear_pointer (&icon_source, g_free);
+ if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&friendly_app_name, g_free);
+
+ no_open_with = g_win32_registry_key_get_value_w (incapable_app,
+ NULL,
+ TRUE,
+ L"NoOpenWith",
+ &vtype,
+ NULL,
+ NULL,
+ NULL);
- g_object_unref (default_icon_key);
- }
+ default_icon_key =
+ g_win32_registry_key_get_child_w (incapable_app,
+ L"DefaultIcon",
+ NULL);
- if (icon_source)
- {
- gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
- icon = g_themed_icon_new (name);
- g_free (name);
- }
+ icon_source = NULL;
- app = g_hash_table_lookup (apps_by_exe, appexe_folded_basename);
+ if (default_icon_key != NULL)
+ {
+ success =
+ g_win32_registry_key_get_value_w (default_icon_key,
+ NULL,
+ TRUE,
+ L"",
+ &vtype,
+ (void **) &icon_source,
+ NULL,
+ NULL);
- if (app == NULL)
- {
- app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
+ if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&icon_source, g_free);
- if (shell_open_command)
- {
- gchar *dll_function;
-
- _g_win32_extract_executable (shell_open_command,
- NULL,
- NULL,
- NULL,
- NULL,
- &dll_function);
- if (dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (shell_open_command);
- g_clear_pointer (&dll_function, g_free);
- }
+ g_object_unref (default_icon_key);
+ }
- app->command =
- shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL;
+ if (icon_source)
+ {
+ gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
+ if (name != NULL)
+ icon = g_themed_icon_new (name);
+ g_free (name);
+ }
- if (shell_open_command)
- app->command_u8 = g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
+ app->no_open_with = no_open_with;
- app->executable = g_strdup (appexe);
- app->executable_basename = &app->executable[appexe_basename - appexe];
- app->executable_folded = g_strdup (appexe_folded);
+ if (friendly_app_name &&
+ app->localized_pretty_name == NULL)
+ {
+ 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);
+ }
- app->no_open_with = no_open_with;
+ if (icon && app->icon == NULL)
+ app->icon = g_object_ref (icon);
- 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);
- }
+ supported_key =
+ g_win32_registry_key_get_child_w (incapable_app,
+ L"SupportedTypes",
+ NULL);
- if (icon)
- app->icon = g_object_ref (icon);
+ if (supported_key &&
+ g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
+ {
+ gunichar2 *ext_name;
+ gsize ext_name_len;
- app->user_specific = FALSE;
- app->default_app = FALSE;
+ while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
+ {
+ 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'.'))
+ continue;
- g_hash_table_insert (apps_by_exe,
- g_strdup (appexe_folded_basename),
- app);
+ get_file_ext (ext_name, ext_name, app, FALSE);
}
- supported_key =
- g_win32_registry_key_get_child_w (incapable_app,
- L"SupportedTypes",
- NULL);
+ g_win32_registry_value_iter_clear (&sup_iter);
+ }
- 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;
+ g_clear_object (&supported_key);
+ g_free (friendly_app_name);
+ g_free (icon_source);
- 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'.') ||
- (!g_utf16_to_utf8_and_fold (ext_name,
- -1,
- &ext_u8,
- &ext_folded)))
- continue;
-
- file_extn = NULL;
- file_ext_known =
- g_hash_table_lookup_extended (extensions,
- ext_folded,
- NULL,
- (void **) &file_extn);
+ g_clear_object (&icon);
+}
- 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);
- }
+/* Iterates over subkeys of HKEY_CLASSES_ROOT\\Applications
+ * and calls read_incapable_app() for each one.
+ */
+static void
+read_exeapps (void)
+{
+ GWin32RegistryKey *applications_key;
+ GWin32RegistrySubkeyIter app_iter;
- g_hash_table_insert (file_extn->other_apps,
- g_strdup (appexe_folded),
- g_object_ref (app));
+ applications_key =
+ g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
- g_free (ext_u8);
- g_free (ext_folded);
- }
+ if (applications_key == NULL)
+ return;
- g_win32_registry_value_iter_clear (&sup_iter);
- }
+ if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
+ {
+ g_object_unref (applications_key);
+ return;
+ }
- g_object_unref (supported_key);
- }
+ while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
+ {
+ const gunichar2 *app_exe_basename;
+ gsize app_exe_basename_len;
+ GWin32RegistryKey *incapable_app;
+ gchar *app_exe_basename_u8;
+ gchar *app_exe_basename_u8_folded;
+
+ if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
+ &app_exe_basename,
+ &app_exe_basename_len,
+ NULL) ||
+ !g_utf16_to_utf8_and_fold (app_exe_basename,
+ app_exe_basename_len,
+ &app_exe_basename_u8,
+ &app_exe_basename_u8_folded))
+ continue;
+ incapable_app =
+ g_win32_registry_key_get_child_w (applications_key,
+ app_exe_basename,
+ NULL);
- g_free (appexe);
- g_free (appexe_folded);
- g_free (shell_open_command);
- g_free (friendly_app_name);
- g_free (icon_source);
+ if (incapable_app != NULL)
+ read_incapable_app (incapable_app,
+ app_exe_basename,
+ app_exe_basename_u8,
+ app_exe_basename_u8_folded);
- g_clear_object (&icon);
g_clear_object (&incapable_app);
+ g_clear_pointer (&app_exe_basename_u8, g_free);
+ g_clear_pointer (&app_exe_basename_u8_folded, g_free);
}
g_win32_registry_subkey_iter_clear (&app_iter);
g_object_unref (applications_key);
}
-
+/* Iterates over subkeys of HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\
+ * and calls get_file_ext() for each associated handler
+ * (starting with user-chosen handler, if any)
+ */
static void
read_exts (GWin32RegistryKey *file_exts)
{
GWin32RegistrySubkeyIter ext_iter;
- gunichar2 *file_extension;
+ const gunichar2 *file_extension;
gsize file_extension_len;
if (file_exts == NULL)
@@ -2375,263 +2508,89 @@ read_exts (GWin32RegistryKey *file_exts)
while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
{
+ GWin32RegistryKey *open_with_progids;
+ gunichar2 *program_id;
+ GWin32RegistryValueIter iter;
+ gunichar2 *value_name;
+ gsize value_name_len;
+ GWin32RegistryValueType value_type;
+ GWin32RegistryKey *user_choice;
+
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;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
- 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;
+ program_id = NULL;
+ user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, file_extension,
+ USER_CHOICE, NULL);
+ if (user_choice &&
+ g_win32_registry_key_get_value_w (user_choice,
+ NULL,
+ TRUE,
+ L"Progid",
+ &value_type,
+ (void **) &program_id,
+ NULL,
+ NULL) &&
+ value_type == G_WIN32_REGISTRY_VALUE_STR)
+ {
+ /* Note: program_id could be "ProgramID" or "Applications\\program.exe".
+ * The code still works, but handler_id might have a backslash
+ * in it - that might trip us up later on.
+ * Even though in that case this is logically an "application"
+ * registry entry, we don't treat it in any special way.
+ * We do scan that registry branch anyway, just not here.
+ */
+ get_file_ext (program_id, file_extension, NULL, TRUE);
+ }
- class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
+ g_clear_object (&user_choice);
+ g_clear_pointer (&program_id, g_free);
- if (class_key == NULL)
- return;
+ open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
+ file_extension,
+ OPEN_WITH_PROGIDS,
+ NULL);
- success = g_win32_registry_key_get_value_w (class_key,
- NULL,
- TRUE,
- L"URL Protocol",
- &vtype,
- NULL,
- NULL,
- NULL);
-
- if (!success ||
- vtype != G_WIN32_REGISTRY_VALUE_STR)
- {
- g_object_unref (class_key);
- return;
- }
+ if (open_with_progids == NULL)
+ continue;
- 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;
- }
+ if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
+ {
+ g_clear_object (&open_with_progids);
+ continue;
+ }
- schema_rec = g_hash_table_lookup (urls, program_id_folded);
- handler_rec = g_hash_table_lookup (handlers, program_id_folded);
+ while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
+ {
+ if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
+ &value_name_len,
+ NULL) ||
+ (value_name_len == 0))
+ continue;
- 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;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ? handler_rec->handler_command : handler_rec->proxy_command);
- 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);
- }
+ get_file_ext (value_name, file_extension, NULL, FALSE);
+ }
- 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);
+ g_win32_registry_value_iter_clear (&iter);
+ g_clear_object (&open_with_progids);
}
- 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);
+ g_win32_registry_subkey_iter_clear (&ext_iter);
}
+/* Iterates over subkeys in HKCR, calls
+ * get_file_ext() for any subkey that starts with ".",
+ * or get_url_association() for any subkey that could
+ * be a URL schema and has a "URL Protocol" value.
+ */
static void
read_classes (GWin32RegistryKey *classes_root)
{
GWin32RegistrySubkeyIter class_iter;
- gunichar2 *class_name;
+ const gunichar2 *class_name;
gsize class_name_len;
if (classes_root == NULL)
@@ -2650,409 +2609,326 @@ read_classes (GWin32RegistryKey *classes_root)
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);
+ GWin32RegistryKey *class_key;
+ GWin32RegistryValueIter iter;
+ GWin32RegistryKey *open_with_progids;
+ gunichar2 *value_name;
+ gsize value_name_len;
- while (g_hash_table_iter_next (&iter,
- (gpointer *) &schema_folded,
- (gpointer *) &schema))
- {
- if (schema->chosen_handler != NULL)
- continue;
+ /* Read the data from the HKCR\\.ext (usually proxied
+ * to another HKCR subkey)
+ */
+ get_file_ext (class_name, class_name, NULL, FALSE);
- g_hash_table_iter_init (&handler_iter, schema->handlers);
+ class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
- while (g_hash_table_iter_next (&handler_iter,
- (gpointer *) &handler_id_folded,
- (gpointer *) &handler))
- {
- gchar *proxy_id_folded;
+ if (class_key == NULL)
+ continue;
- if (schema->chosen_handler != NULL)
- break;
+ open_with_progids = g_win32_registry_key_get_child_w (class_key, L"OpenWithProgids", NULL);
+ g_clear_object (&class_key);
- if (strcmp (handler_id_folded, schema_folded) != 0)
+ if (open_with_progids == NULL)
continue;
- if (handler->proxy_command &&
- handler->proxy_id &&
- g_utf16_to_utf8_and_fold (handler->proxy_id,
- -1,
- NULL,
- &proxy_id_folded))
+ if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
{
- 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);
+ g_clear_object (&open_with_progids);
+ continue;
}
- if (schema->chosen_handler == NULL)
+ /* Read the data for other handlers for this extension */
+ while (g_win32_registry_value_iter_next (&iter, TRUE, 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);
- }
- }
- }
+ if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
+ &value_name_len,
+ NULL) ||
+ (value_name_len == 0))
+ continue;
- g_hash_table_iter_init (&iter, extensions);
+ get_file_ext (value_name, class_name, NULL, FALSE);
+ }
- while (g_hash_table_iter_next (&iter,
- (gpointer *) &ext_folded,
- (gpointer *) &ext))
- {
- if (ext->chosen_handler != NULL)
- continue;
+ g_win32_registry_value_iter_clear (&iter);
+ g_clear_object (&open_with_progids);
+ }
+ else
+ {
+ gsize i;
+ GWin32RegistryKey *class_key;
+ gboolean success;
+ GWin32RegistryValueType vtype;
+ gchar *schema_u8;
+ gchar *schema_u8_folded;
- g_hash_table_iter_init (&handler_iter, ext->handlers);
+ for (i = 0; i < class_name_len; i++)
+ if (!iswalpha (class_name[i]))
+ break;
- while (g_hash_table_iter_next (&handler_iter,
- (gpointer *) &handler_id_folded,
- (gpointer *) &handler))
- {
- gchar *proxy_id_folded;
+ if (i != class_name_len)
+ continue;
- if (ext->chosen_handler != NULL)
- break;
+ class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
- if (strcmp (handler_id_folded, ext_folded) != 0)
+ if (class_key == NULL)
continue;
- if (handler->proxy_command &&
- handler->proxy_id &&
- g_utf16_to_utf8_and_fold (handler->proxy_id,
- -1,
- NULL,
- &proxy_id_folded))
- {
- GWin32AppInfoHandler *proxy;
+ success = g_win32_registry_key_get_value_w (class_key,
+ NULL,
+ TRUE,
+ L"URL Protocol",
+ &vtype,
+ NULL,
+ NULL,
+ NULL);
+ g_clear_object (&class_key);
- proxy = g_hash_table_lookup (handlers, proxy_id_folded);
+ if (!success ||
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
+ continue;
- 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);
- }
+ if (!g_utf16_to_utf8_and_fold (class_name, -1, &schema_u8, &schema_u8_folded))
+ continue;
- g_free (proxy_id_folded);
- }
+ get_url_association (class_name, class_name, schema_u8, schema_u8_folded, NULL, FALSE);
- 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);
- }
+ g_clear_pointer (&schema_u8, g_free);
+ g_clear_pointer (&schema_u8_folded, g_free);
}
}
+
+ g_win32_registry_subkey_iter_clear (&class_iter);
}
+/* Iterates over all handlers and over all apps,
+ * and links handler verbs to apps if a handler
+ * runs the same executable as one of the app verbs.
+ */
static void
-link_handlers_to_registered_apps (void)
+link_handlers_to_unregistered_apps (void)
{
GHashTableIter iter;
- GHashTableIter sup_iter;
- gchar *app_id_folded;
+ GHashTableIter app_iter;
+ GWin32AppInfoHandler *handler;
+ gchar *handler_id_fld;
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;
- }
- }
+ gchar *canonical_name_fld;
+ gchar *appexe_fld_basename;
- g_hash_table_iter_init (&iter, apps_by_id);
+ g_hash_table_iter_init (&iter, handlers);
while (g_hash_table_iter_next (&iter,
- (gpointer *) &app_id_folded,
- (gpointer *) &app))
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
{
- if (app->supported_urls)
- {
- GWin32AppInfoHandler *handler;
+ gsize vi;
- g_hash_table_iter_init (&sup_iter, app->supported_urls);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &schema_folded,
- (gpointer *) &handler))
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+ const gchar *handler_exe_basename;
+ enum
{
- schema = g_hash_table_lookup (urls, schema_folded);
+ SH_UNKNOWN,
+ GOT_SH_INFO,
+ ERROR_GETTING_SH_INFO,
+ } have_stat_handler = SH_UNKNOWN;
+ GWin32PrivateStat handler_verb_exec_info;
- g_assert (schema != NULL);
+ handler_verb = _verb_idx (handler->verbs, vi);
- 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);
- }
- }
+ if (handler_verb->app != NULL)
+ continue;
- g_hash_table_iter_init (&sup_iter, app->supported_urls);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &schema_folded,
- (gpointer *) &handler))
+ handler_exe_basename = g_utf8_find_basename (handler_verb->executable_folded, -1);
+ g_hash_table_iter_init (&app_iter, apps_by_id);
+
+ while (g_hash_table_iter_next (&app_iter,
+ (gpointer *) &canonical_name_fld,
+ (gpointer *) &app))
{
- 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);
- }
- }
- }
+ GWin32AppInfoShellVerb *app_verb;
+ gsize ai;
- if (app->supported_exts)
- {
- GWin32AppInfoHandler *handler;
+ for (ai = 0; ai < app->verbs->len; ai++)
+ {
+ GWin32PrivateStat app_verb_exec_info;
+ const gchar *app_exe_basename;
+ app_verb = _verb_idx (app->verbs, ai);
- 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);
+ app_exe_basename = g_utf8_find_basename (app_verb->executable_folded, -1);
- g_assert (ext != NULL);
+ /* First check that the executable paths are identical */
+ if (g_strcmp0 (app_verb->executable_folded, handler_verb->executable_folded) != 0)
+ {
+ /* If not, check the basenames. If they are different, don't bother
+ * with further checks.
+ */
+ if (g_strcmp0 (app_exe_basename, handler_exe_basename) != 0)
+ continue;
+
+ /* Get filesystem IDs for both files.
+ * For the handler that is attempted only once.
+ */
+ if (have_stat_handler == SH_UNKNOWN)
+ {
+ if (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (handler_verb->executable_folded,
+ &handler_verb_exec_info) == 0)
+ have_stat_handler = GOT_SH_INFO;
+ else
+ have_stat_handler = ERROR_GETTING_SH_INFO;
+ }
+
+ if (have_stat_handler != GOT_SH_INFO ||
+ (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (app_verb->executable_folded,
+ &app_verb_exec_info) != 0) ||
+ app_verb_exec_info.file_index != handler_verb_exec_info.file_index)
+ continue;
+ }
- 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);
+ handler_verb->app = g_object_ref (app);
+ break;
}
}
- 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_verb->app != NULL)
+ continue;
+
+ g_hash_table_iter_init (&app_iter, apps_by_exe);
+
+ while (g_hash_table_iter_next (&app_iter,
+ (gpointer *) &appexe_fld_basename,
+ (gpointer *) &app))
{
- 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);
- }
- }
- }
- }
+ /* Use basename because apps_by_exe only has basenames */
+ if (g_strcmp0 (handler_exe_basename, appexe_fld_basename) != 0)
+ continue;
- 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;
+ handler_verb->app = g_object_ref (app);
+ break;
+ }
}
}
- g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
}
+/* Finds all .ext and schema: handler verbs that have no app linked to them,
+ * creates a "fake app" object and links these verbs to these
+ * objects. Objects are identified by the full path to
+ * the executable being run, thus multiple different invocations
+ * get grouped in a more-or-less natural way.
+ * The iteration goes separately over .ext and schema: handlers
+ * (instead of the global handlers hashmap) to allow us to
+ * put the handlers into supported_urls or supported_exts as
+ * needed (handler objects themselves have no knowledge of extensions
+ * and/or URLs they are associated with).
+ */
static void
-link_handlers_to_unregistered_apps (void)
+link_handlers_to_fake_apps (void)
{
GHashTableIter iter;
- GHashTableIter app_iter;
+ GHashTableIter handler_iter;
+ gchar *extension_utf8_folded;
+ GWin32AppInfoFileExtension *file_extn;
+ gchar *handler_id_fld;
GWin32AppInfoHandler *handler;
- gchar *handler_id_fc;
- GWin32AppInfoApplication *app;
- gchar *canonical_name_fc;
- gchar *appexe_fc_basename;
+ gchar *url_utf8_folded;
+ GWin32AppInfoURLSchema *schema;
- g_hash_table_iter_init (&iter, handlers);
+ g_hash_table_iter_init (&iter, extensions);
while (g_hash_table_iter_next (&iter,
- (gpointer *) &handler_id_fc,
- (gpointer *) &handler))
+ (gpointer *) &extension_utf8_folded,
+ (gpointer *) &file_extn))
{
- gchar *hndexe_fc_basename;
-
- if ((handler->app != NULL) ||
- (handler->executable_folded == 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))
+ g_hash_table_iter_init (&handler_iter, file_extn->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
{
- if (app->executable_folded == NULL)
- continue;
-
- if (strcmp (app->executable_folded,
- handler->executable_folded) != 0)
- continue;
-
- handler->app = app;
- break;
- }
+ gsize vi;
- if (handler->app != NULL)
- continue;
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+ GWin32AppInfoApplication *app;
+ gunichar2 *exename_utf16;
+ handler_verb = _verb_idx (handler->verbs, vi);
- hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
+ if (handler_verb->app != NULL)
+ continue;
- if (hndexe_fc_basename == NULL)
- continue;
+ exename_utf16 = g_utf8_to_utf16 (handler_verb->executable, -1, NULL, NULL, NULL);
+ if (exename_utf16 == NULL)
+ continue;
- g_hash_table_iter_init (&app_iter, apps_by_exe);
+ app = get_app_object (fake_apps,
+ exename_utf16,
+ handler_verb->executable,
+ handler_verb->executable_folded,
+ FALSE,
+ FALSE);
+ g_clear_pointer (&exename_utf16, g_free);
+ handler_verb->app = g_object_ref (app);
+
+ app_add_verb (app,
+ app,
+ handler_verb->verb_name,
+ handler_verb->command,
+ handler_verb->command_utf8,
+ handler_verb->verb_displayname,
+ TRUE,
+ TRUE);
+ g_hash_table_insert (app->supported_exts,
+ g_strdup (extension_utf8_folded),
+ g_object_ref (handler));
+ }
+ }
+ }
- while ((hndexe_fc_basename != NULL) &&
- (g_hash_table_iter_next (&app_iter,
- (gpointer *) &appexe_fc_basename,
- (gpointer *) &app)))
+ g_hash_table_iter_init (&iter, urls);
+ while (g_hash_table_iter_next (&iter,
+ (gpointer *) &url_utf8_folded,
+ (gpointer *) &schema))
+ {
+ g_hash_table_iter_init (&handler_iter, schema->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
{
- /* Use basename because apps_by_exe only has basenames */
- if (strcmp (hndexe_fc_basename, appexe_fc_basename) != 0)
- continue;
+ gsize vi;
- handler->app = app;
- break;
- }
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+ GWin32AppInfoApplication *app;
+ gchar *command_utf8_folded;
+ handler_verb = _verb_idx (handler->verbs, vi);
- g_free (hndexe_fc_basename);
+ if (handler_verb->app != NULL)
+ continue;
- if (handler->app == NULL)
- g_debug ("WARNING: handler that runs %s has no corresponding app\n",
- handler->executable);
+ command_utf8_folded = g_utf8_casefold (handler_verb->command_utf8, -1);
+ app = get_app_object (fake_apps,
+ handler_verb->command,
+ handler_verb->command_utf8,
+ command_utf8_folded,
+ FALSE,
+ FALSE);
+ g_clear_pointer (&command_utf8_folded, g_free);
+ handler_verb->app = g_object_ref (app);
+
+ app_add_verb (app,
+ app,
+ handler_verb->verb_name,
+ handler_verb->command,
+ handler_verb->command_utf8,
+ handler_verb->verb_displayname,
+ TRUE,
+ TRUE);
+ g_hash_table_insert (app->supported_urls,
+ g_strdup (url_utf8_folded),
+ g_object_ref (handler));
+ }
+ }
}
}
@@ -3083,6 +2959,7 @@ update_registry_data (void)
g_clear_pointer (&apps_by_id, g_hash_table_destroy);
g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
+ g_clear_pointer (&fake_apps, 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);
@@ -3104,6 +2981,8 @@ update_registry_data (void)
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);
+ fake_apps =
+ 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 =
@@ -3134,9 +3013,8 @@ update_registry_data (void)
exeapp_end = GetTickCount ();
read_classes (classes_root);
classes_end = GetTickCount ();
- link_chosen_handlers ();
- link_handlers_to_registered_apps ();
link_handlers_to_unregistered_apps ();
+ link_handlers_to_fake_apps ();
postproc_end = GetTickCount ();
g_debug ("Collecting capable appnames: %lums\n"
@@ -3147,7 +3025,7 @@ update_registry_data (void)
"Reading exe-only apps:...... %lums\n"
"Reading classes: %lums\n"
"Postprocessing:..............%lums\n"
- "TOTAL: %lums\n",
+ "TOTAL: %lums",
collect_end - collect_start,
alloc_end - collect_end,
capable_end - alloc_end,
@@ -3337,7 +3215,7 @@ g_win32_app_info_finalize (GObject *object)
info = G_WIN32_APP_INFO (object);
- g_clear_pointer (&info->supported_types, g_free);
+ g_clear_pointer (&info->supported_types, g_strfreev);
g_clear_object (&info->app);
g_clear_object (&info->handler);
@@ -3392,7 +3270,7 @@ g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
if (!ext)
continue;
- new_info->supported_types[i] = (gchar *) ext;
+ new_info->supported_types[i] = g_strdup ((gchar *) ext);
i += 1;
}
@@ -3444,30 +3322,41 @@ static gboolean
g_win32_app_info_equal (GAppInfo *appinfo1,
GAppInfo *appinfo2)
{
+ GWin32AppInfoShellVerb *shverb1 = NULL;
+ GWin32AppInfoShellVerb *shverb2 = NULL;
GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
+ GWin32AppInfoApplication *app1 = info1->app;
+ GWin32AppInfoApplication *app2 = info2->app;
- if (info1->app == NULL ||
- info2->app == NULL)
+ if (app1 == NULL ||
+ app2 == 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 (app1->canonical_name_folded != NULL &&
+ app2->canonical_name_folded != NULL)
+ return (g_strcmp0 (app1->canonical_name_folded,
+ app2->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;
+ if (app1->verbs->len > 0 &&
+ app2->verbs->len > 0)
+ {
+ shverb1 = _verb_idx (app1->verbs, 0);
+ shverb2 = _verb_idx (app2->verbs, 0);
+ if (shverb1->executable_folded != NULL &&
+ shverb2->executable_folded != NULL)
+ return (g_strcmp0 (shverb1->executable_folded,
+ shverb2->executable_folded)) == 0;
+ }
- return info1->app == info2->app;
+ return app1 == app2;
}
static const char *
g_win32_app_info_get_id (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+ GWin32AppInfoShellVerb *shverb;
if (info->app == NULL)
return NULL;
@@ -3475,8 +3364,9 @@ g_win32_app_info_get_id (GAppInfo *appinfo)
if (info->app->canonical_name_u8)
return info->app->canonical_name_u8;
- if (info->app->executable_basename)
- return info->app->executable_basename;
+ if (info->app->verbs->len > 0 &&
+ (shverb = _verb_idx (info->app->verbs, 0))->executable_basename != NULL)
+ return shverb->executable_basename;
return NULL;
}
@@ -3527,7 +3417,10 @@ g_win32_app_info_get_executable (GAppInfo *appinfo)
if (info->app == NULL)
return NULL;
- return info->app->executable;
+ if (info->app->verbs->len > 0)
+ return _verb_idx (info->app->verbs, 0)->executable;
+
+ return NULL;
}
static const char *
@@ -3538,7 +3431,10 @@ g_win32_app_info_get_commandline (GAppInfo *appinfo)
if (info->app == NULL)
return NULL;
- return info->app->command_u8;
+ if (info->app->verbs->len > 0)
+ return _verb_idx (info->app->verbs, 0)->command_utf8;
+
+ return NULL;
}
static GIcon *
@@ -3842,14 +3738,6 @@ expand_application_parameters (GWin32AppInfo *info,
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;
- }
-
expanded_exec = g_string_new (NULL);
res = FALSE;
@@ -3897,42 +3785,44 @@ expand_application_parameters (GWin32AppInfo *info,
static gchar *
-get_appath_for_exe (gunichar2 *exe_basename)
+get_appath_for_exe (const gchar *exe_basename)
{
GWin32RegistryKey *apppath_key = NULL;
GWin32RegistryValueType val_type;
- gunichar2 *appath = NULL;
+ gchar *appath = NULL;
gboolean got_value;
- gchar *result = NULL;
+ gchar *key_path = g_strdup_printf ("HKEY_LOCAL_MACHINE\\"
+ "SOFTWARE\\"
+ "Microsoft\\"
+ "Windows\\"
+ "CurrentVersion\\"
+ "App Paths\\"
+ "%s", exe_basename);
- 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);
+ apppath_key = g_win32_registry_key_new (key_path, NULL);
+ g_clear_pointer (&key_path, g_free);
if (apppath_key == NULL)
return NULL;
- got_value = g_win32_registry_key_get_value_w (apppath_key,
- NULL,
- TRUE,
- L"Path",
- &val_type,
- (void **) &appath,
- NULL,
- NULL);
+ got_value = g_win32_registry_key_get_value (apppath_key,
+ NULL,
+ TRUE,
+ "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);
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR)
+ return appath;
g_clear_pointer (&appath, g_free);
- return result;
+ return appath;
}
@@ -3946,9 +3836,9 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
gboolean completed = FALSE;
char **argv, **envp;
int argc;
- gchar *command;
+ const gchar *command;
gchar *apppath;
- gunichar2 *exe_basename;
+ GWin32AppInfoShellVerb *shverb;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (info->app != NULL, FALSE);
@@ -3960,52 +3850,32 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
else
envp = g_get_environ ();
- command = NULL;
- exe_basename = NULL;
+ shverb = 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 (info->handler != NULL &&
+ info->handler->verbs->len > 0)
+ shverb = _verb_idx (info->handler->verbs, 0);
+ else if (info->app->verbs->len > 0)
+ shverb = _verb_idx (info->app->verbs, 0);
- if (command == NULL)
+ if (shverb == NULL)
{
- command = g_strdup (info->app->command_u8);
- exe_basename = g_utf8_to_utf16 (info->app->executable_basename,
- -1,
- NULL,
- NULL,
- NULL);
- }
+ if (info->handler == NULL)
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ P_("The app ‘%s’ in the application object has no verbs"),
+ g_win32_appinfo_application_get_some_name (info->app));
+ else if (info->handler->verbs->len == 0)
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ P_("The app ‘%s’ and the handler ‘%s’ in the application object have no verbs"),
+ g_win32_appinfo_application_get_some_name (info->app),
+ info->handler->handler_id_folded);
- apppath = get_appath_for_exe (exe_basename);
+ return FALSE;
+ }
- g_free (exe_basename);
+ g_assert (shverb->command_utf8 != NULL);
+ command = shverb->command_utf8;
+ apppath = get_appath_for_exe (shverb->executable_basename);
if (apppath)
{
@@ -4095,7 +3965,6 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
out:
g_strfreev (argv);
g_strfreev (envp);
- g_free (command);
return completed;
}
@@ -4266,7 +4135,9 @@ g_app_info_create_from_commandline (const char *commandline,
info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
- app->command = g_steal_pointer (&app_command);
+ app->no_open_with = FALSE;
+ app->user_specific = FALSE;
+ app->default_app = FALSE;
if (application_name)
{
@@ -4279,21 +4150,16 @@ g_app_info_create_from_commandline (const char *commandline,
app->canonical_name_folded = g_utf8_casefold (application_name, -1);
}
- _g_win32_extract_executable (app->command,
- &app->executable,
- &app->executable_basename,
- &app->executable_folded,
- NULL,
- &app->dll_function);
- if (app->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (app->command);
-
- app->command_u8 = g_utf16_to_utf8 (app->command, -1, NULL, NULL, NULL);
-
- app->no_open_with = FALSE;
- app->user_specific = FALSE;
- app->default_app = FALSE;
+ app_add_verb (app,
+ app,
+ L"open",
+ app_command,
+ commandline,
+ "open",
+ TRUE,
+ FALSE);
+ g_clear_pointer (&app_command, g_free);
info->app = app;
info->handler = NULL;
@@ -4333,9 +4199,10 @@ g_win32_app_info_iface_init (GAppInfoIface *iface)
GAppInfo *
g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
{
- GWin32AppInfoURLSchema *scheme;
+ GWin32AppInfoURLSchema *scheme = NULL;
char *scheme_down;
GAppInfo *result;
+ GWin32AppInfoShellVerb *shverb;
scheme_down = g_utf8_casefold (uri_scheme, -1);
@@ -4345,30 +4212,28 @@ g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
if (strcmp (scheme_down, "file") == 0)
{
g_free (scheme_down);
+
return NULL;
}
g_win32_appinfo_init ();
G_LOCK (gio_win32_appinfo);
- scheme = g_hash_table_lookup (urls, scheme_down);
+ g_set_object (&scheme, g_hash_table_lookup (urls, scheme_down));
g_free (scheme_down);
- if (scheme)
- g_object_ref (scheme);
-
G_UNLOCK (gio_win32_appinfo);
result = NULL;
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->verbs->len > 0 &&
+ (shverb = _verb_idx (scheme->chosen_handler->verbs, 0))->app != NULL)
+ result = g_win32_app_info_new_from_app (shverb->app,
scheme->chosen_handler);
- if (scheme)
- g_object_unref (scheme);
+ g_clear_object (&scheme);
return result;
}
@@ -4377,12 +4242,10 @@ GAppInfo *
g_app_info_get_default_for_type (const char *content_type,
gboolean must_support_uris)
{
- GWin32AppInfoFileExtension *ext;
+ GWin32AppInfoFileExtension *ext = NULL;
char *ext_down;
- GWin32AppInfoHandler *handler;
GAppInfo *result;
- GWin32AppInfoApplication *app;
- GHashTableIter iter;
+ GWin32AppInfoShellVerb *shverb;
ext_down = g_utf8_casefold (content_type, -1);
@@ -4393,56 +4256,47 @@ g_app_info_get_default_for_type (const char *content_type,
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_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
g_free (ext_down);
- result = NULL;
+ G_UNLOCK (gio_win32_appinfo);
- if (ext != NULL)
- g_object_ref (ext);
+ if (ext == NULL)
+ return NULL;
- G_UNLOCK (gio_win32_appinfo);
+ result = NULL;
- if (ext != NULL)
+ if (ext->chosen_handler != NULL &&
+ ext->chosen_handler->verbs->len > 0 &&
+ (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL &&
+ (!must_support_uris ||
+ g_win32_app_supports_uris (shverb->app)))
+ result = g_win32_app_info_new_from_app (shverb->app,
+ ext->chosen_handler);
+ else
{
- 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
+ GHashTableIter iter;
+ GWin32AppInfoHandler *handler;
+
+ g_hash_table_iter_init (&iter, ext->handlers);
+
+ while (result == NULL &&
+ g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
{
- g_hash_table_iter_init (&iter, ext->handlers);
+ if (handler->verbs->len == 0)
+ continue;
- 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;
- }
- }
+ shverb = _verb_idx (handler->verbs, 0);
- 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;
- }
- }
- }
+ if (shverb->app &&
+ (!must_support_uris ||
+ g_win32_app_supports_uris (shverb->app)))
+ result = g_win32_app_info_new_from_app (shverb->app, handler);
}
- g_object_unref (ext);
}
+ g_clear_object (&ext);
+
return result;
}
@@ -4478,12 +4332,13 @@ g_app_info_get_all (void)
GList *
g_app_info_get_all_for_type (const char *content_type)
{
- GWin32AppInfoFileExtension *ext;
+ GWin32AppInfoFileExtension *ext = NULL;
char *ext_down;
GWin32AppInfoHandler *handler;
- GWin32AppInfoApplication *app;
GHashTableIter iter;
+ GHashTable *apps = NULL;
GList *result;
+ GWin32AppInfoShellVerb *shverb;
ext_down = g_utf8_casefold (content_type, -1);
@@ -4494,46 +4349,52 @@ g_app_info_get_all_for_type (const char *content_type)
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_set_object (&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)
return NULL;
+ result = NULL;
+ /* Used as a set to ensure uniqueness */
+ apps = g_hash_table_new (g_direct_hash, g_direct_equal);
+
if (ext->chosen_handler != NULL &&
- ext->chosen_handler->app != NULL)
- result = g_list_prepend (result,
- g_win32_app_info_new_from_app (ext->chosen_handler->app,
- ext->chosen_handler));
+ ext->chosen_handler->verbs->len > 0 &&
+ (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL)
+ {
+ g_hash_table_add (apps, shverb->app);
+ result = g_list_prepend (result,
+ g_win32_app_info_new_from_app (shverb->app,
+ ext->chosen_handler));
+ }
g_hash_table_iter_init (&iter, ext->handlers);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
{
- if (handler->app &&
- (ext->chosen_handler == NULL || ext->chosen_handler->app != handler->app))
- result = g_list_prepend (result,
- g_win32_app_info_new_from_app (handler->app,
- handler));
- }
+ gsize vi;
- g_hash_table_iter_init (&iter, ext->other_apps);
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ shverb = _verb_idx (handler->verbs, vi);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
- {
- result = g_list_prepend (result, g_win32_app_info_new_from_app (app, NULL));
- }
+ if (shverb->app == NULL ||
+ g_hash_table_contains (apps, shverb->app))
+ continue;
- g_object_unref (ext);
+ g_hash_table_add (apps, shverb->app);
+ result = g_list_prepend (result,
+ g_win32_app_info_new_from_app (shverb->app,
+ handler));
+ }
+ }
+ g_clear_object (&ext);
result = g_list_reverse (result);
+ g_hash_table_unref (apps);
return result;
}
diff --git a/gio/gwin32registrykey.c b/gio/gwin32registrykey.c
index 2eb67daf8..6b24fdd90 100644
--- a/gio/gwin32registrykey.c
+++ b/gio/gwin32registrykey.c
@@ -28,8 +28,6 @@
#include <ntstatus.h>
#include <winternl.h>
-#include "gstrfuncsprivate.h"
-
#ifndef _WDMDDK_
typedef enum _KEY_INFORMATION_CLASS {
KeyBasicInformation,
@@ -127,34 +125,16 @@ typedef enum
G_WIN32_REGISTRY_UPDATED_PATH = 1,
} GWin32RegistryKeyUpdateFlag;
-static gsize
-g_utf16_len (const gunichar2 *str)
-{
- gsize result;
-
- for (result = 0; str[0] != 0; str++, result++)
- ;
-
- return result;
-}
-
static gunichar2 *
-g_wcsdup (const gunichar2 *str, gssize str_len)
+g_wcsdup (const gunichar2 *str,
+ gssize str_size)
{
- gsize str_len_unsigned;
- gsize str_size;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- if (str_len < 0)
- str_len_unsigned = g_utf16_len (str);
- else
- str_len_unsigned = (gsize) str_len;
-
- g_assert (str_len_unsigned <= G_MAXSIZE / sizeof (gunichar2) - 1);
- str_size = (str_len_unsigned + 1) * sizeof (gunichar2);
-
- return g_memdup2 (str, str_size);
+ if (str_size == -1)
+ {
+ str_size = wcslen (str) + 1;
+ str_size *= sizeof (gunichar2);
+ }
+ return g_memdup (str, str_size);
}
/**
@@ -267,7 +247,7 @@ g_win32_registry_value_iter_copy (const GWin32RegistryValueIter *iter)
new_iter->value_name_size = iter->value_name_size;
if (iter->value_data != NULL)
- new_iter->value_data = g_memdup2 (iter->value_data, iter->value_data_size);
+ new_iter->value_data = g_memdup (iter->value_data, iter->value_data_size);
new_iter->value_data_size = iter->value_data_size;
@@ -288,8 +268,8 @@ g_win32_registry_value_iter_copy (const GWin32RegistryValueIter *iter)
new_iter->value_data_expanded_charsize = iter->value_data_expanded_charsize;
if (iter->value_data_expanded_u8 != NULL)
- new_iter->value_data_expanded_u8 = g_memdup2 (iter->value_data_expanded_u8,
- iter->value_data_expanded_charsize);
+ new_iter->value_data_expanded_u8 = g_memdup (iter->value_data_expanded_u8,
+ iter->value_data_expanded_charsize);
new_iter->value_data_expanded_u8_size = iter->value_data_expanded_charsize;
@@ -966,7 +946,7 @@ g_win32_registry_subkey_iter_next (GWin32RegistrySubkeyIter *iter,
**/
gboolean
g_win32_registry_subkey_iter_get_name_w (GWin32RegistrySubkeyIter *iter,
- gunichar2 **subkey_name,
+ const gunichar2 **subkey_name,
gsize *subkey_name_len,
GError **error)
{
@@ -1008,7 +988,7 @@ g_win32_registry_subkey_iter_get_name_w (GWin32RegistrySubkeyIter *iter,
**/
gboolean
g_win32_registry_subkey_iter_get_name (GWin32RegistrySubkeyIter *iter,
- gchar **subkey_name,
+ const gchar **subkey_name,
gsize *subkey_name_len,
GError **error)
{
@@ -1033,13 +1013,15 @@ g_win32_registry_subkey_iter_get_name (GWin32RegistrySubkeyIter *iter,
&subkey_name_len_glong,
error);
- if (iter->subkey_name_u8 != NULL)
- {
- *subkey_name_len = subkey_name_len_glong;
- return TRUE;
- }
+ if (iter->subkey_name_u8 == NULL)
+ return FALSE;
- return FALSE;
+ *subkey_name = iter->subkey_name_u8;
+
+ if (subkey_name_len)
+ *subkey_name_len = subkey_name_len_glong;
+
+ return TRUE;
}
/**
diff --git a/gio/gwin32registrykey.h b/gio/gwin32registrykey.h
index 28b57a843..f92a10caf 100644
--- a/gio/gwin32registrykey.h
+++ b/gio/gwin32registrykey.h
@@ -191,12 +191,12 @@ gboolean g_win32_registry_subkey_iter_next (GWin32RegistrySubk
GError **error);
GLIB_AVAILABLE_IN_2_46
gboolean g_win32_registry_subkey_iter_get_name (GWin32RegistrySubkeyIter *iter,
- gchar **subkey_name,
+ const gchar **subkey_name,
gsize *subkey_name_len,
GError **error);
GLIB_AVAILABLE_IN_2_46
gboolean g_win32_registry_subkey_iter_get_name_w (GWin32RegistrySubkeyIter *iter,
- gunichar2 **subkey_name,
+ const gunichar2 **subkey_name,
gsize *subkey_name_len,
GError **error);
diff --git a/gio/meson.build b/gio/meson.build
index 40a9ca6d0..37af9e438 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -874,14 +874,14 @@ endif
# Dependencies used by executables below
have_libelf = false
-libelf = dependency('libelf', version : '>= 0.8.12', required : false)
+libelf = dependency('libelf', version : '>= 0.8.12', required : get_option ('libelf'))
if libelf.found()
have_libelf = true
else
# This fallback is necessary on *BSD. elfutils isn't the only libelf
# implementation, and *BSD usually includes their own libelf as a system
# library which doesn't have a corresponding .pc file.
- libelf = cc.find_library('elf', required : false)
+ libelf = cc.find_library('elf', required : get_option ('libelf'))
have_libelf = libelf.found()
have_libelf = have_libelf and cc.has_function('elf_begin', dependencies : libelf)
have_libelf = have_libelf and cc.has_function('elf_getshdrstrndx', dependencies : libelf)
diff --git a/gio/tests/async-close-output-stream.c b/gio/tests/async-close-output-stream.c
index d3f97a119..5f6620275 100644
--- a/gio/tests/async-close-output-stream.c
+++ b/gio/tests/async-close-output-stream.c
@@ -24,8 +24,6 @@
#include <stdlib.h>
#include <string.h>
-#include "gstrfuncsprivate.h"
-
#define DATA_TO_WRITE "Hello world\n"
typedef struct
@@ -149,9 +147,9 @@ prepare_data (SetupData *data,
data->expected_size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (data->data_stream));
- g_assert_cmpuint (data->expected_size, >, 0);
+ g_assert_cmpint (data->expected_size, >, 0);
- data->expected_output = g_memdup2 (written, data->expected_size);
+ data->expected_output = g_memdup (written, (guint)data->expected_size);
/* then recreate the streams and prepare them for the asynchronous close */
destroy_streams (data);
diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c
index bc50ff491..f4e509a59 100644
--- a/gio/tests/desktop-app-info.c
+++ b/gio/tests/desktop-app-info.c
@@ -571,8 +571,7 @@ assert_implementations (const gchar *interface,
"gnome-terminal.desktop nautilus-autorun-software.desktop gcr-viewer.desktop " \
"nautilus-connect-server.desktop kde4-dolphin.desktop gnome-music.desktop " \
"kde4-konqbrowser.desktop gucharmap.desktop kde4-okular.desktop nautilus.desktop " \
- "gedit.desktop evince.desktop file-roller.desktop dconf-editor.desktop glade.desktop " \
- "invalid-desktop.desktop"
+ "gedit.desktop evince.desktop file-roller.desktop dconf-editor.desktop glade.desktop"
#define HOME_APPS "epiphany-weather-for-toronto-island-9c6a4e022b17686306243dada811d550d25eb1fb.desktop"
#define ALL_HOME_APPS HOME_APPS " eog.desktop"
@@ -727,9 +726,6 @@ test_show_in (void)
assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic");
assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic:KDE");
assert_shown ("gcr-prompter.desktop", TRUE, "KDE:GNOME-Classic");
- assert_shown ("invalid-desktop.desktop", TRUE, "GNOME");
- assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop");
- assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop:../invalid/desktop");
}
/* Test g_desktop_app_info_launch_uris_as_manager() and
diff --git a/gio/tests/desktop-files/usr/applications/invalid-desktop.desktop b/gio/tests/desktop-files/usr/applications/invalid-desktop.desktop
deleted file mode 100644
index dffaa2469..000000000
--- a/gio/tests/desktop-files/usr/applications/invalid-desktop.desktop
+++ /dev/null
@@ -1,5 +0,0 @@
-[Desktop Entry]
-Type=Application
-Name=appinfo-test
-OnlyShowIn=../invalid/desktop;GNOME
-NotShowIn=ROX;
diff --git a/gio/tests/file.c b/gio/tests/file.c
index ddd1ffcba..e951e1fcd 100644
--- a/gio/tests/file.c
+++ b/gio/tests/file.c
@@ -686,7 +686,7 @@ test_replace_cancel (void)
guint count;
GError *error = NULL;
- g_test_bug ("https://bugzilla.gnome.org/629301");
+ g_test_bug ("629301");
path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error);
g_assert_no_error (error);
@@ -806,113 +806,6 @@ test_replace_cancel (void)
}
static void
-test_replace_symlink (void)
-{
-#ifdef G_OS_UNIX
- gchar *tmpdir_path = NULL;
- GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
- GFileOutputStream *stream = NULL;
- const gchar *new_contents = "this is a test message which should be written to source and not target";
- gsize n_written;
- GFileEnumerator *enumerator = NULL;
- GFileInfo *info = NULL;
- gchar *contents = NULL;
- gsize length = 0;
- GError *local_error = NULL;
-
- g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2325");
- g_test_summary ("Test that G_FILE_CREATE_REPLACE_DESTINATION doesn’t follow symlinks");
-
- /* Create a fresh, empty working directory. */
- tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_XXXXXX", &local_error);
- g_assert_no_error (local_error);
- tmpdir = g_file_new_for_path (tmpdir_path);
-
- g_test_message ("Using temporary directory %s", tmpdir_path);
- g_free (tmpdir_path);
-
- /* Create symlink `source` which points to `target`. */
- source_file = g_file_get_child (tmpdir, "source");
- target_file = g_file_get_child (tmpdir, "target");
- g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
- g_assert_no_error (local_error);
-
- /* Ensure that `target` doesn’t exist */
- g_assert_false (g_file_query_exists (target_file, NULL));
-
- /* Replace the `source` symlink with a regular file using
- * %G_FILE_CREATE_REPLACE_DESTINATION, which should replace it *without*
- * following the symlink */
- stream = g_file_replace (source_file, NULL, FALSE /* no backup */,
- G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_error);
- g_assert_no_error (local_error);
-
- g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
- &n_written, NULL, &local_error);
- g_assert_no_error (local_error);
- g_assert_cmpint (n_written, ==, strlen (new_contents));
-
- g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
- g_assert_no_error (local_error);
-
- g_clear_object (&stream);
-
- /* At this point, there should still only be one file: `source`. It should
- * now be a regular file. `target` should not exist. */
- enumerator = g_file_enumerate_children (tmpdir,
- G_FILE_ATTRIBUTE_STANDARD_NAME ","
- G_FILE_ATTRIBUTE_STANDARD_TYPE,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
- g_assert_no_error (local_error);
-
- info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
- g_assert_no_error (local_error);
- g_assert_nonnull (info);
-
- g_assert_cmpstr (g_file_info_get_name (info), ==, "source");
- g_assert_cmpint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR);
-
- g_clear_object (&info);
-
- info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
- g_assert_no_error (local_error);
- g_assert_null (info);
-
- g_file_enumerator_close (enumerator, NULL, &local_error);
- g_assert_no_error (local_error);
- g_clear_object (&enumerator);
-
- /* Double-check that `target` doesn’t exist */
- g_assert_false (g_file_query_exists (target_file, NULL));
-
- /* Check the content of `source`. */
- g_file_load_contents (source_file,
- NULL,
- &contents,
- &length,
- NULL,
- &local_error);
- g_assert_no_error (local_error);
- g_assert_cmpstr (contents, ==, new_contents);
- g_assert_cmpuint (length, ==, strlen (new_contents));
- g_free (contents);
-
- /* Tidy up. */
- g_file_delete (source_file, NULL, &local_error);
- g_assert_no_error (local_error);
-
- g_file_delete (tmpdir, NULL, &local_error);
- g_assert_no_error (local_error);
-
- g_clear_object (&target_file);
- g_clear_object (&source_file);
- g_clear_object (&tmpdir);
-#else /* if !G_OS_UNIX */
- g_test_skip ("Symlink replacement tests can only be run on Unix")
-#endif
-}
-
-static void
on_file_deleted (GObject *object,
GAsyncResult *result,
gpointer user_data)
@@ -1148,7 +1041,7 @@ test_measure (void)
if (size > 0)
g_assert_cmpuint (num_bytes, ==, size);
g_assert_cmpuint (num_dirs, ==, 6);
- g_assert_cmpuint (num_files, ==, 32);
+ g_assert_cmpuint (num_files, ==, 31);
g_object_unref (file);
g_free (path);
@@ -1238,7 +1131,7 @@ test_measure_async (void)
g_free (path);
data->expected_dirs = 6;
- data->expected_files = 32;
+ data->expected_files = 31;
g_file_measure_disk_usage_async (file,
G_FILE_MEASURE_APPARENT_SIZE,
@@ -1887,11 +1780,86 @@ test_writev_async_all_too_big_vectors (void)
g_object_unref (file);
}
+static void
+test_build_attribute_list_for_copy (void)
+{
+ GFile *tmpfile;
+ GFileIOStream *iostream;
+ GError *error = NULL;
+ const GFileCopyFlags test_flags[] =
+ {
+ G_FILE_COPY_NONE,
+ G_FILE_COPY_TARGET_DEFAULT_PERMS,
+ G_FILE_COPY_ALL_METADATA,
+ G_FILE_COPY_ALL_METADATA | G_FILE_COPY_TARGET_DEFAULT_PERMS,
+ };
+ gsize i;
+ char *attrs;
+ gchar *attrs_with_commas;
+
+ tmpfile = g_file_new_tmp ("tmp-build-attribute-list-for-copyXXXXXX",
+ &iostream, &error);
+ g_assert_no_error (error);
+ g_io_stream_close ((GIOStream*)iostream, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_object (&iostream);
+
+ for (i = 0; i < G_N_ELEMENTS (test_flags); i++)
+ {
+ GFileCopyFlags flags = test_flags[i];
+
+ attrs = g_file_build_attribute_list_for_copy (tmpfile, flags, NULL, &error);
+ g_test_message ("Attributes for copy: %s", attrs);
+ g_assert_no_error (error);
+ g_assert_nonnull (attrs);
+ attrs_with_commas = g_strconcat (",", attrs, ",", NULL);
+ g_free (attrs);
+
+ /* See g_local_file_class_init for reference. */
+ if (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS)
+ g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ","));
+ else
+ g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ","));
+#ifdef G_OS_UNIX
+ if (flags & G_FILE_COPY_ALL_METADATA)
+ {
+ g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ","));
+ g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ","));
+ }
+ else
+ {
+ g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ","));
+ g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ","));
+ }
+#endif
+#ifdef HAVE_UTIMES
+ g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED ","));
+ g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","));
+ if (flags & G_FILE_COPY_ALL_METADATA)
+ {
+ g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
+ g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ","));
+ }
+ else
+ {
+ g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
+ g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ","));
+ }
+#endif
+ g_free (attrs_with_commas);
+ }
+
+ (void) g_file_delete (tmpfile, NULL, NULL);
+ g_clear_object (&tmpfile);
+}
+
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
+ g_test_bug_base ("http://bugzilla.gnome.org/");
+
g_test_add_func ("/file/basic", test_basic);
g_test_add_func ("/file/build-filename", test_build_filename);
g_test_add_func ("/file/parent", test_parent);
@@ -1905,7 +1873,6 @@ main (int argc, char *argv[])
g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
g_test_add_func ("/file/replace-load", test_replace_load);
g_test_add_func ("/file/replace-cancel", test_replace_cancel);
- g_test_add_func ("/file/replace-symlink", test_replace_symlink);
g_test_add_func ("/file/async-delete", test_async_delete);
g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
g_test_add_func ("/file/measure", test_measure);
@@ -1923,6 +1890,7 @@ main (int argc, char *argv[])
g_test_add_func ("/file/writev/async_all-no-vectors", test_writev_async_all_no_vectors);
g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors);
g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation);
+ g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy);
return g_test_run ();
}
diff --git a/gio/tests/gdbus-auth.c b/gio/tests/gdbus-auth.c
index 8be4473c7..18288f36d 100644
--- a/gio/tests/gdbus-auth.c
+++ b/gio/tests/gdbus-auth.c
@@ -121,7 +121,7 @@ test_auth_on_timeout (gpointer user_data)
{
g_error ("Timeout waiting for client");
g_assert_not_reached ();
- return FALSE;
+ return G_SOURCE_REMOVE;
}
diff --git a/gio/tests/gdbus-close-pending.c b/gio/tests/gdbus-close-pending.c
index d2c5b62f5..bd8fda5a7 100644
--- a/gio/tests/gdbus-close-pending.c
+++ b/gio/tests/gdbus-close-pending.c
@@ -188,7 +188,7 @@ delayed_close_cb (gpointer data)
close_async (df->stream, df->io_priority, df->cancellable, df->callback,
df->user_data);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
diff --git a/gio/tests/gdbus-connection-loss.c b/gio/tests/gdbus-connection-loss.c
index a34a9923c..cc88cb0cc 100644
--- a/gio/tests/gdbus-connection-loss.c
+++ b/gio/tests/gdbus-connection-loss.c
@@ -53,7 +53,7 @@ on_timeout (gpointer user_data)
{
/* tear down bus */
session_bus_stop ();
- return FALSE; /* remove source */
+ return G_SOURCE_REMOVE;
}
static void
diff --git a/gio/tests/gdbus-connection-slow.c b/gio/tests/gdbus-connection-slow.c
index dd1ababd4..27a6af3c4 100644
--- a/gio/tests/gdbus-connection-slow.c
+++ b/gio/tests/gdbus-connection-slow.c
@@ -49,7 +49,7 @@ test_connection_flush_on_timeout (gpointer user_data)
guint iteration = GPOINTER_TO_UINT (user_data);
g_printerr ("Timeout waiting 1000 msec on iteration %d\n", iteration);
g_assert_not_reached ();
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -125,7 +125,7 @@ large_message_timeout_cb (gpointer data)
g_error ("Error: timeout waiting for dbus name to appear");
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c
index 7bd7a02f4..278570609 100644
--- a/gio/tests/gdbus-connection.c
+++ b/gio/tests/gdbus-connection.c
@@ -65,7 +65,7 @@ test_connection_quit_mainloop (gpointer user_data)
_log ("quit_mainloop_fired");
*quit_mainloop_fired = TRUE;
g_main_loop_quit (loop);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -934,7 +934,7 @@ test_connection_filter_on_timeout (gpointer user_data)
{
g_printerr ("Timeout waiting 30 sec on service\n");
g_assert_not_reached ();
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
diff --git a/gio/tests/gdbus-example-objectmanager-server.c b/gio/tests/gdbus-example-objectmanager-server.c
index c460e66e9..dcedba2d1 100644
--- a/gio/tests/gdbus-example-objectmanager-server.c
+++ b/gio/tests/gdbus-example-objectmanager-server.c
@@ -53,7 +53,7 @@ on_animal_poke (ExampleAnimal *animal,
g_assert_not_reached ();
out:
- return TRUE; /* to indicate that the method was handled */
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
diff --git a/gio/tests/gdbus-example-server.c b/gio/tests/gdbus-example-server.c
index 6f1123d85..00e482724 100644
--- a/gio/tests/gdbus-example-server.c
+++ b/gio/tests/gdbus-example-server.c
@@ -315,7 +315,7 @@ on_timeout_cb (gpointer user_data)
g_assert_no_error (error);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
/* ---------------------------------------------------------------------------------------------------- */
diff --git a/gio/tests/gdbus-exit-on-close.c b/gio/tests/gdbus-exit-on-close.c
index 3c4e5ecd6..4241fc7a4 100644
--- a/gio/tests/gdbus-exit-on-close.c
+++ b/gio/tests/gdbus-exit-on-close.c
@@ -57,7 +57,7 @@ quit_later_cb (gpointer data G_GNUC_UNUSED)
{
g_main_loop_quit (loop);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c
index ad2489dab..ba5388600 100644
--- a/gio/tests/gdbus-export.c
+++ b/gio/tests/gdbus-export.c
@@ -23,7 +23,6 @@
#include <string.h>
#include "gdbus-tests.h"
-#include "gstrfuncsprivate.h"
/* all tests rely on a shared mainloop */
static GMainLoop *loop = NULL;
@@ -672,7 +671,7 @@ subtree_introspect (GDBusConnection *connection,
g_assert_not_reached ();
}
- return g_memdup2 (interfaces, 2 * sizeof (void *));
+ return g_memdup (interfaces, 2 * sizeof (void *));
}
static const GDBusInterfaceVTable *
@@ -728,7 +727,7 @@ dynamic_subtree_introspect (GDBusConnection *connection,
{
const GDBusInterfaceInfo *interfaces[2] = { &dyna_interface_info, NULL };
- return g_memdup2 (interfaces, 2 * sizeof (void *));
+ return g_memdup (interfaces, 2 * sizeof (void *));
}
static const GDBusInterfaceVTable *
diff --git a/gio/tests/gdbus-overflow.c b/gio/tests/gdbus-overflow.c
index 53ec9c0b6..ca3d5d0e7 100644
--- a/gio/tests/gdbus-overflow.c
+++ b/gio/tests/gdbus-overflow.c
@@ -95,7 +95,7 @@ static gboolean
overflow_on_500ms_later_func (gpointer user_data)
{
g_main_loop_quit (loop);
- return FALSE; /* don't keep the idle */
+ return G_SOURCE_REMOVE;
}
static void
diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c
index a03ebbbbe..ca53528d2 100644
--- a/gio/tests/gdbus-peer.c
+++ b/gio/tests/gdbus-peer.c
@@ -76,12 +76,6 @@ typedef struct
gboolean signal_received;
} PeerData;
-/* This needs to be enough to usually take more than one write(),
- * to reproduce
- * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>.
- * 1 MiB ought to be enough. */
-#define BIG_MESSAGE_ARRAY_SIZE (1024 * 1024)
-
static const gchar *test_interface_introspection_xml =
"<node>"
" <interface name='org.gtk.GDBus.PeerTestInterface'>"
@@ -94,11 +88,6 @@ static const gchar *test_interface_introspection_xml =
" <method name='OpenFile'>"
" <arg type='s' name='path' direction='in'/>"
" </method>"
- " <method name='OpenFileWithBigMessage'>"
- " <arg type='s' name='path' direction='in'/>"
- " <arg type='h' name='handle' direction='out'/>"
- " <arg type='ay' name='junk' direction='out'/>"
- " </method>"
" <signal name='PeerSignal'>"
" <arg type='s' name='a_string'/>"
" </signal>"
@@ -175,8 +164,7 @@ test_interface_method_call (GDBusConnection *connection,
g_dbus_method_invocation_return_value (invocation, NULL);
}
- else if (g_strcmp0 (method_name, "OpenFile") == 0 ||
- g_strcmp0 (method_name, "OpenFileWithBigMessage") == 0)
+ else if (g_strcmp0 (method_name, "OpenFile") == 0)
{
#ifdef G_OS_UNIX
const gchar *path;
@@ -202,21 +190,6 @@ test_interface_method_call (GDBusConnection *connection,
g_object_unref (fd_list);
g_object_unref (invocation);
- if (g_strcmp0 (method_name, "OpenFileWithBigMessage") == 0)
- {
- char *junk;
-
- junk = g_new0 (char, BIG_MESSAGE_ARRAY_SIZE);
- g_dbus_message_set_body (reply,
- g_variant_new ("(h@ay)",
- 0,
- g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
- junk,
- BIG_MESSAGE_ARRAY_SIZE,
- 1)));
- g_free (junk);
- }
-
error = NULL;
g_dbus_connection_send_message (connection,
reply,
@@ -671,7 +644,7 @@ check_connection (gpointer user_data)
}
}
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean
@@ -681,7 +654,7 @@ on_do_disconnect_in_idle (gpointer data)
g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count);
g_dbus_connection_disconnect (c);
g_object_unref (c);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
#endif
@@ -750,7 +723,6 @@ do_test_peer (void)
const gchar *s;
GThread *service_thread;
gulong signal_handler_id;
- gsize i;
memset (&data, '\0', sizeof (PeerData));
data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
@@ -871,116 +843,73 @@ do_test_peer (void)
g_assert_cmpint (data.num_method_calls, ==, 3);
g_signal_handler_disconnect (proxy, signal_handler_id);
- /*
- * Check for UNIX fd passing.
- *
- * The first time through, we use a very simple method call. Note that
- * because this does not have a G_VARIANT_TYPE_HANDLE in the message body
- * to refer to the fd, it is a GDBus-specific idiom that would not
- * interoperate with libdbus or sd-bus
- * (see <https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1726>).
- *
- * The second time, we call a method that returns a fd attached to a
- * large message, to reproduce
- * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>. It also happens
- * to follow the more usual pattern for D-Bus messages containing a
- * G_VARIANT_TYPE_HANDLE to refer to attached fds.
- */
- for (i = 0; i < 2; i++)
- {
+ /* check for UNIX fd passing */
#ifdef G_OS_UNIX
- GDBusMessage *method_call_message;
- GDBusMessage *method_reply_message;
- GUnixFDList *fd_list;
- gint fd;
- gchar *buf;
- gsize len;
- gchar *buf2;
- gsize len2;
- const char *testfile = g_test_get_filename (G_TEST_DIST, "file.c", NULL);
- const char *method = "OpenFile";
- GVariant *body;
-
- if (i == 1)
- method = "OpenFileWithBigMessage";
-
- method_call_message = g_dbus_message_new_method_call (NULL, /* name */
- "/org/gtk/GDBus/PeerTestObject",
- "org.gtk.GDBus.PeerTestInterface",
- method);
- g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", testfile));
- error = NULL;
- method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
- method_call_message,
- G_DBUS_SEND_MESSAGE_FLAGS_NONE,
- -1,
- NULL, /* out_serial */
- NULL, /* cancellable */
- &error);
- g_assert_no_error (error);
- g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
-
- body = g_dbus_message_get_body (method_reply_message);
-
- if (i == 1)
- {
- gint32 handle = -1;
- GVariant *junk = NULL;
-
- g_assert_cmpstr (g_variant_get_type_string (body), ==, "(hay)");
- g_variant_get (body, "(h@ay)", &handle, &junk);
- g_assert_cmpint (handle, ==, 0);
- g_assert_cmpuint (g_variant_n_children (junk), ==, BIG_MESSAGE_ARRAY_SIZE);
- g_variant_unref (junk);
- }
- else
- {
- g_assert_null (body);
- }
-
- fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
- g_assert (fd_list != NULL);
- g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1);
- error = NULL;
- fd = g_unix_fd_list_get (fd_list, 0, &error);
- g_assert_no_error (error);
- g_object_unref (method_call_message);
- g_object_unref (method_reply_message);
+ {
+ GDBusMessage *method_call_message;
+ GDBusMessage *method_reply_message;
+ GUnixFDList *fd_list;
+ gint fd;
+ gchar *buf;
+ gsize len;
+ gchar *buf2;
+ gsize len2;
+ const char *testfile = g_test_get_filename (G_TEST_DIST, "file.c", NULL);
+
+ method_call_message = g_dbus_message_new_method_call (NULL, /* name */
+ "/org/gtk/GDBus/PeerTestObject",
+ "org.gtk.GDBus.PeerTestInterface",
+ "OpenFile");
+ g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", testfile));
+ error = NULL;
+ method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
+ method_call_message,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ -1,
+ NULL, /* out_serial */
+ NULL, /* cancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
+ fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
+ g_assert (fd_list != NULL);
+ g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1);
+ error = NULL;
+ fd = g_unix_fd_list_get (fd_list, 0, &error);
+ g_assert_no_error (error);
+ g_object_unref (method_call_message);
+ g_object_unref (method_reply_message);
- error = NULL;
- len = 0;
- buf = read_all_from_fd (fd, &len, &error);
- g_assert_no_error (error);
- g_assert (buf != NULL);
- close (fd);
+ error = NULL;
+ len = 0;
+ buf = read_all_from_fd (fd, &len, &error);
+ g_assert_no_error (error);
+ g_assert (buf != NULL);
+ close (fd);
- error = NULL;
- g_file_get_contents (testfile,
- &buf2,
- &len2,
- &error);
- g_assert_no_error (error);
- g_assert_cmpmem (buf, len, buf2, len2);
- g_free (buf2);
- g_free (buf);
+ error = NULL;
+ g_file_get_contents (testfile,
+ &buf2,
+ &len2,
+ &error);
+ g_assert_no_error (error);
+ g_assert_cmpmem (buf, len, buf2, len2);
+ g_free (buf2);
+ g_free (buf);
+ }
#else
- /* We do the same number of iterations on non-Unix, so that
- * the method call count will match. In this case we use
- * OpenFile both times, because the difference between this
- * and OpenFileWithBigMessage is only relevant on Unix. */
- error = NULL;
- result = g_dbus_proxy_call_sync (proxy,
- "OpenFile",
- g_variant_new ("(s)", "boo"),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL, /* GCancellable */
- &error);
- g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
- g_assert (result == NULL);
- g_error_free (error);
+ error = NULL;
+ result = g_dbus_proxy_call_sync (proxy,
+ "OpenFile",
+ g_variant_new ("(s)", "boo"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL, /* GCancellable */
+ &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
+ g_assert (result == NULL);
+ g_error_free (error);
#endif /* G_OS_UNIX */
- }
/* Check that g_socket_get_credentials() work - (though this really
* should be in socket.c)
@@ -1088,7 +1017,7 @@ do_test_peer (void)
g_variant_get (result, "(&s)", &s);
g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'.");
g_variant_unref (result);
- g_assert_cmpint (data.num_method_calls, ==, 6);
+ g_assert_cmpint (data.num_method_calls, ==, 5);
#if 0
/* TODO: THIS TEST DOESN'T WORK YET */
@@ -1139,183 +1068,6 @@ test_peer (void)
/* ---------------------------------------------------------------------------------------------------- */
-#define VALID_GUID "0123456789abcdef0123456789abcdef"
-
-static void
-test_peer_invalid_server (void)
-{
- GDBusServer *server;
-
- if (!g_test_undefined ())
- {
- g_test_skip ("Not exercising programming errors");
- return;
- }
-
- if (g_test_subprocess ())
- {
- /* This assumes we are not going to run out of GDBusServerFlags
- * any time soon */
- server = g_dbus_server_new_sync ("tcp:", (GDBusServerFlags) (1 << 30),
- VALID_GUID,
- NULL, NULL, NULL);
- g_assert_null (server);
- }
- else
- {
- g_test_trap_subprocess (NULL, 0, 0);
- g_test_trap_assert_failed ();
- g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_SERVER_FLAGS_ALL*");
- }
-}
-
-static void
-test_peer_invalid_conn_stream_sync (void)
-{
- GSocket *sock;
- GSocketConnection *socket_conn;
- GIOStream *iostream;
- GDBusConnection *conn;
-
- if (!g_test_undefined ())
- {
- g_test_skip ("Not exercising programming errors");
- return;
- }
-
- sock = g_socket_new (G_SOCKET_FAMILY_IPV4,
- G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_TCP,
- NULL);
-
- if (sock == NULL)
- {
- g_test_skip ("TCP not available?");
- return;
- }
-
- socket_conn = g_socket_connection_factory_create_connection (sock);
- g_assert_nonnull (socket_conn);
- iostream = G_IO_STREAM (socket_conn);
- g_assert_nonnull (iostream);
-
- if (g_test_subprocess ())
- {
- /* This assumes we are not going to run out of GDBusConnectionFlags
- * any time soon */
- conn = g_dbus_connection_new_sync (iostream, VALID_GUID,
- (GDBusConnectionFlags) (1 << 30),
- NULL, NULL, NULL);
- g_assert_null (conn);
- }
- else
- {
- g_test_trap_subprocess (NULL, 0, 0);
- g_test_trap_assert_failed ();
- g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*");
- }
-
- g_clear_object (&sock);
- g_clear_object (&socket_conn);
-}
-
-static void
-test_peer_invalid_conn_stream_async (void)
-{
- GSocket *sock;
- GSocketConnection *socket_conn;
- GIOStream *iostream;
-
- if (!g_test_undefined ())
- {
- g_test_skip ("Not exercising programming errors");
- return;
- }
-
- sock = g_socket_new (G_SOCKET_FAMILY_IPV4,
- G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_TCP,
- NULL);
-
- if (sock == NULL)
- {
- g_test_skip ("TCP not available?");
- return;
- }
-
- socket_conn = g_socket_connection_factory_create_connection (sock);
- g_assert_nonnull (socket_conn);
- iostream = G_IO_STREAM (socket_conn);
- g_assert_nonnull (iostream);
-
- if (g_test_subprocess ())
- {
- g_dbus_connection_new (iostream, VALID_GUID,
- (GDBusConnectionFlags) (1 << 30),
- NULL, NULL, NULL, NULL);
- }
- else
- {
- g_test_trap_subprocess (NULL, 0, 0);
- g_test_trap_assert_failed ();
- g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*");
- }
-
- g_clear_object (&sock);
- g_clear_object (&socket_conn);
-}
-
-static void
-test_peer_invalid_conn_addr_sync (void)
-{
- GDBusConnection *conn;
-
- if (!g_test_undefined ())
- {
- g_test_skip ("Not exercising programming errors");
- return;
- }
-
- if (g_test_subprocess ())
- {
- conn = g_dbus_connection_new_for_address_sync ("tcp:",
- (GDBusConnectionFlags) (1 << 30),
- NULL, NULL, NULL);
- g_assert_null (conn);
- }
- else
- {
- g_test_trap_subprocess (NULL, 0, 0);
- g_test_trap_assert_failed ();
- g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*");
- }
-}
-
-static void
-test_peer_invalid_conn_addr_async (void)
-{
- if (!g_test_undefined ())
- {
- g_test_skip ("Not exercising programming errors");
- return;
- }
-
- if (g_test_subprocess ())
- {
- g_dbus_connection_new_for_address ("tcp:",
- (GDBusConnectionFlags) (1 << 30),
- NULL, NULL, NULL, NULL);
- }
- else
- {
- g_test_trap_subprocess (NULL, 0, 0);
- g_test_trap_assert_failed ();
- g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*");
- }
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
static void
test_peer_signals (void)
{
@@ -1967,7 +1719,7 @@ codegen_on_animal_poke (ExampleAnimal *animal,
g_assert_not_reached ();
out:
- return TRUE; /* to indicate that the method was handled */
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
@@ -2042,7 +1794,7 @@ static gboolean
codegen_quit_mainloop_timeout (gpointer data)
{
g_main_loop_quit (loop);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -2187,16 +1939,6 @@ main (int argc,
test_interface_introspection_data = introspection_data->interfaces[0];
g_test_add_func ("/gdbus/peer-to-peer", test_peer);
- g_test_add_func ("/gdbus/peer-to-peer/invalid/server",
- test_peer_invalid_server);
- g_test_add_func ("/gdbus/peer-to-peer/invalid/conn/stream/async",
- test_peer_invalid_conn_stream_async);
- g_test_add_func ("/gdbus/peer-to-peer/invalid/conn/stream/sync",
- test_peer_invalid_conn_stream_sync);
- g_test_add_func ("/gdbus/peer-to-peer/invalid/conn/addr/async",
- test_peer_invalid_conn_addr_async);
- g_test_add_func ("/gdbus/peer-to-peer/invalid/conn/addr/sync",
- test_peer_invalid_conn_addr_sync);
g_test_add_func ("/gdbus/peer-to-peer/signals", test_peer_signals);
g_test_add_func ("/gdbus/delayed-message-processing", delayed_message_processing);
g_test_add_func ("/gdbus/nonce-tcp", test_nonce_tcp);
diff --git a/gio/tests/gdbus-test-codegen.c b/gio/tests/gdbus-test-codegen.c
index bb1d059ca..9a3ffa927 100644
--- a/gio/tests/gdbus-test-codegen.c
+++ b/gio/tests/gdbus-test-codegen.c
@@ -33,6 +33,11 @@
#include "gdbus-test-codegen-generated-interface-info.h"
+#if GLIB_VERSION_MIN_REQUIRED < GLIB_VERSION_2_68
+# undef G_DBUS_METHOD_INVOCATION_HANDLED
+# define G_DBUS_METHOD_INVOCATION_HANDLED TRUE
+#endif
+
/* ---------------------------------------------------------------------------------------------------- */
static guint
@@ -100,7 +105,7 @@ on_handle_hello_world (FooiGenBar *object,
response = g_strdup_printf ("Word! You said '%s'. I'm Skeleton, btw!", greeting);
foo_igen_bar_complete_hello_world (object, invocation, response);
g_free (response);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -145,7 +150,7 @@ on_handle_test_primitive_types (FooiGenBar *object,
g_free (s1);
g_free (s2);
g_free (s3);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -185,7 +190,7 @@ on_handle_test_non_primitive_types (FooiGenBar *object,
array_of_bytestrings,
str->str);
g_string_free (str, TRUE);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -202,7 +207,7 @@ on_handle_request_signal_emission (FooiGenBar *object,
foo_igen_bar_emit_test_signal (object, 43, a_strv, a_bytestring_array, a_variant); /* consumes a_variant */
foo_igen_bar_complete_request_signal_emission (object, invocation);
}
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -218,7 +223,7 @@ on_handle_request_multi_property_mods (FooiGenBar *object,
foo_igen_bar_set_y (object, foo_igen_bar_get_y (object) + 1);
foo_igen_bar_set_i (object, foo_igen_bar_get_i (object) + 1);
foo_igen_bar_complete_request_multi_property_mods (object, invocation);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -238,7 +243,7 @@ on_handle_property_cancellation (FooiGenBar *object,
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (object));
/* this makes us return the reply D-Bus method */
foo_igen_bar_complete_property_cancellation (object, invocation);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -293,7 +298,7 @@ on_handle_force_method (FooiGenBat *object,
g_variant_unref (ret_ay);
g_variant_unref (ret_struct);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
@@ -382,7 +387,7 @@ on_handle_check_not_authorized (FooiGenAuthorize *object,
gpointer user_data)
{
foo_igen_authorize_complete_check_not_authorized (object, invocation);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -391,7 +396,7 @@ on_handle_check_authorized (FooiGenAuthorize *object,
gpointer user_data)
{
foo_igen_authorize_complete_check_authorized (object, invocation);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -400,7 +405,7 @@ on_handle_check_not_authorized_from_object (FooiGenAuthorize *object,
gpointer user_data)
{
foo_igen_authorize_complete_check_not_authorized_from_object (object, invocation);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -414,7 +419,7 @@ on_handle_get_self (FooiGenMethodThreads *object,
s = g_strdup_printf ("%p", (void *)g_thread_self ());
foo_igen_method_threads_complete_get_self (object, invocation, s);
g_free (s);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -2645,7 +2650,7 @@ handle_hello_fd (FooiGenFDPassing *object,
const gchar *arg_greeting)
{
foo_igen_fdpassing_complete_hello_fd (object, invocation, fd_list, arg_greeting);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
@@ -2657,7 +2662,7 @@ handle_no_annotation (FooiGenFDPassing *object,
const gchar *arg_greeting_locale)
{
foo_igen_fdpassing_complete_no_annotation (object, invocation, fd_list, arg_greeting, arg_greeting_locale);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -2667,7 +2672,7 @@ handle_no_annotation_nested (FooiGenFDPassing *object,
GVariant *arg_files)
{
foo_igen_fdpassing_complete_no_annotation_nested (object, invocation, fd_list);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
#else
static gboolean
@@ -2677,7 +2682,7 @@ handle_no_annotation (FooiGenFDPassing *object,
const gchar *arg_greeting_locale)
{
foo_igen_fdpassing_complete_no_annotation (object, invocation, arg_greeting, arg_greeting_locale);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
@@ -2686,7 +2691,7 @@ handle_no_annotation_nested (FooiGenFDPassing *object,
GVariant *arg_files)
{
foo_igen_fdpassing_complete_no_annotation_nested (object, invocation);
- return TRUE;
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
}
#endif
diff --git a/gio/tests/gdbus-tests.c b/gio/tests/gdbus-tests.c
index bed0d24f7..1003d07e0 100644
--- a/gio/tests/gdbus-tests.c
+++ b/gio/tests/gdbus-tests.c
@@ -48,7 +48,7 @@ on_property_notify_timeout (gpointer user_data)
PropertyNotifyData *data = user_data;
data->timed_out = TRUE;
g_main_loop_quit (data->loop);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
gboolean
@@ -83,7 +83,7 @@ static gboolean
_give_up (gpointer data)
{
g_error ("%s", (const gchar *) data);
- g_return_val_if_reached (TRUE);
+ g_return_val_if_reached (G_SOURCE_CONTINUE);
}
typedef struct
@@ -175,7 +175,7 @@ on_signal_received_timeout (gpointer user_data)
SignalReceivedData *data = user_data;
data->timed_out = TRUE;
g_main_loop_quit (data->loop);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
gboolean
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index 6e5a6d367..baadca8f5 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -1740,14 +1740,6 @@ key_changed_cb (GSettings *settings, const gchar *key, gpointer data)
(*b) = TRUE;
}
-typedef struct
-{
- const gchar *path;
- const gchar *root_group;
- const gchar *keyfile_group;
- const gchar *root_path;
-} KeyfileTestData;
-
/*
* Test that using a keyfile works
*/
@@ -1842,11 +1834,7 @@ test_keyfile (Fixture *fixture,
g_free (str);
g_settings_set (settings, "farewell", "s", "cheerio");
-
- /* Check that empty keys/groups are not allowed. */
- g_assert_false (g_settings_is_writable (settings, ""));
- g_assert_false (g_settings_is_writable (settings, "/"));
-
+
/* When executing as root, changing the mode of the keyfile will have
* no effect on the writability of the settings.
*/
@@ -1878,149 +1866,6 @@ test_keyfile (Fixture *fixture,
g_free (keyfile_path);
}
-/*
- * Test that using a keyfile works with a schema with no path set.
- */
-static void
-test_keyfile_no_path (Fixture *fixture,
- gconstpointer user_data)
-{
- const KeyfileTestData *test_data = user_data;
- GSettingsBackend *kf_backend;
- GSettings *settings;
- GKeyFile *keyfile;
- gboolean writable;
- gchar *key = NULL;
- GError *error = NULL;
- gchar *keyfile_path = NULL, *store_path = NULL;
-
- keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL);
- store_path = g_build_filename (keyfile_path, "gsettings.store", NULL);
- kf_backend = g_keyfile_settings_backend_new (store_path, test_data->root_path, test_data->root_group);
- settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, test_data->path);
- g_object_unref (kf_backend);
-
- g_settings_reset (settings, "test-boolean");
- g_assert_true (g_settings_get_boolean (settings, "test-boolean"));
-
- writable = g_settings_is_writable (settings, "test-boolean");
- g_assert_true (writable);
- g_settings_set (settings, "test-boolean", "b", FALSE);
-
- g_assert_false (g_settings_get_boolean (settings, "test-boolean"));
-
- g_settings_delay (settings);
- g_settings_set (settings, "test-boolean", "b", TRUE);
- g_settings_apply (settings);
-
- keyfile = g_key_file_new ();
- g_assert_true (g_key_file_load_from_file (keyfile, store_path, 0, NULL));
-
- g_assert_true (g_key_file_get_boolean (keyfile, test_data->keyfile_group, "test-boolean", NULL));
-
- g_key_file_free (keyfile);
-
- g_settings_reset (settings, "test-boolean");
- g_settings_apply (settings);
- keyfile = g_key_file_new ();
- g_assert_true (g_key_file_load_from_file (keyfile, store_path, 0, NULL));
-
- g_assert_false (g_key_file_get_string (keyfile, test_data->keyfile_group, "test-boolean", &error));
- g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND);
- g_clear_error (&error);
-
- /* Check that empty keys/groups are not allowed. */
- g_assert_false (g_settings_is_writable (settings, ""));
- g_assert_false (g_settings_is_writable (settings, "/"));
-
- /* Keys which ghost the root group name are not allowed. This can only be
- * tested when the path is `/` as otherwise it acts as a prefix and prevents
- * any ghosting. */
- if (g_str_equal (test_data->path, "/"))
- {
- key = g_strdup_printf ("%s/%s", test_data->root_group, "");
- g_assert_false (g_settings_is_writable (settings, key));
- g_free (key);
-
- key = g_strdup_printf ("%s/%s", test_data->root_group, "/");
- g_assert_false (g_settings_is_writable (settings, key));
- g_free (key);
-
- key = g_strdup_printf ("%s/%s", test_data->root_group, "test-boolean");
- g_assert_false (g_settings_is_writable (settings, key));
- g_free (key);
- }
-
- g_key_file_free (keyfile);
- g_object_unref (settings);
-
- /* Clean up the temporary directory. */
- g_assert_no_errno (g_chmod (keyfile_path, 0777));
- g_assert_no_errno (g_remove (store_path));
- g_assert_no_errno (g_rmdir (keyfile_path));
- g_free (store_path);
- g_free (keyfile_path);
-}
-
-/*
- * Test that a keyfile rejects writes to keys outside its root path.
- */
-static void
-test_keyfile_outside_root_path (Fixture *fixture,
- gconstpointer user_data)
-{
- GSettingsBackend *kf_backend;
- GSettings *settings;
- gchar *keyfile_path = NULL, *store_path = NULL;
-
- keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL);
- store_path = g_build_filename (keyfile_path, "gsettings.store", NULL);
- kf_backend = g_keyfile_settings_backend_new (store_path, "/tests/basic-types/", "root");
- settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, "/tests/");
- g_object_unref (kf_backend);
-
- g_assert_false (g_settings_is_writable (settings, "test-boolean"));
-
- g_object_unref (settings);
-
- /* Clean up the temporary directory. The keyfile probably doesn’t exist, so
- * don’t error on failure. */
- g_remove (store_path);
- g_assert_no_errno (g_rmdir (keyfile_path));
- g_free (store_path);
- g_free (keyfile_path);
-}
-
-/*
- * Test that a keyfile rejects writes to keys in the root if no root group is set.
- */
-static void
-test_keyfile_no_root_group (Fixture *fixture,
- gconstpointer user_data)
-{
- GSettingsBackend *kf_backend;
- GSettings *settings;
- gchar *keyfile_path = NULL, *store_path = NULL;
-
- keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL);
- store_path = g_build_filename (keyfile_path, "gsettings.store", NULL);
- kf_backend = g_keyfile_settings_backend_new (store_path, "/", NULL);
- settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, "/");
- g_object_unref (kf_backend);
-
- g_assert_false (g_settings_is_writable (settings, "test-boolean"));
- g_assert_true (g_settings_is_writable (settings, "child/test-boolean"));
-
- g_object_unref (settings);
-
- /* Clean up the temporary directory. The keyfile probably doesn’t exist, so
- * don’t error on failure. */
- g_remove (store_path);
- g_assert_no_errno (g_rmdir (keyfile_path));
- g_free (store_path);
- g_free (keyfile_path);
-}
-
/* Test that getting child schemas works
*/
static void
@@ -2999,14 +2844,6 @@ main (int argc, char *argv[])
gchar *override_text;
gchar *enums;
gint result;
- const KeyfileTestData keyfile_test_data_explicit_path = { "/tests/", "root", "tests", "/" };
- const KeyfileTestData keyfile_test_data_empty_path = { "/", "root", "root", "/" };
- const KeyfileTestData keyfile_test_data_long_path = {
- "/tests/path/is/very/long/and/this/makes/some/comparisons/take/a/different/branch/",
- "root",
- "tests/path/is/very/long/and/this/makes/some/comparisons/take/a/different/branch",
- "/"
- };
/* Meson build sets this */
#ifdef TEST_LOCALE_PATH
@@ -3130,11 +2967,6 @@ main (int argc, char *argv[])
}
g_test_add ("/gsettings/keyfile", Fixture, NULL, setup, test_keyfile, teardown);
- g_test_add ("/gsettings/keyfile/explicit-path", Fixture, &keyfile_test_data_explicit_path, setup, test_keyfile_no_path, teardown);
- g_test_add ("/gsettings/keyfile/empty-path", Fixture, &keyfile_test_data_empty_path, setup, test_keyfile_no_path, teardown);
- g_test_add ("/gsettings/keyfile/long-path", Fixture, &keyfile_test_data_long_path, setup, test_keyfile_no_path, teardown);
- g_test_add ("/gsettings/keyfile/outside-root-path", Fixture, NULL, setup, test_keyfile_outside_root_path, teardown);
- g_test_add ("/gsettings/keyfile/no-root-group", Fixture, NULL, setup, test_keyfile_no_root_group, teardown);
g_test_add_func ("/gsettings/child-schema", test_child_schema);
g_test_add_func ("/gsettings/strinfo", test_strinfo);
g_test_add_func ("/gsettings/enums", test_enums);
diff --git a/gio/tests/gsocketclient-slow.c b/gio/tests/gsocketclient-slow.c
index 803ed9082..34410f4cf 100644
--- a/gio/tests/gsocketclient-slow.c
+++ b/gio/tests/gsocketclient-slow.c
@@ -79,26 +79,23 @@ on_connected_cancelled (GObject *source_object,
g_main_loop_quit (user_data);
}
-typedef struct
+static int
+on_timer (GCancellable *cancel)
{
- GCancellable *cancellable;
- gboolean completed;
-} EventCallbackData;
+ g_cancellable_cancel (cancel);
+ return G_SOURCE_REMOVE;
+}
static void
on_event (GSocketClient *client,
GSocketClientEvent event,
GSocketConnectable *connectable,
GIOStream *connection,
- EventCallbackData *data)
+ gboolean *got_completed_event)
{
- if (data->cancellable && event == G_SOCKET_CLIENT_CONNECTED)
- {
- g_cancellable_cancel (data->cancellable);
- }
- else if (event == G_SOCKET_CLIENT_COMPLETE)
+ if (event == G_SOCKET_CLIENT_COMPLETE)
{
- data->completed = TRUE;
+ *got_completed_event = TRUE;
g_assert_null (connection);
}
}
@@ -111,7 +108,8 @@ test_happy_eyeballs_cancel_delayed (void)
GError *error = NULL;
guint16 port;
GMainLoop *loop;
- EventCallbackData data = { NULL, FALSE };
+ GCancellable *cancel;
+ gboolean got_completed_event = FALSE;
/* This just tests that cancellation works as expected, still emits the completed signal,
* and never returns a connection */
@@ -124,16 +122,17 @@ test_happy_eyeballs_cancel_delayed (void)
g_socket_service_start (service);
client = g_socket_client_new ();
- data.cancellable = g_cancellable_new ();
- g_socket_client_connect_to_host_async (client, "localhost", port, data.cancellable, on_connected_cancelled, loop);
- g_signal_connect (client, "event", G_CALLBACK (on_event), &data);
+ cancel = g_cancellable_new ();
+ g_socket_client_connect_to_host_async (client, "localhost", port, cancel, on_connected_cancelled, loop);
+ g_timeout_add (1, (GSourceFunc) on_timer, cancel);
+ g_signal_connect (client, "event", G_CALLBACK (on_event), &got_completed_event);
g_main_loop_run (loop);
- g_assert_true (data.completed);
+ g_assert_true (got_completed_event);
g_main_loop_unref (loop);
g_object_unref (service);
g_object_unref (client);
- g_object_unref (data.cancellable);
+ g_object_unref (cancel);
}
static void
@@ -145,7 +144,7 @@ test_happy_eyeballs_cancel_instant (void)
guint16 port;
GMainLoop *loop;
GCancellable *cancel;
- EventCallbackData data = { NULL, FALSE };
+ gboolean got_completed_event = FALSE;
/* This tests the same things as above, test_happy_eyeballs_cancel_delayed(), but
* with different timing since it sends an already cancelled cancellable */
@@ -161,10 +160,10 @@ test_happy_eyeballs_cancel_instant (void)
cancel = g_cancellable_new ();
g_cancellable_cancel (cancel);
g_socket_client_connect_to_host_async (client, "localhost", port, cancel, on_connected_cancelled, loop);
- g_signal_connect (client, "event", G_CALLBACK (on_event), &data);
+ g_signal_connect (client, "event", G_CALLBACK (on_event), &got_completed_event);
g_main_loop_run (loop);
- g_assert_true (data.completed);
+ g_assert_true (got_completed_event);
g_main_loop_unref (loop);
g_object_unref (service);
g_object_unref (client);
diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
index bccf6a308..3c248e610 100644
--- a/gio/tests/gsubprocess.c
+++ b/gio/tests/gsubprocess.c
@@ -6,6 +6,8 @@
#include <glib-unix.h>
#include <gio/gunixinputstream.h>
#include <gio/gfiledescriptorbased.h>
+#include <unistd.h>
+#include <fcntl.h>
#endif
/* We write 2^1 + 2^2 ... + 2^10 or 2047 copies of "Hello World!\n"
@@ -1483,6 +1485,35 @@ test_cwd (void)
g_object_unref (launcher);
}
#ifdef G_OS_UNIX
+
+static void
+test_subprocess_launcher_close (void)
+{
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ GSubprocessLauncher *launcher;
+ GSubprocess *proc;
+ GPtrArray *args;
+ int fd;
+ gboolean is_open;
+
+ fd = dup(0);
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+ g_subprocess_launcher_take_fd (launcher, fd, fd);
+ is_open = fcntl (fd, F_GETFD) != -1;
+ g_assert_true (is_open);
+ g_subprocess_launcher_close (launcher);
+ is_open = fcntl (fd, F_GETFD) != -1;
+ g_assert_false (is_open);
+ args = get_test_subprocess_args ("cat", NULL);
+ proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
+ g_ptr_array_free (args, TRUE);
+ g_assert_null (proc);
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CLOSED);
+ g_clear_error (error);
+ g_object_unref (launcher);
+}
+
static void
test_stdout_file (void)
{
@@ -1835,6 +1866,7 @@ main (int argc, char **argv)
g_test_add_func ("/gsubprocess/env/inherit", test_env_inherit);
g_test_add_func ("/gsubprocess/cwd", test_cwd);
#ifdef G_OS_UNIX
+ g_test_add_func ("/gsubprocess/launcher-close", test_subprocess_launcher_close);
g_test_add_func ("/gsubprocess/stdout-file", test_stdout_file);
g_test_add_func ("/gsubprocess/stdout-fd", test_stdout_fd);
g_test_add_func ("/gsubprocess/child-setup", test_child_setup);
diff --git a/gio/tests/gtesttlsbackend.c b/gio/tests/gtesttlsbackend.c
index 346a20dd9..56d155031 100644
--- a/gio/tests/gtesttlsbackend.c
+++ b/gio/tests/gtesttlsbackend.c
@@ -93,6 +93,8 @@ struct _GTestTlsCertificate {
gchar *key_pem;
gchar *cert_pem;
GTlsCertificate *issuer;
+ gchar *pkcs11_uri;
+ gchar *private_key_pkcs11_uri;
};
struct _GTestTlsCertificateClass {
@@ -105,7 +107,9 @@ enum
PROP_CERT_CERTIFICATE_PEM,
PROP_CERT_PRIVATE_KEY,
PROP_CERT_PRIVATE_KEY_PEM,
- PROP_CERT_ISSUER
+ PROP_CERT_ISSUER,
+ PROP_CERT_PKCS11_URI,
+ PROP_CERT_PRIVATE_KEY_PKCS11_URI,
};
static void g_test_tls_certificate_initable_iface_init (GInitableIface *iface);
@@ -143,6 +147,15 @@ g_test_tls_certificate_get_property (GObject *object,
case PROP_CERT_ISSUER:
g_value_set_object (value, cert->issuer);
break;
+ case PROP_CERT_PKCS11_URI:
+ /* This test value simulates a backend that ignores the value
+ because it is unsupported */
+ if (g_strcmp0 (cert->pkcs11_uri, "unsupported") != 0)
+ g_value_set_string (value, cert->pkcs11_uri);
+ break;
+ case PROP_CERT_PRIVATE_KEY_PKCS11_URI:
+ g_value_set_string (value, cert->private_key_pkcs11_uri);
+ break;
default:
g_assert_not_reached ();
break;
@@ -168,6 +181,12 @@ g_test_tls_certificate_set_property (GObject *object,
case PROP_CERT_ISSUER:
cert->issuer = g_value_dup_object (value);
break;
+ case PROP_CERT_PKCS11_URI:
+ cert->pkcs11_uri = g_value_dup_string (value);
+ break;
+ case PROP_CERT_PRIVATE_KEY_PKCS11_URI:
+ cert->private_key_pkcs11_uri = g_value_dup_string (value);
+ break;
case PROP_CERT_CERTIFICATE:
case PROP_CERT_PRIVATE_KEY:
/* ignore */
@@ -185,6 +204,8 @@ g_test_tls_certificate_finalize (GObject *object)
g_free (cert->cert_pem);
g_free (cert->key_pem);
+ g_free (cert->pkcs11_uri);
+ g_free (cert->private_key_pkcs11_uri);
g_clear_object (&cert->issuer);
G_OBJECT_CLASS (g_test_tls_certificate_parent_class)->finalize (object);
@@ -207,6 +228,8 @@ g_test_tls_certificate_class_init (GTestTlsCertificateClass *test_class)
g_object_class_override_property (gobject_class, PROP_CERT_PRIVATE_KEY, "private-key");
g_object_class_override_property (gobject_class, PROP_CERT_PRIVATE_KEY_PEM, "private-key-pem");
g_object_class_override_property (gobject_class, PROP_CERT_ISSUER, "issuer");
+ g_object_class_override_property (gobject_class, PROP_CERT_PKCS11_URI, "pkcs11-uri");
+ g_object_class_override_property (gobject_class, PROP_CERT_PRIVATE_KEY_PKCS11_URI, "private-key-pkcs11-uri");
}
static void
diff --git a/gio/tests/tls-certificate.c b/gio/tests/tls-certificate.c
index 89e3d1421..c0fc80c4b 100644
--- a/gio/tests/tls-certificate.c
+++ b/gio/tests/tls-certificate.c
@@ -398,6 +398,38 @@ list_from_file (const Reference *ref)
g_assert_cmpint (g_list_length (list), ==, 0);
}
+static void
+from_pkcs11_uri (void)
+{
+ GError *error = NULL;
+ GTlsCertificate *cert;
+ gchar *pkcs11_uri = NULL;
+
+ cert = g_tls_certificate_new_from_pkcs11_uris ("pkcs11:model=p11-kit-trust;manufacturer=PKCS%2311%20Kit;serial=1;token=ca-bundle.crt", NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (cert);
+
+ g_object_get (cert, "pkcs11-uri", &pkcs11_uri, NULL);
+ g_assert_cmpstr ("pkcs11:model=p11-kit-trust;manufacturer=PKCS%2311%20Kit;serial=1;token=ca-bundle.crt", ==, pkcs11_uri);
+ g_free (pkcs11_uri);
+
+ g_object_unref (cert);
+}
+
+static void
+from_unsupported_pkcs11_uri (void)
+{
+ GError *error = NULL;
+ GTlsCertificate *cert;
+
+ /* This is a magic value in gtesttlsbackend.c simulating an unsupported backend */
+ cert = g_tls_certificate_new_from_pkcs11_uris ("unsupported", NULL, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+ g_assert_null (cert);
+
+ g_clear_error (&error);
+}
+
int
main (int argc,
char *argv[])
@@ -464,6 +496,10 @@ main (int argc,
&ref, (GTestDataFunc)from_files_pkcs8enc);
g_test_add_data_func ("/tls-certificate/list_from_file",
&ref, (GTestDataFunc)list_from_file);
+ g_test_add_func ("/tls-certificate/pkcs11-uri",
+ from_pkcs11_uri);
+ g_test_add_func ("/tls-certificate/pkcs11-uri-unsupported",
+ from_unsupported_pkcs11_uri);
rtv = g_test_run();
diff --git a/gio/tests/tls-interaction.c b/gio/tests/tls-interaction.c
index 5661e8e0d..4f0737d7e 100644
--- a/gio/tests/tls-interaction.c
+++ b/gio/tests/tls-interaction.c
@@ -174,38 +174,6 @@ test_interaction_ask_password_finish_failure (GTlsInteraction *interaction,
}
-/* Return a copy of @str that is allocated in a silly way, to exercise
- * custom free-functions. The returned pointer points to a copy of @str
- * in a buffer of the form "BEFORE \0 str \0 AFTER". */
-static guchar *
-special_dup (const char *str)
-{
- GString *buf = g_string_new ("BEFORE");
- guchar *ret;
-
- g_string_append_c (buf, '\0');
- g_string_append (buf, str);
- g_string_append_c (buf, '\0');
- g_string_append (buf, "AFTER");
- ret = (guchar *) g_string_free (buf, FALSE);
- return ret + strlen ("BEFORE") + 1;
-}
-
-
-/* Free a copy of @str that was made with special_dup(), after asserting
- * that it has not been corrupted. */
-static void
-special_free (gpointer p)
-{
- gchar *s = p;
- gchar *buf = s - strlen ("BEFORE") - 1;
-
- g_assert_cmpstr (buf, ==, "BEFORE");
- g_assert_cmpstr (s + strlen (s) + 1, ==, "AFTER");
- g_free (buf);
-}
-
-
static GTlsInteractionResult
test_interaction_ask_password_sync_success (GTlsInteraction *interaction,
GTlsPassword *password,
@@ -213,8 +181,6 @@ test_interaction_ask_password_sync_success (GTlsInteraction *interaction,
GError **error)
{
TestInteraction *self;
- const guchar *value;
- gsize len;
g_assert (TEST_IS_INTERACTION (interaction));
self = TEST_INTERACTION (interaction);
@@ -226,27 +192,6 @@ test_interaction_ask_password_sync_success (GTlsInteraction *interaction,
g_assert (error != NULL);
g_assert (*error == NULL);
- /* Exercise different ways to set the value */
- g_tls_password_set_value (password, (const guchar *) "foo", 4);
- len = 0;
- value = g_tls_password_get_value (password, &len);
- g_assert_cmpmem (value, len, "foo", 4);
-
- g_tls_password_set_value (password, (const guchar *) "bar", -1);
- len = 0;
- value = g_tls_password_get_value (password, &len);
- g_assert_cmpmem (value, len, "bar", 3);
-
- g_tls_password_set_value_full (password, special_dup ("baa"), 4, special_free);
- len = 0;
- value = g_tls_password_get_value (password, &len);
- g_assert_cmpmem (value, len, "baa", 4);
-
- g_tls_password_set_value_full (password, special_dup ("baz"), -1, special_free);
- len = 0;
- value = g_tls_password_get_value (password, &len);
- g_assert_cmpmem (value, len, "baz", 3);
-
/* Don't do this in real life. Include a null terminator for testing */
g_tls_password_set_value (password, (const guchar *)"the password", 13);
return G_TLS_INTERACTION_HANDLED;
diff --git a/gio/win32/gwin32fsmonitorutils.c b/gio/win32/gwin32fsmonitorutils.c
index b47124634..e6b7d1dcd 100644
--- a/gio/win32/gwin32fsmonitorutils.c
+++ b/gio/win32/gwin32fsmonitorutils.c
@@ -345,7 +345,7 @@ g_win32_fs_monitor_init (GWin32FSMonitorPrivate *monitor,
monitor->file_attribs = INVALID_FILE_ATTRIBUTES;
monitor->pfni_prev = NULL;
monitor->hDirectory = CreateFileW (wdirname_with_long_prefix != NULL ? wdirname_with_long_prefix : monitor->wfullpath_with_long_prefix,
- FILE_LIST_DIRECTORY,
+ FILE_GENERIC_READ | FILE_GENERIC_WRITE,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
diff --git a/gio/win32/gwinhttpfile.c b/gio/win32/gwinhttpfile.c
index e0340e247..509cdeb33 100644
--- a/gio/win32/gwinhttpfile.c
+++ b/gio/win32/gwinhttpfile.c
@@ -30,7 +30,6 @@
#include "gio/gfileattribute.h"
#include "gio/gfileinfo.h"
#include "gio/gfileinfo-priv.h"
-#include "gstrfuncsprivate.h"
#include "gwinhttpfile.h"
#include "gwinhttpfileinputstream.h"
#include "gwinhttpfileoutputstream.h"
@@ -410,10 +409,10 @@ g_winhttp_file_resolve_relative_path (GFile *file,
child = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
child->vfs = winhttp_file->vfs;
child->url = winhttp_file->url;
- child->url.lpszScheme = g_memdup2 (winhttp_file->url.lpszScheme, ((gsize) winhttp_file->url.dwSchemeLength + 1) * 2);
- child->url.lpszHostName = g_memdup2 (winhttp_file->url.lpszHostName, ((gsize) winhttp_file->url.dwHostNameLength + 1) * 2);
- child->url.lpszUserName = g_memdup2 (winhttp_file->url.lpszUserName, ((gsize) winhttp_file->url.dwUserNameLength + 1) * 2);
- child->url.lpszPassword = g_memdup2 (winhttp_file->url.lpszPassword, ((gsize) winhttp_file->url.dwPasswordLength + 1) * 2);
+ child->url.lpszScheme = g_memdup (winhttp_file->url.lpszScheme, (winhttp_file->url.dwSchemeLength+1)*2);
+ child->url.lpszHostName = g_memdup (winhttp_file->url.lpszHostName, (winhttp_file->url.dwHostNameLength+1)*2);
+ child->url.lpszUserName = g_memdup (winhttp_file->url.lpszUserName, (winhttp_file->url.dwUserNameLength+1)*2);
+ child->url.lpszPassword = g_memdup (winhttp_file->url.lpszPassword, (winhttp_file->url.dwPasswordLength+1)*2);
child->url.lpszUrlPath = wnew_path;
child->url.dwUrlPathLength = wcslen (wnew_path);
child->url.lpszExtraInfo = NULL;