2013-10-15 17:21:10 +02:00
|
|
|
/*
|
|
|
|
* Copyright © 2013 Lars Uebernickel
|
|
|
|
*
|
|
|
|
* 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
|
2017-05-27 18:21:30 +02:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2013-10-15 17:21:10 +02:00
|
|
|
*
|
|
|
|
* 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
|
2014-01-23 12:58:29 +01:00
|
|
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2013-10-15 17:21:10 +02:00
|
|
|
*
|
|
|
|
* Authors: Lars Uebernickel <lars@uebernic.de>
|
|
|
|
*/
|
|
|
|
|
2013-12-29 13:55:14 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
#include "gnotificationbackend.h"
|
|
|
|
|
|
|
|
#include "gapplication.h"
|
|
|
|
#include "giomodule-priv.h"
|
|
|
|
#include "gnotification-private.h"
|
|
|
|
#include "gdbusconnection.h"
|
2020-01-13 14:13:28 +01:00
|
|
|
#include "gdbusnamewatching.h"
|
2013-10-21 18:54:19 +02:00
|
|
|
#include "gactiongroup.h"
|
2013-10-15 17:21:10 +02:00
|
|
|
#include "gaction.h"
|
2015-03-05 14:07:00 +01:00
|
|
|
#include "gthemedicon.h"
|
2013-10-15 17:21:10 +02:00
|
|
|
#include "gfileicon.h"
|
|
|
|
#include "gfile.h"
|
|
|
|
#include "gdbusutils.h"
|
|
|
|
|
|
|
|
#define G_TYPE_FDO_NOTIFICATION_BACKEND (g_fdo_notification_backend_get_type ())
|
|
|
|
#define G_FDO_NOTIFICATION_BACKEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FDO_NOTIFICATION_BACKEND, GFdoNotificationBackend))
|
|
|
|
|
|
|
|
typedef struct _GFdoNotificationBackend GFdoNotificationBackend;
|
|
|
|
typedef GNotificationBackendClass GFdoNotificationBackendClass;
|
|
|
|
|
|
|
|
struct _GFdoNotificationBackend
|
|
|
|
{
|
|
|
|
GNotificationBackend parent;
|
|
|
|
|
2020-01-13 14:13:28 +01:00
|
|
|
guint bus_name_id;
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
guint notify_subscription;
|
|
|
|
GSList *notifications;
|
|
|
|
};
|
|
|
|
|
|
|
|
GType g_fdo_notification_backend_get_type (void);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GFdoNotificationBackend, g_fdo_notification_backend, G_TYPE_NOTIFICATION_BACKEND,
|
|
|
|
_g_io_modules_ensure_extension_points_registered ();
|
|
|
|
g_io_extension_point_implement (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME,
|
|
|
|
g_define_type_id, "freedesktop", 0))
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
GFdoNotificationBackend *backend;
|
|
|
|
gchar *id;
|
|
|
|
guint32 notify_id;
|
|
|
|
gchar *default_action;
|
|
|
|
GVariant *default_action_target;
|
|
|
|
} FreedesktopNotification;
|
|
|
|
|
|
|
|
static void
|
|
|
|
freedesktop_notification_free (gpointer data)
|
|
|
|
{
|
|
|
|
FreedesktopNotification *n = data;
|
|
|
|
|
2018-06-14 06:21:11 +02:00
|
|
|
g_object_unref (n->backend);
|
2013-10-15 17:21:10 +02:00
|
|
|
g_free (n->id);
|
|
|
|
g_free (n->default_action);
|
|
|
|
if (n->default_action_target)
|
|
|
|
g_variant_unref (n->default_action_target);
|
|
|
|
|
|
|
|
g_slice_free (FreedesktopNotification, n);
|
|
|
|
}
|
|
|
|
|
gfdonotificationbackend: Fix possible invalid pointer in dbus callback
The way things were before: a FreedesktopNotification struct is
allocated before the dbus call, and this same struct is possibly re-used
for other dbus calls. If the server becomes unavailable, the callback
will be invoked after the call times out, which leaves a long time where
other dbus calls can happen, re-using the same FreedesktopNotification
as user data. When the first call times out, the callback is invoked,
and the user data is freed. Subsequent calls that used the same user
data will time out later on, and try to free a pointer that was already
freed, hence segfaults.
This bug can be reproduced in Cinnamon 3.6.7, as mentioned in:
<https://github.com/linuxmint/Cinnamon/issues/7491>
This commit fixes that by always allocating a new
FreedesktopNotification before invoking dbus_call(), ensuring that the
callback always have a valid user data.
Signed-off-by: Arnaud Rebillout <elboulangero@gmail.com>
2018-06-10 15:56:12 +02:00
|
|
|
static FreedesktopNotification *
|
|
|
|
freedesktop_notification_new (GFdoNotificationBackend *backend,
|
|
|
|
const gchar *id,
|
|
|
|
GNotification *notification)
|
|
|
|
{
|
|
|
|
FreedesktopNotification *n;
|
|
|
|
|
|
|
|
n = g_slice_new0 (FreedesktopNotification);
|
2018-06-14 06:21:11 +02:00
|
|
|
n->backend = g_object_ref (backend);
|
gfdonotificationbackend: Fix possible invalid pointer in dbus callback
The way things were before: a FreedesktopNotification struct is
allocated before the dbus call, and this same struct is possibly re-used
for other dbus calls. If the server becomes unavailable, the callback
will be invoked after the call times out, which leaves a long time where
other dbus calls can happen, re-using the same FreedesktopNotification
as user data. When the first call times out, the callback is invoked,
and the user data is freed. Subsequent calls that used the same user
data will time out later on, and try to free a pointer that was already
freed, hence segfaults.
This bug can be reproduced in Cinnamon 3.6.7, as mentioned in:
<https://github.com/linuxmint/Cinnamon/issues/7491>
This commit fixes that by always allocating a new
FreedesktopNotification before invoking dbus_call(), ensuring that the
callback always have a valid user data.
Signed-off-by: Arnaud Rebillout <elboulangero@gmail.com>
2018-06-10 15:56:12 +02:00
|
|
|
n->id = g_strdup (id);
|
|
|
|
n->notify_id = 0;
|
|
|
|
g_notification_get_default_action (notification,
|
|
|
|
&n->default_action,
|
|
|
|
&n->default_action_target);
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
static FreedesktopNotification *
|
|
|
|
g_fdo_notification_backend_find_notification (GFdoNotificationBackend *backend,
|
|
|
|
const gchar *id)
|
|
|
|
{
|
|
|
|
GSList *it;
|
|
|
|
|
|
|
|
for (it = backend->notifications; it != NULL; it = it->next)
|
|
|
|
{
|
|
|
|
FreedesktopNotification *n = it->data;
|
|
|
|
if (g_str_equal (n->id, id))
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FreedesktopNotification *
|
|
|
|
g_fdo_notification_backend_find_notification_by_notify_id (GFdoNotificationBackend *backend,
|
|
|
|
guint32 id)
|
|
|
|
{
|
|
|
|
GSList *it;
|
|
|
|
|
|
|
|
for (it = backend->notifications; it != NULL; it = it->next)
|
|
|
|
{
|
|
|
|
FreedesktopNotification *n = it->data;
|
|
|
|
if (n->notify_id == id)
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-21 18:54:19 +02:00
|
|
|
static void
|
|
|
|
activate_action (GFdoNotificationBackend *backend,
|
|
|
|
const gchar *name,
|
|
|
|
GVariant *parameter)
|
|
|
|
{
|
|
|
|
GNotificationBackend *g_backend = G_NOTIFICATION_BACKEND (backend);
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
{
|
|
|
|
if (g_str_has_prefix (name, "app."))
|
|
|
|
g_action_group_activate_action (G_ACTION_GROUP (g_backend->application), name + 4, parameter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_application_activate (g_backend->application);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
static void
|
|
|
|
notify_signal (GDBusConnection *connection,
|
|
|
|
const gchar *sender_name,
|
|
|
|
const gchar *object_path,
|
|
|
|
const gchar *interface_name,
|
|
|
|
const gchar *signal_name,
|
|
|
|
GVariant *parameters,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GFdoNotificationBackend *backend = user_data;
|
|
|
|
guint32 id = 0;
|
|
|
|
const gchar *action = NULL;
|
|
|
|
FreedesktopNotification *n;
|
|
|
|
|
|
|
|
if (g_str_equal (signal_name, "NotificationClosed") &&
|
|
|
|
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)")))
|
|
|
|
{
|
|
|
|
g_variant_get (parameters, "(uu)", &id, NULL);
|
|
|
|
}
|
|
|
|
else if (g_str_equal (signal_name, "ActionInvoked") &&
|
|
|
|
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(us)")))
|
|
|
|
{
|
|
|
|
g_variant_get (parameters, "(u&s)", &id, &action);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
n = g_fdo_notification_backend_find_notification_by_notify_id (backend, id);
|
|
|
|
if (n == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (action)
|
|
|
|
{
|
|
|
|
if (g_str_equal (action, "default"))
|
|
|
|
{
|
2013-10-21 18:54:19 +02:00
|
|
|
activate_action (backend, n->default_action, n->default_action_target);
|
2013-10-15 17:21:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gchar *name;
|
|
|
|
GVariant *target;
|
|
|
|
|
|
|
|
if (g_action_parse_detailed_name (action, &name, &target, NULL))
|
|
|
|
{
|
2013-10-21 18:54:19 +02:00
|
|
|
activate_action (backend, name, target);
|
2013-10-15 17:21:10 +02:00
|
|
|
g_free (name);
|
|
|
|
if (target)
|
|
|
|
g_variant_unref (target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 08:18:59 +01:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
backend->notifications = g_slist_remove (backend->notifications, n);
|
|
|
|
freedesktop_notification_free (n);
|
|
|
|
}
|
2013-10-15 17:21:10 +02:00
|
|
|
}
|
|
|
|
|
2020-01-13 14:13:28 +01:00
|
|
|
static void
|
|
|
|
name_vanished_handler_cb (GDBusConnection *connection,
|
|
|
|
const gchar *name,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GFdoNotificationBackend *backend = user_data;
|
|
|
|
|
|
|
|
if (backend->notifications)
|
|
|
|
{
|
|
|
|
g_slist_free_full (backend->notifications, freedesktop_notification_free);
|
|
|
|
backend->notifications = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-15 15:42:31 +02:00
|
|
|
/* Converts a GNotificationPriority to an urgency level as defined by
|
|
|
|
* the freedesktop spec (0: low, 1: normal, 2: critical).
|
|
|
|
*/
|
|
|
|
static guchar
|
|
|
|
urgency_from_priority (GNotificationPriority priority)
|
|
|
|
{
|
|
|
|
switch (priority)
|
|
|
|
{
|
|
|
|
case G_NOTIFICATION_PRIORITY_LOW:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
case G_NOTIFICATION_PRIORITY_NORMAL:
|
2017-07-11 21:28:50 +02:00
|
|
|
case G_NOTIFICATION_PRIORITY_HIGH:
|
2014-06-15 15:42:31 +02:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
case G_NOTIFICATION_PRIORITY_URGENT:
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
static void
|
|
|
|
call_notify (GDBusConnection *con,
|
|
|
|
GApplication *app,
|
|
|
|
guint32 replace_id,
|
|
|
|
GNotification *notification,
|
|
|
|
GAsyncReadyCallback callback,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GVariantBuilder action_builder;
|
|
|
|
guint n_buttons;
|
|
|
|
guint i;
|
|
|
|
GVariantBuilder hints_builder;
|
2013-10-15 18:19:09 +02:00
|
|
|
GIcon *icon;
|
2013-10-15 17:21:10 +02:00
|
|
|
GVariant *parameters;
|
2021-06-15 20:45:06 +02:00
|
|
|
const gchar *app_name;
|
2013-10-15 17:21:10 +02:00
|
|
|
const gchar *body;
|
2014-06-15 15:42:31 +02:00
|
|
|
guchar urgency;
|
2013-10-15 17:21:10 +02:00
|
|
|
|
|
|
|
g_variant_builder_init (&action_builder, G_VARIANT_TYPE_STRING_ARRAY);
|
|
|
|
if (g_notification_get_default_action (notification, NULL, NULL))
|
|
|
|
{
|
|
|
|
g_variant_builder_add (&action_builder, "s", "default");
|
|
|
|
g_variant_builder_add (&action_builder, "s", "");
|
|
|
|
}
|
|
|
|
|
|
|
|
n_buttons = g_notification_get_n_buttons (notification);
|
|
|
|
for (i = 0; i < n_buttons; i++)
|
|
|
|
{
|
|
|
|
gchar *label;
|
|
|
|
gchar *action;
|
|
|
|
GVariant *target;
|
|
|
|
gchar *detailed_name;
|
|
|
|
|
|
|
|
g_notification_get_button (notification, i, &label, &action, &target);
|
|
|
|
detailed_name = g_action_print_detailed_name (action, target);
|
|
|
|
|
|
|
|
/* Actions named 'default' collide with libnotify's naming of the
|
|
|
|
* default action. Rewriting them to something unique is enough,
|
|
|
|
* because those actions can never be activated (they aren't
|
|
|
|
* prefixed with 'app.').
|
|
|
|
*/
|
|
|
|
if (g_str_equal (detailed_name, "default"))
|
|
|
|
{
|
|
|
|
g_free (detailed_name);
|
|
|
|
detailed_name = g_dbus_generate_guid ();
|
|
|
|
}
|
|
|
|
|
|
|
|
g_variant_builder_add_value (&action_builder, g_variant_new_take_string (detailed_name));
|
|
|
|
g_variant_builder_add_value (&action_builder, g_variant_new_take_string (label));
|
|
|
|
|
|
|
|
g_free (action);
|
|
|
|
if (target)
|
|
|
|
g_variant_unref (target);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_variant_builder_init (&hints_builder, G_VARIANT_TYPE ("a{sv}"));
|
|
|
|
g_variant_builder_add (&hints_builder, "{sv}", "desktop-entry",
|
|
|
|
g_variant_new_string (g_application_get_application_id (app)));
|
2014-06-15 15:42:31 +02:00
|
|
|
urgency = urgency_from_priority (g_notification_get_priority (notification));
|
|
|
|
g_variant_builder_add (&hints_builder, "{sv}", "urgency", g_variant_new_byte (urgency));
|
2013-10-15 18:19:09 +02:00
|
|
|
icon = g_notification_get_icon (notification);
|
2015-03-05 14:07:00 +01:00
|
|
|
if (icon != NULL)
|
2013-10-15 17:21:10 +02:00
|
|
|
{
|
2015-03-05 14:07:00 +01:00
|
|
|
if (G_IS_FILE_ICON (icon))
|
|
|
|
{
|
|
|
|
GFile *file;
|
2013-10-15 17:21:10 +02:00
|
|
|
|
2015-03-05 14:07:00 +01:00
|
|
|
file = g_file_icon_get_file (G_FILE_ICON (icon));
|
|
|
|
g_variant_builder_add (&hints_builder, "{sv}", "image-path",
|
|
|
|
g_variant_new_take_string (g_file_get_path (file)));
|
|
|
|
}
|
|
|
|
else if (G_IS_THEMED_ICON (icon))
|
|
|
|
{
|
|
|
|
const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (icon));
|
|
|
|
/* Take first name from GThemedIcon */
|
|
|
|
g_variant_builder_add (&hints_builder, "{sv}", "image-path",
|
|
|
|
g_variant_new_string (icon_names[0]));
|
|
|
|
}
|
2013-10-15 17:21:10 +02:00
|
|
|
}
|
|
|
|
|
2021-06-15 20:45:06 +02:00
|
|
|
app_name = g_get_application_name ();
|
2013-10-15 17:21:10 +02:00
|
|
|
body = g_notification_get_body (notification);
|
|
|
|
|
|
|
|
parameters = g_variant_new ("(susssasa{sv}i)",
|
2021-06-15 20:45:06 +02:00
|
|
|
app_name ? app_name : "",
|
2013-10-15 17:21:10 +02:00
|
|
|
replace_id,
|
|
|
|
"", /* app icon */
|
|
|
|
g_notification_get_title (notification),
|
|
|
|
body ? body : "",
|
|
|
|
&action_builder,
|
|
|
|
&hints_builder,
|
|
|
|
-1); /* expire_timeout */
|
|
|
|
|
|
|
|
g_dbus_connection_call (con, "org.freedesktop.Notifications", "/org/freedesktop/Notifications",
|
|
|
|
"org.freedesktop.Notifications", "Notify",
|
|
|
|
parameters, G_VARIANT_TYPE ("(u)"),
|
|
|
|
G_DBUS_CALL_FLAGS_NONE, -1, NULL,
|
|
|
|
callback, user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
notification_sent (GObject *source_object,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
FreedesktopNotification *n = user_data;
|
|
|
|
GVariant *val;
|
|
|
|
GError *error = NULL;
|
|
|
|
static gboolean warning_printed = FALSE;
|
|
|
|
|
|
|
|
val = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), result, &error);
|
|
|
|
if (val)
|
|
|
|
{
|
gfdonotificationbackend: Fix possible invalid pointer in dbus callback
The way things were before: a FreedesktopNotification struct is
allocated before the dbus call, and this same struct is possibly re-used
for other dbus calls. If the server becomes unavailable, the callback
will be invoked after the call times out, which leaves a long time where
other dbus calls can happen, re-using the same FreedesktopNotification
as user data. When the first call times out, the callback is invoked,
and the user data is freed. Subsequent calls that used the same user
data will time out later on, and try to free a pointer that was already
freed, hence segfaults.
This bug can be reproduced in Cinnamon 3.6.7, as mentioned in:
<https://github.com/linuxmint/Cinnamon/issues/7491>
This commit fixes that by always allocating a new
FreedesktopNotification before invoking dbus_call(), ensuring that the
callback always have a valid user data.
Signed-off-by: Arnaud Rebillout <elboulangero@gmail.com>
2018-06-10 15:56:12 +02:00
|
|
|
GFdoNotificationBackend *backend = n->backend;
|
|
|
|
FreedesktopNotification *match;
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
g_variant_get (val, "(u)", &n->notify_id);
|
|
|
|
g_variant_unref (val);
|
gfdonotificationbackend: Fix possible invalid pointer in dbus callback
The way things were before: a FreedesktopNotification struct is
allocated before the dbus call, and this same struct is possibly re-used
for other dbus calls. If the server becomes unavailable, the callback
will be invoked after the call times out, which leaves a long time where
other dbus calls can happen, re-using the same FreedesktopNotification
as user data. When the first call times out, the callback is invoked,
and the user data is freed. Subsequent calls that used the same user
data will time out later on, and try to free a pointer that was already
freed, hence segfaults.
This bug can be reproduced in Cinnamon 3.6.7, as mentioned in:
<https://github.com/linuxmint/Cinnamon/issues/7491>
This commit fixes that by always allocating a new
FreedesktopNotification before invoking dbus_call(), ensuring that the
callback always have a valid user data.
Signed-off-by: Arnaud Rebillout <elboulangero@gmail.com>
2018-06-10 15:56:12 +02:00
|
|
|
|
|
|
|
match = g_fdo_notification_backend_find_notification_by_notify_id (backend, n->notify_id);
|
|
|
|
if (match != NULL)
|
|
|
|
{
|
|
|
|
backend->notifications = g_slist_remove (backend->notifications, match);
|
|
|
|
freedesktop_notification_free (match);
|
|
|
|
}
|
|
|
|
backend->notifications = g_slist_prepend (backend->notifications, n);
|
2013-10-15 17:21:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!warning_printed)
|
|
|
|
{
|
|
|
|
g_warning ("unable to send notifications through org.freedesktop.Notifications: %s",
|
|
|
|
error->message);
|
|
|
|
warning_printed = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
freedesktop_notification_free (n);
|
|
|
|
g_error_free (error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_fdo_notification_backend_dispose (GObject *object)
|
|
|
|
{
|
|
|
|
GFdoNotificationBackend *backend = G_FDO_NOTIFICATION_BACKEND (object);
|
|
|
|
|
2020-01-13 14:13:28 +01:00
|
|
|
if (backend->bus_name_id)
|
|
|
|
{
|
|
|
|
g_bus_unwatch_name (backend->bus_name_id);
|
|
|
|
backend->bus_name_id = 0;
|
|
|
|
}
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
if (backend->notify_subscription)
|
|
|
|
{
|
|
|
|
GDBusConnection *session_bus;
|
|
|
|
|
2013-10-21 18:54:19 +02:00
|
|
|
session_bus = G_NOTIFICATION_BACKEND (backend)->dbus_connection;
|
2013-10-15 17:21:10 +02:00
|
|
|
g_dbus_connection_signal_unsubscribe (session_bus, backend->notify_subscription);
|
|
|
|
backend->notify_subscription = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (backend->notifications)
|
|
|
|
{
|
|
|
|
g_slist_free_full (backend->notifications, freedesktop_notification_free);
|
|
|
|
backend->notifications = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (g_fdo_notification_backend_parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
g_fdo_notification_backend_is_supported (void)
|
|
|
|
{
|
|
|
|
/* This is the fallback backend with the lowest priority. To avoid an
|
|
|
|
* unnecessary synchronous dbus call to check for
|
|
|
|
* org.freedesktop.Notifications, this function always succeeds. A
|
|
|
|
* warning will be printed when sending the first notification fails.
|
|
|
|
*/
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_fdo_notification_backend_send_notification (GNotificationBackend *backend,
|
|
|
|
const gchar *id,
|
|
|
|
GNotification *notification)
|
|
|
|
{
|
|
|
|
GFdoNotificationBackend *self = G_FDO_NOTIFICATION_BACKEND (backend);
|
gfdonotificationbackend: Fix possible invalid pointer in dbus callback
The way things were before: a FreedesktopNotification struct is
allocated before the dbus call, and this same struct is possibly re-used
for other dbus calls. If the server becomes unavailable, the callback
will be invoked after the call times out, which leaves a long time where
other dbus calls can happen, re-using the same FreedesktopNotification
as user data. When the first call times out, the callback is invoked,
and the user data is freed. Subsequent calls that used the same user
data will time out later on, and try to free a pointer that was already
freed, hence segfaults.
This bug can be reproduced in Cinnamon 3.6.7, as mentioned in:
<https://github.com/linuxmint/Cinnamon/issues/7491>
This commit fixes that by always allocating a new
FreedesktopNotification before invoking dbus_call(), ensuring that the
callback always have a valid user data.
Signed-off-by: Arnaud Rebillout <elboulangero@gmail.com>
2018-06-10 15:56:12 +02:00
|
|
|
FreedesktopNotification *n, *tmp;
|
2013-10-15 17:21:10 +02:00
|
|
|
|
2020-01-13 14:13:28 +01:00
|
|
|
if (self->bus_name_id == 0)
|
|
|
|
{
|
|
|
|
self->bus_name_id = g_bus_watch_name_on_connection (backend->dbus_connection,
|
|
|
|
"org.freedesktop.Notifications",
|
|
|
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
|
|
|
NULL,
|
|
|
|
name_vanished_handler_cb,
|
|
|
|
backend,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2013-10-15 17:21:10 +02:00
|
|
|
if (self->notify_subscription == 0)
|
|
|
|
{
|
|
|
|
self->notify_subscription =
|
2013-10-21 18:54:19 +02:00
|
|
|
g_dbus_connection_signal_subscribe (backend->dbus_connection,
|
2013-10-15 17:21:10 +02:00
|
|
|
"org.freedesktop.Notifications",
|
|
|
|
"org.freedesktop.Notifications", NULL,
|
|
|
|
"/org/freedesktop/Notifications", NULL,
|
|
|
|
G_DBUS_SIGNAL_FLAGS_NONE,
|
|
|
|
notify_signal, backend, NULL);
|
|
|
|
}
|
|
|
|
|
gfdonotificationbackend: Fix possible invalid pointer in dbus callback
The way things were before: a FreedesktopNotification struct is
allocated before the dbus call, and this same struct is possibly re-used
for other dbus calls. If the server becomes unavailable, the callback
will be invoked after the call times out, which leaves a long time where
other dbus calls can happen, re-using the same FreedesktopNotification
as user data. When the first call times out, the callback is invoked,
and the user data is freed. Subsequent calls that used the same user
data will time out later on, and try to free a pointer that was already
freed, hence segfaults.
This bug can be reproduced in Cinnamon 3.6.7, as mentioned in:
<https://github.com/linuxmint/Cinnamon/issues/7491>
This commit fixes that by always allocating a new
FreedesktopNotification before invoking dbus_call(), ensuring that the
callback always have a valid user data.
Signed-off-by: Arnaud Rebillout <elboulangero@gmail.com>
2018-06-10 15:56:12 +02:00
|
|
|
n = freedesktop_notification_new (self, id, notification);
|
2013-10-15 17:21:10 +02:00
|
|
|
|
gfdonotificationbackend: Fix possible invalid pointer in dbus callback
The way things were before: a FreedesktopNotification struct is
allocated before the dbus call, and this same struct is possibly re-used
for other dbus calls. If the server becomes unavailable, the callback
will be invoked after the call times out, which leaves a long time where
other dbus calls can happen, re-using the same FreedesktopNotification
as user data. When the first call times out, the callback is invoked,
and the user data is freed. Subsequent calls that used the same user
data will time out later on, and try to free a pointer that was already
freed, hence segfaults.
This bug can be reproduced in Cinnamon 3.6.7, as mentioned in:
<https://github.com/linuxmint/Cinnamon/issues/7491>
This commit fixes that by always allocating a new
FreedesktopNotification before invoking dbus_call(), ensuring that the
callback always have a valid user data.
Signed-off-by: Arnaud Rebillout <elboulangero@gmail.com>
2018-06-10 15:56:12 +02:00
|
|
|
tmp = g_fdo_notification_backend_find_notification (self, id);
|
|
|
|
if (tmp)
|
|
|
|
n->notify_id = tmp->notify_id;
|
2013-10-15 17:21:10 +02:00
|
|
|
|
2013-10-21 18:54:19 +02:00
|
|
|
call_notify (backend->dbus_connection, backend->application, n->notify_id, notification, notification_sent, n);
|
2013-10-15 17:21:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_fdo_notification_backend_withdraw_notification (GNotificationBackend *backend,
|
|
|
|
const gchar *id)
|
|
|
|
{
|
|
|
|
GFdoNotificationBackend *self = G_FDO_NOTIFICATION_BACKEND (backend);
|
|
|
|
FreedesktopNotification *n;
|
|
|
|
|
|
|
|
n = g_fdo_notification_backend_find_notification (self, id);
|
|
|
|
if (n)
|
|
|
|
{
|
|
|
|
if (n->notify_id > 0)
|
|
|
|
{
|
2013-10-21 18:54:19 +02:00
|
|
|
g_dbus_connection_call (backend->dbus_connection,
|
2013-10-15 17:21:10 +02:00
|
|
|
"org.freedesktop.Notifications",
|
|
|
|
"/org/freedesktop/Notifications",
|
|
|
|
"org.freedesktop.Notifications", "CloseNotification",
|
2016-12-07 19:32:33 +01:00
|
|
|
g_variant_new ("(u)", n->notify_id), NULL,
|
2013-10-15 17:21:10 +02:00
|
|
|
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
self->notifications = g_slist_remove (self->notifications, n);
|
|
|
|
freedesktop_notification_free (n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_fdo_notification_backend_init (GFdoNotificationBackend *backend)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_fdo_notification_backend_class_init (GFdoNotificationBackendClass *class)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
GNotificationBackendClass *backend_class = G_NOTIFICATION_BACKEND_CLASS (class);
|
|
|
|
|
|
|
|
object_class->dispose = g_fdo_notification_backend_dispose;
|
|
|
|
|
|
|
|
backend_class->is_supported = g_fdo_notification_backend_is_supported;
|
|
|
|
backend_class->send_notification = g_fdo_notification_backend_send_notification;
|
|
|
|
backend_class->withdraw_notification = g_fdo_notification_backend_withdraw_notification;
|
|
|
|
}
|