summaryrefslogtreecommitdiff
path: root/gio/gfdonotificationbackend.c
diff options
context:
space:
mode:
Diffstat (limited to 'gio/gfdonotificationbackend.c')
-rw-r--r--gio/gfdonotificationbackend.c75
1 files changed, 52 insertions, 23 deletions
diff --git a/gio/gfdonotificationbackend.c b/gio/gfdonotificationbackend.c
index e0bfea535..594ed9966 100644
--- a/gio/gfdonotificationbackend.c
+++ b/gio/gfdonotificationbackend.c
@@ -63,8 +63,8 @@ typedef struct
GFdoNotificationBackend *backend;
gchar *id;
guint32 notify_id;
- gchar *default_action;
- GVariant *default_action_target;
+ gchar *default_action; /* (nullable) (owned) */
+ GVariant *default_action_target; /* (nullable) (owned), not floating */
} FreedesktopNotification;
static void
@@ -131,22 +131,40 @@ g_fdo_notification_backend_find_notification_by_notify_id (GFdoNotificationBacke
return NULL;
}
-static void
+static gboolean
activate_action (GFdoNotificationBackend *backend,
const gchar *name,
GVariant *parameter)
{
GNotificationBackend *g_backend = G_NOTIFICATION_BACKEND (backend);
- if (name)
+ /* Callers should not provide a floating variant here */
+ g_assert (parameter == NULL || !g_variant_is_floating (parameter));
+
+ if (name != NULL &&
+ g_str_has_prefix (name, "app."))
{
- if (g_str_has_prefix (name, "app."))
- g_action_group_activate_action (G_ACTION_GROUP (g_backend->application), name + 4, parameter);
+ const GVariantType *parameter_type = NULL;
+ const gchar *action_name = name + strlen ("app.");
+
+ /* @name and @parameter come as untrusted input over D-Bus, so validate them first */
+ if (g_action_group_query_action (G_ACTION_GROUP (g_backend->application),
+ action_name, NULL, &parameter_type,
+ NULL, NULL, NULL) &&
+ ((parameter_type == NULL && parameter == NULL) ||
+ (parameter_type != NULL && parameter != NULL && g_variant_is_of_type (parameter, parameter_type))))
+ {
+ g_action_group_activate_action (G_ACTION_GROUP (g_backend->application), action_name, parameter);
+ return TRUE;
+ }
}
- else
+ else if (name == NULL)
{
g_application_activate (g_backend->application);
+ return TRUE;
}
+
+ return FALSE;
}
static void
@@ -162,6 +180,7 @@ notify_signal (GDBusConnection *connection,
guint32 id = 0;
const gchar *action = NULL;
FreedesktopNotification *n;
+ gboolean notification_closed = TRUE;
if (g_str_equal (signal_name, "NotificationClosed") &&
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)")))
@@ -184,29 +203,39 @@ notify_signal (GDBusConnection *connection,
{
if (g_str_equal (action, "default"))
{
- activate_action (backend, n->default_action, n->default_action_target);
+ if (!activate_action (backend, n->default_action, n->default_action_target))
+ notification_closed = FALSE;
}
else
{
- gchar *name;
- GVariant *target;
-
- if (g_action_parse_detailed_name (action, &name, &target, NULL))
- {
- activate_action (backend, name, target);
- g_free (name);
- if (target)
- g_variant_unref (target);
- }
+ gchar *name = NULL;
+ GVariant *target = NULL;
+
+ if (!g_action_parse_detailed_name (action, &name, &target, NULL) ||
+ !activate_action (backend, name, target))
+ notification_closed = FALSE;
+
+ g_free (name);
+ g_clear_pointer (&target, g_variant_unref);
}
}
- /* Get the notification again in case the action redrew it */
- n = g_fdo_notification_backend_find_notification_by_notify_id (backend, id);
- if (n != NULL)
+ /* Remove the notification, as it’s either been explicitly closed
+ * (`NotificationClosed` signal) or has been closed as a result of activating
+ * an action successfully. GLib doesn’t currently support the `resident` hint
+ * on notifications which would allow them to stay around after having an
+ * action invoked on them (see
+ * https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html#idm45877717456448)
+ *
+ * First, get the notification again in case the action redrew it */
+ if (notification_closed)
{
- backend->notifications = g_slist_remove (backend->notifications, n);
- freedesktop_notification_free (n);
+ n = g_fdo_notification_backend_find_notification_by_notify_id (backend, id);
+ if (n != NULL)
+ {
+ backend->notifications = g_slist_remove (backend->notifications, n);
+ freedesktop_notification_free (n);
+ }
}
}