diff --git a/data/org.freedesktop.portal.Notification.xml b/data/org.freedesktop.portal.Notification.xml index 1c068b6dc..a4fa4ead3 100644 --- a/data/org.freedesktop.portal.Notification.xml +++ b/data/org.freedesktop.portal.Notification.xml @@ -118,6 +118,7 @@ Target parameter to send along when activating the action. --> + diff --git a/src/notification.c b/src/notification.c index ccceac17a..3876cfe99 100644 --- a/src/notification.c +++ b/src/notification.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "notification.h" @@ -107,6 +108,7 @@ struct _CallData { char *sender; char *id; GVariant *notification; + GUnixFDList *fd_list; }; G_DECLARE_FINAL_TYPE (CallData, call_data, CALL, DATA, GObject) @@ -124,7 +126,8 @@ call_data_new (GDBusMethodInvocation *inv, XdpAppInfo *app_info, const char *sender, const char *id, - GVariant *notification) + GVariant *notification, + GUnixFDList *fd_list) { CallData *call_data = g_object_new (call_data_get_type(), NULL); @@ -134,6 +137,8 @@ call_data_new (GDBusMethodInvocation *inv, call_data->id = g_strdup (id); if (notification) call_data->notification = g_variant_ref (notification); + if (fd_list) + call_data->fd_list = g_object_ref (fd_list); return call_data; } @@ -149,6 +154,8 @@ call_data_finalize (GObject *object) g_free (call_data->sender); if (call_data->notification) g_variant_unref (call_data->notification); + if (call_data->fd_list) + g_object_unref (call_data->fd_list); G_OBJECT_CLASS (call_data_parent_class)->finalize (object); } @@ -346,9 +353,50 @@ parse_buttons (GVariantBuilder *builder, return result; } +static gboolean +parse_serialized_fd_icon (GVariantBuilder *builder, + GVariant *handle, + XdpAppInfo *app_info, + GUnixFDList *fd_list, + GError **error) +{ + int fd_id, fd; + g_autofree char *path = NULL; + GVariant *serialized_file_icon = NULL; + + if (!check_value_type ("file-descriptor", handle, G_VARIANT_TYPE_HANDLE, error)) + return FALSE; + + + fd_id = g_variant_get_handle (handle); + fd = g_unix_fd_list_get (fd_list, fd_id, error); + + if (fd == -1) + return FALSE; + + path = xdp_app_info_get_path_for_fd (app_info, fd, 0, NULL, NULL, error); + close (fd); + + if (path == NULL) + return FALSE; + + /* This is a serialized GFileIcon but there is no point in creating an object and then deserializing it */ + serialized_file_icon = g_variant_new ("(sv)", + "file", + g_variant_new_take_string (g_steal_pointer (&path))); + + if (xdp_validate_serialized_icon (serialized_file_icon, FALSE, NULL, NULL)) + g_variant_builder_add_value (builder, serialized_file_icon); + + return TRUE; +} + + static gboolean parse_serialized_icon (GVariantBuilder *builder, GVariant *icon, + XdpAppInfo *app_info, + GUnixFDList *fd_list, GError **error) { const char *key; @@ -369,6 +417,8 @@ parse_serialized_icon (GVariantBuilder *builder, g_prefix_error (error, "invalid icon: "); return FALSE; } + if (xdp_validate_serialized_icon (value, FALSE, NULL, NULL)) + g_variant_builder_add_value (builder, value); } else if (strcmp (key, "bytes") == 0) { @@ -377,6 +427,16 @@ parse_serialized_icon (GVariantBuilder *builder, g_prefix_error (error, "invalid icon: "); return FALSE; } + if (xdp_validate_serialized_icon (value, FALSE, NULL, NULL)) + g_variant_builder_add_value (builder, value); + } + else if (strcmp (key, "file-descriptor") == 0) + { + if (!parse_serialized_fd_icon (builder, value, app_info, fd_list, error)) + { + g_prefix_error (error, "invalid icon: "); + return FALSE; + } } else { @@ -384,15 +444,14 @@ parse_serialized_icon (GVariantBuilder *builder, return TRUE; } - if (xdp_validate_serialized_icon (value, FALSE, NULL, NULL)) - g_variant_builder_add_value (builder, value); - return TRUE; } static gboolean parse_notification (GVariantBuilder *builder, GVariant *notification, + XdpAppInfo *app_info, + GUnixFDList *fd_list, GError **error) { int i; @@ -416,7 +475,7 @@ parse_notification (GVariantBuilder *builder, } else if (strcmp (key, "icon") == 0) { - if (!parse_serialized_icon (builder, value, error)) + if (!parse_serialized_icon (builder, value, app_info, fd_list, error)) return FALSE; } else if (strcmp (key, "priority") == 0) @@ -464,7 +523,8 @@ add_in_thread_finished_cb (GObject *source_object, if (g_task_propagate_boolean (G_TASK (result), &error)) { xdp_dbus_notification_complete_add_notification (XDP_DBUS_NOTIFICATION (source_object), - call_data->inv); + call_data->inv, + NULL); } else { @@ -500,7 +560,11 @@ handle_add_in_thread_func (GTask *task, g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - if (!parse_notification (&builder, call_data->notification, &error)) + if (!parse_notification (&builder, + call_data->notification, + call_data->app_info, + call_data->fd_list, + &error)) { g_variant_builder_clear (&builder); @@ -523,6 +587,7 @@ handle_add_in_thread_func (GTask *task, static gboolean notification_handle_add_notification (XdpDbusNotification *object, GDBusMethodInvocation *invocation, + GUnixFDList *fd_list, const char *arg_id, GVariant *notification) { @@ -531,7 +596,12 @@ notification_handle_add_notification (XdpDbusNotification *object, g_autoptr(GError) error = NULL; CallData *call_data; - call_data = call_data_new (invocation, call->app_info, call->sender, arg_id, notification); + call_data = call_data_new (invocation, + call->app_info, + call->sender, + arg_id, + notification, + fd_list); task = g_task_new (object, NULL, add_in_thread_finished_cb, NULL); g_task_set_task_data (task, call_data, g_object_unref); g_task_run_in_thread (task, handle_add_in_thread_func); @@ -571,7 +641,12 @@ notification_handle_remove_notification (XdpDbusNotification *object, const char *arg_id) { Call *call = call_from_invocation (invocation); - CallData *call_data = call_data_new (invocation, call->app_info, call->sender, arg_id, NULL); + CallData *call_data = call_data_new (invocation, + call->app_info, + call->sender, + arg_id, + NULL, + NULL); xdp_dbus_impl_notification_call_remove_notification (impl, xdp_app_info_get_id (call->app_info), diff --git a/src/xdp-utils.c b/src/xdp-utils.c index eb59444a4..3cfb4058d 100644 --- a/src/xdp-utils.c +++ b/src/xdp-utils.c @@ -2296,6 +2296,7 @@ xdp_validate_serialized_icon (GVariant *v, g_autoptr(GIcon) icon = NULL; GBytes *bytes; __attribute__((cleanup(cleanup_temp_file))) char *name = NULL; + g_autofree char *path = NULL; xdp_autofd int fd = -1; g_autoptr(GOutputStream) stream = NULL; int status; @@ -2327,11 +2328,6 @@ xdp_validate_serialized_icon (GVariant *v, return TRUE; } - if (!G_IS_BYTES_ICON (icon)) - { - g_warning ("Unexpected icon type: %s", G_OBJECT_TYPE_NAME (icon)); - return FALSE; - } if (!g_file_test (icon_validator, G_FILE_TEST_EXISTS)) { @@ -2339,29 +2335,47 @@ xdp_validate_serialized_icon (GVariant *v, return FALSE; } - bytes = g_bytes_icon_get_bytes (G_BYTES_ICON (icon)); - fd = g_file_open_tmp ("iconXXXXXX", &name, &error); - if (fd == -1) + if (G_IS_BYTES_ICON (icon)) { - g_warning ("Icon validation: %s", error->message); - return FALSE; - } + bytes = g_bytes_icon_get_bytes (G_BYTES_ICON (icon)); + fd = g_file_open_tmp ("iconXXXXXX", &name, &error); + if (fd == -1) + { + g_warning ("Icon validation: %s", error->message); + return FALSE; + } - stream = g_unix_output_stream_new (fd, FALSE); + stream = g_unix_output_stream_new (fd, FALSE); - /* Use write_all() instead of write_bytes() so we don't have to worry about - * partial writes (https://gitlab.gnome.org/GNOME/glib/-/issues/570). - */ - bytes_data = g_bytes_get_data (bytes, &bytes_len); - if (!g_output_stream_write_all (stream, bytes_data, bytes_len, NULL, NULL, &error)) - { - g_warning ("Icon validation: %s", error->message); - return FALSE; + /* Use write_all() instead of write_bytes() so we don't have to worry about + * partial writes (https://gitlab.gnome.org/GNOME/glib/-/issues/570). + */ + bytes_data = g_bytes_get_data (bytes, &bytes_len); + if (!g_output_stream_write_all (stream, bytes_data, bytes_len, NULL, NULL, &error)) + { + g_warning ("Icon validation: %s", error->message); + return FALSE; + } + + if (!g_output_stream_close (stream, NULL, &error)) + { + g_warning ("Icon validation: %s", error->message); + return FALSE; + } } + else if (G_IS_FILE_ICON (icon) && !bytes_only) + { + path = g_file_get_path (g_file_icon_get_file (G_FILE_ICON (icon))); - if (!g_output_stream_close (stream, NULL, &error)) + if (!path) + { + g_warning ("Icon validation: Invalid icon path"); + return FALSE; + } + } + else { - g_warning ("Icon validation: %s", error->message); + g_warning ("Unexpected icon type: %s", G_OBJECT_TYPE_NAME (icon)); return FALSE; } @@ -2369,7 +2383,7 @@ xdp_validate_serialized_icon (GVariant *v, args[1] = "--sandbox"; args[2] = MAX_ICON_SIZE; args[3] = MAX_ICON_SIZE; - args[4] = name; + args[4] = (name) ? name : path; args[5] = NULL; if (!g_spawn_sync (NULL, (char **)args, NULL, 0, NULL, NULL, &stdoutlog, &stderrlog, &status, &error))