Skip to content

Commit

Permalink
notification: Accept icons as file descriptor
Browse files Browse the repository at this point in the history
  • Loading branch information
jsparber committed Feb 23, 2024
1 parent cdea1dd commit 1f6a5e7
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 32 deletions.
1 change: 1 addition & 0 deletions data/org.freedesktop.portal.Notification.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
Target parameter to send along when activating the action.
-->
<method name="AddNotification">
<annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
<arg type="s" name="id" direction="in"/>
<arg type="a{sv}" name="notification" direction="in"/>
</method>
Expand Down
93 changes: 84 additions & 9 deletions src/notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <string.h>
#include <stdio.h>
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include <gio/gunixoutputstream.h>

#include "notification.h"
Expand Down Expand Up @@ -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)
Expand All @@ -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);

Expand All @@ -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;
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -337,9 +344,50 @@ parse_buttons (GVariantBuilder *builder,
return FALSE;
}

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;
Expand All @@ -360,6 +408,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)
{
Expand All @@ -368,22 +418,31 @@ 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
{
g_warning ("Unsupported icon %s filtered from notification", key);
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;
Expand All @@ -407,7 +466,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)
Expand Down Expand Up @@ -457,7 +516,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
{
Expand Down Expand Up @@ -493,7 +553,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);

Expand All @@ -516,6 +580,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)
{
Expand All @@ -524,7 +589,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);
Expand Down Expand Up @@ -564,7 +634,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),
Expand Down
60 changes: 37 additions & 23 deletions src/xdp-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2327,49 +2328,62 @@ 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))
{
g_warning ("Icon validation: %s not found, rejecting icon by default.", icon_validator);
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;
}

args[0] = icon_validator;
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))
Expand Down

0 comments on commit 1f6a5e7

Please sign in to comment.