[evolution-data-server] Add various base classes for backends and servers.



commit 9c80feddc9d1291e3ae4b21f604706b31d4d660a
Author: Matthew Barnes <mbarnes redhat com>
Date:   Thu Sep 8 09:01:59 2011 -0400

    Add various base classes for backends and servers.
    
    EBackend is an abstract base class for address book and calendar
    backends.
    
    EBackendFactory is an abstract base class for creating address book and
    calendar backends.  It is also an EExtension that extends EDataFactory.
    
    EDBusServer is an abstract base class for an extensible D-Bus server.
    
    EDataFactory is a type of EDBusServer that exports connection objects
    for individual address books or calendars.

 docs/reference/libebackend/libebackend-docs.xml    |    4 +
 .../reference/libebackend/libebackend-sections.txt |   79 ++++
 docs/reference/libebackend/libebackend.types       |    8 +
 .../libebackend/tmpl/e-backend-factory.sgml        |   47 +++
 docs/reference/libebackend/tmpl/e-backend.sgml     |   80 ++++
 .../reference/libebackend/tmpl/e-data-factory.sgml |   62 +++
 docs/reference/libebackend/tmpl/e-dbus-server.sgml |   76 ++++
 libebackend/Makefile.am                            |    8 +
 libebackend/e-backend-factory.c                    |  108 ++++++
 libebackend/e-backend-factory.h                    |   81 ++++
 libebackend/e-backend.c                            |  267 +++++++++++++
 libebackend/e-backend.h                            |   80 ++++
 libebackend/e-data-factory.c                       |  392 ++++++++++++++++++++
 libebackend/e-data-factory.h                       |   79 ++++
 libebackend/e-dbus-server.c                        |  251 +++++++++++++
 libebackend/e-dbus-server.h                        |   86 +++++
 16 files changed, 1708 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/libebackend/libebackend-docs.xml b/docs/reference/libebackend/libebackend-docs.xml
index 514fffe..312b5f0 100644
--- a/docs/reference/libebackend/libebackend-docs.xml
+++ b/docs/reference/libebackend/libebackend-docs.xml
@@ -10,6 +10,10 @@
 
   <chapter>
     <title>Evolution-Data-Server Manual: Backend Utilities (libebackend)</title>
+    <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-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 d23466a..6e401dd 100644
--- a/docs/reference/libebackend/libebackend-sections.txt
+++ b/docs/reference/libebackend/libebackend-sections.txt
@@ -1,4 +1,83 @@
 <SECTION>
+<FILE>e-backend</FILE>
+<TITLE>EBackend</TITLE>
+EBackend
+e_backend_get_online
+e_backend_set_online
+e_backend_get_source
+e_backend_last_client_gone
+<SUBSECTION Standard>
+E_BACKEND
+E_IS_BACKEND
+E_TYPE_BACKEND
+E_BACKEND_CLASS
+E_IS_BACKEND_CLASS
+E_BACKEND_GET_CLASS
+EBackendClass
+<SUBSECTION Private>
+EBackendPrivate
+e_backend_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-backend-factory</FILE>
+<TITLE>EBackendFactory</TITLE>
+EBackendFactory
+e_backend_factory_get_hash_key
+e_backend_factory_new_backend
+<SUBSECTION Standard>
+E_BACKEND_FACTORY
+E_IS_BACKEND_FACTORY
+E_TYPE_BACKEND_FACTORY
+E_BACKEND_FACTORY_CLASS
+E_IS_BACKEND_FACTORY_CLASS
+E_BACKEND_FACTORY_GET_CLASS
+EBackendFactoryClass
+<SUBSECTION Private>
+EBackendFactoryPrivate
+e_backend_factory_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-data-factory</FILE>
+<TITLE>EDataFactory</TITLE>
+EDataFactory
+e_data_factory_get_backend
+e_data_factory_get_online
+e_data_factory_set_online
+<SUBSECTION Standard>
+E_DATA_FACTORY
+E_IS_DATA_FACTORY
+E_TYPE_DATA_FACTORY
+E_DATA_FACTORY_CLASS
+E_IS_DATA_FACTORY_CLASS
+E_DATA_FACTORY_GET_CLASS
+<SUBSECTION Private>
+EDataFactoryPrivate
+e_data_factory_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-dbus-server</FILE>
+<TITLE>EDBusServer</TITLE>
+EDBusServer
+e_dbus_server_run
+e_dbus_server_quit
+e_dbus_server_load_modules
+<SUBSECTION Standard>
+E_DBUS_SERVER
+E_IS_DBUS_SERVER
+E_TYPE_DBUS_SERVER
+E_DBUS_SERVER_CLASS
+E_IS_DBUS_SERVER_CLASS
+E_DBUS_SERVER_GET_CLASS
+EDBusServerClass
+<SUBSECTION Private>
+EDBusServerPrivate
+e_dbus_server_get_type
+</SECTION>
+
+<SECTION>
 <FILE>e-file-cache</FILE>
 <TITLE>EFileCache</TITLE>
 EFileCache
diff --git a/docs/reference/libebackend/libebackend.types b/docs/reference/libebackend/libebackend.types
index d5ad893..e23f54b 100644
--- a/docs/reference/libebackend/libebackend.types
+++ b/docs/reference/libebackend/libebackend.types
@@ -1,9 +1,17 @@
+#include <libebackend/e-backend.h>
+#include <libebackend/e-backend-factory.h>
+#include <libebackend/e-data-factory.h>
+#include <libebackend/e-dbus-server.h>
 #include <libebackend/e-extensible.h>
 #include <libebackend/e-extension.h>
 #include <libebackend/e-file-cache.h>
 #include <libebackend/e-module.h>
 #include <libebackend/e-offline-listener.h>
 
+e_backend_get_type
+e_backend_factory_get_type
+e_data_factory_get_type
+e_dbus_server_get_type
 e_extensible_get_type
 e_extension_get_type
 e_file_cache_get_type
diff --git a/docs/reference/libebackend/tmpl/e-backend-factory.sgml b/docs/reference/libebackend/tmpl/e-backend-factory.sgml
new file mode 100644
index 0000000..477fa60
--- /dev/null
+++ b/docs/reference/libebackend/tmpl/e-backend-factory.sgml
@@ -0,0 +1,47 @@
+<!-- ##### SECTION Title ##### -->
+EBackendFactory
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT EBackendFactory ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### FUNCTION e_backend_factory_get_hash_key ##### -->
+<para>
+
+</para>
+
+ factory: 
+ Returns: 
+
+
+<!-- ##### FUNCTION e_backend_factory_new_backend ##### -->
+<para>
+
+</para>
+
+ factory: 
+ source: 
+ Returns: 
+
+
diff --git a/docs/reference/libebackend/tmpl/e-backend.sgml b/docs/reference/libebackend/tmpl/e-backend.sgml
new file mode 100644
index 0000000..0ce3cad
--- /dev/null
+++ b/docs/reference/libebackend/tmpl/e-backend.sgml
@@ -0,0 +1,80 @@
+<!-- ##### SECTION Title ##### -->
+EBackend
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT EBackend ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SIGNAL EBackend::last-client-gone ##### -->
+<para>
+
+</para>
+
+ ebackend: the object which received the signal.
+
+<!-- ##### ARG EBackend:online ##### -->
+<para>
+
+</para>
+
+<!-- ##### ARG EBackend:source ##### -->
+<para>
+
+</para>
+
+<!-- ##### FUNCTION e_backend_get_online ##### -->
+<para>
+
+</para>
+
+ backend: 
+ Returns: 
+
+
+<!-- ##### FUNCTION e_backend_set_online ##### -->
+<para>
+
+</para>
+
+ backend: 
+ online: 
+
+
+<!-- ##### FUNCTION e_backend_get_source ##### -->
+<para>
+
+</para>
+
+ backend: 
+ Returns: 
+
+
+<!-- ##### FUNCTION e_backend_last_client_gone ##### -->
+<para>
+
+</para>
+
+ backend: 
+
+
diff --git a/docs/reference/libebackend/tmpl/e-data-factory.sgml b/docs/reference/libebackend/tmpl/e-data-factory.sgml
new file mode 100644
index 0000000..11a56ae
--- /dev/null
+++ b/docs/reference/libebackend/tmpl/e-data-factory.sgml
@@ -0,0 +1,62 @@
+<!-- ##### SECTION Title ##### -->
+EDataFactory
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT EDataFactory ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### ARG EDataFactory:online ##### -->
+<para>
+
+</para>
+
+<!-- ##### FUNCTION e_data_factory_get_backend ##### -->
+<para>
+
+</para>
+
+ factory: 
+ hash_key: 
+ source: 
+ Returns: 
+
+
+<!-- ##### FUNCTION e_data_factory_get_online ##### -->
+<para>
+
+</para>
+
+ factory: 
+ Returns: 
+
+
+<!-- ##### FUNCTION e_data_factory_set_online ##### -->
+<para>
+
+</para>
+
+ factory: 
+ online: 
+
+
diff --git a/docs/reference/libebackend/tmpl/e-dbus-server.sgml b/docs/reference/libebackend/tmpl/e-dbus-server.sgml
new file mode 100644
index 0000000..1aaf4c3
--- /dev/null
+++ b/docs/reference/libebackend/tmpl/e-dbus-server.sgml
@@ -0,0 +1,76 @@
+<!-- ##### SECTION Title ##### -->
+EDBusServer
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT EDBusServer ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SIGNAL EDBusServer::bus-acquired ##### -->
+<para>
+
+</para>
+
+ edbusserver: the object which received the signal.
+ arg1: 
+
+<!-- ##### SIGNAL EDBusServer::bus-name-acquired ##### -->
+<para>
+
+</para>
+
+ edbusserver: the object which received the signal.
+ arg1: 
+
+<!-- ##### SIGNAL EDBusServer::bus-name-lost ##### -->
+<para>
+
+</para>
+
+ edbusserver: the object which received the signal.
+ arg1: 
+
+<!-- ##### FUNCTION e_dbus_server_run ##### -->
+<para>
+
+</para>
+
+ server: 
+
+
+<!-- ##### FUNCTION e_dbus_server_quit ##### -->
+<para>
+
+</para>
+
+ server: 
+
+
+<!-- ##### FUNCTION e_dbus_server_load_modules ##### -->
+<para>
+
+</para>
+
+ server: 
+
+
diff --git a/libebackend/Makefile.am b/libebackend/Makefile.am
index e4e2bf8..09845c4 100644
--- a/libebackend/Makefile.am
+++ b/libebackend/Makefile.am
@@ -9,6 +9,10 @@ libebackend_1_2_la_CPPFLAGS = \
 	$(E_BACKEND_CFLAGS)
 
 libebackend_1_2_la_SOURCES =		\
+	e-backend.c			\
+	e-backend-factory.c		\
+	e-data-factory.c		\
+	e-dbus-server.c			\
 	e-extensible.c			\
 	e-extension.c			\
 	e-offline-listener.c		\
@@ -30,6 +34,10 @@ libebackend_1_2_la_LDFLAGS = \
 libebackendincludedir = $(privincludedir)/libebackend
 
 libebackendinclude_HEADERS =		\
+	e-backend.h			\
+	e-backend-factory.h		\
+	e-data-factory.h		\
+	e-dbus-server.h			\
 	e-extensible.h			\
 	e-extension.h			\
 	e-offline-listener.h		\
diff --git a/libebackend/e-backend-factory.c b/libebackend/e-backend-factory.c
new file mode 100644
index 0000000..8050f38
--- /dev/null
+++ b/libebackend/e-backend-factory.c
@@ -0,0 +1,108 @@
+/*
+ * e-backend-factory.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/>
+ *
+ */
+
+/**
+ * SECTION: e-backend-factory
+ * @short_description: an abstract base class for backend factories
+ * @include: libebackend/e-backend-factory.h
+ *
+ * An #EBackendFactory's job is to create an #EBackend instance for a
+ * given #ESource.  #EBackendFactory and #EBackend should be subclassed
+ * together, so that each type of #EBackendFactory creates a unique type
+ * of #EBackend.
+ *
+ * Each #EBackendFactory subclass must define a hash key to uniquely
+ * identify itself among other #EBackendFactory subclasses.  #EDataFactory
+ * then services incoming connection requests by deriving a hash key from
+ * the requested #ESource, using the dervied hash key to find an appropriate
+ * #EBackendFactory, and creating an #EBackend instance from that factory
+ * to pair with the requested #ESource.
+ **/
+
+#include "e-backend-factory.h"
+
+#include <config.h>
+
+#include <libebackend/e-data-factory.h>
+
+G_DEFINE_ABSTRACT_TYPE (EBackendFactory, e_backend_factory, E_TYPE_EXTENSION)
+
+static void
+e_backend_factory_class_init (EBackendFactoryClass *class)
+{
+	EExtensionClass *extension_class;
+
+	extension_class = E_EXTENSION_CLASS (class);
+	extension_class->extensible_type = E_TYPE_DATA_FACTORY;
+}
+
+static void
+e_backend_factory_init (EBackendFactory *factory)
+{
+}
+
+/**
+ * e_backend_factory_get_hash_key:
+ * @factory: an #EBackendFactory
+ *
+ * Returns a hash key which uniquely identifies @factory.
+ *
+ * Since only one instance of each #EBackendFactory subclass is ever created,
+ * the hash key need only be unique among subclasses, not among instances of
+ * each subclass.
+ *
+ * Since: 3.4
+ **/
+const gchar *
+e_backend_factory_get_hash_key (EBackendFactory *factory)
+{
+	EBackendFactoryClass *class;
+
+	g_return_val_if_fail (E_IS_BACKEND_FACTORY (factory), NULL);
+
+	class = E_BACKEND_FACTORY_GET_CLASS (factory);
+	g_return_val_if_fail (class->get_hash_key != NULL, NULL);
+
+	return class->get_hash_key (factory);
+}
+
+/**
+ * e_backend_factory_new_backend:
+ * @factory: an #EBackendFactory
+ * @source: an #ESource
+ *
+ * Returns a new #EBackend instance for @source.
+ *
+ * Returns: a new #EBackend instance for @source
+ *
+ * Since: 3.4
+ **/
+EBackend *
+e_backend_factory_new_backend (EBackendFactory *factory,
+                               ESource *source)
+{
+	EBackendFactoryClass *class;
+
+	g_return_val_if_fail (E_IS_BACKEND_FACTORY (factory), NULL);
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	class = E_BACKEND_FACTORY_GET_CLASS (factory);
+	g_return_val_if_fail (class->new_backend != NULL, NULL);
+
+	return class->new_backend (factory, source);
+}
diff --git a/libebackend/e-backend-factory.h b/libebackend/e-backend-factory.h
new file mode 100644
index 0000000..4937e0f
--- /dev/null
+++ b/libebackend/e-backend-factory.h
@@ -0,0 +1,81 @@
+/*
+ * e-backend-factory.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_BACKEND_FACTORY_H
+#define E_BACKEND_FACTORY_H
+
+#include <libebackend/e-backend.h>
+#include <libebackend/e-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BACKEND_FACTORY \
+	(e_backend_factory_get_type ())
+#define E_BACKEND_FACTORY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_BACKEND_FACTORY, EBackendFactory))
+#define E_BACKEND_FACTORY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_BACKEND_FACTORY, EBackendFactoryClass))
+#define E_IS_BACKEND_FACTORY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_BACKEND_FACTORY))
+#define E_IS_BACKEND_FACTORY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_BACKEND_FACTORY))
+#define E_BACKEND_FACTORY_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_BACKEND_FACTORY, EBackendFactoryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBackendFactory EBackendFactory;
+typedef struct _EBackendFactoryClass EBackendFactoryClass;
+typedef struct _EBackendFactoryPrivate EBackendFactoryPrivate;
+
+/**
+ * EBackendFactory:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.4
+ **/
+struct _EBackendFactory {
+	EExtension parent;
+	EBackendFactoryPrivate *priv;
+};
+
+struct _EBackendFactoryClass {
+	EExtensionClass parent_class;
+
+	/* Methods */
+	const gchar *	(*get_hash_key)		(EBackendFactory *factory);
+	EBackend *	(*new_backend)		(EBackendFactory *factory,
+						 ESource *source);
+
+	gpointer reserved[16];
+};
+
+GType		e_backend_factory_get_type	(void) G_GNUC_CONST;
+const gchar *	e_backend_factory_get_hash_key	(EBackendFactory *factory);
+EBackend *	e_backend_factory_new_backend	(EBackendFactory *factory,
+						 ESource *source);
+
+G_END_DECLS
+
+#endif /* E_BACKEND_FACTORY_H */
diff --git a/libebackend/e-backend.c b/libebackend/e-backend.c
new file mode 100644
index 0000000..346f078
--- /dev/null
+++ b/libebackend/e-backend.c
@@ -0,0 +1,267 @@
+/*
+ * e-backend.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/>
+ *
+ */
+
+/**
+ * SECTION: e-backend
+ * @short_description: an abstract base class for backends
+ * @include: libebackend/e-backend.h
+ *
+ * An #EBackend is paired with an #ESource to facilitate performing
+ * actions on the local or remote resource described by the #ESource.
+ *
+ * In other words, whereas a certain backend type knows how to talk to a
+ * certain type of server or data store, the #ESource fills in configuration
+ * details such as host name, user name, resource path, etc.
+ *
+ * All #EBackend instances are created by an #EBackendFactory.
+ **/
+
+#include "e-backend.h"
+
+#include <config.h>
+
+#define E_BACKEND_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_BACKEND, EBackendPrivate))
+
+struct _EBackendPrivate {
+	ESource *source;
+	gboolean online;
+};
+
+enum {
+	PROP_0,
+	PROP_ONLINE,
+	PROP_SOURCE
+};
+
+enum {
+	LAST_CLIENT_GONE,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_ABSTRACT_TYPE (EBackend, e_backend, G_TYPE_OBJECT)
+
+static void
+backend_set_source (EBackend *backend,
+                    ESource *source)
+{
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (backend->priv->source == NULL);
+
+	backend->priv->source = g_object_ref (source);
+}
+
+static void
+backend_set_property (GObject *object,
+                      guint property_id,
+                      const GValue *value,
+                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ONLINE:
+			e_backend_set_online (
+				E_BACKEND (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_SOURCE:
+			backend_set_source (
+				E_BACKEND (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+backend_get_property (GObject *object,
+                      guint property_id,
+                      GValue *value,
+                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ONLINE:
+			g_value_set_boolean (
+				value, e_backend_get_online (
+				E_BACKEND (object)));
+			return;
+
+		case PROP_SOURCE:
+			g_value_set_object (
+				value, e_backend_get_source (
+				E_BACKEND (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+backend_dispose (GObject *object)
+{
+	EBackendPrivate *priv;
+
+	priv = E_BACKEND_GET_PRIVATE (object);
+
+	if (priv->source != NULL) {
+		g_object_unref (priv->source);
+		priv->source = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_backend_parent_class)->dispose (object);
+}
+
+static void
+e_backend_class_init (EBackendClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (EBackendPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = backend_set_property;
+	object_class->get_property = backend_get_property;
+	object_class->dispose = backend_dispose;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ONLINE,
+		g_param_spec_boolean (
+			"online",
+			"Online",
+			"Whether the backend is online",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SOURCE,
+		g_param_spec_object (
+			"source",
+			"Source",
+			"The data source being acted upon",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+
+	signals[LAST_CLIENT_GONE] = g_signal_new (
+		"last-client-gone",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EBackendClass, last_client_gone),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+static void
+e_backend_init (EBackend *backend)
+{
+	backend->priv = E_BACKEND_GET_PRIVATE (backend);
+}
+
+/**
+ * e_backend_get_online:
+ * @backend: an #EBackend
+ *
+ * Returns the online state of @backend: %TRUE if @backend is online,
+ * %FALSE if offline.  The online state of each backend is bound to the
+ * online state of the #EDataFactory that created it.
+ *
+ * Returns: the online state
+ *
+ * Since: 3.4
+ **/
+gboolean
+e_backend_get_online (EBackend *backend)
+{
+	g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
+
+	return backend->priv->online;
+}
+
+/**
+ * e_backend_set_online:
+ * @backend: an #EBackend
+ * @online: the online state
+ *
+ * Sets the online state of @backend: %TRUE if @backend is online,
+ * @FALSE if offline.  The online state of each backend is bound to
+ * the online state of the #EDataFactory that created it.
+ *
+ * Since: 3.4
+ **/
+void
+e_backend_set_online (EBackend *backend,
+                      gboolean online)
+{
+	g_return_if_fail (E_IS_BACKEND (backend));
+
+	/* Avoid unnecessary "notify" signals. */
+	if (online == backend->priv->online)
+		return;
+
+	backend->priv->online = online;
+
+	g_object_notify (G_OBJECT (backend), "online");
+}
+
+/**
+ * e_backend_get_source:
+ * @backend: an #EBackend
+ *
+ * Returns the #ESource to which @backend is paired.
+ *
+ * Returns: the #ESource to which @backend is paired
+ *
+ * Since: 3.4
+ **/
+ESource *
+e_backend_get_source (EBackend *backend)
+{
+	g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
+
+	return backend->priv->source;
+}
+
+/**
+ * e_backend_last_client_gone:
+ * @backend: an #EBackend
+ *
+ * Emits the #EBackend::last-client-gone signal to indicate the last
+ * client connection to @backend has been closed.  The @backend may be
+ * finalized after a short period to reclaim resources if no new client
+ * connections are established.
+ *
+ * Since: 3.4
+ **/
+void
+e_backend_last_client_gone (EBackend *backend)
+{
+	g_return_if_fail (E_IS_BACKEND (backend));
+
+	g_signal_emit (backend, signals[LAST_CLIENT_GONE], 0);
+}
diff --git a/libebackend/e-backend.h b/libebackend/e-backend.h
new file mode 100644
index 0000000..2649aee
--- /dev/null
+++ b/libebackend/e-backend.h
@@ -0,0 +1,80 @@
+/*
+ * e-backend.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_BACKEND_H
+#define E_BACKEND_H
+
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BACKEND \
+	(e_backend_get_type ())
+#define E_BACKEND(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_BACKEND, EBackend))
+#define E_BACKEND_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_BACKEND, EBackendClass))
+#define E_IS_BACKEND(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_BACKEND))
+#define E_IS_BACKEND_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_BACKEND))
+#define E_BACKEND_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_BACKEND, EBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBackend EBackend;
+typedef struct _EBackendClass EBackendClass;
+typedef struct _EBackendPrivate EBackendPrivate;
+
+/**
+ * EBackend:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.4
+ **/
+struct _EBackend {
+	GObject parent;
+	EBackendPrivate *priv;
+};
+
+struct _EBackendClass {
+	GObjectClass parent_class;
+
+	/* Signals */
+	void		(*last_client_gone)	(EBackend *backend);
+
+	gpointer reserved[16];
+};
+
+GType		e_backend_get_type		(void) G_GNUC_CONST;
+gboolean	e_backend_get_online		(EBackend *backend);
+void		e_backend_set_online		(EBackend *backend,
+						 gboolean online);
+ESource *	e_backend_get_source		(EBackend *backend);
+void		e_backend_last_client_gone	(EBackend *backend);
+
+G_END_DECLS
+
+#endif /* E_BACKEND_H */
diff --git a/libebackend/e-data-factory.c b/libebackend/e-data-factory.c
new file mode 100644
index 0000000..8975575
--- /dev/null
+++ b/libebackend/e-data-factory.c
@@ -0,0 +1,392 @@
+/*
+ * e-data-factory.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/>
+ *
+ */
+
+/**
+ * SECTION: e-data-factory
+ * @short_description: an abstract base class for a D-Bus server
+ * @include: libebackend/e-data-factory
+ **/
+
+#include "e-data-factory.h"
+
+#include <config.h>
+#include <gconf/gconf-client.h>
+
+#include <libebackend/e-extensible.h>
+#include <libebackend/e-backend-factory.h>
+
+#define E_DATA_FACTORY_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_DATA_FACTORY, EDataFactoryPrivate))
+
+struct _EDataFactoryPrivate {
+	/* The mutex guards the 'backends' hash table.  The
+	 * 'backend_factories' hash table doesn't really need
+	 * guarding since it gets populated during construction
+	 * and is read-only thereafter. */
+	GMutex *mutex;
+
+	/* ESource UID -> EBackend */
+	GHashTable *backends;
+
+	/* Hash Key -> EBackendFactory */
+	GHashTable *backend_factories;
+
+	GConfClient *gconf_client;
+	guint gconf_cnxn_id;
+
+	gboolean online;
+};
+
+enum {
+	PROP_0,
+	PROP_ONLINE
+};
+
+/* Forward Declarations */
+static void	e_data_factory_initable_init	(GInitableIface *interface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
+	EDataFactory, e_data_factory, E_TYPE_DBUS_SERVER,
+	G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, e_data_factory_initable_init)
+	G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+static void
+data_factory_online_changed (GConfClient *client,
+                             guint cnxn_id,
+                             GConfEntry *entry,
+                             EDataFactory *factory)
+{
+	GConfValue *value;
+	gboolean start_offline;
+
+	value = gconf_entry_get_value (entry);
+	start_offline = gconf_value_get_bool (value);
+
+	e_data_factory_set_online (factory, !start_offline);
+}
+
+static void
+data_factory_init_online_monitoring (EDataFactory *factory)
+{
+	gboolean start_offline;
+	GError *error = NULL;
+
+	/* XXX For the record, we're doing this completely wrong.
+	 *     EDataFactory should monitor network availability itself
+	 *     instead of relying on one particular client application
+	 *     to tell us when we're offline.  But I'll deal with this
+	 *     at some later date. */
+
+	factory->priv->gconf_client = gconf_client_get_default ();
+
+	gconf_client_add_dir (
+		factory->priv->gconf_client,
+		"/apps/evolution/shell",
+		GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
+
+	gconf_client_notify_add (
+		factory->priv->gconf_client,
+		"/apps/evolution/shell/start_offline",
+		(GConfClientNotifyFunc) data_factory_online_changed,
+		factory, (GFreeFunc) NULL, &error);
+
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_clear_error (&error);
+	}
+
+	start_offline = gconf_client_get_bool (
+		factory->priv->gconf_client,
+		"/apps/evolution/shell/start_offline", &error);
+
+	if (error == NULL) {
+		e_data_factory_set_online (factory, !start_offline);
+	} else {
+		g_warning ("%s", error->message);
+		g_clear_error (&error);
+	}
+}
+
+static void
+data_factory_last_client_gone_cb (EBackend *backend,
+                                  EDataFactory *factory)
+{
+	ESource *source;
+	const gchar *uid;
+
+	source = e_backend_get_source (backend);
+	uid = e_source_peek_uid (source);
+	g_return_if_fail (uid != NULL);
+
+	g_mutex_lock (factory->priv->mutex);
+	g_hash_table_remove (factory->priv->backends, uid);
+	g_mutex_unlock (factory->priv->mutex);
+}
+
+static void
+data_factory_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ONLINE:
+			e_data_factory_set_online (
+				E_DATA_FACTORY (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+data_factory_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ONLINE:
+			g_value_set_boolean (
+				value, e_data_factory_get_online (
+				E_DATA_FACTORY (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+data_factory_dispose (GObject *object)
+{
+	EDataFactoryPrivate *priv;
+
+	priv = E_DATA_FACTORY_GET_PRIVATE (object);
+
+	g_hash_table_remove_all (priv->backends);
+	g_hash_table_remove_all (priv->backend_factories);
+
+	if (priv->gconf_client != NULL) {
+		gconf_client_notify_remove (
+			priv->gconf_client, priv->gconf_cnxn_id);
+		g_object_unref (priv->gconf_client);
+		priv->gconf_client = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_data_factory_parent_class)->dispose (object);
+}
+
+static void
+data_factory_finalize (GObject *object)
+{
+	EDataFactoryPrivate *priv;
+
+	priv = E_DATA_FACTORY_GET_PRIVATE (object);
+
+	g_mutex_free (priv->mutex);
+
+	g_hash_table_destroy (priv->backends);
+	g_hash_table_destroy (priv->backend_factories);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_data_factory_parent_class)->finalize (object);
+}
+
+static gboolean
+data_factory_initable_init (GInitable *initable,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+	EDataFactoryPrivate *priv;
+	GList *list, *link;
+
+	priv = E_DATA_FACTORY_GET_PRIVATE (initable);
+
+	/* Load all module libraries containing extensions. */
+
+	e_dbus_server_load_modules (E_DBUS_SERVER (initable));
+
+	/* Collect all backend factories into a hash table. */
+
+	list = e_extensible_list_extensions (
+		E_EXTENSIBLE (initable), E_TYPE_BACKEND_FACTORY);
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		EBackendFactory *backend_factory;
+		const gchar *hash_key;
+
+		backend_factory = E_BACKEND_FACTORY (link->data);
+		hash_key = e_backend_factory_get_hash_key (backend_factory);
+
+		if (hash_key != NULL) {
+			g_hash_table_insert (
+				priv->backend_factories,
+				g_strdup (hash_key),
+				g_object_ref (backend_factory));
+			g_print (
+				"Registering %s ('%s')\n",
+				G_OBJECT_TYPE_NAME (backend_factory),
+				hash_key);
+		}
+	}
+
+	g_list_free (list);
+
+	return TRUE;
+}
+
+static void
+e_data_factory_class_init (EDataFactoryClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (EDataFactoryPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = data_factory_set_property;
+	object_class->get_property = data_factory_get_property;
+	object_class->dispose = data_factory_dispose;
+	object_class->finalize = data_factory_finalize;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ONLINE,
+		g_param_spec_boolean (
+			"online",
+			"Online",
+			"Whether the server is online",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_data_factory_initable_init (GInitableIface *interface)
+{
+	interface->init = data_factory_initable_init;
+}
+
+static void
+e_data_factory_init (EDataFactory *factory)
+{
+	factory->priv = E_DATA_FACTORY_GET_PRIVATE (factory);
+
+	factory->priv->mutex = g_mutex_new ();
+
+	factory->priv->backends = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_object_unref);
+
+	factory->priv->backend_factories = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_object_unref);
+
+	data_factory_init_online_monitoring (factory);
+}
+
+EBackend *
+e_data_factory_get_backend (EDataFactory *factory,
+                            const gchar *hash_key,
+                            ESource *source)
+{
+	EBackendFactory *backend_factory;
+	EBackend *backend;
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_DATA_FACTORY (factory), NULL);
+	g_return_val_if_fail (hash_key != NULL, NULL);
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	uid = e_source_peek_uid (source);
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	g_mutex_lock (factory->priv->mutex);
+
+	/* Check if we already have a backend for the given source. */
+	backend = g_hash_table_lookup (factory->priv->backends, uid);
+
+	if (backend != NULL)
+		goto exit;
+
+	/* Find a suitable backend factory using the hash key. */
+	backend_factory = g_hash_table_lookup (
+		factory->priv->backend_factories, hash_key);
+
+	if (backend_factory == NULL)
+		goto exit;
+
+	/* Create a new backend for the given source and store it. */
+	backend = e_backend_factory_new_backend (backend_factory, source);
+
+	if (backend == NULL)
+		goto exit;
+
+	g_object_bind_property (
+		factory, "online",
+		backend, "online",
+		G_BINDING_SYNC_CREATE);
+
+	g_signal_connect (
+		backend, "last-client-gone",
+		G_CALLBACK (data_factory_last_client_gone_cb), factory);
+
+	g_hash_table_insert (
+		factory->priv->backends,
+		g_strdup (uid), backend);
+
+exit:
+	g_mutex_unlock (factory->priv->mutex);
+
+	return backend;
+}
+
+gboolean
+e_data_factory_get_online (EDataFactory *factory)
+{
+	g_return_val_if_fail (E_IS_DATA_FACTORY (factory), FALSE);
+
+	return factory->priv->online;
+}
+
+void
+e_data_factory_set_online (EDataFactory *factory,
+                           gboolean online)
+{
+	g_return_if_fail (E_IS_DATA_FACTORY (factory));
+
+	/* Avoid unnecessary "notify" signals. */
+	if (online == factory->priv->online)
+		return;
+
+	factory->priv->online = online;
+
+	g_object_notify (G_OBJECT (factory), "online");
+
+	g_print (
+		"%s is now %s.\n",
+		G_OBJECT_TYPE_NAME (factory),
+		online ? "online" : "offline");
+}
diff --git a/libebackend/e-data-factory.h b/libebackend/e-data-factory.h
new file mode 100644
index 0000000..0b57fd3
--- /dev/null
+++ b/libebackend/e-data-factory.h
@@ -0,0 +1,79 @@
+/*
+ * e-data-factory.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_DATA_FACTORY_H
+#define E_DATA_FACTORY_H
+
+#include <libebackend/e-backend.h>
+#include <libebackend/e-dbus-server.h>
+
+/* Standard GObject macros */
+#define E_TYPE_DATA_FACTORY \
+	(e_data_factory_get_type ())
+#define E_DATA_FACTORY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_DATA_FACTORY, EDataFactory))
+#define E_DATA_FACTORY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_DATA_FACTORY, EDataFactoryClass))
+#define E_IS_DATA_FACTORY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_DATA_FACTORY))
+#define E_IS_DATA_FACTORY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_DATA_FACTORY))
+#define E_DATA_FACTORY_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_DATA_FACTORY, EDataFactoryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EDataFactory EDataFactory;
+typedef struct _EDataFactoryClass EDataFactoryClass;
+typedef struct _EDataFactoryPrivate EDataFactoryPrivate;
+
+/**
+ * EDataFactory:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.4
+ **/
+struct _EDataFactory {
+	EDBusServer parent;
+	EDataFactoryPrivate *priv;
+};
+
+struct _EDataFactoryClass {
+	EDBusServerClass parent_class;
+
+	gpointer reserved[16];
+};
+
+GType		e_data_factory_get_type		(void) G_GNUC_CONST;
+EBackend *	e_data_factory_get_backend	(EDataFactory *factory,
+						 const gchar *hash_key,
+						 ESource *source);
+gboolean	e_data_factory_get_online	(EDataFactory *factory);
+void		e_data_factory_set_online	(EDataFactory *factory,
+						 gboolean online);
+
+G_END_DECLS
+
+#endif /* E_DATA_FACTORY_H */
diff --git a/libebackend/e-dbus-server.c b/libebackend/e-dbus-server.c
new file mode 100644
index 0000000..367b4cf
--- /dev/null
+++ b/libebackend/e-dbus-server.c
@@ -0,0 +1,251 @@
+/*
+ * e-dbus-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/>
+ *
+ */
+
+/**
+ * SECTION: e-dbus-server
+ * @short_description: an abstract base class for a D-Bus server
+ * @include: libebackend/e-dbus-server
+ **/
+
+#include "e-dbus-server.h"
+
+#include <config.h>
+
+#ifdef G_OS_UNIX
+#include <glib-unix.h>
+#endif
+
+#include <libebackend/e-module.h>
+#include <libebackend/e-extensible.h>
+
+#define E_DBUS_SERVER_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_DBUS_SERVER, EDBusServerPrivate))
+
+struct _EDBusServerPrivate {
+	GMainLoop *main_loop;
+	guint bus_owner_id;
+	guint terminate_id;
+};
+
+enum {
+	BUS_ACQUIRED,
+	BUS_NAME_ACQUIRED,
+	BUS_NAME_LOST,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
+	EDBusServer, e_dbus_server, G_TYPE_OBJECT,
+	G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+static void
+dbus_server_bus_acquired_cb (GDBusConnection *connection,
+                             const gchar *bus_name,
+                             EDBusServer *server)
+{
+	g_signal_emit (server, signals[BUS_ACQUIRED], 0, connection);
+}
+
+static void
+dbus_server_name_acquired_cb (GDBusConnection *connection,
+                              const gchar *bus_name,
+                              EDBusServer *server)
+{
+	g_signal_emit (server, signals[BUS_NAME_ACQUIRED], 0, connection);
+}
+
+static void
+dbus_server_name_lost_cb (GDBusConnection *connection,
+                          const gchar *bus_name,
+                          EDBusServer *server)
+{
+	g_signal_emit (server, signals[BUS_NAME_LOST], 0, connection);
+}
+
+#ifdef G_OS_UNIX
+static gboolean
+dbus_server_terminate_cb (EDBusServer *server)
+{
+	g_print ("Received terminate signal.\n");
+	e_dbus_server_quit (server);
+
+	return FALSE;
+}
+#endif
+
+static void
+dbus_server_finalize (GObject *object)
+{
+	EDBusServerPrivate *priv;
+
+	priv = E_DBUS_SERVER_GET_PRIVATE (object);
+
+	g_main_loop_unref (priv->main_loop);
+	g_bus_unown_name (priv->bus_owner_id);
+
+	if (priv->terminate_id > 0)
+		g_source_remove (priv->terminate_id);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_dbus_server_parent_class)->finalize (object);
+}
+
+static void
+dbus_server_bus_acquired (EDBusServer *server,
+                          GDBusConnection *connection)
+{
+	/* Placeholder so subclasses can safely chain up. */
+}
+
+static void
+dbus_server_bus_name_acquired (EDBusServer *server,
+                               GDBusConnection *connection)
+{
+	EDBusServerClass *class;
+
+	class = E_DBUS_SERVER_GET_CLASS (server);
+	g_return_if_fail (class->bus_name != NULL);
+
+	g_print ("Bus name '%s' acquired.\n", class->bus_name);
+}
+
+static void
+dbus_server_bus_name_lost (EDBusServer *server,
+                           GDBusConnection *connection)
+{
+	EDBusServerClass *class;
+
+	class = E_DBUS_SERVER_GET_CLASS (server);
+	g_return_if_fail (class->bus_name != NULL);
+
+	g_print ("Bus name '%s' lost.\n", class->bus_name);
+
+	e_dbus_server_quit (server);
+}
+
+static void
+e_dbus_server_class_init (EDBusServerClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (EDBusServerPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->finalize = dbus_server_finalize;
+
+	class->bus_acquired = dbus_server_bus_acquired;
+	class->bus_name_acquired = dbus_server_bus_name_acquired;
+	class->bus_name_lost = dbus_server_bus_name_lost;
+
+	signals[BUS_ACQUIRED] = g_signal_new (
+		"bus-acquired",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EDBusServerClass, bus_acquired),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		G_TYPE_DBUS_CONNECTION);
+
+	signals[BUS_NAME_ACQUIRED] = g_signal_new (
+		"bus-name-acquired",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EDBusServerClass, bus_name_acquired),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		G_TYPE_DBUS_CONNECTION);
+
+	signals[BUS_NAME_LOST] = g_signal_new (
+		"bus-name-lost",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EDBusServerClass, bus_name_lost),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		G_TYPE_DBUS_CONNECTION);
+}
+
+static void
+e_dbus_server_init (EDBusServer *server)
+{
+	server->priv = E_DBUS_SERVER_GET_PRIVATE (server);
+	server->priv->main_loop = g_main_loop_new (NULL, FALSE);
+
+#ifdef G_OS_UNIX
+	server->priv->terminate_id = g_unix_signal_add (
+		SIGTERM, (GSourceFunc) dbus_server_terminate_cb, server);
+#endif
+}
+
+void
+e_dbus_server_run (EDBusServer *server)
+{
+	EDBusServerClass *class;
+
+	g_return_if_fail (E_IS_DBUS_SERVER (server));
+
+	if (g_main_loop_is_running (server->priv->main_loop))
+		return;
+
+	/* Try to acquire the well-known bus name. */
+
+	class = E_DBUS_SERVER_GET_CLASS (server);
+	g_return_if_fail (class->bus_name != NULL);
+
+	server->priv->bus_owner_id = g_bus_own_name (
+		G_BUS_TYPE_SESSION,
+		class->bus_name,
+		G_BUS_NAME_OWNER_FLAGS_REPLACE |
+		G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
+		(GBusAcquiredCallback) dbus_server_bus_acquired_cb,
+		(GBusNameAcquiredCallback) dbus_server_name_acquired_cb,
+		(GBusNameLostCallback) dbus_server_name_lost_cb,
+		g_object_ref (server),
+		(GDestroyNotify) g_object_unref);
+
+	g_main_loop_run (server->priv->main_loop);
+}
+
+void
+e_dbus_server_quit (EDBusServer *server)
+{
+	g_return_if_fail (E_IS_DBUS_SERVER (server));
+
+	g_main_loop_quit (server->priv->main_loop);
+}
+
+void
+e_dbus_server_load_modules (EDBusServer *server)
+{
+	EDBusServerClass *class;
+	GList *list;
+
+	g_return_if_fail (E_IS_DBUS_SERVER (server));
+
+	class = E_DBUS_SERVER_GET_CLASS (server);
+	g_return_if_fail (class->module_directory != NULL);
+
+	list = e_module_load_all_in_directory (class->module_directory);
+	g_list_free_full (list, (GDestroyNotify) g_type_module_unuse);
+}
diff --git a/libebackend/e-dbus-server.h b/libebackend/e-dbus-server.h
new file mode 100644
index 0000000..82d8c6f
--- /dev/null
+++ b/libebackend/e-dbus-server.h
@@ -0,0 +1,86 @@
+/*
+ * e-dbus-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_SERVER_H
+#define E_DBUS_SERVER_H
+
+#include <gio/gio.h>
+
+/* Standard GObject macros */
+#define E_TYPE_DBUS_SERVER \
+	(e_dbus_server_get_type ())
+#define E_DBUS_SERVER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_DBUS_SERVER, EDBusServer))
+#define E_DBUS_SERVER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_DBUS_SERVER, EDBusServerClass))
+#define E_IS_DBUS_SERVER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_DBUS_SERVER))
+#define E_IS_DBUS_SERVER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_DBUS_SERVER))
+#define E_DBUS_SERVER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_DBUS_SERVER, EDBusServerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EDBusServer EDBusServer;
+typedef struct _EDBusServerClass EDBusServerClass;
+typedef struct _EDBusServerPrivate EDBusServerPrivate;
+
+/**
+ * EDBusServer:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.4
+ **/
+struct _EDBusServer {
+	GObject parent;
+	EDBusServerPrivate *priv;
+};
+
+struct _EDBusServerClass {
+	GObjectClass parent_class;
+
+	const gchar *bus_name;
+	const gchar *module_directory;
+
+	/* Signals */
+	void		(*bus_acquired)		(EDBusServer *server,
+						 GDBusConnection *connection);
+	void		(*bus_name_acquired)	(EDBusServer *server,
+						 GDBusConnection *connection);
+	void		(*bus_name_lost)	(EDBusServer *server,
+						 GDBusConnection *connection);
+
+	gpointer reserved[16];
+};
+
+GType		e_dbus_server_get_type		(void) G_GNUC_CONST;
+void		e_dbus_server_run		(EDBusServer *server);
+void		e_dbus_server_quit		(EDBusServer *server);
+void		e_dbus_server_load_modules	(EDBusServer *server);
+
+G_END_DECLS
+
+#endif /* E_DBUS_SERVER_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]