summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
authorHyunjee Kim <hj0426.kim@samsung.com>2019-12-03 10:43:46 +0900
committerHyunjee Kim <hj0426.kim@samsung.com>2019-12-03 10:43:46 +0900
commit7547eb76bf27ff88b990f39d1dc706d6ce605da1 (patch)
tree9c842bd0d9d13ed0c930c20bb00744877a31ad36 /gio
parent99d572044d3b94498607a05ad7aaccf372e8cbff (diff)
downloadglib-7547eb76bf27ff88b990f39d1dc706d6ce605da1.tar.gz
glib-7547eb76bf27ff88b990f39d1dc706d6ce605da1.tar.bz2
glib-7547eb76bf27ff88b990f39d1dc706d6ce605da1.zip
Imported Upstream version 2.59.2
Diffstat (limited to 'gio')
-rw-r--r--gio/gappinfo.c324
-rw-r--r--gio/gappinfo.h25
-rw-r--r--gio/gdbusauth.c2
-rw-r--r--gio/gdbusmessage.c7
-rw-r--r--gio/gdesktopappinfo.c160
-rw-r--r--gio/gdocumentportal.c82
-rw-r--r--gio/gdocumentportal.h3
-rw-r--r--gio/gfile.c125
-rw-r--r--gio/gfile.h11
-rw-r--r--gio/gio-tool-open.c120
-rw-r--r--gio/gkeyfilesettingsbackend.c4
-rw-r--r--gio/glocalfile.c5
-rw-r--r--gio/gopenuriportal.c7
-rw-r--r--gio/gsettings.c8
-rw-r--r--gio/gsettingsbackend.c4
-rw-r--r--gio/gsettingsschema.c2
-rw-r--r--gio/gsocketclient.c74
-rw-r--r--gio/gtrashportal.c123
-rw-r--r--gio/gtrashportal.h31
-rw-r--r--gio/meson.build6
-rw-r--r--gio/org.freedesktop.portal.Trash.xml48
-rw-r--r--gio/tests/gdbus-message.c72
-rw-r--r--gio/tests/gsettings.c1
-rw-r--r--gio/tests/gsocketclient-slow.c55
-rw-r--r--gio/tests/trash.c42
25 files changed, 1061 insertions, 280 deletions
diff --git a/gio/gappinfo.c b/gio/gappinfo.c
index 47cd73366..84e667575 100644
--- a/gio/gappinfo.c
+++ b/gio/gappinfo.c
@@ -24,6 +24,7 @@
#include "gappinfoprivate.h"
#include "gcontextspecificgroup.h"
#include "gtask.h"
+#include "gcancellable.h"
#include "glibintl.h"
#include <gioerror.h>
@@ -662,6 +663,86 @@ g_app_info_launch_uris (GAppInfo *appinfo,
return (* iface->launch_uris) (appinfo, uris, launch_context, error);
}
+/**
+ * g_app_info_launch_uris_async:
+ * @appinfo: a #GAppInfo
+ * @uris: (nullable) (element-type utf8): a #GList containing URIs to launch.
+ * @context: (nullable): a #GAppLaunchContext or %NULL
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
+ * @user_data: (nullable): data to pass to @callback
+ *
+ * Async version of g_app_info_launch_uris().
+ *
+ * The @callback is invoked immediately after the application launch, but it
+ * waits for activation in case of D-Bus–activated applications and also provides
+ * extended error information for sandboxed applications, see notes for
+ * g_app_info_launch_default_for_uri_async().
+ *
+ * Since: 2.60
+ **/
+void
+g_app_info_launch_uris_async (GAppInfo *appinfo,
+ GList *uris,
+ GAppLaunchContext *context,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GAppInfoIface *iface;
+
+ g_return_if_fail (G_IS_APP_INFO (appinfo));
+ g_return_if_fail (context == NULL || G_IS_APP_LAUNCH_CONTEXT (context));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ iface = G_APP_INFO_GET_IFACE (appinfo);
+ if (iface->launch_uris_async == NULL)
+ {
+ GTask *task;
+
+ task = g_task_new (appinfo, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_app_info_launch_uris_async);
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Operation not supported for the current backend.");
+ g_object_unref (task);
+
+ return;
+ }
+
+ (* iface->launch_uris_async) (appinfo, uris, context, cancellable, callback, user_data);
+}
+
+/**
+ * g_app_info_launch_uris_finish:
+ * @appinfo: a #GAppInfo
+ * @result: a #GAsyncResult
+ * @error: (nullable): a #GError
+ *
+ * Finishes a g_app_info_launch_uris_async() operation.
+ *
+ * Returns: %TRUE on successful launch, %FALSE otherwise.
+ *
+ * Since: 2.60
+ */
+gboolean
+g_app_info_launch_uris_finish (GAppInfo *appinfo,
+ GAsyncResult *result,
+ GError **error)
+{
+ GAppInfoIface *iface;
+
+ g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+ iface = G_APP_INFO_GET_IFACE (appinfo);
+ if (iface->launch_uris_finish == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Operation not supported for the current backend.");
+ return FALSE;
+ }
+
+ return (* iface->launch_uris_finish) (appinfo, result, error);
+}
/**
* g_app_info_should_show:
@@ -684,15 +765,31 @@ g_app_info_should_show (GAppInfo *appinfo)
return (* iface->should_show) (appinfo);
}
-static gboolean
-launch_default_for_uri (const char *uri,
- GAppLaunchContext *context,
- GError **error)
+/**
+ * g_app_info_launch_default_for_uri:
+ * @uri: the uri to show
+ * @context: (nullable): an optional #GAppLaunchContext
+ * @error: (nullable): return location for an error, or %NULL
+ *
+ * Utility function that launches the default application
+ * registered to handle the specified uri. Synchronous I/O
+ * is done on the uri to detect the type of the file if
+ * required.
+ *
+ * The D-Bus–activated applications don't have to be started if your application
+ * terminates too soon after this function. To prevent this, use
+ * g_app_info_launch_default_for_uri() instead.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ **/
+gboolean
+g_app_info_launch_default_for_uri (const char *uri,
+ GAppLaunchContext *launch_context,
+ GError **error)
{
char *uri_scheme;
GAppInfo *app_info = NULL;
- GList l;
- gboolean res;
+ gboolean res = FALSE;
/* g_file_query_default_handler() calls
* g_app_info_get_default_for_uri_scheme() too, but we have to do it
@@ -712,56 +809,148 @@ launch_default_for_uri (const char *uri,
g_object_unref (file);
}
- if (app_info == NULL)
- return FALSE;
+ if (app_info)
+ {
+ GList l;
- l.data = (char *)uri;
- l.next = l.prev = NULL;
- res = g_app_info_launch_uris (app_info, &l, context, error);
+ l.data = (char *)uri;
+ l.next = l.prev = NULL;
+ res = g_app_info_launch_uris (app_info, &l, launch_context, error);
+ g_object_unref (app_info);
+ }
- g_object_unref (app_info);
+#ifdef G_OS_UNIX
+ if (!res && glib_should_use_portal ())
+ {
+ const char *parent_window = NULL;
+
+ /* Reset any error previously set by launch_default_for_uri */
+ g_clear_error (error);
+
+ if (launch_context && launch_context->priv->envp)
+ parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
+
+ return g_openuri_portal_open_uri (uri, parent_window, error);
+ }
+#endif
return res;
}
-/**
- * g_app_info_launch_default_for_uri:
- * @uri: the uri to show
- * @context: (nullable): an optional #GAppLaunchContext
- * @error: (nullable): return location for an error, or %NULL
- *
- * Utility function that launches the default application
- * registered to handle the specified uri. Synchronous I/O
- * is done on the uri to detect the type of the file if
- * required.
- *
- * Returns: %TRUE on success, %FALSE on error.
- **/
-gboolean
-g_app_info_launch_default_for_uri (const char *uri,
- GAppLaunchContext *launch_context,
- GError **error)
+typedef struct
+{
+ gchar *uri;
+ GAppLaunchContext *context;
+} LaunchDefaultForUriData;
+
+static void
+launch_default_for_uri_data_free (LaunchDefaultForUriData *data)
+{
+ g_free (data->uri);
+ g_clear_object (&data->context);
+ g_free (data);
+}
+
+#ifdef G_OS_UNIX
+static void
+launch_default_for_uri_portal_open_uri_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- if (launch_default_for_uri (uri, launch_context, error))
- return TRUE;
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+
+ if (g_openuri_portal_open_uri_finish (result, &error))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+}
+#endif
+static void
+launch_default_for_uri_portal_open_uri (GTask *task, GError *error)
+{
#ifdef G_OS_UNIX
+ LaunchDefaultForUriData *data = g_task_get_task_data (task);
+ GCancellable *cancellable = g_task_get_cancellable (task);
+
if (glib_should_use_portal ())
{
const char *parent_window = NULL;
/* Reset any error previously set by launch_default_for_uri */
- g_clear_error (error);
+ g_error_free (error);
- if (launch_context && launch_context->priv->envp)
- parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
-
- return g_openuri_portal_open_uri (uri, parent_window, error);
+ if (data->context && data->context->priv->envp)
+ parent_window = g_environ_getenv (data->context->priv->envp,
+ "PARENT_WINDOW_ID");
+ g_openuri_portal_open_uri_async (data->uri,
+ parent_window,
+ cancellable,
+ launch_default_for_uri_portal_open_uri_cb,
+ g_steal_pointer (&task));
+ return;
}
#endif
- return FALSE;
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+}
+
+static void
+launch_default_for_uri_launch_uris_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GAppInfo *app_info = G_APP_INFO (object);
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+
+ if (g_app_info_launch_uris_finish (app_info, result, &error))
+ {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ }
+ else
+ launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
+}
+
+static void
+launch_default_for_uri_launch_uris (GTask *task,
+ GAppInfo *app_info)
+{
+ GCancellable *cancellable = g_task_get_cancellable (task);
+ GList l;
+ LaunchDefaultForUriData *data = g_task_get_task_data (task);
+
+ l.data = (char *)data->uri;
+ l.next = l.prev = NULL;
+ g_app_info_launch_uris_async (app_info,
+ &l,
+ data->context,
+ cancellable,
+ launch_default_for_uri_launch_uris_cb,
+ g_steal_pointer (&task));
+ g_object_unref (app_info);
+}
+
+static void
+launch_default_for_uri_default_handler_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (object);
+ GTask *task = G_TASK (user_data);
+ GAppInfo *app_info = NULL;
+ GError *error = NULL;
+
+ app_info = g_file_query_default_handler_finish (file, result, &error);
+ if (app_info)
+ launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
+ else
+ launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
}
/**
@@ -779,6 +968,10 @@ g_app_info_launch_default_for_uri (const char *uri,
* sandboxed and the portal may present an application chooser
* dialog to the user.
*
+ * This is also useful if you want to be sure that the D-Bus–activated
+ * applications are really started before termination and if you are interested
+ * in receiving error information from their activation.
+ *
* Since: 2.50
*/
void
@@ -788,32 +981,45 @@ g_app_info_launch_default_for_uri_async (const char *uri,
GAsyncReadyCallback callback,
gpointer user_data)
{
- gboolean res;
- GError *error = NULL;
GTask *task;
+ char *uri_scheme;
+ GAppInfo *app_info = NULL;
+ LaunchDefaultForUriData *data;
- res = launch_default_for_uri (uri, context, &error);
+ g_return_if_fail (uri != NULL);
-#ifdef G_OS_UNIX
- if (!res && glib_should_use_portal ())
- {
- const char *parent_window = NULL;
+ task = g_task_new (NULL, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_app_info_launch_default_for_uri_async);
- if (context && context->priv->envp)
- parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID");
+ data = g_new (LaunchDefaultForUriData, 1);
+ data->uri = g_strdup (uri);
+ data->context = (context != NULL) ? g_object_ref (context) : NULL;
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_default_for_uri_data_free);
- g_openuri_portal_open_uri_async (uri, parent_window, cancellable, callback, user_data);
- return;
- }
-#endif
+ /* g_file_query_default_handler_async() calls
+ * g_app_info_get_default_for_uri_scheme() too, but we have to do it
+ * here anyway in case GFile can't parse @uri correctly.
+ */
+ uri_scheme = g_uri_parse_scheme (uri);
+ if (uri_scheme && uri_scheme[0] != '\0')
+ /* FIXME: The following still uses blocking calls. */
+ app_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
+ g_free (uri_scheme);
- task = g_task_new (context, cancellable, callback, user_data);
- if (!res)
- g_task_return_error (task, error);
- else
- g_task_return_boolean (task, TRUE);
+ if (!app_info)
+ {
+ GFile *file;
- g_object_unref (task);
+ file = g_file_new_for_uri (uri);
+ g_file_query_default_handler_async (file,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ launch_default_for_uri_default_handler_cb,
+ g_steal_pointer (&task));
+ g_object_unref (file);
+ }
+ else
+ launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
}
/**
@@ -831,11 +1037,9 @@ gboolean
g_app_info_launch_default_for_uri_finish (GAsyncResult *result,
GError **error)
{
-#ifdef G_OS_UNIX
- return g_openuri_portal_open_uri_finish (result, error);
-#else
+ g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
+
return g_task_propagate_boolean (G_TASK (result), error);
-#endif
}
/**
diff --git a/gio/gappinfo.h b/gio/gappinfo.h
index 4889be923..d26d048a5 100644
--- a/gio/gappinfo.h
+++ b/gio/gappinfo.h
@@ -78,7 +78,9 @@ typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate;
* @get_display_name: Gets the display name for the #GAppInfo. Since 2.24
* @set_as_last_used_for_type: Sets the application as the last used. See g_app_info_set_as_last_used_for_type().
* @get_supported_types: Retrieves the list of content types that @app_info claims to support.
- *
+ * @launch_uris_async: Asynchronously launches an application with a list of URIs. (Since: 2.60)
+ * @launch_uris_finish: Finishes an operation started with @launch_uris_async. (Since: 2.60)
+
* Application Information interface, for operating system portability.
*/
typedef struct _GAppInfoIface GAppInfoIface;
@@ -131,6 +133,15 @@ struct _GAppInfoIface
const char *content_type,
GError **error);
const char ** (* get_supported_types) (GAppInfo *appinfo);
+ void (* launch_uris_async) (GAppInfo *appinfo,
+ GList *uris,
+ GAppLaunchContext *context,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* launch_uris_finish) (GAppInfo *appinfo,
+ GAsyncResult *result,
+ GError **error);
};
GLIB_AVAILABLE_IN_ALL
@@ -173,6 +184,18 @@ gboolean g_app_info_launch_uris (GAppInfo *appin
GList *uris,
GAppLaunchContext *context,
GError **error);
+GLIB_AVAILABLE_IN_2_60
+void g_app_info_launch_uris_async (GAppInfo *appinfo,
+ GList *uris,
+ GAppLaunchContext *context,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_60
+gboolean g_app_info_launch_uris_finish (GAppInfo *appinfo,
+ GAsyncResult *result,
+ GError **error);
+
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_should_show (GAppInfo *appinfo);
diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c
index 1f8ea8057..752ec23fc 100644
--- a/gio/gdbusauth.c
+++ b/gio/gdbusauth.c
@@ -1272,9 +1272,9 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
&line_length,
cancellable,
error);
- debug_print ("SERVER: WaitingForBegin, read '%s'", line);
if (line == NULL)
goto out;
+ debug_print ("SERVER: WaitingForBegin, read '%s'", line);
if (g_strcmp0 (line, "BEGIN") == 0)
{
/* YAY, done! */
diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
index b9f32e921..3a1a1f9e9 100644
--- a/gio/gdbusmessage.c
+++ b/gio/gdbusmessage.c
@@ -1984,7 +1984,7 @@ g_dbus_message_bytes_needed (guchar *blob,
"Unable to determine message blob length - given blob is malformed");
}
- if (ret > (2<<27))
+ if (ret > (1<<27))
{
g_set_error (error,
G_IO_ERROR,
@@ -2731,7 +2731,6 @@ g_dbus_message_to_blob (GDBusMessage *message,
if (message->body != NULL)
{
gchar *tupled_signature_str;
- tupled_signature_str = g_strdup_printf ("(%s)", signature_str);
if (signature == NULL)
{
g_set_error (error,
@@ -2739,10 +2738,10 @@ g_dbus_message_to_blob (GDBusMessage *message,
G_IO_ERROR_INVALID_ARGUMENT,
_("Message body has signature “%s” but there is no signature header"),
signature_str);
- g_free (tupled_signature_str);
goto out;
}
- else if (g_strcmp0 (tupled_signature_str, g_variant_get_type_string (message->body)) != 0)
+ tupled_signature_str = g_strdup_printf ("(%s)", signature_str);
+ if (g_strcmp0 (tupled_signature_str, g_variant_get_type_string (message->body)) != 0)
{
g_set_error (error,
G_IO_ERROR,
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index 9ebfce4f0..7d7425ea9 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -2870,7 +2870,10 @@ static void
launch_uris_with_dbus (GDesktopAppInfo *info,
GDBusConnection *session_bus,
GList *uris,
- GAppLaunchContext *launch_context)
+ GAppLaunchContext *launch_context,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GVariantBuilder builder;
gchar *object_path;
@@ -2889,14 +2892,11 @@ launch_uris_with_dbus (GDesktopAppInfo *info,
g_variant_builder_add_value (&builder, g_desktop_app_info_make_platform_data (info, uris, launch_context));
- /* This is non-blocking API. Similar to launching via fork()/exec()
- * we don't wait around to see if the program crashed during startup.
- * This is what startup-notification's job is...
- */
object_path = object_path_from_appid (info->app_id);
g_dbus_connection_call (session_bus, info->app_id, object_path, "org.freedesktop.Application",
uris ? "Open" : "Activate", g_variant_builder_end (&builder),
- NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+ NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+ cancellable, callback, user_data);
g_free (object_path);
}
@@ -2904,7 +2904,10 @@ static gboolean
g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo *info,
GDBusConnection *session_bus,
GList *uris,
- GAppLaunchContext *launch_context)
+ GAppLaunchContext *launch_context,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GList *ruris = uris;
char *app_id = NULL;
@@ -2921,7 +2924,8 @@ g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo *info,
}
#endif
- launch_uris_with_dbus (info, session_bus, ruris, launch_context);
+ launch_uris_with_dbus (info, session_bus, ruris, launch_context,
+ cancellable, callback, user_data);
if (ruris != uris)
g_list_free_full (ruris, g_free);
@@ -2952,7 +2956,12 @@ g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
if (session_bus && info->app_id)
- g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context);
+ /* This is non-blocking API. Similar to launching via fork()/exec()
+ * we don't wait around to see if the program crashed during startup.
+ * This is what startup-notification's job is...
+ */
+ g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context,
+ NULL, NULL, NULL);
else
success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
spawn_flags, user_setup, user_setup_data,
@@ -2986,6 +2995,137 @@ g_desktop_app_info_launch_uris (GAppInfo *appinfo,
error);
}
+typedef struct
+{
+ GAppInfo *appinfo;
+ GList *uris;
+ GAppLaunchContext *context;
+} LaunchUrisData;
+
+static void
+launch_uris_data_free (LaunchUrisData *data)
+{
+ g_clear_object (&data->context);
+ g_list_free_full (data->uris, g_free);
+ g_free (data);
+}
+
+static void
+launch_uris_with_dbus_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+
+ g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
+ if (error != NULL)
+ {
+ g_dbus_error_strip_remote_error (error);
+ g_task_return_error (task, g_steal_pointer (&error));
+ }
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+launch_uris_flush_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+
+ g_dbus_connection_flush_finish (G_DBUS_CONNECTION (object), result, NULL);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+launch_uris_bus_get_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (g_task_get_source_object (task));
+ LaunchUrisData *data = g_task_get_task_data (task);
+ GCancellable *cancellable = g_task_get_cancellable (task);
+ GDBusConnection *session_bus;
+ GError *error = NULL;
+
+ session_bus = g_bus_get_finish (result, NULL);
+
+ if (session_bus && info->app_id)
+ {
+ /* FIXME: The g_document_portal_add_documents() function, which is called
+ * from the g_desktop_app_info_launch_uris_with_dbus() function, still
+ * uses blocking calls.
+ */
+ g_desktop_app_info_launch_uris_with_dbus (info, session_bus,
+ data->uris, data->context,
+ cancellable,
+ launch_uris_with_dbus_cb,
+ g_steal_pointer (&task));
+ }
+ else
+ {
+ /* FIXME: The D-Bus message from the notify_desktop_launch() function
+ * can be still lost even if flush is called later. See:
+ * https://gitlab.freedesktop.org/dbus/dbus/issues/72
+ */
+ g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec,
+ data->uris, data->context,
+ _SPAWN_FLAGS_DEFAULT, NULL,
+ NULL, NULL, NULL, -1, -1, -1,
+ &error);
+ if (error != NULL)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ }
+ else
+ g_dbus_connection_flush (session_bus,
+ cancellable,
+ launch_uris_flush_cb,
+ g_steal_pointer (&task));
+ }
+
+ g_clear_object (&session_bus);
+}
+
+static void
+g_desktop_app_info_launch_uris_async (GAppInfo *appinfo,
+ GList *uris,
+ GAppLaunchContext *context,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ LaunchUrisData *data;
+
+ task = g_task_new (appinfo, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_desktop_app_info_launch_uris_async);
+
+ data = g_new0 (LaunchUrisData, 1);
+ data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
+ data->context = (context != NULL) ? g_object_ref (context) : NULL;
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
+
+ g_bus_get (G_BUS_TYPE_SESSION, cancellable, launch_uris_bus_get_cb, task);
+}
+
+static gboolean
+g_desktop_app_info_launch_uris_finish (GAppInfo *appinfo,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, appinfo), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
static gboolean
g_desktop_app_info_supports_uris (GAppInfo *appinfo)
{
@@ -3876,6 +4016,8 @@ g_desktop_app_info_iface_init (GAppInfoIface *iface)
iface->supports_uris = g_desktop_app_info_supports_uris;
iface->supports_files = g_desktop_app_info_supports_files;
iface->launch_uris = g_desktop_app_info_launch_uris;
+ iface->launch_uris_async = g_desktop_app_info_launch_uris_async;
+ iface->launch_uris_finish = g_desktop_app_info_launch_uris_finish;
iface->should_show = g_desktop_app_info_should_show;
iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
diff --git a/gio/gdocumentportal.c b/gio/gdocumentportal.c
index 56378b1e6..154cc74df 100644
--- a/gio/gdocumentportal.c
+++ b/gio/gdocumentportal.c
@@ -31,9 +31,6 @@
#include "gunixfdlist.h"
#endif
-#ifndef O_PATH
-#define O_PATH 0
-#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#else
@@ -92,76 +89,6 @@ init_document_portal (void)
return (documents != NULL && documents_mountpoint != NULL);
}
-char *
-g_document_portal_add_document (GFile *file,
- GError **error)
-{
- char *doc_path, *basename;
- char *doc_id = NULL;
- char *doc_uri = NULL;
- char *path = NULL;
- GUnixFDList *fd_list = NULL;
- int fd, fd_in, errsv;
- gboolean ret;
-
- if (!init_document_portal ())
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
- "Document portal is not available");
- goto out;
- }
-
- path = g_file_get_path (file);
- fd = g_open (path, O_PATH | O_CLOEXEC);
- errsv = errno;
-
- if (fd == -1)
- {
- g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
- "Failed to open %s", path);
- goto out;
- }
-
-#ifndef HAVE_O_CLOEXEC
- fcntl (fd, F_SETFD, FD_CLOEXEC);
-#endif
-
- fd_list = g_unix_fd_list_new ();
- fd_in = g_unix_fd_list_append (fd_list, fd, error);
- g_close (fd, NULL);
-
- if (fd_in == -1)
- goto out;
-
- ret = gxdp_documents_call_add_sync (documents,
- g_variant_new_handle (fd_in),
- TRUE,
- TRUE,
- fd_list,
- &doc_id,
- NULL,
- NULL,
- error);
-
- if (!ret)
- goto out;
-
- basename = g_path_get_basename (path);
- doc_path = g_build_filename (documents_mountpoint, doc_id, basename, NULL);
- g_free (basename);
-
- doc_uri = g_filename_to_uri (doc_path, NULL, NULL);
- g_free (doc_path);
-
- out:
- if (fd_list)
- g_object_unref (fd_list);
- g_free (path);
- g_free (doc_id);
-
- return doc_uri;
-}
-
/* Flags accepted by org.freedesktop.portal.Documents.AddFull */
enum {
XDP_ADD_FLAGS_REUSE_EXISTING = (1 << 0),
@@ -210,7 +137,14 @@ g_document_portal_add_documents (GList *uris,
{
int fd;
- fd = g_open (path, O_CLOEXEC | O_PATH);
+ fd = g_open (path, O_CLOEXEC | O_RDWR);
+ if (fd == -1 && (errno == EACCES || errno == EISDIR))
+ {
+ /* If we don't have write access, fall back to read-only,
+ * and stop requesting the write permission */
+ fd = g_open (path, O_CLOEXEC | O_RDONLY);
+ permissions[1] = NULL;
+ }
if (fd >= 0)
{
#ifndef HAVE_O_CLOEXEC
diff --git a/gio/gdocumentportal.h b/gio/gdocumentportal.h
index adb1b974e..82d32640e 100644
--- a/gio/gdocumentportal.h
+++ b/gio/gdocumentportal.h
@@ -23,9 +23,6 @@
G_BEGIN_DECLS
-char * g_document_portal_add_document (GFile *file,
- GError **error);
-
GList * g_document_portal_add_documents (GList *uris,
const char *app_id,
GError **error);
diff --git a/gio/gfile.c b/gio/gfile.c
index a5709a4cc..1cc69166a 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -6851,6 +6851,8 @@ g_file_query_default_handler (GFile *file,
if (appinfo != NULL)
return appinfo;
}
+ else
+ g_free (uri_scheme);
info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
@@ -6883,6 +6885,129 @@ g_file_query_default_handler (GFile *file,
return NULL;
}
+static void
+query_default_handler_query_info_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (object);
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+ GFileInfo *info;
+ const char *content_type;
+ GAppInfo *appinfo = NULL;
+
+ info = g_file_query_info_finish (file, result, &error);
+ if (info == NULL)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ content_type = g_file_info_get_content_type (info);
+ if (content_type)
+ {
+ char *path;
+
+ /* Don't use is_native(), as we want to support fuse paths if available */
+ path = g_file_get_path (file);
+
+ /* FIXME: The following still uses blocking calls. */
+ appinfo = g_app_info_get_default_for_type (content_type,
+ path == NULL);
+ g_free (path);
+ }
+
+ g_object_unref (info);
+
+ if (appinfo != NULL)
+ g_task_return_pointer (task, g_steal_pointer (&appinfo), g_object_unref);
+ else
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("No application is registered as handling this file"));
+ g_object_unref (task);
+}
+
+/**
+ * g_file_query_default_handler_async:
+ * @file: a #GFile to open
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
+ * @user_data: (nullable): data to pass to @callback
+ *
+ * Async version of g_file_query_default_handler().
+ *
+ * Since: 2.60
+ */
+void
+g_file_query_default_handler_async (GFile *file,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ char *uri_scheme;
+
+ task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_query_default_handler_async);
+
+ uri_scheme = g_file_get_uri_scheme (file);
+ if (uri_scheme && uri_scheme[0] != '\0')
+ {
+ GAppInfo *appinfo;
+
+ /* FIXME: The following still uses blocking calls. */
+ appinfo = g_app_info_get_default_for_uri_scheme (uri_scheme);
+ g_free (uri_scheme);
+
+ if (appinfo != NULL)
+ {
+ g_task_return_pointer (task, g_steal_pointer (&appinfo), g_object_unref);
+ g_object_unref (task);
+ return;
+ }
+ }
+ else
+ g_free (uri_scheme);
+
+ g_file_query_info_async (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0,
+ io_priority,
+ cancellable,
+ query_default_handler_query_info_cb,
+ g_steal_pointer (&task));
+}
+
+/**
+ * g_file_query_default_handler_finish:
+ * @file: a #GFile to open
+ * @result: a #GAsyncResult
+ * @error: (nullable): a #GError
+ *
+ * Finishes a g_file_query_default_handler_async() operation.
+ *
+ * Returns: (transfer full): a #GAppInfo if the handle was found,
+ * %NULL if there were errors.
+ * When you are done with it, release it with g_object_unref()
+ *
+ * Since: 2.60
+ */
+GAppInfo *
+g_file_query_default_handler_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (g_task_is_valid (result, file), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
#define GET_CONTENT_BLOCK_SIZE 8192
/**
diff --git a/gio/gfile.h b/gio/gfile.h
index 4aff644c3..6e25b0de0 100644
--- a/gio/gfile.h
+++ b/gio/gfile.h
@@ -1183,6 +1183,17 @@ GLIB_AVAILABLE_IN_ALL
GAppInfo *g_file_query_default_handler (GFile *file,
GCancellable *cancellable,
GError **error);
+GLIB_AVAILABLE_IN_2_60
+void g_file_query_default_handler_async (GFile *file,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_60
+GAppInfo *g_file_query_default_handler_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error);
+
GLIB_AVAILABLE_IN_ALL
gboolean g_file_load_contents (GFile *file,
GCancellable *cancellable,
diff --git a/gio/gio-tool-open.c b/gio/gio-tool-open.c
index 73863c7c5..ac6764a97 100644
--- a/gio/gio-tool-open.c
+++ b/gio/gio-tool-open.c
@@ -29,73 +29,32 @@
#include "gio-tool.h"
+static int n_outstanding = 0;
+static gboolean success = TRUE;
static const GOptionEntry entries[] = {
{ NULL }
};
-#if defined(G_OS_UNIX) && !defined(HAVE_COCOA)
-static gboolean
-get_bus_name_and_path_from_uri (const char *uri,
- char **bus_name_out,
- char **object_path_out)
+static void
+launch_default_for_uri_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GAppInfo *app_info = NULL;
- char *bus_name = NULL;
- char *object_path = NULL;
- char *uri_scheme;
- const char *filename;
- char *basename = NULL;
- char *p;
- gboolean got_name = FALSE;
-
- uri_scheme = g_uri_parse_scheme (uri);
- if (uri_scheme && uri_scheme[0] != '\0')
- app_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
- g_free (uri_scheme);
-
- if (app_info == NULL)
- {
- GFile *file;
+ GError *error = NULL;
+ gchar *uri = user_data;
- file = g_file_new_for_uri (uri);
- app_info = g_file_query_default_handler (file, NULL, NULL);
- g_object_unref (file);
+ if (!g_app_info_launch_default_for_uri_finish (res, &error))
+ {
+ print_error ("%s: %s", uri, error->message);
+ g_clear_error (&error);
+ success = FALSE;
}
- if (app_info == NULL || !G_IS_DESKTOP_APP_INFO (app_info) ||
- !g_desktop_app_info_get_boolean (G_DESKTOP_APP_INFO (app_info), "DBusActivatable"))
- goto out;
-
- filename = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (app_info));
- if (filename == NULL)
- goto out;
-
- basename = g_path_get_basename (filename);
- if (!g_str_has_suffix (basename, ".desktop"))
- goto out;
-
- basename[strlen (basename) - strlen (".desktop")] = '\0';
- if (!g_dbus_is_name (basename))
- goto out;
-
- bus_name = g_strdup (basename);
- object_path = g_strdup_printf ("/%s", bus_name);
- for (p = object_path; *p != '\0'; p++)
- if (*p == '.')
- *p = '/';
+ n_outstanding--;
- *bus_name_out = g_steal_pointer (&bus_name);
- *object_path_out = g_steal_pointer (&object_path);
- got_name = TRUE;
-
-out:
- g_clear_object (&app_info);
- g_clear_pointer (&basename, g_free);
-
- return got_name;
+ g_free (uri);
}
-#endif
int
handle_open (int argc, char *argv[], gboolean do_help)
@@ -104,8 +63,6 @@ handle_open (int argc, char *argv[], gboolean do_help)
gchar *param;
GError *error = NULL;
int i;
- gboolean success;
- gboolean res;
g_set_prgname ("gio open");
@@ -143,7 +100,6 @@ handle_open (int argc, char *argv[], gboolean do_help)
g_option_context_free (context);
- success = TRUE;
for (i = 1; i < argc; i++)
{
char *uri = NULL;
@@ -162,47 +118,23 @@ handle_open (int argc, char *argv[], gboolean do_help)
uri = g_file_get_uri (file);
g_object_unref (file);
}
+ else
+ uri = g_strdup (argv[i]);
g_free (uri_scheme);
- res = g_app_info_launch_default_for_uri (uri ? uri : argv[i], NULL, &error);
- if (!res)
- {
- print_error ("%s: %s", uri ? uri : argv[i], error->message);
- g_clear_error (&error);
- success = FALSE;
- }
+ g_app_info_launch_default_for_uri_async (uri,
+ NULL,
+ NULL,
+ launch_default_for_uri_cb,
+ g_strdup (uri));
-#if defined(G_OS_UNIX) && !defined(HAVE_COCOA)
- /* FIXME: This chunk of madness is a workaround for a dbus-daemon bug.
- * See https://bugzilla.gnome.org/show_bug.cgi?id=780296
- */
- if (res)
- {
- char *bus_name = NULL;
- char *object_path = NULL;
-
- if (get_bus_name_and_path_from_uri (uri ? uri : argv[i], &bus_name, &object_path))
- {
- GDBusConnection *connection;
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
-
- if (connection)
- g_dbus_connection_call_sync (connection,
- bus_name,
- object_path,
- "org.freedesktop.DBus.Peer",
- "Ping",
- NULL, NULL,
- G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
- g_clear_object (&connection);
- g_free (bus_name);
- g_free (object_path);
- }
- }
-#endif
+ n_outstanding++;
g_free (uri);
}
+ while (n_outstanding > 0)
+ g_main_context_iteration (NULL, TRUE);
+
return success ? 0 : 2;
}
diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index d5796b706..5ea632305 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -225,6 +225,10 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb,
if (str)
{
return_value = g_variant_parse (type, str, NULL, NULL, NULL);
+
+ /* As a special case, support values of type %G_VARIANT_TYPE_STRING
+ * not being quoted, since users keep forgetting to do it and then
+ * getting confused. */
if (return_value == NULL &&
g_variant_type_equal (type, G_VARIANT_TYPE_STRING) &&
str[0] != '\"')
diff --git a/gio/glocalfile.c b/gio/glocalfile.c
index 064755981..62f30b561 100644
--- a/gio/glocalfile.c
+++ b/gio/glocalfile.c
@@ -66,6 +66,8 @@
#include "glibintl.h"
#ifdef G_OS_UNIX
#include "glib-unix.h"
+#include "gportalsupport.h"
+#include "gtrashportal.h"
#endif
#include "glib-private.h"
@@ -1950,6 +1952,9 @@ g_local_file_trash (GFile *file,
GVfs *vfs;
int errsv;
+ if (glib_should_use_portal ())
+ return g_trash_portal_trash_file (file, error);
+
if (g_lstat (local->filename, &file_stat) != 0)
{
errsv = errno;
diff --git a/gio/gopenuriportal.c b/gio/gopenuriportal.c
index 38d60bf68..b798d7cd1 100644
--- a/gio/gopenuriportal.c
+++ b/gio/gopenuriportal.c
@@ -31,9 +31,6 @@
#include "gunixfdlist.h"
#endif
-#ifndef O_PATH
-#define O_PATH 0
-#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#else
@@ -107,7 +104,7 @@ g_openuri_portal_open_uri (const char *uri,
path = g_file_get_path (file);
- fd = g_open (path, O_PATH | O_CLOEXEC);
+ fd = g_open (path, O_RDONLY | O_CLOEXEC);
errsv = errno;
if (fd == -1)
{
@@ -318,7 +315,7 @@ g_openuri_portal_open_uri_async (const char *uri,
g_object_set_data (G_OBJECT (task), "open-file", GINT_TO_POINTER (TRUE));
path = g_file_get_path (file);
- fd = g_open (path, O_PATH | O_CLOEXEC);
+ fd = g_open (path, O_RDONLY | O_CLOEXEC);
errsv = errno;
if (fd == -1)
{
diff --git a/gio/gsettings.c b/gio/gsettings.c
index 2934f2b4b..dd8f94485 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -1137,6 +1137,8 @@ g_settings_new_full (GSettingsSchema *schema,
}
/* Internal read/write utilities {{{1 */
+
+/* @value will be sunk */
static gboolean
g_settings_write_to_backend (GSettings *settings,
GSettingsSchemaKey *key,
@@ -1417,7 +1419,7 @@ g_settings_set_enum (GSettings *settings,
return FALSE;
}
- success = g_settings_write_to_backend (settings, &skey, variant);
+ success = g_settings_write_to_backend (settings, &skey, g_steal_pointer (&variant));
g_settings_schema_key_clear (&skey);
return success;
@@ -1528,7 +1530,7 @@ g_settings_set_flags (GSettings *settings,
return FALSE;
}
- success = g_settings_write_to_backend (settings, &skey, variant);
+ success = g_settings_write_to_backend (settings, &skey, g_steal_pointer (&variant));
g_settings_schema_key_clear (&skey);
return success;
@@ -1672,7 +1674,7 @@ g_settings_set (GSettings *settings,
value = g_variant_new_va (format, NULL, &ap);
va_end (ap);
- return g_settings_set_value (settings, key, value);
+ return g_settings_set_value (settings, key, g_steal_pointer (&value));
}
/**
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index edc4ff4d3..18026ae56 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -777,6 +777,8 @@ g_settings_backend_read_user_value (GSettingsBackend *backend,
* to indicate that the affected keys have suddenly "changed back" to their
* old values.
*
+ * If @value has a floating reference, it will be sunk.
+ *
* Returns: %TRUE if the write succeeded, %FALSE if the key was not writable
*/
gboolean
@@ -1050,5 +1052,7 @@ g_settings_backend_sync_default (void)
if (class->sync)
class->sync (backend);
+
+ g_object_unref (backend);
}
}
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index 38c9d78b9..60b3fe0b3 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -1472,6 +1472,7 @@ g_settings_schema_key_to_enum (GSettingsSchemaKey *key,
return result;
}
+/* Returns a new floating #GVariant. */
GVariant *
g_settings_schema_key_from_enum (GSettingsSchemaKey *key,
gint value)
@@ -1511,6 +1512,7 @@ g_settings_schema_key_to_flags (GSettingsSchemaKey *key,
return result;
}
+/* Returns a new floating #GVariant. */
GVariant *
g_settings_schema_key_from_flags (GSettingsSchemaKey *key,
guint value)
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index 5c6513c3d..29a5e5598 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -1327,7 +1327,7 @@ g_socket_client_connect_to_uri (GSocketClient *client,
typedef struct
{
- GTask *task;
+ GTask *task; /* unowned */
GSocketClient *client;
GSocketConnectable *connectable;
@@ -1345,6 +1345,7 @@ static void connection_attempt_unref (gpointer attempt);
static void
g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data)
{
+ data->task = NULL;
g_clear_object (&data->connectable);
g_clear_object (&data->enumerator);
g_clear_object (&data->proxy_addr);
@@ -1444,13 +1445,19 @@ set_last_error (GSocketClientAsyncConnectData *data,
}
static void
-enumerator_next_async (GSocketClientAsyncConnectData *data)
+enumerator_next_async (GSocketClientAsyncConnectData *data,
+ gboolean add_task_ref)
{
/* We need to cleanup the state */
g_clear_object (&data->socket);
g_clear_object (&data->proxy_addr);
g_clear_object (&data->connection);
+ /* Each enumeration takes a ref. This arg just avoids repeated unrefs when
+ an enumeration starts another enumeration */
+ if (add_task_ref)
+ g_object_ref (data->task);
+
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL);
g_socket_address_enumerator_next_async (data->enumerator,
g_task_get_cancellable (data->task),
@@ -1478,7 +1485,7 @@ g_socket_client_tls_handshake_callback (GObject *object,
else
{
g_object_unref (object);
- enumerator_next_async (data);
+ enumerator_next_async (data, FALSE);
}
}
@@ -1509,7 +1516,7 @@ g_socket_client_tls_handshake (GSocketClientAsyncConnectData *data)
}
else
{
- enumerator_next_async (data);
+ enumerator_next_async (data, FALSE);
}
}
@@ -1530,13 +1537,24 @@ g_socket_client_proxy_connect_callback (GObject *object,
}
else
{
- enumerator_next_async (data);
+ enumerator_next_async (data, FALSE);
return;
}
g_socket_client_tls_handshake (data);
}
+static gboolean
+task_completed_or_cancelled (GTask *task)
+{
+ if (g_task_get_completed (task))
+ return TRUE;
+ else if (g_task_return_error_if_cancelled (task))
+ return TRUE;
+ else
+ return FALSE;
+}
+
static void
g_socket_client_connected_callback (GObject *source,
GAsyncResult *result,
@@ -1549,8 +1567,7 @@ g_socket_client_connected_callback (GObject *source,
GProxy *proxy;
const gchar *protocol;
- /* data is NULL once the task is completed */
- if (data && g_task_return_error_if_cancelled (data->task))
+ if (g_cancellable_is_cancelled (attempt->cancellable) || task_completed_or_cancelled (data->task))
{
g_object_unref (data->task);
connection_attempt_unref (attempt);
@@ -1570,17 +1587,15 @@ g_socket_client_connected_callback (GObject *source,
{
clarify_connect_error (error, data->connectable, attempt->address);
set_last_error (data, error);
+ connection_attempt_remove (attempt);
+ enumerator_next_async (data, FALSE);
}
else
- g_clear_error (&error);
-
- if (data)
{
- connection_attempt_remove (attempt);
- enumerator_next_async (data);
+ g_clear_error (&error);
+ g_object_unref (data->task);
+ connection_attempt_unref (attempt);
}
- else
- connection_attempt_unref (attempt);
return;
}
@@ -1592,7 +1607,6 @@ g_socket_client_connected_callback (GObject *source,
{
ConnectionAttempt *attempt_entry = l->data;
g_cancellable_cancel (attempt_entry->cancellable);
- attempt_entry->data = NULL;
connection_attempt_unref (attempt_entry);
}
g_slist_free (data->connection_attempts);
@@ -1625,7 +1639,7 @@ g_socket_client_connected_callback (GObject *source,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Proxying over a non-TCP connection is not supported."));
- enumerator_next_async (data);
+ enumerator_next_async (data, FALSE);
}
else if (g_hash_table_contains (data->client->priv->app_proxies, protocol))
{
@@ -1652,7 +1666,7 @@ g_socket_client_connected_callback (GObject *source,
_("Proxy protocol “%s” is not supported."),
protocol);
- enumerator_next_async (data);
+ enumerator_next_async (data, FALSE);
}
}
@@ -1661,7 +1675,7 @@ on_connection_attempt_timeout (gpointer data)
{
ConnectionAttempt *attempt = data;
- enumerator_next_async (attempt->data);
+ enumerator_next_async (attempt->data, TRUE);
g_clear_pointer (&attempt->timeout_source, g_source_unref);
return G_SOURCE_REMOVE;
@@ -1687,7 +1701,7 @@ g_socket_client_enumerator_callback (GObject *object,
ConnectionAttempt *attempt;
GError *error = NULL;
- if (g_task_return_error_if_cancelled (data->task))
+ if (task_completed_or_cancelled (data->task))
{
g_object_unref (data->task);
return;
@@ -1698,7 +1712,10 @@ g_socket_client_enumerator_callback (GObject *object,
if (address == NULL)
{
if (data->connection_attempts)
- return;
+ {
+ g_object_unref (data->task);
+ return;
+ }
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
if (!error)
@@ -1732,7 +1749,7 @@ g_socket_client_enumerator_callback (GObject *object,
if (socket == NULL)
{
g_object_unref (address);
- enumerator_next_async (data);
+ enumerator_next_async (data, FALSE);
return;
}
@@ -1804,11 +1821,24 @@ g_socket_client_connect_async (GSocketClient *client,
else
data->enumerator = g_socket_connectable_enumerate (connectable);
+ /* The flow and ownership here isn't quite obvious:
+ - The task starts an async attempt to connect.
+ - Each attempt holds a single ref on task.
+ - Each attempt may create new attempts by timing out (not a failure) so
+ there are multiple attempts happening in parallel.
+ - Upon failure an attempt will start a new attempt that steals its ref
+ until there are no more attempts left and it drops its ref.
+ - Upon success it will cancel all other attempts and continue on
+ to the rest of the connection (tls, proxies, etc) which do not
+ happen in parallel and at the very end drop its ref.
+ - Upon cancellation an attempt drops its ref.
+ */
+
data->task = g_task_new (client, cancellable, callback, user_data);
g_task_set_source_tag (data->task, g_socket_client_connect_async);
g_task_set_task_data (data->task, data, (GDestroyNotify)g_socket_client_async_connect_data_free);
- enumerator_next_async (data);
+ enumerator_next_async (data, FALSE);
}
/**
diff --git a/gio/gtrashportal.c b/gio/gtrashportal.c
new file mode 100644
index 000000000..a1e82102b
--- /dev/null
+++ b/gio/gtrashportal.c
@@ -0,0 +1,123 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2018, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include "gtrashportal.h"
+#include "xdp-dbus.h"
+#include "gstdio.h"
+
+#ifdef G_OS_UNIX
+#include "gunixfdlist.h"
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#else
+#define HAVE_O_CLOEXEC 1
+#endif
+
+static GXdpTrash *
+ensure_trash_portal (void)
+{
+ static GXdpTrash *trash = NULL;
+
+ if (g_once_init_enter (&trash))
+ {
+ GDBusConnection *connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ GXdpTrash *proxy = NULL;
+
+ if (connection != NULL)
+ {
+ proxy = gxdp_trash_proxy_new_sync (connection, 0,
+ "org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ NULL, NULL);
+ g_object_unref (connection);
+ }
+
+ g_once_init_leave (&trash, proxy);
+ }
+
+ return trash;
+}
+
+gboolean
+g_trash_portal_trash_file (GFile *file,
+ GError **error)
+{
+ char *path = NULL;
+ GUnixFDList *fd_list = NULL;
+ int fd, fd_in, errsv;
+ gboolean ret = FALSE;
+ GXdpTrash *proxy;
+
+ proxy = ensure_trash_portal ();
+ if (proxy == NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
+ "Trash portal is not available");
+ goto out;
+ }
+
+ path = g_file_get_path (file);
+
+ fd = g_open (path, O_RDWR | O_CLOEXEC);
+ if (fd == -1 && (errno == EACCES || errno == EISDIR))
+ /* If we don't have write access, fall back to read-only */
+ fd = g_open (path, O_CLOEXEC | O_RDONLY);
+
+ errsv = errno;
+
+ if (fd == -1)
+ {
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ "Failed to open %s", path);
+ goto out;
+ }
+
+#ifndef HAVE_O_CLOEXEC
+ fcntl (fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+ fd_list = g_unix_fd_list_new ();
+ fd_in = g_unix_fd_list_append (fd_list, fd, error);
+ g_close (fd, NULL);
+
+ if (fd_in == -1)
+ goto out;
+
+ ret = gxdp_trash_call_trash_file_sync (proxy,
+ g_variant_new_handle (fd_in),
+ fd_list,
+ NULL,
+ NULL,
+ NULL,
+ error);
+
+ out:
+ g_clear_object (&fd_list);
+ g_free (path);
+
+ return ret;
+}
diff --git a/gio/gtrashportal.h b/gio/gtrashportal.h
new file mode 100644
index 000000000..a53de8a6f
--- /dev/null
+++ b/gio/gtrashportal.h
@@ -0,0 +1,31 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __G_TRASH_PORTAL_H__
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+gboolean g_trash_portal_trash_file (GFile *file,
+ GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/gio/meson.build b/gio/meson.build
index 47aa0b48d..d7f4f3f31 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -238,7 +238,8 @@ subdir('gdbus-2.0/codegen')
xdp_dbus_generated = custom_target('xdp-dbus',
input : ['org.freedesktop.portal.Documents.xml',
'org.freedesktop.portal.OpenURI.xml',
- 'org.freedesktop.portal.ProxyResolver.xml'],
+ 'org.freedesktop.portal.ProxyResolver.xml',
+ 'org.freedesktop.portal.Trash.xml'],
output : ['xdp-dbus.h', 'xdp-dbus.c'],
depend_files : gdbus_codegen_built_files,
command : [python, gdbus_codegen,
@@ -254,6 +255,8 @@ xdp_dbus_generated = custom_target('xdp-dbus',
'org.gtk.GDBus.C.UnixFD', 'true',
'--annotate', 'org.freedesktop.portal.OpenURI.OpenFile()',
'org.gtk.GDBus.C.UnixFD', 'true',
+ '--annotate', 'org.freedesktop.portal.Trash.TrashFile()',
+ 'org.gtk.GDBus.C.UnixFD', 'true',
'@INPUT@'])
# Generate gdbus-generated.{c,h}
@@ -392,6 +395,7 @@ if host_system != 'windows'
'gopenuriportal.c',
'gnetworkmonitorportal.c',
'gproxyresolverportal.c',
+ 'gtrashportal.c',
'gportalsupport.c',
'gportalnotificationbackend.c'),
xdp_dbus_generated
diff --git a/gio/org.freedesktop.portal.Trash.xml b/gio/org.freedesktop.portal.Trash.xml
new file mode 100644
index 000000000..6142f3d2c
--- /dev/null
+++ b/gio/org.freedesktop.portal.Trash.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<!--
+ Copyright (C) 2016 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ Author: Matthias Clasen <mclasen@redhat.com>
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <!--
+ org.freedesktop.portal.Trash:
+ @short_description: Portal for trashing files
+
+ This simple interface lets sandboxed applications send files to
+ the trashcan.
+
+ This documentation describes version 1 of this interface.
+ -->
+ <interface name="org.freedesktop.portal.Trash">
+ <!--
+ TrashFile:
+ @fd: file descriptor for the file to trash
+ @result: the result. 0 if trashing failed, 1 if trashing succeeded, other values may be returned in the future
+
+ Sends a file to the trashcan. Applications are allowed to
+ trash a file if they can open it in r/w mode.
+ -->
+ <method name="TrashFile">
+ <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
+ <arg type="h" name="fd" direction="in"/>
+ <arg type="u" name="result" direction="out"/>
+ </method>
+
+ <property name="version" type="u" access="read"/>
+ </interface>
+</node>
diff --git a/gio/tests/gdbus-message.c b/gio/tests/gdbus-message.c
index 278ccc474..5cb141d94 100644
--- a/gio/tests/gdbus-message.c
+++ b/gio/tests/gdbus-message.c
@@ -141,6 +141,74 @@ message_copy (void)
/* ---------------------------------------------------------------------------------------------------- */
+/* Test g_dbus_message_bytes_needed() returns correct results for a variety of
+ * arbitrary binary inputs.*/
+static void
+message_bytes_needed (void)
+{
+ const struct
+ {
+ const guint8 blob[16];
+ gssize expected_bytes_needed;
+ }
+ vectors[] =
+ {
+ /* Little endian with header rounding */
+ { { 'l', 0, 0, 1, /* endianness, message type, flags, protocol version */
+ 50, 0, 0, 0, /* body length */
+ 1, 0, 0, 0, /* message serial */
+ 7, 0, 0, 0 /* header length */}, 74 },
+ /* Little endian without header rounding */
+ { { 'l', 0, 0, 1, /* endianness, message type, flags, protocol version */
+ 50, 0, 0, 0, /* body length */
+ 1, 0, 0, 0, /* message serial */
+ 8, 0, 0, 0 /* header length */}, 74 },
+ /* Big endian with header rounding */
+ { { 'B', 0, 0, 1, /* endianness, message type, flags, protocol version */
+ 0, 0, 0, 50, /* body length */
+ 0, 0, 0, 1, /* message serial */
+ 0, 0, 0, 7 /* header length */}, 74 },
+ /* Big endian without header rounding */
+ { { 'B', 0, 0, 1, /* endianness, message type, flags, protocol version */
+ 0, 0, 0, 50, /* body length */
+ 0, 0, 0, 1, /* message serial */
+ 0, 0, 0, 8 /* header length */}, 74 },
+ /* Invalid endianness */
+ { { '!', 0, 0, 1, /* endianness, message type, flags, protocol version */
+ 0, 0, 0, 50, /* body length */
+ 0, 0, 0, 1, /* message serial */
+ 0, 0, 0, 8 /* header length */}, -1 },
+ /* Oversized */
+ { { 'l', 0, 0, 1, /* endianness, message type, flags, protocol version */
+ 0, 0, 0, 0x08, /* body length (128MiB) */
+ 1, 0, 0, 0, /* message serial */
+ 7, 0, 0, 0 /* header length */}, -1 },
+ };
+ gsize i;
+
+ for (i = 0; i < G_N_ELEMENTS (vectors); i++)
+ {
+ gssize bytes_needed;
+ GError *local_error = NULL;
+
+ g_test_message ("Vector: %" G_GSIZE_FORMAT, i);
+
+ bytes_needed = g_dbus_message_bytes_needed ((guchar *) vectors[i].blob,
+ G_N_ELEMENTS (vectors[i].blob),
+ &local_error);
+
+ if (vectors[i].expected_bytes_needed < 0)
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
+ else
+ g_assert_no_error (local_error);
+ g_assert_cmpint (bytes_needed, ==, vectors[i].expected_bytes_needed);
+
+ g_clear_error (&local_error);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
int
main (int argc,
char *argv[])
@@ -151,6 +219,8 @@ main (int argc,
g_test_add_func ("/gdbus/message/lock", message_lock);
g_test_add_func ("/gdbus/message/copy", message_copy);
- return g_test_run();
+ g_test_add_func ("/gdbus/message/bytes-needed", message_bytes_needed);
+
+ return g_test_run ();
}
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index fb19e5156..f254c3195 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -2442,6 +2442,7 @@ test_schema_source (void)
g_settings_schema_unref (schema);
g_settings_schema_source_unref (source);
+ g_object_unref (backend);
}
static void
diff --git a/gio/tests/gsocketclient-slow.c b/gio/tests/gsocketclient-slow.c
index bc7d02772..a78ea71d0 100644
--- a/gio/tests/gsocketclient-slow.c
+++ b/gio/tests/gsocketclient-slow.c
@@ -63,12 +63,65 @@ test_happy_eyeballs (void)
g_object_unref (client);
}
+static void
+on_connected_cancelled (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSocketConnection *conn;
+ GError *error = NULL;
+
+ conn = g_socket_client_connect_to_uri_finish (G_SOCKET_CLIENT (source_object), result, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert_null (conn);
+
+ g_error_free (error);
+ g_main_loop_quit (user_data);
+}
+
+static int
+on_timer (GCancellable *cancel)
+{
+ g_cancellable_cancel (cancel);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+test_happy_eyeballs_cancel (void)
+{
+ GSocketClient *client;
+ GSocketService *service;
+ GError *error = NULL;
+ guint16 port;
+ GMainLoop *loop;
+ GCancellable *cancel;
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ service = g_socket_service_new ();
+ port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, &error);
+ g_assert_no_error (error);
+ g_socket_service_start (service);
+
+ client = g_socket_client_new ();
+ 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_main_loop_run (loop);
+
+ g_main_loop_unref (loop);
+ g_object_unref (service);
+ g_object_unref (client);
+ g_object_unref (cancel);
+}
+
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
- g_test_add_func ("/socket-client/happy-eyeballs", test_happy_eyeballs);
+ g_test_add_func ("/socket-client/happy-eyeballs/slow", test_happy_eyeballs);
+ g_test_add_func ("/socket-client/happy-eyeballs/cancellation", test_happy_eyeballs_cancel);
return g_test_run ();
} \ No newline at end of file
diff --git a/gio/tests/trash.c b/gio/tests/trash.c
index 1055585e5..4bf8fd0ef 100644
--- a/gio/tests/trash.c
+++ b/gio/tests/trash.c
@@ -107,14 +107,54 @@ test_trash_symlinks (void)
g_test_bug ("1522");
- /* The test assumes that ~/.local always exists. */
target = g_build_filename (g_get_home_dir (), ".local", NULL);
+
+ if (!g_file_test (target, G_FILE_TEST_IS_DIR))
+ {
+ gchar *message;
+
+ message = g_strdup_printf ("Directory '%s' does not exist", target);
+ g_test_skip (message);
+ g_free (message);
+ g_free (target);
+ return;
+ }
+
target_mount = g_unix_mount_for (target, NULL);
+
+ if (target_mount == NULL)
+ {
+ gchar *message;
+
+ message = g_strdup_printf ("Unable to determine mount point for %s",
+ target);
+ g_test_skip (message);
+ g_free (message);
+ g_free (target);
+ return;
+ }
+
g_assert_nonnull (target_mount);
g_test_message ("Target: %s (mount: %s)", target, g_unix_mount_get_mount_path (target_mount));
tmp = g_dir_make_tmp ("test-trashXXXXXX", &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (tmp);
tmp_mount = g_unix_mount_for (tmp, NULL);
+
+ if (tmp_mount == NULL)
+ {
+ gchar *message;
+
+ message = g_strdup_printf ("Unable to determine mount point for %s", tmp);
+ g_test_skip (message);
+ g_free (message);
+ g_unix_mount_free (target_mount);
+ g_free (target);
+ g_free (tmp);
+ return;
+ }
+
g_assert_nonnull (tmp_mount);
g_test_message ("Tmp: %s (mount: %s)", tmp, g_unix_mount_get_mount_path (tmp_mount));