[evolution-data-server/account-mgmt: 6/37] Add a new "evolution-source-registry" D-Bus service.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/account-mgmt: 6/37] Add a new "evolution-source-registry" D-Bus service.
- Date: Sat, 1 Oct 2011 15:46:46 +0000 (UTC)
commit 8299648e4a2e00110e59d7d9516933fe3f9a8c1f
Author: Matthew Barnes <mbarnes redhat com>
Date: Thu Sep 29 16:08:20 2011 -0400
Add a new "evolution-source-registry" D-Bus service.
This new service manages data source key files and serves them to
clients, through the ESource and ESourceRegistry client-side APIs.
configure.ac | 23 +-
docs/reference/libebackend/libebackend-docs.xml | 4 +-
.../reference/libebackend/libebackend-sections.txt | 52 ++
.../libebackend/tmpl/e-dbus-source-object.sgml | 132 ++++
.../libebackend/tmpl/e-dbus-source-server.sgml | 120 ++++
libebackend/Makefile.am | 7 +-
libebackend/e-dbus-source-object.c | 615 ++++++++++++++++++
libebackend/e-dbus-source-object.h | 98 +++
libebackend/e-dbus-source-server.c | 681 ++++++++++++++++++++
libebackend/e-dbus-source-server.h | 105 +++
services/Makefile.am | 1 +
services/evolution-source-registry/Makefile.am | 32 +
.../evolution-source-registry.c | 66 ++
...g.gnome.evolution.dataserver.Sources.service.in | 3 +
14 files changed, 1930 insertions(+), 9 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 9742f4a..360fedf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,6 +60,7 @@ dnl D-Bus versioning
dnl ******************************
ADDRESS_BOOK_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.AddressBook1"
CALENDAR_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Calendar1"
+SOURCES_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Sources0"
AC_DEFINE_UNQUOTED(
ADDRESS_BOOK_DBUS_SERVICE_NAME,
@@ -71,8 +72,14 @@ AC_DEFINE_UNQUOTED(
["$CALENDAR_DBUS_SERVICE_NAME"],
[D-Bus service name for the calendar factory])
+AC_DEFINE_UNQUOTED(
+ SOURCES_DBUS_SERVICE_NAME,
+ ["$SOURCES_DBUS_SERVICE_NAME"],
+ [D-Bus service name for the source registry])
+
AC_SUBST(ADDRESS_BOOK_DBUS_SERVICE_NAME)
AC_SUBST(CALENDAR_DBUS_SERVICE_NAME)
+AC_SUBST(SOURCES_DBUS_SERVICE_NAME)
dnl ******************************
dnl Libtool versioning
@@ -384,14 +391,15 @@ if test "x$enable_goa" = xyes; then
fi
AM_CONDITIONAL(HAVE_GOA, [test x$enable_goa = xyes])
+dnl ***********************************
+dnl Check for GNOME Keyring.
+dnl ***********************************
if test x$os_win32 = xno; then
- dnl ***********************************
- dnl Check for GNOME Keyring.
- dnl ***********************************
- PKG_CHECK_MODULES(GNOME_KEYRING,
- [gnome-keyring-1 >= gnome_keyring_minimum_version])
+ PKG_CHECK_MODULES(GNOME_KEYRING,
+ [gnome-keyring-1 >= gnome_keyring_minimum_version])
fi
-
+AC_SUBST(GNOME_KEYRING_CFLAGS)
+AC_SUBST(GNOME_KEYRING_LIBS)
LIBICAL_REQUIRED=libical_minimum_version
AC_SUBST(LIBICAL_REQUIRED)
@@ -1489,7 +1497,7 @@ dnl *******************
dnl D-BUS service stuff
dnl *******************
m4_pattern_allow([AM_V_GEN])
-EVO_SUBST_SERVICE_RULE='%.service: %.service.in Makefile ; $(AM_V_GEN) sed -e "s|\ libexecdir\@|$(libexecdir)|" -e s"|\ ADDRESS_BOOK_DBUS_SERVICE_NAME\@|$(ADDRESS_BOOK_DBUS_SERVICE_NAME)|" -e "s|\ CALENDAR_DBUS_SERVICE_NAME\@|$(CALENDAR_DBUS_SERVICE_NAME)|" $< > $@'
+EVO_SUBST_SERVICE_RULE='%.service: %.service.in Makefile ; $(AM_V_GEN) sed -e "s|\ libexecdir\@|$(libexecdir)|" -e s"|\ ADDRESS_BOOK_DBUS_SERVICE_NAME\@|$(ADDRESS_BOOK_DBUS_SERVICE_NAME)|" -e "s|\ CALENDAR_DBUS_SERVICE_NAME\@|$(CALENDAR_DBUS_SERVICE_NAME)|" -e "s|\ SOURCES_DBUS_SERVICE_NAME\@|$(SOURCES_DBUS_SERVICE_NAME)|" $< > $@'
AC_SUBST(EVO_SUBST_SERVICE_RULE)
dnl ******************************
@@ -1604,6 +1612,7 @@ libedataserverui/libedataserverui.pc
services/Makefile
services/evolution-addressbook-factory/Makefile
services/evolution-calendar-factory/Makefile
+services/evolution-source-registry/Makefile
tests/Makefile
tests/libebook/Makefile
tests/libebook/client/Makefile
diff --git a/docs/reference/libebackend/libebackend-docs.xml b/docs/reference/libebackend/libebackend-docs.xml
index 312b5f0..c774ee2 100644
--- a/docs/reference/libebackend/libebackend-docs.xml
+++ b/docs/reference/libebackend/libebackend-docs.xml
@@ -13,7 +13,9 @@
<xi:include href="xml/e-backend.xml"/>
<xi:include href="xml/e-backend-factory.xml"/>
<xi:include href="xml/e-data-factory.xml"/>
- <xi:include href="xml/e-dbus-service.xml"/>
+ <xi:include href="xml/e-dbus-server.xml"/>
+ <xi:include href="xml/e-dbus-source-object.xml"/>
+ <xi:include href="xml/e-dbus-source-server.xml"/>
<xi:include href="xml/e-extensible.xml"/>
<xi:include href="xml/e-extension.xml"/>
<xi:include href="xml/e-module.xml"/>
diff --git a/docs/reference/libebackend/libebackend-sections.txt b/docs/reference/libebackend/libebackend-sections.txt
index c54cdbc..b550b86 100644
--- a/docs/reference/libebackend/libebackend-sections.txt
+++ b/docs/reference/libebackend/libebackend-sections.txt
@@ -80,6 +80,58 @@ e_dbus_server_get_type
</SECTION>
<SECTION>
+<FILE>e-dbus-source-object</FILE>
+<TITLE>EDBusSourceObject</TITLE>
+EDBusSourceObject
+e_dbus_source_object_get_user_dir
+e_dbus_source_object_new
+e_dbus_source_object_get_file
+e_dbus_source_object_get_uid
+e_dbus_source_object_get_writable
+e_dbus_source_object_set_writable
+e_dbus_source_object_to_string
+e_dbus_source_object_parse
+e_dbus_source_object_reload
+e_dbus_source_object_write
+<SUBSECTION Standard>
+E_DBUS_SOURCE_OBJECT
+E_IS_DBUS_SOURCE_OBJECT
+E_TYPE_DBUS_SOURCE_OBJECT
+E_DBUS_SOURCE_OBJECT_CLASS
+E_IS_DBUS_SOURCE_OBJECT_CLASS
+E_DBUS_SOURCE_OBJECT_GET_CLASS
+EDBusSourceObjectClass
+<SUBSECTION Private>
+EDBusSourceObjectPrivate
+e_dbus_source_object_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-dbus-source-server</FILE>
+EDBusSourceServer
+e_dbus_source_server_new
+e_dbus_source_server_add_object
+e_dbus_source_server_remove_object
+e_dbus_source_server_load_all
+e_dbus_source_server_load_directory
+e_dbus_source_server_load_file
+e_dbus_source_server_load_error
+e_dbus_source_server_lookup_by_uid
+e_dbus_source_server_lookup_by_file
+<SUBSECTION Standard>
+E_DBUS_SOURCE_SERVER
+E_IS_DBUS_SOURCE_SERVER
+E_TYPE_DBUS_SOURCE_SERVER
+E_DBUS_SOURCE_SERVER_CLASS
+E_IS_DBUS_SOURCE_SERVER_CLASS
+E_DBUS_SOURCE_SERVER_GET_CLASS
+EDBusSourceServerClass
+<SUBSECTION Private>
+EDBusSourceServerPrivate
+e_dbus_source_server_get_type
+</SECTION>
+
+<SECTION>
<FILE>e-file-cache</FILE>
<TITLE>EFileCache</TITLE>
EFileCache
diff --git a/docs/reference/libebackend/tmpl/e-dbus-source-object.sgml b/docs/reference/libebackend/tmpl/e-dbus-source-object.sgml
new file mode 100644
index 0000000..7af46a3
--- /dev/null
+++ b/docs/reference/libebackend/tmpl/e-dbus-source-object.sgml
@@ -0,0 +1,132 @@
+<!-- ##### SECTION Title ##### -->
+e-dbus-source-object
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT EDBusSourceObject ##### -->
+<para>
+
+</para>
+
+ parent:
+ priv:
+
+<!-- ##### FUNCTION e_dbus_source_object_get_user_dir ##### -->
+<para>
+
+</para>
+
+ void:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_new ##### -->
+<para>
+
+</para>
+
+ file:
+ cancellable:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_get_file ##### -->
+<para>
+
+</para>
+
+ object:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_get_uid ##### -->
+<para>
+
+</para>
+
+ object:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_get_writable ##### -->
+<para>
+
+</para>
+
+ object:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_set_writable ##### -->
+<para>
+
+</para>
+
+ object:
+ writable:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_to_string ##### -->
+<para>
+
+</para>
+
+ object:
+ length:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_parse ##### -->
+<para>
+
+</para>
+
+ object:
+ data:
+ length:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_reload ##### -->
+<para>
+
+</para>
+
+ object:
+ cancellable:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_object_write ##### -->
+<para>
+
+</para>
+
+ object:
+ etag:
+ new_etag:
+ cancellable:
+ error:
+ Returns:
+
+
diff --git a/docs/reference/libebackend/tmpl/e-dbus-source-server.sgml b/docs/reference/libebackend/tmpl/e-dbus-source-server.sgml
new file mode 100644
index 0000000..1bb80e7
--- /dev/null
+++ b/docs/reference/libebackend/tmpl/e-dbus-source-server.sgml
@@ -0,0 +1,120 @@
+<!-- ##### SECTION Title ##### -->
+e-dbus-source-server
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT EDBusSourceServer ##### -->
+<para>
+
+</para>
+
+ parent:
+ priv:
+
+<!-- ##### FUNCTION e_dbus_source_server_new ##### -->
+<para>
+
+</para>
+
+ void:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_add_object ##### -->
+<para>
+
+</para>
+
+ server:
+ object:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_remove_object ##### -->
+<para>
+
+</para>
+
+ server:
+ object:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_load_all ##### -->
+<para>
+
+</para>
+
+ server:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_load_directory ##### -->
+<para>
+
+</para>
+
+ server:
+ path:
+ monitor_for_changes:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_load_file ##### -->
+<para>
+
+</para>
+
+ server:
+ file:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_load_error ##### -->
+<para>
+
+</para>
+
+ server:
+ file:
+ error:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_lookup_by_uid ##### -->
+<para>
+
+</para>
+
+ server:
+ uid:
+ Returns:
+
+
+<!-- ##### FUNCTION e_dbus_source_server_lookup_by_file ##### -->
+<para>
+
+</para>
+
+ server:
+ file:
+ Returns:
+
+
diff --git a/libebackend/Makefile.am b/libebackend/Makefile.am
index 562afce..1f78eb7 100644
--- a/libebackend/Makefile.am
+++ b/libebackend/Makefile.am
@@ -3,7 +3,8 @@ lib_LTLIBRARIES = libebackend-1.2.la
libebackend_1_2_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
-I$(top_srcdir) \
- -DG_LOG_DOMAIN=\"e-data-server\" \
+ -DG_LOG_DOMAIN=\"libebackend\" \
+ -DE_DATA_SERVER_PRIVDATADIR=\"$(privdatadir)\" \
$(DB_CFLAGS) \
$(SQLITE3_CFLAGS) \
$(E_BACKEND_CFLAGS) \
@@ -14,6 +15,8 @@ libebackend_1_2_la_SOURCES = \
e-backend-factory.c \
e-data-factory.c \
e-dbus-server.c \
+ e-dbus-source-object.c \
+ e-dbus-source-server.c \
e-extensible.c \
e-extension.c \
e-offline-listener.c \
@@ -40,6 +43,8 @@ libebackendinclude_HEADERS = \
e-backend-factory.h \
e-data-factory.h \
e-dbus-server.h \
+ e-dbus-source-object.h \
+ e-dbus-source-server.h \
e-extensible.h \
e-extension.h \
e-offline-listener.h \
diff --git a/libebackend/e-dbus-source-object.c b/libebackend/e-dbus-source-object.c
new file mode 100644
index 0000000..de6e205
--- /dev/null
+++ b/libebackend/e-dbus-source-object.c
@@ -0,0 +1,615 @@
+/*
+ * e-dbus-source-object.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-dbus-source-object.h"
+
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/e-dbus-manager.h>
+#include <libedataserver/e-data-server-util.h>
+
+#define E_DBUS_SOURCE_OBJECT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_DBUS_SOURCE_OBJECT, EDBusSourceObjectPrivate))
+
+#define REQD_GROUP_NAME "Data Source"
+#define DBUS_OBJECT_PATH "/org/gnome/evolution/dataserver/SourceManager/Source"
+
+struct _EDBusSourceObjectPrivate {
+ EDBusSource *interface;
+
+ GFile *file;
+ GKeyFile *key_file;
+ GFileMonitor *file_monitor;
+ gulong file_monitor_handler_id;
+
+ gboolean writable;
+};
+
+enum {
+ PROP_0,
+ PROP_FILE,
+ PROP_UID,
+ PROP_WRITABLE
+};
+
+/* Forward Declarations */
+static void e_dbus_source_object_initable_init
+ (GInitableIface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EDBusSourceObject,
+ e_dbus_source_object,
+ G_TYPE_DBUS_OBJECT_SKELETON,
+ G_IMPLEMENT_INTERFACE (
+ G_TYPE_INITABLE,
+ e_dbus_source_object_initable_init))
+
+static void
+dbus_source_object_monitor_changed_cb (GFileMonitor *file_monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ EDBusSourceObject *object)
+{
+ GError *error = NULL;
+
+ /* Ignore file deletion events. */
+ if (event_type != G_FILE_MONITOR_EVENT_DELETED)
+ e_dbus_source_object_reload (object, NULL, &error);
+
+ /* XXX Not much we can do beyond emitting a
+ * warning if we fail to reload the file. */
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+dbus_source_object_monitor_file (EDBusSourceObject *object,
+ GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileMonitor *file_monitor;
+
+ /* Attempt to create a new file monitor before destroying the
+ * old one, so if this fails the old file monitor will persist. */
+ file_monitor = g_file_monitor (
+ file, G_FILE_MONITOR_NONE, cancellable, error);
+
+ if (file_monitor == NULL)
+ return FALSE;
+
+ if (object->priv->file_monitor != NULL) {
+ g_signal_handler_disconnect (
+ object->priv->file_monitor,
+ object->priv->file_monitor_handler_id);
+ g_object_unref (object->priv->file_monitor);
+ }
+
+ object->priv->file_monitor = g_object_ref (file_monitor);
+
+ object->priv->file_monitor_handler_id = g_signal_connect (
+ object->priv->file_monitor, "changed",
+ G_CALLBACK (dbus_source_object_monitor_changed_cb),
+ object);
+
+ return TRUE;
+}
+
+static gboolean
+dbus_source_object_submit_data_cb (EDBusSource *interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *data)
+{
+ GDBusObject *dbus_object;
+ GDBusInterface *dbus_interface;
+ EDBusSourceObject *object;
+ const gchar *dirname;
+ gchar *basename;
+ gchar *filename;
+ GFile *file;
+ GError *error = NULL;
+
+ dbus_interface = G_DBUS_INTERFACE (interface);
+ dbus_object = g_dbus_interface_get_object (dbus_interface);
+ object = E_DBUS_SOURCE_OBJECT (dbus_object);
+
+ if (!e_dbus_source_object_get_writable (object)) {
+ g_dbus_method_invocation_return_error (
+ invocation, G_IO_ERROR,
+ G_IO_ERROR_PERMISSION_DENIED,
+ _("Data source '%s' is not writable"),
+ e_dbus_source_object_get_uid (object));
+ return TRUE;
+ }
+
+ /* When writing source data to disk, always write to the
+ * user directory, even if the key file was originally in
+ * the system-wide directory. */
+
+ dirname = e_dbus_source_object_get_user_dir ();
+ basename = g_file_get_basename (object->priv->file);
+ filename = g_build_filename (dirname, basename, NULL);
+ file = g_file_new_for_path (filename);
+ g_free (filename);
+ g_free (basename);
+
+ /* If we need to replace the GFile, make sure we
+ * successfully place a file monitor on it first. */
+ if (!g_file_equal (file, object->priv->file) &&
+ dbus_source_object_monitor_file (object, file, NULL, &error)) {
+ g_object_unref (object->priv->file);
+ object->priv->file = g_object_ref (file);
+ }
+
+ g_object_unref (file);
+
+ /* Write the data to disk. The file monitor should then
+ * notice the change and call e_dbus_source_object_reload(). */
+
+ if (error == NULL)
+ e_dbus_source_object_write (object, NULL, NULL, NULL, &error);
+
+ if (error != NULL)
+ g_dbus_method_invocation_take_error (invocation, error);
+
+ return TRUE;
+}
+
+static void
+dbus_source_object_set_file (EDBusSourceObject *object,
+ GFile *file)
+{
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (object->priv->file == NULL);
+
+ object->priv->file = g_object_ref (file);
+}
+
+static void
+dbus_source_object_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_FILE:
+ dbus_source_object_set_file (
+ E_DBUS_SOURCE_OBJECT (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_WRITABLE:
+ e_dbus_source_object_set_writable (
+ E_DBUS_SOURCE_OBJECT (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+dbus_source_object_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_FILE:
+ g_value_set_object (
+ value,
+ e_dbus_source_object_get_file (
+ E_DBUS_SOURCE_OBJECT (object)));
+ return;
+
+ case PROP_UID:
+ g_value_set_string (
+ value,
+ e_dbus_source_object_get_uid (
+ E_DBUS_SOURCE_OBJECT (object)));
+ return;
+
+ case PROP_WRITABLE:
+ g_value_set_boolean (
+ value,
+ e_dbus_source_object_get_writable (
+ E_DBUS_SOURCE_OBJECT (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+dbus_source_object_dispose (GObject *object)
+{
+ EDBusSourceObjectPrivate *priv;
+
+ priv = E_DBUS_SOURCE_OBJECT_GET_PRIVATE (object);
+
+ if (priv->interface != NULL) {
+ g_object_unref (priv->interface);
+ priv->interface = NULL;
+ }
+
+ if (priv->file != NULL) {
+ g_object_unref (priv->file);
+ priv->file = NULL;
+ }
+
+ if (priv->file_monitor != NULL) {
+ g_signal_handler_disconnect (
+ priv->file_monitor,
+ priv->file_monitor_handler_id);
+ g_object_unref (priv->file_monitor);
+ priv->file_monitor = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_dbus_source_object_parent_class)->dispose (object);
+}
+
+static void
+dbus_source_object_finalize (GObject *object)
+{
+ EDBusSourceObjectPrivate *priv;
+
+ priv = E_DBUS_SOURCE_OBJECT_GET_PRIVATE (object);
+
+ g_key_file_free (priv->key_file);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_dbus_source_object_parent_class)->finalize (object);
+}
+
+static void
+dbus_source_object_constructed (GObject *object)
+{
+ EDBusSourceObjectPrivate *priv;
+ const gchar *user_dir;
+ GFile *directory;
+ gchar *uid;
+
+ priv = E_DBUS_SOURCE_OBJECT_GET_PRIVATE (object);
+
+ uid = g_file_get_basename (priv->file);
+ e_dbus_source_set_uid (priv->interface, uid);
+ g_free (uid);
+
+ /* Assume the source is writable only if and only if
+ * it's in the current user-specific config directory. */
+ user_dir = e_dbus_source_object_get_user_dir ();
+ directory = g_file_new_for_path (user_dir);
+ priv->writable = g_file_has_prefix (priv->file, directory);
+ g_object_unref (directory);
+
+ g_object_bind_property (
+ object, "writable",
+ priv->interface, "writable",
+ G_BINDING_SYNC_CREATE);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_dbus_source_object_parent_class)->constructed (object);
+}
+
+static gboolean
+dbus_source_object_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EDBusSourceObject *object;
+ GFile *file;
+ gboolean success;
+
+ object = E_DBUS_SOURCE_OBJECT (initable);
+ file = e_dbus_source_object_get_file (object);
+
+ /* We do depend on a working file monitor, so the object
+ * should fail to initialize if we can't set one up. */
+
+ success =
+ dbus_source_object_monitor_file (
+ object, file, cancellable, error) &&
+ e_dbus_source_object_reload (object, cancellable, error);
+
+ return success;
+}
+
+static void
+e_dbus_source_object_class_init (EDBusSourceObjectClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EDBusSourceObjectPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = dbus_source_object_set_property;
+ object_class->get_property = dbus_source_object_get_property;
+ object_class->dispose = dbus_source_object_dispose;
+ object_class->finalize = dbus_source_object_finalize;
+ object_class->constructed = dbus_source_object_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FILE,
+ g_param_spec_object (
+ "file",
+ "File",
+ "The key file for the data source",
+ G_TYPE_FILE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_UID,
+ g_param_spec_string (
+ "uid",
+ "UID",
+ "The unique identity of the data source",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_WRITABLE,
+ g_param_spec_boolean (
+ "writable",
+ "Writable",
+ "Whether the file contents can be changed",
+ TRUE, /* default varies; see constructed() */
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_dbus_source_object_initable_init (GInitableIface *interface)
+{
+ interface->init = dbus_source_object_initable_init;
+}
+
+static void
+e_dbus_source_object_init (EDBusSourceObject *object)
+{
+ EDBusSource *interface;
+
+ interface = e_dbus_source_skeleton_new ();
+
+ object->priv = E_DBUS_SOURCE_OBJECT_GET_PRIVATE (object);
+ object->priv->interface = interface;
+ object->priv->key_file = g_key_file_new ();
+
+ g_dbus_object_skeleton_add_interface (
+ G_DBUS_OBJECT_SKELETON (object),
+ G_DBUS_INTERFACE_SKELETON (interface));
+
+ g_signal_connect (
+ interface, "handle-submit-data",
+ G_CALLBACK (dbus_source_object_submit_data_cb), NULL);
+}
+
+const gchar *
+e_dbus_source_object_get_user_dir (void)
+{
+ static gchar *dirname = NULL;
+
+ if (G_UNLIKELY (dirname == NULL)) {
+ const gchar *config_dir = e_get_user_config_dir ();
+ dirname = g_build_filename (config_dir, "sources", NULL);
+ g_mkdir_with_parents (dirname, 0700);
+ }
+
+ return dirname;
+}
+
+EDBusSourceObject *
+e_dbus_source_object_new (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ return g_initable_new (
+ E_TYPE_DBUS_SOURCE_OBJECT,
+ cancellable, error,
+ "file", file,
+ "object-path", DBUS_OBJECT_PATH,
+ NULL);
+}
+
+GFile *
+e_dbus_source_object_get_file (EDBusSourceObject *object)
+{
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), NULL);
+
+ return object->priv->file;
+}
+
+const gchar *
+e_dbus_source_object_get_uid (EDBusSourceObject *object)
+{
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), NULL);
+
+ return e_dbus_source_get_uid (object->priv->interface);
+}
+
+gboolean
+e_dbus_source_object_get_writable (EDBusSourceObject *object)
+{
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), FALSE);
+
+ return object->priv->writable;
+}
+
+void
+e_dbus_source_object_set_writable (EDBusSourceObject *object,
+ gboolean writable)
+{
+ g_return_if_fail (E_IS_DBUS_SOURCE_OBJECT (object));
+
+ object->priv->writable = writable;
+
+ g_object_notify (G_OBJECT (object), "writable");
+}
+
+gchar *
+e_dbus_source_object_to_string (EDBusSourceObject *object,
+ gsize *length)
+{
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), NULL);
+
+ return g_key_file_to_data (object->priv->key_file, length, NULL);
+}
+
+gboolean
+e_dbus_source_object_parse (EDBusSourceObject *object,
+ const gchar *data,
+ gsize length,
+ GError **error)
+{
+ GKeyFile *key_file;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ key_file = object->priv->key_file;
+
+ success = g_key_file_load_from_data (
+ key_file, data, length,
+ G_KEY_FILE_KEEP_COMMENTS |
+ G_KEY_FILE_KEEP_TRANSLATIONS,
+ error);
+
+ if (!success)
+ return FALSE;
+
+ /* Make sure the key file has a [Data Source] group.
+ * This is the only place where we peek at the data. */
+ if (!g_key_file_has_group (key_file, REQD_GROUP_NAME)) {
+ g_set_error (
+ error, G_KEY_FILE_ERROR,
+ G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+ _("Source file is missing a [%s] group"),
+ REQD_GROUP_NAME);
+ success = FALSE;
+ }
+
+ return success;
+}
+
+gboolean
+e_dbus_source_object_reload (EDBusSourceObject *object,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileInfo *file_info;
+ gchar *contents = NULL;
+ gchar *etag = NULL;
+ gboolean file_is_writable = TRUE;
+ gboolean success;
+ gsize length;
+
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), FALSE);
+
+ success = g_file_load_contents (
+ object->priv->file, cancellable,
+ &contents, &length, &etag, error);
+
+ if (!success)
+ goto exit;
+
+ g_return_val_if_fail (contents != NULL, FALSE);
+ g_return_val_if_fail (etag != NULL, FALSE);
+
+ success = e_dbus_source_object_parse (
+ object, contents, length, error);
+
+ if (!success)
+ goto exit;
+
+ file_info = g_file_query_info (
+ object->priv->file,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+ G_FILE_QUERY_INFO_NONE,
+ cancellable, NULL);
+
+ /* This part is optional, so ignore errors. */
+ if (file_info != NULL) {
+ file_is_writable = g_file_info_get_attribute_boolean (
+ file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+ g_object_unref (file_info);
+ }
+
+ /* Update the D-Bus interface properties. */
+
+ g_object_set (
+ object->priv->interface,
+ "data", contents, NULL);
+
+ /* If we cannot write to the key file then clearly the
+ * data source is not writable, but the inverse is not
+ * necessarily true: the data source may still not be
+ * writable even if its key file is. */
+ if (!file_is_writable)
+ e_dbus_source_set_writable (object->priv->interface, FALSE);
+
+exit:
+ g_free (contents);
+ g_free (etag);
+
+ return success;
+}
+
+gboolean
+e_dbus_source_object_write (EDBusSourceObject *object,
+ const gchar *etag,
+ gchar **new_etag,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFile *file;
+ gchar *contents;
+ gboolean success;
+ gsize length;
+
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), FALSE);
+
+ /* Note: Don't update D-Bus interface properties here.
+ * If we're successful, the file monitor will notice
+ * the change and call e_dbus_source_object_reload(). */
+
+ file = e_dbus_source_object_get_file (object);
+ contents = e_dbus_source_object_to_string (object, &length);
+
+ success = g_file_replace_contents (
+ file, contents, length, etag, FALSE,
+ G_FILE_CREATE_NONE, new_etag, NULL, error);
+
+ g_free (contents);
+
+ return success;
+}
+
diff --git a/libebackend/e-dbus-source-object.h b/libebackend/e-dbus-source-object.h
new file mode 100644
index 0000000..01d1860
--- /dev/null
+++ b/libebackend/e-dbus-source-object.h
@@ -0,0 +1,98 @@
+/*
+ * e-dbus-source-object.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_DBUS_SOURCE_OBJECT_H
+#define E_DBUS_SOURCE_OBJECT_H
+
+#include <gio/gio.h>
+
+/* Standard GObject macros */
+#define E_TYPE_DBUS_SOURCE_OBJECT \
+ (e_dbus_source_object_get_type ())
+#define E_DBUS_SOURCE_OBJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_DBUS_SOURCE_OBJECT, EDBusSourceObject))
+#define E_DBUS_SOURCE_OBJECT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_DBUS_SOURCE_OBJECT, EDBusSourceObjectClass))
+#define E_IS_DBUS_SOURCE_OBJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_DBUS_SOURCE_OBJECT))
+#define E_IS_DBUS_SOURCE_OBJECT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_DBUS_SOURCE_OBJECT))
+#define E_DBUS_SOURCE_OBJECT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_DBUS_SOURCE_OBJECT, EDBusSourceObjectClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EDBusSourceObject EDBusSourceObject;
+typedef struct _EDBusSourceObjectClass EDBusSourceObjectClass;
+typedef struct _EDBusSourceObjectPrivate EDBusSourceObjectPrivate;
+
+/**
+ * EDBusSourceObject:
+ *
+ * #EDBusSourceObject is a simplified representation of #ESource that only
+ * concerns itself with key file management and servicing D-Bus operations.
+ * The file content itself is treated as an opaque blob.
+ *
+ * Since: 3.4
+ **/
+struct _EDBusSourceObject {
+ GDBusObjectSkeleton parent;
+ EDBusSourceObjectPrivate *priv;
+};
+
+struct _EDBusSourceObjectClass {
+ GDBusObjectSkeletonClass parent_class;
+};
+
+GType e_dbus_source_object_get_type (void) G_GNUC_CONST;
+const gchar * e_dbus_source_object_get_user_dir
+ (void) G_GNUC_CONST;
+EDBusSourceObject *
+ e_dbus_source_object_new (GFile *file,
+ GCancellable *cancellable,
+ GError **error);
+GFile * e_dbus_source_object_get_file (EDBusSourceObject *object);
+const gchar * e_dbus_source_object_get_uid (EDBusSourceObject *object);
+gboolean e_dbus_source_object_get_writable
+ (EDBusSourceObject *object);
+void e_dbus_source_object_set_writable
+ (EDBusSourceObject *object,
+ gboolean writable);
+gchar * e_dbus_source_object_to_string (EDBusSourceObject *object,
+ gsize *length);
+gboolean e_dbus_source_object_parse (EDBusSourceObject *object,
+ const gchar *data,
+ gsize length,
+ GError **error);
+gboolean e_dbus_source_object_reload (EDBusSourceObject *object,
+ GCancellable *cancellable,
+ GError **error);
+gboolean e_dbus_source_object_write (EDBusSourceObject *object,
+ const gchar *etag,
+ gchar **new_etag,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_DBUS_SOURCE_OBJECT_H */
diff --git a/libebackend/e-dbus-source-server.c b/libebackend/e-dbus-source-server.c
new file mode 100644
index 0000000..1f6f392
--- /dev/null
+++ b/libebackend/e-dbus-source-server.c
@@ -0,0 +1,681 @@
+/*
+ * e-dbus-source-server.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-dbus-source-server.h"
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/e-uid.h>
+#include <libedataserver/e-marshal.h>
+#include <libedataserver/e-dbus-manager.h>
+#include <libedataserver/e-data-server-util.h>
+
+#define E_DBUS_SOURCE_SERVER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_DBUS_SOURCE_SERVER, EDBusSourceServerPrivate))
+
+#define DBUS_OBJECT_PATH \
+ "/org/gnome/evolution/dataserver/SourceManager"
+
+#define SYSTEM_WIDE_SOURCES_DIRECTORY \
+ E_DATA_SERVER_PRIVDATADIR G_DIR_SEPARATOR_S "sources"
+
+struct _EDBusSourceServerPrivate {
+ GDBusObjectManagerServer *object_manager;
+ EDBusSourceManager *source_manager;
+ GHashTable *objects;
+ GHashTable *monitors;
+};
+
+enum {
+ LOAD_ERROR,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+ EDBusSourceServer,
+ e_dbus_source_server,
+ E_TYPE_DBUS_SERVER)
+
+static gboolean
+dbus_source_server_create_source_cb (EDBusSourceManager *interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *data,
+ EDBusSourceServer *server)
+{
+ GFile *file;
+ GKeyFile *key_file;
+ const gchar *user_dir;
+ gchar *path;
+ gchar *uid;
+ gsize length;
+ GError *error = NULL;
+
+ length = strlen (data);
+
+ /* Make sure the data is syntactically valid. */
+ key_file = g_key_file_new ();
+ g_key_file_load_from_data (
+ key_file, data, length, G_KEY_FILE_NONE, &error);
+ g_key_file_free (key_file);
+
+ if (error != NULL) {
+ g_dbus_method_invocation_take_error (invocation, error);
+ return TRUE;
+ }
+
+ /* Generate a new UID and build a file path from it. */
+ uid = e_uid_new ();
+ e_filename_make_safe (uid);
+ user_dir = e_dbus_source_object_get_user_dir ();
+ path = g_build_filename (user_dir, uid, NULL);
+ file = g_file_new_for_path (path);
+ g_free (path);
+ g_free (uid);
+
+ /* Write the data to the new file path. The file
+ * monitor will notice the new file and create an
+ * EDBusSourceObject for it. */
+ g_file_replace_contents (
+ file, data, length, NULL, FALSE,
+ G_FILE_CREATE_PRIVATE, NULL, NULL, &error);
+
+ g_object_unref (file);
+
+ if (error != NULL)
+ g_dbus_method_invocation_take_error (invocation, error);
+
+ return TRUE;
+}
+
+static gboolean
+dbus_source_server_remove_sources_cb (EDBusSourceManager *interface,
+ GDBusMethodInvocation *invocation,
+ GStrv uids,
+ EDBusSourceServer *server)
+{
+ EDBusSourceObject *object;
+ GQueue queue = G_QUEUE_INIT;
+ guint ii, length;
+
+ length = g_strv_length (uids);
+
+ /* Make sure all the UIDs are valid before removing anything. */
+ for (ii = 0; ii < length; ii++) {
+ object = e_dbus_source_server_lookup_by_uid (server, uids[ii]);
+
+ if (object == NULL) {
+ g_dbus_method_invocation_return_error (
+ invocation,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Unrecognized data source '%s'"),
+ uids[ii]);
+ goto exit;
+ }
+
+ if (!e_dbus_source_object_get_writable (object)) {
+ g_dbus_method_invocation_return_error (
+ invocation,
+ G_IO_ERROR,
+ G_IO_ERROR_PERMISSION_DENIED,
+ _("Data source '%s' cannot be removed"),
+ uids[ii]);
+ goto exit;
+ }
+
+ /* Do not reference the object. */
+ g_queue_push_tail (&queue, object);
+ }
+
+ /* All the UIDs are valid, so we can remove objects now. */
+ while ((object = g_queue_pop_head (&queue)) != NULL)
+ e_dbus_source_server_remove_object (server, object);
+
+exit:
+ g_queue_clear (&queue);
+
+ return TRUE;
+}
+
+static void
+dbus_source_server_monitor_changed_cb (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ EDBusSourceServer *server)
+{
+ gchar *basename;
+ gboolean hidden;
+
+ basename = g_file_get_basename (file);
+ hidden = g_str_has_prefix (basename, ".");
+ g_free (basename);
+
+ /* XXX Ignore hidden files so we don't try
+ * to load .goutputstream-XXXXXX files. */
+ if (hidden)
+ return;
+
+ if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
+ GError *error = NULL;
+
+ e_dbus_source_server_load_file (server, file, &error);
+ if (error != NULL) {
+ e_dbus_source_server_load_error (
+ server, file, error);
+ g_error_free (error);
+ }
+ }
+
+ if (event_type == G_FILE_MONITOR_EVENT_DELETED) {
+ EDBusSourceObject *object;
+
+ object = e_dbus_source_server_lookup_by_file (server, file);
+ if (object != NULL)
+ e_dbus_source_server_remove_object (server, object);
+ }
+}
+
+static void
+dbus_source_server_dispose (GObject *object)
+{
+ EDBusSourceServerPrivate *priv;
+
+ priv = E_DBUS_SOURCE_SERVER_GET_PRIVATE (object);
+
+ if (priv->object_manager != NULL) {
+ g_object_unref (priv->object_manager);
+ priv->object_manager = NULL;
+ }
+
+ if (priv->source_manager != NULL) {
+ g_object_unref (priv->source_manager);
+ priv->source_manager = NULL;
+ }
+
+ g_hash_table_remove_all (priv->objects);
+ g_hash_table_remove_all (priv->monitors);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_dbus_source_server_parent_class)->dispose (object);
+}
+
+static void
+dbus_source_server_finalize (GObject *object)
+{
+ EDBusSourceServerPrivate *priv;
+
+ priv = E_DBUS_SOURCE_SERVER_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->objects);
+ g_hash_table_destroy (priv->monitors);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_dbus_source_server_parent_class)->finalize (object);
+}
+
+static void
+dbus_source_server_bus_acquired (EDBusServer *server,
+ GDBusConnection *connection)
+{
+ EDBusSourceServerPrivate *priv;
+ GError *error = NULL;
+
+ priv = E_DBUS_SOURCE_SERVER_GET_PRIVATE (server);
+
+ g_dbus_object_manager_server_set_connection (
+ priv->object_manager, connection);
+
+ g_dbus_interface_skeleton_export (
+ G_DBUS_INTERFACE_SKELETON (priv->source_manager),
+ connection, DBUS_OBJECT_PATH, &error);
+
+ /* Terminate the server if we can't export the interface. */
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ e_dbus_server_quit (server);
+ g_error_free (error);
+ }
+
+ /* Chain up to parent's bus_acquired() method. */
+ E_DBUS_SERVER_CLASS (e_dbus_source_server_parent_class)->
+ bus_acquired (server, connection);
+}
+
+static void
+e_dbus_source_server_class_init (EDBusSourceServerClass *class)
+{
+ GObjectClass *object_class;
+ EDBusServerClass *dbus_server_class;
+
+ g_type_class_add_private (class, sizeof (EDBusSourceServerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = dbus_source_server_dispose;
+ object_class->finalize = dbus_source_server_finalize;
+
+ dbus_server_class = E_DBUS_SERVER_CLASS (class);
+ dbus_server_class->bus_name = SOURCES_DBUS_SERVICE_NAME;
+ dbus_server_class->bus_acquired = dbus_source_server_bus_acquired;
+
+ /**
+ * EDBusSourceServer::load-error:
+ * @server: the #EDBusSourceServer which emitted the signal
+ * @file: the #GFile being loaded
+ * @error: a #GError describing the error
+ *
+ * Emitted when an error occurs while loading or parsing a
+ * data source key file.
+ **/
+ signals[LOAD_ERROR] = g_signal_new (
+ "load-error",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EDBusSourceServerClass, load_error),
+ NULL, NULL,
+ e_marshal_VOID__OBJECT_BOXED,
+ G_TYPE_NONE, 2,
+ G_TYPE_FILE,
+ G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE);
+}
+
+static void
+e_dbus_source_server_init (EDBusSourceServer *server)
+{
+ GDBusObjectManagerServer *object_manager;
+ EDBusSourceManager *source_manager;
+ GHashTable *objects;
+ GHashTable *monitors;
+
+ object_manager = g_dbus_object_manager_server_new (DBUS_OBJECT_PATH);
+ source_manager = e_dbus_source_manager_skeleton_new ();
+
+ objects = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ monitors = g_hash_table_new_full (
+ (GHashFunc) g_file_hash,
+ (GEqualFunc) g_file_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) g_object_unref);
+
+ server->priv = E_DBUS_SOURCE_SERVER_GET_PRIVATE (server);
+ server->priv->object_manager = object_manager;
+ server->priv->source_manager = source_manager;
+ server->priv->objects = objects;
+ server->priv->monitors = monitors;
+
+ g_signal_connect (
+ source_manager, "handle-create-source",
+ G_CALLBACK (dbus_source_server_create_source_cb),
+ server);
+
+ g_signal_connect (
+ source_manager, "handle-remove-sources",
+ G_CALLBACK (dbus_source_server_remove_sources_cb),
+ server);
+}
+
+/**
+ * e_dbus_source_server_new:
+ *
+ * Creates a new instance of #EDBusSourceServer.
+ *
+ * Returns: a new instance of #EDBusSourceServer
+ *
+ * Since: 3.4
+ **/
+EDBusServer *
+e_dbus_source_server_new (void)
+{
+ return g_object_new (E_TYPE_DBUS_SOURCE_SERVER, NULL);
+}
+
+/**
+ * e_dbus_source_server_add_object:
+ * @server: an #EDBusSourceServer
+ * @object: an #EDBusSourceObject
+ *
+ * Adds @object to @server.
+ *
+ * Since: 3.4
+ **/
+void
+e_dbus_source_server_add_object (EDBusSourceServer *server,
+ EDBusSourceObject *object)
+{
+ const gchar *object_name;
+ const gchar *object_path;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_DBUS_SOURCE_SERVER (server));
+ g_return_if_fail (E_IS_DBUS_SOURCE_OBJECT (object));
+
+ uid = e_dbus_source_object_get_uid (object);
+
+ /* Check if we already this object in the server. */
+ if (e_dbus_source_server_lookup_by_uid (server, uid) != NULL)
+ return;
+
+ g_hash_table_insert (
+ server->priv->objects,
+ g_strdup (uid), g_object_ref (object));
+
+ g_dbus_object_manager_server_export_uniquely (
+ server->priv->object_manager,
+ G_DBUS_OBJECT_SKELETON (object));
+
+ object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
+ object_name = strrchr (object_path, '/') + 1;
+
+ g_print ("Adding UID:%s ('%s')\n", uid, object_name);
+}
+
+/**
+ * e_dbus_source_server_remove_object:
+ * @server: an #EDBusSourceServer
+ * @object: an #EDBusSourceObject
+ *
+ * Removes @object from @server.
+ *
+ * Since: 3.4
+ **/
+void
+e_dbus_source_server_remove_object (EDBusSourceServer *server,
+ EDBusSourceObject *object)
+{
+ const gchar *object_name;
+ const gchar *object_path;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_DBUS_SOURCE_SERVER (server));
+ g_return_if_fail (E_IS_DBUS_SOURCE_OBJECT (object));
+
+ uid = e_dbus_source_object_get_uid (object);
+
+ /* The system object cannot be removed. */
+ if (g_strcmp0 (uid, "system") == 0)
+ return;
+
+ g_hash_table_remove (server->priv->objects, uid);
+
+ object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
+ object_name = strrchr (object_path, '/') + 1;
+
+ g_print ("Removing UID:%s ('%s')\n", uid, object_name);
+
+ g_dbus_object_manager_server_unexport (
+ server->priv->object_manager, object_path);
+}
+
+/**
+ * e_dbus_source_server_load_all:
+ * @server: an #EDBusSourceServer
+ * @error: return location for a #GError, or %NULL
+ *
+ * Loads data source key files from standard system-wide and user-specific
+ * locations. Because multiple errors can occur when loading multiple files,
+ * @error is only set if a directory can not be opened. If a key file fails
+ * to load, the error is broadcast through the #EDBusSourceServer::load-error
+ * signal.
+ *
+ * Returns: %TRUE if the standard directories were successfully opened,
+ * but this does not imply the key files were successfully loaded
+ *
+ * Since: 3.4
+ **/
+gboolean
+e_dbus_source_server_load_all (EDBusSourceServer *server,
+ GError **error)
+{
+ const gchar *directory;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_SERVER (server), FALSE);
+
+ /* Load the user's sources directory first so that user-specific
+ * data sources overshadow predefined data sources with identical
+ * UIDs. The "system" data sources is one such example. */
+
+ directory = e_dbus_source_object_get_user_dir ();
+ success = e_dbus_source_server_load_directory (
+ server, directory, TRUE, error);
+ g_prefix_error (error, "%s: ", directory);
+
+ if (!success)
+ return FALSE;
+
+ directory = SYSTEM_WIDE_SOURCES_DIRECTORY;
+ success = e_dbus_source_server_load_directory (
+ server, directory, FALSE, error);
+ g_prefix_error (error, "%s: ", directory);
+
+ return success;
+}
+
+/**
+ * e_dbus_source_server_load_directory:
+ * @server: an #EDBusSourceServer
+ * @path: the path to the directory to load
+ * @monitor_for_changes: monitor the directory for changes
+ * @error: return location for a #GError, or %NULL
+ *
+ * Loads data source key files in @path. Because multiple errors can
+ * occur when loading multiple files, @error is only set if @path can
+ * not be opened. If a key file fails to load, the error is broadcast
+ * through the #EDBusSourceServer::load-error signal.
+ *
+ * If @monitor_for_changes is %TRUE, then the @server will emit signals
+ * on the D-Bus interface when key files are created and deleted in @path.
+ *
+ * Returns: %TRUE if @path was successfully opened, but this
+ * does not imply the key files were successfully loaded
+ *
+ * Since: 3.4
+ **/
+gboolean
+e_dbus_source_server_load_directory (EDBusSourceServer *server,
+ const gchar *path,
+ gboolean monitor_for_changes,
+ GError **error)
+{
+ GDir *dir;
+ GFile *file;
+ const gchar *name;
+
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_SERVER (server), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ /* If the directory doesn't exist then there's nothing to load.
+ * Note we do not use G_FILE_TEST_DIR here. If the given path
+ * exists but is not a directory then we let g_dir_open() fail. */
+ if (!g_file_test (path, G_FILE_TEST_EXISTS))
+ return TRUE;
+
+ dir = g_dir_open (path, 0, error);
+ if (dir == NULL)
+ return FALSE;
+
+ file = g_file_new_for_path (path);
+
+ while ((name = g_dir_read_name (dir)) != NULL) {
+ GFile *child;
+ GError *local_error = NULL;
+
+ /* XXX Ignore hidden files so we don't try
+ * to load .goutputstream-XXXXXX files. */
+ if (g_str_has_prefix (name, "."))
+ continue;
+
+ child = g_file_get_child (file, name);
+
+ e_dbus_source_server_load_file (
+ server, child, &local_error);
+
+ if (local_error != NULL) {
+ e_dbus_source_server_load_error (
+ server, child, local_error);
+ g_error_free (local_error);
+ }
+
+ g_object_unref (child);
+ }
+
+ g_dir_close (dir);
+
+ if (monitor_for_changes) {
+ GFileMonitor *monitor;
+
+ monitor = g_file_monitor_directory (
+ file, G_FILE_MONITOR_NONE, NULL, error);
+ if (monitor == NULL)
+ return FALSE;
+
+ g_signal_connect (
+ monitor, "changed",
+ G_CALLBACK (dbus_source_server_monitor_changed_cb),
+ server);
+
+ g_hash_table_insert (
+ server->priv->monitors,
+ g_object_ref (file), monitor);
+ }
+
+ g_object_unref (file);
+
+ return TRUE;
+}
+
+/**
+ * e_dbus_source_server_load_file:
+ * @server: an #EDBusSourceServer
+ * @file: the data source key file to load
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates an #EDBusSourceObject for a native key file and adds it to
+ * @server. If an error occurs, the function returns %NULL and sets
+ * @error.
+ *
+ * Returns: the newly-added #EDBusSourceObject, or %NULL on error
+ *
+ * Since: 3.4
+ **/
+EDBusSourceObject *
+e_dbus_source_server_load_file (EDBusSourceServer *server,
+ GFile *file,
+ GError **error)
+{
+ EDBusSourceObject *object;
+
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_SERVER (server), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ /* Check if we already have this file loaded. */
+ object = e_dbus_source_server_lookup_by_file (server, file);
+ if (object != NULL)
+ return object;
+
+ /* Create a new ESourceBlob and add it to the server. */
+ object = e_dbus_source_object_new (file, NULL, error);
+ if (object != NULL) {
+ e_dbus_source_server_add_object (server, object);
+ g_object_unref (object);
+ }
+
+ return object;
+}
+
+/**
+ * e_dbus_source_server_load_error:
+ * @server: an #EBusSourceServer
+ * @file: the #GFile that failed to load
+ * @error: a #GError describing the load error
+ *
+ * Emits the #EDBusSourceServer::load-error signal.
+ *
+ * Since: 3.4
+ **/
+void
+e_dbus_source_server_load_error (EDBusSourceServer *server,
+ GFile *file,
+ const GError *error)
+{
+ g_return_if_fail (E_IS_DBUS_SOURCE_SERVER (server));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (error != NULL);
+
+ g_signal_emit (server, signals[LOAD_ERROR], 0, file, error);
+}
+
+/**
+ * e_dbus_source_server_lookup_by_uid:
+ * @server: an #EDBusSourceServer
+ * @uid: a unique identifier string
+ *
+ * Looks up an #EDBusSourceObject in @server by its unique identifier string.
+ *
+ * Returns: an #EDBusSourceObject, or %NULL if no match was found
+ *
+ * Since: 3.4
+ **/
+EDBusSourceObject *
+e_dbus_source_server_lookup_by_uid (EDBusSourceServer *server,
+ const gchar *uid)
+{
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_SERVER (server), NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ return g_hash_table_lookup (server->priv->objects, uid);
+}
+
+/**
+ * e_dbus_source_server_lookup_by_file:
+ * @server: an #EDBusSourceServer
+ * @file: a #GFile
+ *
+ * Looks up an #EDBusSourceObject in @server by its #GFile.
+ *
+ * Returns: an #EDBusSourceObject, or %NULL if no match was found
+ *
+ * Since: 3.4
+ **/
+EDBusSourceObject *
+e_dbus_source_server_lookup_by_file (EDBusSourceServer *server,
+ GFile *file)
+{
+ EDBusSourceObject *object;
+ gchar *uid;
+
+ g_return_val_if_fail (E_IS_DBUS_SOURCE_SERVER (server), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ uid = g_file_get_basename (file);
+ object = e_dbus_source_server_lookup_by_uid (server, uid);
+ g_free (uid);
+
+ return object;
+}
+
diff --git a/libebackend/e-dbus-source-server.h b/libebackend/e-dbus-source-server.h
new file mode 100644
index 0000000..6cc90f2
--- /dev/null
+++ b/libebackend/e-dbus-source-server.h
@@ -0,0 +1,105 @@
+/*
+ * e-dbus-source-server.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_DBUS_SOURCE_SERVER_H
+#define E_DBUS_SOURCE_SERVER_H
+
+#include <libebackend/e-dbus-server.h>
+#include <libebackend/e-dbus-source-object.h>
+
+/* Standard GObject macros */
+#define E_TYPE_DBUS_SOURCE_SERVER \
+ (e_dbus_source_server_get_type ())
+#define E_DBUS_SOURCE_SERVER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_DBUS_SOURCE_SERVER, EDBusSourceServer))
+#define E_DBUS_SOURCE_SERVER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_DBUS_SOURCE_SERVER, EDBusSourceServerClass))
+#define E_IS_DBUS_SOURCE_SERVER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_DBUS_SOURCE_SERVER))
+#define E_IS_DBUS_SOURCE_SERVER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_DBUS_SOURCE_SERVER))
+#define E_DBUS_SOURCE_SERVER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_DBUS_SOURCE_SERVER, EDBusSourceServerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EDBusSourceServer EDBusSourceServer;
+typedef struct _EDBusSourceServerClass EDBusSourceServerClass;
+typedef struct _EDBusSourceServerPrivate EDBusSourceServerPrivate;
+
+/**
+ * EDBusSourceServer:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.4
+ **/
+struct _EDBusSourceServer {
+ EDBusServer parent;
+ EDBusSourceServerPrivate *priv;
+};
+
+struct _EDBusSourceServerClass {
+ EDBusServerClass parent_class;
+
+ /* Signals */
+ void (*load_error) (EDBusSourceServer *server,
+ GFile *file,
+ const GError *error);
+};
+
+GType e_dbus_source_server_get_type (void) G_GNUC_CONST;
+EDBusServer * e_dbus_source_server_new (void);
+void e_dbus_source_server_add_object
+ (EDBusSourceServer *server,
+ EDBusSourceObject *object);
+void e_dbus_source_server_remove_object
+ (EDBusSourceServer *server,
+ EDBusSourceObject *object);
+gboolean e_dbus_source_server_load_all (EDBusSourceServer *server,
+ GError **error);
+gboolean e_dbus_source_server_load_directory
+ (EDBusSourceServer *server,
+ const gchar *path,
+ gboolean monitor_for_changes,
+ GError **error);
+EDBusSourceObject *
+ e_dbus_source_server_load_file (EDBusSourceServer *server,
+ GFile *file,
+ GError **error);
+void e_dbus_source_server_load_error (EDBusSourceServer *server,
+ GFile *file,
+ const GError *error);
+EDBusSourceObject *
+ e_dbus_source_server_lookup_by_uid
+ (EDBusSourceServer *server,
+ const gchar *uid);
+EDBusSourceObject *
+ e_dbus_source_server_lookup_by_file
+ (EDBusSourceServer *server,
+ GFile *file);
+
+G_END_DECLS
+
+#endif /* E_DBUS_SOURCE_SERVER_H */
diff --git a/services/Makefile.am b/services/Makefile.am
index 1750bfa..551a440 100644
--- a/services/Makefile.am
+++ b/services/Makefile.am
@@ -3,6 +3,7 @@ NULL =
SUBDIRS = \
evolution-addressbook-factory \
evolution-calendar-factory \
+ evolution-source-registry \
$(NULL)
-include $(top_srcdir)/git.mk
diff --git a/services/evolution-source-registry/Makefile.am b/services/evolution-source-registry/Makefile.am
new file mode 100644
index 0000000..308744d
--- /dev/null
+++ b/services/evolution-source-registry/Makefile.am
@@ -0,0 +1,32 @@
+NULL =
+
+service_in_files = org.gnome.evolution.dataserver.Sources.service.in
+servicedir = $(datadir)/dbus-1/services
+service_DATA = $(service_in_files:.service.in=.service)
+ EVO_SUBST_SERVICE_RULE@
+
+CLEANFILES = $(service_DATA)
+EXTRA_DIST = $(service_in_files)
+
+libexec_PROGRAMS = evolution-source-registry
+
+evolution_source_registry_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DG_LOG_DOMAIN=\"evolution-source-registry\" \
+ -DLOCALEDIR=\"$(localedir)\" \
+ $(E_DATA_SERVER_CFLAGS) \
+ $(NULL)
+
+evolution_source_registry_SOURCES = \
+ evolution-source-registry.c \
+ $(NULL)
+
+evolution_source_registry_LDADD = \
+ $(top_builddir)/libebackend/libebackend-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(E_DATA_SERVER_LIBS) \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/services/evolution-source-registry/evolution-source-registry.c b/services/evolution-source-registry/evolution-source-registry.c
new file mode 100644
index 0000000..6cd1cba
--- /dev/null
+++ b/services/evolution-source-registry/evolution-source-registry.c
@@ -0,0 +1,66 @@
+/*
+ * e-source-registry.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include <libebackend/e-dbus-source-server.h>
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ EDBusServer *server;
+ GError *error = NULL;
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ g_type_init ();
+
+ server = e_dbus_source_server_new ();
+
+ /* Failure here is fatal. Don't even try to keep going. */
+ e_dbus_source_server_load_all (
+ E_DBUS_SOURCE_SERVER (server), &error);
+
+ if (error != NULL) {
+ g_printerr ("%s\n", error->message);
+ g_object_unref (server);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("Server is up and running...\n");
+
+ /* Keep the server from quitting on its own.
+ * We don't have a way of tracking number of
+ * active clients, so once the server is up,
+ * it's up until the session bus closes. */
+ e_dbus_server_hold (server);
+
+ e_dbus_server_run (server);
+
+ g_object_unref (server);
+
+ g_print ("Bye.\n");
+
+ return 0;
+}
diff --git a/services/evolution-source-registry/org.gnome.evolution.dataserver.Sources.service.in b/services/evolution-source-registry/org.gnome.evolution.dataserver.Sources.service.in
new file mode 100644
index 0000000..1db5de8
--- /dev/null
+++ b/services/evolution-source-registry/org.gnome.evolution.dataserver.Sources.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name= SOURCES_DBUS_SERVICE_NAME@
+Exec= libexecdir@/e-source-registry
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]