Commit ff6ae6b8 authored by Milan Crha's avatar Milan Crha

Replace evolution-dbus-session tool with DBUS_SERVICES_PREFIX option

The option is still meant to be used in Flatpak only, to prefix
the D-Bus services with certain prefix (usually the same as the Flatpak
application ID), thus the services do not clash with those installed
in the host system. It fixes many issues in evolution-dbus-session.

Related to GNOME/evolution#165
parent 2326d014
......@@ -30,13 +30,13 @@ set(PROJECT_DISTCONFIGURE_PARAMS
-DENABLE_VALA_BINDINGS=ON
-DENABLE_INSTALLED_TESTS=ON
-DENABLE_GTK_DOC=ON
-DENABLE_DBUS_SESSION_TOOL=ON
-DWITH_PRIVATE_DOCS=ON
)
# ******************************
# D-Bus versioning
# ******************************
# Actual name can be modified with DBUS_SERVICES_PREFIX option
set(ADDRESS_BOOK_DBUS_SERVICE_NAME "org.gnome.evolution.dataserver.AddressBook9")
set(CALENDAR_DBUS_SERVICE_NAME "org.gnome.evolution.dataserver.Calendar7")
set(SOURCES_DBUS_SERVICE_NAME "org.gnome.evolution.dataserver.Sources5")
......@@ -170,6 +170,15 @@ ensure_default_value(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share")
ensure_default_value(LOCALE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/locale")
ensure_default_value(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/etc")
add_printable_variable(DBUS_SERVICES_PREFIX "Prefix for D-Bus services, usually left empty, without trailing dot" "")
if(NOT ("${DBUS_SERVICES_PREFIX}" STREQUAL ""))
set(ADDRESS_BOOK_DBUS_SERVICE_NAME "${DBUS_SERVICES_PREFIX}.${ADDRESS_BOOK_DBUS_SERVICE_NAME}")
set(CALENDAR_DBUS_SERVICE_NAME "${DBUS_SERVICES_PREFIX}.${CALENDAR_DBUS_SERVICE_NAME}")
set(SOURCES_DBUS_SERVICE_NAME "${DBUS_SERVICES_PREFIX}.${SOURCES_DBUS_SERVICE_NAME}")
set(USER_PROMPTER_DBUS_SERVICE_NAME "${DBUS_SERVICES_PREFIX}.${USER_PROMPTER_DBUS_SERVICE_NAME}")
endif(NOT ("${DBUS_SERVICES_PREFIX}" STREQUAL ""))
# ******************************
# Special directories
# ******************************
......@@ -980,12 +989,6 @@ if(ENABLE_VALA_BINDINGS)
endif(ENABLE_VALA_BINDINGS)
# ******************************
# D-Bus session tool, a Flatpak helper
# ******************************
add_printable_option(ENABLE_DBUS_SESSION_TOOL "Build evolution-dbus-session tool" OFF)
# Generate the ${PROJECT_NAME}-config.h file
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-config.h)
......
add_subdirectory(addressbook-export)
add_subdirectory(list-sources)
if(ENABLE_DBUS_SESSION_TOOL)
add_subdirectory(evolution-dbus-session)
endif(ENABLE_DBUS_SESSION_TOOL)
set(SOURCES
evolution-dbus-session.c
)
add_executable(evolution-dbus-session
${SOURCES}
)
target_compile_definitions(evolution-dbus-session PRIVATE
-DG_LOG_DOMAIN=\"evolution-dbus-session\"
)
target_compile_options(edataserver PUBLIC
${GNOME_PLATFORM_CFLAGS}
)
target_include_directories(evolution-dbus-session PUBLIC
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/src
${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR}
${GNOME_PLATFORM_INCLUDE_DIRS}
)
target_link_libraries(evolution-dbus-session
${GNOME_PLATFORM_LDFLAGS}
)
install(TARGETS evolution-dbus-session
DESTINATION ${privlibexecdir}
)
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
*
* This program 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.
*
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "evolution-data-server-config.h"
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#define DBUS_CALL_TIMEOUT -1 /* as set by D-Bus settings */
static void
object_manager_object_added_cb (GDBusObjectManager *manager,
GDBusObject *object,
gpointer user_data);
static void
object_manager_object_removed_cb (GDBusObjectManager *manager,
GDBusObject *object,
gpointer user_data);
typedef struct _ProxyData {
gchar *iface_name;
gchar *path;
/* The original D-Bus session part */
GDBusProxy *proxy;
GDBusObjectManager *object_manager;
GSList *sigids; /* GUINT_TO_POINTER(sigid) */
/* The new D-Bus session part */
GDBusConnection *regs_connection;
guint ownid;
GSList *regids; /* GUINT_TO_POINTER(regid) */
GSList *object_manager_pds; /* ProxyData *, when object_manager is not NULL */
} ProxyData;
static void
proxy_data_free (gpointer ptr)
{
ProxyData *pd = ptr;
if (pd) {
if (pd->object_manager)
g_signal_handlers_disconnect_by_data (pd->object_manager, pd);
g_slist_free_full (pd->object_manager_pds, proxy_data_free);
if (pd->regs_connection && !g_dbus_connection_is_closed (pd->regs_connection)) {
GSList *link;
for (link = pd->regids; link; link = g_slist_next (link)) {
guint regid = GPOINTER_TO_UINT (link->data);
g_dbus_connection_unregister_object (pd->regs_connection, regid);
}
if (pd->object_manager) {
GList *objects, *llink;
objects = g_dbus_object_manager_get_objects (pd->object_manager);
for (llink = objects; llink; llink = g_list_next (llink)) {
GDBusObject *object = llink->data;
object_manager_object_removed_cb (pd->object_manager, object, pd);
}
g_list_free_full (objects, g_object_unref);
}
}
if (pd->ownid) {
g_bus_unown_name (pd->ownid);
pd->ownid = 0;
}
if (pd->proxy && pd->sigids) {
GDBusConnection *connection;
connection = g_dbus_proxy_get_connection (pd->proxy);
if (connection && !g_dbus_connection_is_closed (connection)) {
GSList *link;
for (link = pd->sigids; link; link = g_slist_next (link)) {
guint sigid = GPOINTER_TO_UINT (link->data);
g_dbus_connection_signal_unsubscribe (connection, sigid);
}
}
}
g_clear_object (&pd->proxy);
g_clear_object (&pd->object_manager);
g_clear_object (&pd->regs_connection);
g_slist_free (pd->regids);
g_slist_free (pd->sigids);
g_free (pd->iface_name);
g_free (pd->path);
g_free (pd);
}
}
/* encoded_string :== iface_name + ":" + path */
static ProxyData *
proxy_data_new (const gchar *encoded_string)
{
ProxyData *pd;
gchar **strv;
GError *error = NULL;
strv = g_strsplit (encoded_string, ":", -1);
g_return_val_if_fail (strv != NULL, NULL);
if (!strv[0] || !strv[1] || strv[2]) {
g_warning ("Unexpected proxy data string format: '%s'", encoded_string);
g_strfreev (strv);
return NULL;
}
pd = g_new0 (ProxyData, 1);
pd->iface_name = g_strdup (strv[0]); /* like "org.freedesktop.portal.Desktop" */
pd->path = g_strdup (strv[1]); /* like "/org/freedesktop/portal/desktop" */
pd->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL,
pd->iface_name, pd->path, pd->iface_name, NULL, &error);
if (!pd->proxy) {
g_warning ("Failed to open proxy for interface '%s' at path '%s': %s", pd->iface_name, pd->path, error ? error->message : "Unknown error");
proxy_data_free (pd);
pd = NULL;
}
g_clear_error (&error);
g_strfreev (strv);
return pd;
}
static void
handle_method_call_cb (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
#ifdef G_OS_UNIX
GDBusMessage *message;
GUnixFDList *in_fd_list, *out_fd_list = NULL;
#endif
ProxyData *pd = user_data;
GVariant *result;
GError *error = NULL;
g_return_if_fail (pd != NULL);
#ifdef G_OS_UNIX
message = g_dbus_method_invocation_get_message (invocation);
in_fd_list = g_dbus_message_get_unix_fd_list (message);
result = g_dbus_connection_call_with_unix_fd_list_sync (g_dbus_proxy_get_connection (pd->proxy),
g_dbus_proxy_get_name (pd->proxy),
object_path,
interface_name,
method_name,
parameters,
NULL,
G_DBUS_CALL_FLAGS_NONE,
DBUS_CALL_TIMEOUT,
in_fd_list,
&out_fd_list,
NULL, &error);
#else
result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (pd->proxy),
g_dbus_proxy_get_name (pd->proxy),
object_path,
interface_name,
method_name,
parameters,
NULL,
G_DBUS_CALL_FLAGS_NONE,
DBUS_CALL_TIMEOUT,
NULL, &error);
#endif
if (result) {
#ifdef G_OS_UNIX
if (out_fd_list) {
g_dbus_method_invocation_return_value_with_unix_fd_list (invocation, result, out_fd_list);
g_object_unref (out_fd_list);
} else {
g_dbus_method_invocation_return_value (invocation, result);
}
#else
g_dbus_method_invocation_return_value (invocation, result);
#endif
g_variant_unref (result);
} else {
g_dbus_method_invocation_return_gerror (invocation, error);
}
g_clear_error (&error);
}
static GVariant *
handle_get_property_cb (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
ProxyData *pd = user_data;
g_return_val_if_fail (pd != NULL, NULL);
return g_dbus_connection_call_sync (g_dbus_proxy_get_connection (pd->proxy),
g_dbus_proxy_get_name (pd->proxy),
object_path,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)", interface_name, property_name),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_CALL_TIMEOUT,
NULL, error);
}
static gboolean
handle_set_property_cb (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data)
{
ProxyData *pd = user_data;
GVariant *result;
GError *local_error = NULL;
g_return_val_if_fail (pd != NULL, FALSE);
result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (pd->proxy),
g_dbus_proxy_get_name (pd->proxy),
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)", interface_name, property_name, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
DBUS_CALL_TIMEOUT,
NULL, &local_error);
if (result)
g_variant_unref (result);
if (local_error)
g_propagate_error (error, local_error);
return local_error != NULL;
}
static void
handle_signal_cb (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
ProxyData *pd = user_data;
GError *error = NULL;
g_return_if_fail (pd != NULL);
if (!g_dbus_connection_emit_signal (pd->regs_connection, NULL, object_path,
interface_name, signal_name, parameters, &error)) {
g_clear_error (&error);
}
}
static void
proxy_data_reg_interfaces_for_object_path (ProxyData *pd,
GDBusConnection *connection,
const gchar *object_path)
{
const GDBusInterfaceVTable interface_vtable = {
handle_method_call_cb,
handle_get_property_cb,
handle_set_property_cb
};
GDBusNodeInfo *introspection_data;
GVariant *result;
const gchar *xml_data;
guint regid;
gint ii;
gboolean is_object_manager = FALSE;
GError *error = NULL;
g_return_if_fail (pd != NULL);
g_return_if_fail (pd->proxy != NULL);
g_return_if_fail (connection != NULL);
result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (pd->proxy),
g_dbus_proxy_get_name (pd->proxy),
object_path,
"org.freedesktop.DBus.Introspectable",
"Introspect",
NULL,
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_CALL_TIMEOUT,
NULL, &error);
if (!result) {
g_warning ("Failed to get introspected data for '%s': %s", g_dbus_proxy_get_name (pd->proxy), error ? error->message : "Unknown error");
g_clear_error (&error);
return;
}
g_variant_get (result, "(&s)", &xml_data);
introspection_data = g_dbus_node_info_new_for_xml (xml_data, NULL);
g_variant_unref (result);
if (!introspection_data) {
g_warning ("Failed to parse introspected data for '%s': %s", g_dbus_proxy_get_name (pd->proxy), error ? error->message : "Unknown error");
g_clear_error (&error);
return;
}
pd->regs_connection = g_object_ref (connection);
for (ii = 0; introspection_data->interfaces && introspection_data->interfaces[ii]; ii++) {
GDBusInterfaceInfo *iface_info = introspection_data->interfaces[ii];
if (!iface_info)
continue;
regid = g_dbus_connection_register_object (pd->regs_connection, object_path,
iface_info,
&interface_vtable,
pd,
NULL,
&error);
if (regid) {
pd->regids = g_slist_prepend (pd->regids, GUINT_TO_POINTER (regid));
if (iface_info->signals) {
gint jj;
for (jj = 0; iface_info->signals[jj]; jj++) {
guint sigid;
sigid = g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (pd->proxy), NULL,
iface_info->name,
iface_info->signals[jj]->name,
object_path,
NULL, G_DBUS_SIGNAL_FLAGS_NONE,
handle_signal_cb, pd, NULL);
if (sigid)
pd->sigids = g_slist_prepend (pd->sigids, GUINT_TO_POINTER (sigid));
}
}
if (g_strcmp0 (iface_info->name, "org.freedesktop.DBus.ObjectManager") == 0) {
is_object_manager = TRUE;
pd->object_manager = g_dbus_object_manager_client_new_sync (g_dbus_proxy_get_connection (pd->proxy),
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
g_dbus_proxy_get_name (pd->proxy),
object_path,
NULL, NULL, NULL, NULL, &error);
if (pd->object_manager) {
GList *objects, *link;
g_signal_connect (pd->object_manager, "object-added",
G_CALLBACK (object_manager_object_added_cb), pd);
g_signal_connect (pd->object_manager, "object-removed",
G_CALLBACK (object_manager_object_removed_cb), pd);
objects = g_dbus_object_manager_get_objects (pd->object_manager);
for (link = objects; link; link = g_list_next (link)) {
GDBusObject *object = link->data;
object_manager_object_added_cb (pd->object_manager, object, pd);
}
g_list_free_full (objects, g_object_unref);
} else {
g_clear_error (&error);
}
}
} else {
g_clear_error (&error);
}
}
if (!is_object_manager && introspection_data->nodes) {
for (ii = 0; introspection_data->nodes[ii]; ii++) {
GDBusNodeInfo *node_info = introspection_data->nodes[ii];
if (node_info && node_info->path) {
gchar *path;
if (node_info->path[0] == '/')
path = g_strdup (node_info->path);
else
path = g_strconcat (object_path, "/", node_info->path, NULL);
if (g_variant_is_object_path (path))
proxy_data_reg_interfaces_for_object_path (pd, connection, path);
g_free (path);
}
}
}
g_dbus_node_info_unref (introspection_data);
}
static void
proxy_data_reg_interfaces (ProxyData *pd,
GDBusConnection *connection)
{
g_return_if_fail (pd != NULL);
g_return_if_fail (pd->proxy != NULL);
g_return_if_fail (pd->regs_connection == NULL);
g_return_if_fail (connection != NULL);
proxy_data_reg_interfaces_for_object_path (pd, connection, g_dbus_proxy_get_object_path (pd->proxy));
}
static void
bus_name_acquired_cb (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
ProxyData *pd = user_data;
g_return_if_fail (pd != NULL);
proxy_data_reg_interfaces (pd, connection);
}
static void
bus_name_lost_cb (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
ProxyData *pd = user_data;
g_return_if_fail (pd != NULL);
}
static void
object_manager_object_added_cb (GDBusObjectManager *manager,
GDBusObject *object,
gpointer user_data)
{
ProxyData *parent_pd = user_data, *child_pd;
gchar *encoded_string;
encoded_string = g_strconcat (parent_pd->iface_name, ":", g_dbus_object_get_object_path (object), NULL);
child_pd = proxy_data_new (encoded_string);
g_free (encoded_string);
if (child_pd) {
proxy_data_reg_interfaces (child_pd, parent_pd->regs_connection);
parent_pd->object_manager_pds = g_slist_prepend (parent_pd->object_manager_pds, child_pd);
}
}
static void
object_manager_object_removed_cb (GDBusObjectManager *manager,
GDBusObject *object,
gpointer user_data)
{
ProxyData *parent_pd = user_data;
GSList *link;
const gchar *obj_path;
obj_path = g_dbus_object_get_object_path (object);
for (link = parent_pd->object_manager_pds; link; link = g_slist_next (link)) {
ProxyData *child_pd = link->data;
if (child_pd && g_strcmp0 (child_pd->path, obj_path) == 0) {
proxy_data_free (child_pd);
parent_pd->object_manager_pds = g_slist_remove (parent_pd->object_manager_pds, child_pd);
break;
}
}
}