[evolution-data-server/account-mgmt: 6/38] Add a new "evolution-source-registry" D-Bus service.



commit 4867a36dd2e65781316242d899edcf541a91119b
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    |    5 +-
 .../reference/libebackend/libebackend-sections.txt |   78 ++
 docs/reference/libebackend/libebackend.types       |    6 +
 libebackend/Makefile.am                            |   20 +-
 libebackend/e-backend-enums.h                      |   41 +
 libebackend/e-dbus-source-authenticator.c          |  371 ++++++++
 libebackend/e-dbus-source-authenticator.h          |   81 ++
 libebackend/e-dbus-source-object.c                 |  747 +++++++++++++++
 libebackend/e-dbus-source-object.h                 |   98 ++
 libebackend/e-dbus-source-server.c                 |  966 ++++++++++++++++++++
 libebackend/e-dbus-source-server.h                 |  114 +++
 services/Makefile.am                               |    1 +
 services/evolution-source-registry/Makefile.am     |   42 +
 .../e-authentication-dialog.c                      |  679 ++++++++++++++
 .../e-authentication-dialog.h                      |   78 ++
 .../evolution-source-registry/e-authenticator.c    |  304 ++++++
 .../evolution-source-registry/e-authenticator.h    |   63 ++
 .../evolution-source-registry.c                    |   74 ++
 ...g.gnome.evolution.dataserver.Sources.service.in |    3 +
 20 files changed, 3785 insertions(+), 9 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 2b72707..d27ef93 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.AddressBook2"
 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
@@ -380,14 +387,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)
@@ -1485,7 +1493,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 ******************************
@@ -1600,6 +1608,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..28db209 100644
--- a/docs/reference/libebackend/libebackend-docs.xml
+++ b/docs/reference/libebackend/libebackend-docs.xml
@@ -13,7 +13,10 @@
     <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-dbus-source-authenticator.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..cc48e18 100644
--- a/docs/reference/libebackend/libebackend-sections.txt
+++ b/docs/reference/libebackend/libebackend-sections.txt
@@ -80,6 +80,84 @@ e_dbus_server_get_type
 </SECTION>
 
 <SECTION>
+<FILE>e-dbus-source-authenticator</FILE>
+<TITLE>EDBusSourceAuthenticator</TITLE>
+EDBusSourceAuthenticator
+EDBusSourceAuthenticatorResult
+e_dbus_source_authenticator_get_object
+e_dbus_source_authenticator_initiate
+e_dbus_source_authenticator_complete
+<SUBSECTION Standard>
+E_DBUS_SOURCE_AUTHENTICATOR
+E_IS_DBUS_SOURCE_AUTHENTICATOR
+E_TYPE_DBUS_SOURCE_AUTHENTICATOR
+E_DBUS_SOURCE_AUTHENTICATOR_CLASS
+E_IS_DBUS_SOURCE_AUTHENTICATOR_CLASS
+E_DBUS_SOURCE_AUTHENTICATOR_GET_CLASS
+E_TYPE_DBUS_SOURCE_AUTHENTICATOR_RESULT
+EDBusSourceAuthenticatorClass
+<SUBSECTION Private>
+EDBusSourceAuthenticatorPrivate
+e_dbus_source_authenticator_get_type
+e_dbus_source_authenticator_result_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_server
+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
+<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_get_authenticator_type
+e_dbus_source_server_set_authenticator_type
+e_dbus_source_server_add_object
+e_dbus_source_server_remove_object
+e_dbus_source_server_add_authenticator
+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/libebackend.types b/docs/reference/libebackend/libebackend.types
index e23f54b..a0cce19 100644
--- a/docs/reference/libebackend/libebackend.types
+++ b/docs/reference/libebackend/libebackend.types
@@ -2,6 +2,9 @@
 #include <libebackend/e-backend-factory.h>
 #include <libebackend/e-data-factory.h>
 #include <libebackend/e-dbus-server.h>
+#include <libebackend/e-dbus-source-authenticator.h>
+#include <libebackend/e-dbus-source-object.h>
+#include <libebackend/e-dbus-source-server.h>
 #include <libebackend/e-extensible.h>
 #include <libebackend/e-extension.h>
 #include <libebackend/e-file-cache.h>
@@ -12,6 +15,9 @@ e_backend_get_type
 e_backend_factory_get_type
 e_data_factory_get_type
 e_dbus_server_get_type
+e_dbus_source_authenticator_get_type
+e_dbus_source_object_get_type
+e_dbus_source_server_get_type
 e_extensible_get_type
 e_extension_get_type
 e_file_cache_get_type
diff --git a/libebackend/Makefile.am b/libebackend/Makefile.am
index 562afce..a8e46d5 100644
--- a/libebackend/Makefile.am
+++ b/libebackend/Makefile.am
@@ -1,19 +1,33 @@
+include $(top_srcdir)/glib-gen.mak
+glib_enum_headers=e-backend-enums.h
+glib_enum_define=E
+glib_enum_prefix=e
+
+ENUM_GENERATED = e-backend-enumtypes.h e-backend-enumtypes.c
+
+BUILT_SOURCES = $(ENUM_GENERATED)
+
 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)					\
 	$(GIO_UNIX_CFLAGS)
 
 libebackend_1_2_la_SOURCES =		\
+	$(BUILT_SOURCES)		\
 	e-backend.c			\
 	e-backend-factory.c		\
 	e-data-factory.c		\
 	e-dbus-server.c			\
+	e-dbus-source-authenticator.c	\
+	e-dbus-source-object.c		\
+	e-dbus-source-server.c		\
 	e-extensible.c			\
 	e-extension.c			\
 	e-offline-listener.c		\
@@ -37,9 +51,13 @@ libebackendincludedir = $(privincludedir)/libebackend
 
 libebackendinclude_HEADERS =		\
 	e-backend.h			\
+	e-backend-enums.h		\
 	e-backend-factory.h		\
 	e-data-factory.h		\
 	e-dbus-server.h			\
+	e-dbus-source-authenticator.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-backend-enums.h b/libebackend/e-backend-enums.h
new file mode 100644
index 0000000..dfb263d
--- /dev/null
+++ b/libebackend/e-backend-enums.h
@@ -0,0 +1,41 @@
+/*
+ * e-backend-enums.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_ENUMS_H
+#define E_BACKEND_ENUMS_H
+
+/**
+ * EDBusSourceAuthenticatorResult:
+ * @E_DBUS_SOURCE_AUTHENTICATOR_SUCCESSFUL:
+ *   Client reported successful authentication.
+ * @E_DBUS_SOURCE_AUTHENTICATOR_DISMISSED:
+ *   User dismissed the authentication prompt.
+ * @E_DBUS_SOURCE_AUTHENTICATOR_CANCELLED:
+ *   Client cancelled or failed to respond.
+ *
+ * Completion codes used by #EDBusSourceAuthenticator.
+ *
+ * Since: 3.4
+ **/
+typedef enum {
+	E_DBUS_SOURCE_AUTHENTICATOR_SUCCESSFUL,
+	E_DBUS_SOURCE_AUTHENTICATOR_DISMISSED,
+	E_DBUS_SOURCE_AUTHENTICATOR_CANCELLED
+} EDBusSourceAuthenticatorResult;
+
+#endif /* E_BACKEND_ENUMS_H */
diff --git a/libebackend/e-dbus-source-authenticator.c b/libebackend/e-dbus-source-authenticator.c
new file mode 100644
index 0000000..40498f5
--- /dev/null
+++ b/libebackend/e-dbus-source-authenticator.c
@@ -0,0 +1,371 @@
+/*
+ * e-dbus-source-authenticator.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-authenticator.h"
+
+#include <libebackend/e-backend-enumtypes.h>
+
+#define E_DBUS_SOURCE_AUTHENTICATOR_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_DBUS_SOURCE_AUTHENTICATOR, EDBusSourceAuthenticatorPrivate))
+
+/* Be generous and give the client a full 10 minutes to
+ * respond before timing out the authentication session. */
+#define INACTIVITY_TIMEOUT (60 * 10)  /* seconds */
+
+struct _EDBusSourceAuthenticatorPrivate {
+	EDBusSourceObject *object;
+	gboolean client_ready;
+	gboolean server_ready;
+	guint timeout_id;
+};
+
+enum {
+	PROP_0,
+	PROP_OBJECT
+};
+
+enum {
+	COMPLETE,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_ABSTRACT_TYPE (
+	EDBusSourceAuthenticator,
+	e_dbus_source_authenticator,
+	E_DBUS_TYPE_AUTHENTICATOR_SKELETON)
+
+static void
+dbus_source_authenticator_maybe_initiate (EDBusSourceAuthenticator *authenticator)
+{
+	EDBusSourceAuthenticatorClass *class;
+
+	if (!authenticator->priv->client_ready)
+		return;
+
+	if (!authenticator->priv->server_ready)
+		return;
+
+	class = E_DBUS_SOURCE_AUTHENTICATOR_GET_CLASS (authenticator);
+	g_return_if_fail (class->initiate != NULL);
+
+	class->initiate (authenticator);
+}
+
+static gboolean
+dbus_source_authenticator_timeout_cb (EDBusSourceAuthenticator *authenticator)
+{
+	EDBusSourceAuthenticatorResult result;
+
+	authenticator->priv->timeout_id = 0;
+
+	result = E_DBUS_SOURCE_AUTHENTICATOR_CANCELLED;
+	e_dbus_source_authenticator_complete (authenticator, result);
+
+	return FALSE;
+}
+
+static gboolean
+dbus_source_authenticator_handle_cancel (EDBusAuthenticator *authenticator,
+                                         GDBusMethodInvocation *invocation)
+{
+	e_dbus_source_authenticator_complete (
+		E_DBUS_SOURCE_AUTHENTICATOR (authenticator),
+		E_DBUS_SOURCE_AUTHENTICATOR_CANCELLED);
+
+	e_dbus_authenticator_complete_cancel (authenticator, invocation);
+
+	return TRUE;
+}
+
+static gboolean
+dbus_source_authenticator_handle_ready (EDBusAuthenticator *authenticator,
+                                        GDBusMethodInvocation *invocation)
+{
+	EDBusSourceAuthenticatorPrivate *priv;
+
+	priv = E_DBUS_SOURCE_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	/* This method is idempotent. */
+	if (!priv->client_ready) {
+		priv->client_ready = TRUE;
+		dbus_source_authenticator_maybe_initiate (
+			E_DBUS_SOURCE_AUTHENTICATOR (authenticator));
+	}
+
+	e_dbus_authenticator_complete_ready (authenticator, invocation);
+
+	return TRUE;
+}
+
+static gboolean
+dbus_source_authenticator_cancel_timeout (EDBusAuthenticator *authenticator,
+                                          GDBusMethodInvocation *invocation)
+{
+	EDBusSourceAuthenticatorPrivate *priv;
+
+	priv = E_DBUS_SOURCE_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	/* Client responded, cancel the timeout. */
+	if (priv->timeout_id > 0) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	/* This signal handler does NOT handle the method invocation. */
+	return FALSE;
+}
+
+static void
+dbus_source_authenticator_start_timeout (EDBusAuthenticator *authenticator)
+{
+	EDBusSourceAuthenticatorPrivate *priv;
+
+	priv = E_DBUS_SOURCE_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	/* An authentication session starts when a D-Bus object path is
+	 * exported with an Authenticator interface (that's us) and the
+	 * client calls its 'Ready' method.  The session then ping pongs
+	 * between 'Authenticate' signals TO the client and 'Accepted'
+	 * or 'Rejected' method calls FROM the client.
+	 *
+	 * For each 'ping' we start a countdown timer on the order of
+	 * several minutes.  If the client does not 'pong' (respond to)
+	 * us before the countdown timer expires, we assume the client
+	 * has terminated and we close the authentication session. */
+
+	/* Timer should not already be running. */
+	g_warn_if_fail (priv->timeout_id == 0);
+
+	priv->timeout_id = g_timeout_add_seconds (
+		INACTIVITY_TIMEOUT, (GSourceFunc)
+		dbus_source_authenticator_timeout_cb,
+		authenticator);
+}
+
+static void
+dbus_source_authenticator_set_object (EDBusSourceAuthenticator *authenticator,
+                                      EDBusSourceObject *object)
+{
+	g_return_if_fail (E_IS_DBUS_SOURCE_OBJECT (object));
+	g_return_if_fail (authenticator->priv->object == NULL);
+
+	authenticator->priv->object = g_object_ref (object);
+}
+
+static void
+dbus_source_authenticator_set_property (GObject *object,
+                                        guint property_id,
+                                        const GValue *value,
+                                        GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_OBJECT:
+			dbus_source_authenticator_set_object (
+				E_DBUS_SOURCE_AUTHENTICATOR (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+dbus_source_authenticator_get_property (GObject *object,
+                                        guint property_id,
+                                        GValue *value,
+                                        GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_OBJECT:
+			g_value_set_object (
+				value,
+				e_dbus_source_authenticator_get_object (
+				E_DBUS_SOURCE_AUTHENTICATOR (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+dbus_source_authenticator_dispose (GObject *object)
+{
+	EDBusSourceAuthenticatorPrivate *priv;
+
+	priv = E_DBUS_SOURCE_AUTHENTICATOR_GET_PRIVATE (object);
+
+	if (priv->object != NULL) {
+		g_object_unref (priv->object);
+		priv->object = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_dbus_source_authenticator_parent_class)->dispose (object);
+}
+
+static void
+dbus_source_authenticator_finalize (GObject *object)
+{
+	EDBusSourceAuthenticatorPrivate *priv;
+
+	priv = E_DBUS_SOURCE_AUTHENTICATOR_GET_PRIVATE (object);
+
+	if (priv->timeout_id > 0)
+		g_source_remove (priv->timeout_id);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_dbus_source_authenticator_parent_class)->finalize (object);
+}
+
+static void
+dbus_source_authenticator_initiate (EDBusSourceAuthenticator *authenticator)
+{
+	/* This function is just a placeholder so subclasses can safely
+	 * chain up.  We might want to do something here in the future. */
+}
+
+static void
+dbus_source_authenticator_complete (EDBusSourceAuthenticator *authenticator,
+                                    EDBusSourceAuthenticatorResult result)
+{
+	/* This function is just a placeholder so subclasses can safely
+	 * chain up.  We might want to do something here in the future. */
+}
+
+static void
+e_dbus_source_authenticator_class_init (EDBusSourceAuthenticatorClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (
+		class, sizeof (EDBusSourceAuthenticatorPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = dbus_source_authenticator_set_property;
+	object_class->get_property = dbus_source_authenticator_get_property;
+	object_class->dispose = dbus_source_authenticator_dispose;
+	object_class->finalize = dbus_source_authenticator_finalize;
+
+	class->initiate = dbus_source_authenticator_initiate;
+	class->complete = dbus_source_authenticator_complete;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_OBJECT,
+		g_param_spec_object (
+			"object",
+			"Object",
+			"The source object being authenticated",
+			E_TYPE_DBUS_SOURCE_OBJECT,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+
+	signals[COMPLETE] = g_signal_new (
+		"complete",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EDBusSourceAuthenticatorClass, complete),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__ENUM,
+		G_TYPE_NONE, 1,
+		E_TYPE_DBUS_SOURCE_AUTHENTICATOR_RESULT);
+}
+
+static void
+e_dbus_source_authenticator_init (EDBusSourceAuthenticator *authenticator)
+{
+	authenticator->priv =
+		E_DBUS_SOURCE_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	/* Start the timeout immediately. */
+
+	dbus_source_authenticator_start_timeout (
+		E_DBUS_AUTHENTICATOR (authenticator));
+
+	/* The authenticate signal also starts the timeout. */
+
+	g_signal_connect (
+		authenticator, "authenticate",
+		G_CALLBACK (dbus_source_authenticator_start_timeout), NULL);
+
+	/* All method invocations cancel the timeout. */
+
+	g_signal_connect (
+		authenticator, "handle-cancel",
+		G_CALLBACK (dbus_source_authenticator_cancel_timeout), NULL);
+
+	g_signal_connect (
+		authenticator, "handle-ready",
+		G_CALLBACK (dbus_source_authenticator_cancel_timeout), NULL);
+
+	g_signal_connect (
+		authenticator, "handle-accepted",
+		G_CALLBACK (dbus_source_authenticator_cancel_timeout), NULL);
+
+	g_signal_connect (
+		authenticator, "handle-rejected",
+		G_CALLBACK (dbus_source_authenticator_cancel_timeout), NULL);
+
+	/* These method invocations do additional stuff. */
+
+	g_signal_connect (
+		authenticator, "handle-cancel",
+		G_CALLBACK (dbus_source_authenticator_handle_cancel), NULL);
+
+	g_signal_connect (
+		authenticator, "handle-ready",
+		G_CALLBACK (dbus_source_authenticator_handle_ready), NULL);
+}
+
+EDBusSourceObject *
+e_dbus_source_authenticator_get_object (EDBusSourceAuthenticator *authenticator)
+{
+	g_return_val_if_fail (E_IS_DBUS_SOURCE_AUTHENTICATOR (authenticator), NULL);
+
+	return authenticator->priv->object;
+}
+
+void
+e_dbus_source_authenticator_initiate (EDBusSourceAuthenticator *authenticator)
+{
+	/* Note, we delay invoking the class method until the client
+	 * has also indicated it's ready.  This way the subclass can
+	 * start taking action directly in its initiate() method. */
+
+	g_return_if_fail (E_IS_DBUS_SOURCE_AUTHENTICATOR (authenticator));
+
+	/* This function is idempotent. */
+	if (!authenticator->priv->server_ready) {
+		authenticator->priv->server_ready = TRUE;
+		dbus_source_authenticator_maybe_initiate (authenticator);
+	}
+}
+
+void
+e_dbus_source_authenticator_complete (EDBusSourceAuthenticator *authenticator,
+                                      EDBusSourceAuthenticatorResult result)
+{
+	g_return_if_fail (E_IS_DBUS_SOURCE_AUTHENTICATOR (authenticator));
+
+	g_signal_emit (authenticator, signals[COMPLETE], 0, result);
+}
+
diff --git a/libebackend/e-dbus-source-authenticator.h b/libebackend/e-dbus-source-authenticator.h
new file mode 100644
index 0000000..cfc8ddd
--- /dev/null
+++ b/libebackend/e-dbus-source-authenticator.h
@@ -0,0 +1,81 @@
+/*
+ * e-dbus-source-authenticator.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_AUTHENTICATOR_H
+#define E_DBUS_SOURCE_AUTHENTICATOR_H
+
+#include <libebackend/e-backend-enums.h>
+#include <libebackend/e-dbus-source-object.h>
+#include <libedataserver/e-dbus-authenticator.h>
+
+/* Standard GObject macros */
+#define E_TYPE_DBUS_SOURCE_AUTHENTICATOR \
+	(e_dbus_source_authenticator_get_type ())
+#define E_DBUS_SOURCE_AUTHENTICATOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_DBUS_SOURCE_AUTHENTICATOR, EDBusSourceAuthenticator))
+#define E_DBUS_SOURCE_AUTHENTICATOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_DBUS_SOURCE_AUTHENTICATOR, EDBusSourceAuthenticatorClass))
+#define E_IS_DBUS_SOURCE_AUTHENTICATOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_DBUS_SOURCE_AUTHENTICATOR))
+#define E_IS_DBUS_SOURCE_AUTHENTICATOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_DBUS_SOURCE_AUTHENTICATOR))
+#define E_DBUS_SOURCE_AUTHENTICATOR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_DBUS_SOURCE_AUTHENTICATOR, EDBusSourceAuthenticatorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EDBusSourceAuthenticator EDBusSourceAuthenticator;
+typedef struct _EDBusSourceAuthenticatorClass EDBusSourceAuthenticatorClass;
+typedef struct _EDBusSourceAuthenticatorPrivate EDBusSourceAuthenticatorPrivate;
+
+struct _EDBusSourceAuthenticator {
+	EDBusAuthenticatorSkeleton parent;
+	EDBusSourceAuthenticatorPrivate *priv;
+};
+
+struct _EDBusSourceAuthenticatorClass {
+	EDBusAuthenticatorSkeletonClass parent_class;
+
+	/* Methods */
+	void	(*initiate)	(EDBusSourceAuthenticator *authenticator);
+
+	/* Signals */
+	void	(*complete)	(EDBusSourceAuthenticator *authenticator,
+				 EDBusSourceAuthenticatorResult result);
+};
+
+GType		e_dbus_source_authenticator_get_type
+				(void) G_GNUC_CONST;
+EDBusSourceObject *
+		e_dbus_source_authenticator_get_object
+				(EDBusSourceAuthenticator *authenticator);
+void		e_dbus_source_authenticator_initiate
+				(EDBusSourceAuthenticator *authenticator);
+void		e_dbus_source_authenticator_complete
+				(EDBusSourceAuthenticator *authenticator,
+				 EDBusSourceAuthenticatorResult result);
+
+G_END_DECLS
+
+#endif /* E_DBUS_SOURCE_AUTHENTICATOR_H */
+
diff --git a/libebackend/e-dbus-source-object.c b/libebackend/e-dbus-source-object.c
new file mode 100644
index 0000000..afb23da
--- /dev/null
+++ b/libebackend/e-dbus-source-object.c
@@ -0,0 +1,747 @@
+/*
+ * 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-source.h>
+#include <libedataserver/e-data-server-util.h>
+#include <libebackend/e-dbus-source-authenticator.h>
+#include <libebackend/e-dbus-source-server.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 {
+
+	gpointer server;  /* weak pointer */
+
+	GFile *file;
+	GKeyFile *key_file;
+
+	GFileMonitor *file_monitor;
+	gulong file_monitor_handler_id;
+
+	GQueue authenticators;
+	guint authentication_count;
+
+	gboolean writable;
+};
+
+enum {
+	PROP_0,
+	PROP_FILE,
+	PROP_SERVER,
+	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,
+	E_DBUS_TYPE_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) {
+		g_print (
+			"Reloading UID:%s\n",
+			e_dbus_source_object_get_uid (object));
+		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: %s", G_STRFUNC, 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 void
+dbus_source_object_auth_complete_cb (EDBusSourceAuthenticator *authenticator,
+                                     EDBusSourceAuthenticatorResult result,
+                                     EDBusSourceObject *object)
+{
+	if (g_queue_remove (&object->priv->authenticators, authenticator))
+		g_object_unref (authenticator);
+}
+
+static gboolean
+dbus_source_object_authenticate_cb (EDBusSource *interface,
+                                    GDBusMethodInvocation *invocation)
+{
+	GDBusObject *dbus_object;
+	GDBusInterface *dbus_interface;
+	GDBusConnection *connection;
+	EDBusSourceAuthenticator *authenticator;
+	EDBusSourceObject *object;
+	EDBusSourceServer *server;
+	GType auth_type;
+	const gchar *base_object_path;
+	gchar *auth_object_path;
+	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);
+
+	connection = g_dbus_method_invocation_get_connection (invocation);
+
+	server = e_dbus_source_object_get_server (object);
+	auth_type = e_dbus_source_server_get_authenticator_type (server);
+
+	/* If no valid authenticator type has been installed,
+	 * we have to pass on handling the authenticate() call. */
+	if (!g_type_is_a (auth_type, E_TYPE_DBUS_SOURCE_AUTHENTICATOR))
+		return FALSE;
+
+	/* Create the authenticator object. */
+	authenticator = g_object_new (auth_type, "object", object, NULL);
+
+	/* Export the authenticator to a unique object path.  This
+	 * effectively initiates a new authentication session with
+	 * the method caller. */
+
+	base_object_path = g_dbus_object_get_object_path (dbus_object);
+
+	auth_object_path = g_strdup_printf (
+		"%s/auth_%u", base_object_path,
+		object->priv->authentication_count++);
+
+	g_dbus_interface_skeleton_export (
+		G_DBUS_INTERFACE_SKELETON (authenticator),
+		connection, auth_object_path, &error);
+
+	if (error != NULL) {
+		g_dbus_method_invocation_take_error (invocation, error);
+		g_object_unref (authenticator);
+		g_free (auth_object_path);
+		return TRUE;
+	}
+
+	g_signal_connect (
+		authenticator, "complete",
+		G_CALLBACK (dbus_source_object_auth_complete_cb),
+		object);
+
+	/* This takes ownership of the authenticator reference. */
+	g_queue_push_tail (&object->priv->authenticators, authenticator);
+
+	/* This appends the authenticator to a queue. */
+	e_dbus_source_server_add_authenticator (server, authenticator);
+
+	e_dbus_source_complete_authenticate (
+		interface, invocation, auth_object_path);
+
+	g_free (auth_object_path);
+
+	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);
+	}
+
+	/* 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(). */
+
+	if (error == NULL)
+		g_file_replace_contents (
+			file, data, strlen (data), NULL, FALSE,
+			G_FILE_CREATE_NONE, NULL, NULL, &error);
+
+	if (error != NULL)
+		g_dbus_method_invocation_take_error (invocation, error);
+	else
+		e_dbus_source_complete_submit_data (interface, invocation);
+
+	g_object_unref (file);
+
+	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_server (EDBusSourceObject *object,
+                               EDBusSourceServer *server)
+{
+	g_return_if_fail (E_IS_DBUS_SOURCE_SERVER (server));
+	g_return_if_fail (object->priv->server == NULL);
+
+	object->priv->server = server;
+
+	g_object_add_weak_pointer (
+		G_OBJECT (object),
+		&object->priv->server);
+}
+
+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_SERVER:
+			dbus_source_object_set_server (
+				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_SERVER:
+			g_value_set_object (
+				value,
+				e_dbus_source_object_get_server (
+				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->server != NULL) {
+		g_object_remove_weak_pointer (
+			G_OBJECT (priv->server), &priv->server);
+		priv->server = 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;
+	}
+
+	/* The authenticators queue should be empty by now. */
+	g_warn_if_fail (g_queue_is_empty (&priv->authenticators));
+
+	/* 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;
+	EDBusSource *dbus_source;
+	const gchar *user_dir;
+	GFile *directory;
+	gchar *uid;
+
+	priv = E_DBUS_SOURCE_OBJECT_GET_PRIVATE (object);
+	dbus_source = e_dbus_object_peek_source (E_DBUS_OBJECT (object));
+
+	uid = g_file_get_basename (priv->file);
+	e_dbus_source_set_uid (dbus_source, 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",
+		dbus_source, "writable",
+		G_BINDING_SYNC_CREATE);
+
+	g_signal_connect (
+		dbus_source, "handle-authenticate",
+		G_CALLBACK (dbus_source_object_authenticate_cb), NULL);
+
+	g_signal_connect (
+		dbus_source, "handle-submit-data",
+		G_CALLBACK (dbus_source_object_submit_data_cb), NULL);
+
+	/* 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_SERVER,
+		g_param_spec_object (
+			"server",
+			"Server",
+			"The server to which the data source belongs",
+			E_TYPE_DBUS_SOURCE_SERVER,
+			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 *dbus_source;
+
+	object->priv = E_DBUS_SOURCE_OBJECT_GET_PRIVATE (object);
+	object->priv->key_file = g_key_file_new ();
+
+	g_queue_init (&object->priv->authenticators);
+
+	dbus_source = e_dbus_source_skeleton_new ();
+	e_dbus_object_skeleton_set_source (
+		E_DBUS_OBJECT_SKELETON (object), dbus_source);
+	g_object_unref (dbus_source);
+}
+
+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,
+                          EDBusSourceServer *server,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+	g_return_val_if_fail (G_IS_FILE (file), NULL);
+	g_return_val_if_fail (E_IS_DBUS_SOURCE_SERVER (server), NULL);
+
+	return g_initable_new (
+		E_TYPE_DBUS_SOURCE_OBJECT,
+		cancellable, error,
+		"file", file,
+		"server", server,
+		"g-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;
+}
+
+EDBusSourceServer *
+e_dbus_source_object_get_server (EDBusSourceObject *object)
+{
+	g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), NULL);
+
+	return E_DBUS_SOURCE_SERVER (object->priv->server);
+}
+
+const gchar *
+e_dbus_source_object_get_uid (EDBusSourceObject *object)
+{
+	EDBusSource *dbus_source;
+
+	g_return_val_if_fail (E_IS_DBUS_SOURCE_OBJECT (object), NULL);
+
+	dbus_source = e_dbus_object_peek_source (E_DBUS_OBJECT (object));
+
+	return e_dbus_source_get_uid (dbus_source);
+}
+
+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)
+{
+	EDBusSource *dbus_source;
+	GFileInfo *file_info;
+	gchar *data = 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,
+		&data, &length, NULL, error);
+
+	if (!success) {
+		g_warn_if_fail (data == NULL);
+		return FALSE;
+	}
+
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	if (!e_dbus_source_object_parse (object, data, length, error)) {
+		g_free (data);
+		return FALSE;
+	}
+
+	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. */
+
+	dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (object));
+
+	e_dbus_source_set_data (dbus_source, data);
+
+	/* 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 (dbus_source, FALSE);
+
+	g_object_unref (dbus_source);
+
+	g_free (data);
+
+	return TRUE;
+}
+
diff --git a/libebackend/e-dbus-source-object.h b/libebackend/e-dbus-source-object.h
new file mode 100644
index 0000000..0e60c5a
--- /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 <libedataserver/e-dbus-source.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
+
+struct _EDBusSourceServer;
+
+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 {
+	EDBusObjectSkeleton parent;
+	EDBusSourceObjectPrivate *priv;
+};
+
+struct _EDBusSourceObjectClass {
+	EDBusObjectSkeletonClass 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,
+						 struct _EDBusSourceServer *server,
+						 GCancellable *cancellable,
+						 GError **error);
+GFile *		e_dbus_source_object_get_file	(EDBusSourceObject *object);
+struct _EDBusSourceServer *
+		e_dbus_source_object_get_server	(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);
+
+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..ed2a2dc
--- /dev/null
+++ b/libebackend/e-dbus-source-server.c
@@ -0,0 +1,966 @@
+/*
+ * 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-data-server-util.h>
+#include <libedataserver/e-dbus-source-manager.h>
+#include <libebackend/e-dbus-source-authenticator.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;
+
+	/* In pseudo-Python notation:
+	 *
+	 * auth_queue = [ UID, ... ]
+	 * auth_table = { UID : [ authenticator, ... ] }
+	 * active_auth_table_queue = auth_table[auth_queue[0]]
+	 * active_auth = active_auth_table_queue[0]
+	 *
+	 * We process all authenticators for a given source UID at once.
+	 * The thought being after the first authenticator for a given UID
+	 * completes (the first being most likely to trigger a user prompt),
+	 * then any other authenticators for that same UID should complete
+	 * quickly, hopefully without having to reprompt.  That is unless
+	 * the user decides not to cache the secret at all, in which case
+	 * he gets what he asked for: lots of annoying prompts.
+	 */
+	GQueue *auth_queue;
+	GHashTable *auth_table;
+	GQueue *active_auth_table_queue;
+	EDBusSourceAuthenticator *active_auth;
+
+	GType authenticator_type;
+};
+
+enum {
+	PROP_0,
+	PROP_AUTHENTICATOR_TYPE
+};
+
+enum {
+	LOAD_ERROR,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+	EDBusSourceServer,
+	e_dbus_source_server,
+	E_TYPE_DBUS_SERVER)
+
+/* GDestroyNotify callback for 'auth_table' values */
+static void
+free_auth_queue (GQueue *queue)
+{
+	/* XXX g_queue_clear_full() would be nice here. */
+	while (!g_queue_is_empty (queue))
+		g_object_unref (g_queue_pop_head (queue));
+
+	g_queue_free (queue);
+}
+
+static GQueue *
+dbus_source_server_auth_table_lookup (EDBusSourceServer *server,
+                                      const gchar *uid)
+{
+	GHashTable *hash_table;
+	GQueue *queue;
+
+	hash_table = server->priv->auth_table;
+	queue = g_hash_table_lookup (hash_table, uid);
+
+	if (queue == NULL) {
+		queue = g_queue_new ();
+		g_hash_table_insert (hash_table, g_strdup (uid), queue);
+	}
+
+	return queue;
+}
+
+static void
+dbus_source_server_maybe_initiate_next_authenticator (EDBusSourceServer *server)
+{
+	GQueue *queue;
+
+	if (server->priv->active_auth != NULL)
+		return;
+
+	/* Check if there's any authenticators left in the active
+	 * auth table queue.  If the user provided a valid secret
+	 * and elected to save it in the keyring, or if the user
+	 * dismissed the authentication prompt, then we should be
+	 * able to process the remaining authenticators quickly. */
+	if (server->priv->active_auth_table_queue != NULL &&
+            !g_queue_is_empty (server->priv->active_auth_table_queue)) {
+		queue = server->priv->active_auth_table_queue;
+		server->priv->active_auth = g_queue_peek_head (queue);
+
+	/* Otherwise find the next non-empty auth table queue to
+	 * be processed, according to the UIDs in the auth queue. */
+	} else while (!g_queue_is_empty (server->priv->auth_queue)) {
+		gchar *uid;
+
+		uid = g_queue_pop_head (server->priv->auth_queue);
+		queue = dbus_source_server_auth_table_lookup (server, uid);
+		g_free (uid);
+
+		if (g_queue_is_empty (queue))
+			continue;
+
+		server->priv->active_auth_table_queue = queue;
+		server->priv->active_auth = g_queue_peek_head (queue);
+	}
+
+	/* Initiate the new active authenicator.  This signals it to
+	 * respond with a cached secret in the keyring if it can, or
+	 * else show an authentication prompt and wait for input. */
+	if (server->priv->active_auth != NULL)
+		e_dbus_source_authenticator_initiate (
+			server->priv->active_auth);
+	else
+		server->priv->active_auth_table_queue = NULL;
+}
+
+static void
+dbus_source_server_authenticator_complete (EDBusSourceAuthenticator *authenticator,
+                                           EDBusSourceAuthenticatorResult result,
+                                           EDBusSourceServer *server)
+{
+	EDBusSourceObject *object;
+	GQueue *queue;
+	const gchar *uid;
+
+	object = e_dbus_source_authenticator_get_object (authenticator);
+	uid = e_dbus_source_object_get_uid (object);
+	g_return_if_fail (uid != NULL);
+
+	queue = dbus_source_server_auth_table_lookup (server, uid);
+
+	/* Remove the completed authenticator from its queue. */
+	if (g_queue_remove (queue, authenticator))
+		g_object_unref (authenticator);
+
+	/* If the completed authenticator was the active one,
+	 * clear the active pointer for the next authenticator. */
+	if (authenticator == server->priv->active_auth)
+		server->priv->active_auth = NULL;
+
+	dbus_source_server_maybe_initiate_next_authenticator (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;
+	}
+
+	e_dbus_source_manager_complete_create_source (interface, invocation);
+
+	return TRUE;
+}
+
+static gboolean
+dbus_source_server_remove_sources_cb (EDBusSourceManager *interface,
+                                      GDBusMethodInvocation *invocation,
+                                      GStrv uids,
+                                      EDBusSourceServer *server)
+{
+	GFile *file;
+	GQueue queue = G_QUEUE_INIT;
+	guint ii, length;
+
+	length = g_strv_length (uids);
+
+	/* First make sure all the UIDs are valid. */
+	for (ii = 0; ii < length; ii++) {
+		EDBusSourceObject *object;
+		GFile *file;
+
+		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;
+		}
+
+		file = e_dbus_source_object_get_file (object);
+		g_queue_push_tail (&queue, g_object_ref (file));
+	}
+
+	/* Just delete the key files.  The file monitor will
+	 * notice the deletion and unexport the corresponding
+	 * D-Bus object path. */
+	while ((file = g_queue_pop_head (&queue)) != NULL) {
+		g_file_delete (file, NULL, NULL);
+		g_object_unref (file);
+	}
+
+	e_dbus_source_manager_complete_remove_sources (interface, invocation);
+
+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_set_property (GObject *object,
+                                 guint property_id,
+                                 const GValue *value,
+                                 GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AUTHENTICATOR_TYPE:
+			e_dbus_source_server_set_authenticator_type (
+				E_DBUS_SOURCE_SERVER (object),
+				g_value_get_gtype (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+dbus_source_server_get_property (GObject *object,
+                                 guint property_id,
+                                 GValue *value,
+                                 GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AUTHENTICATOR_TYPE:
+			g_value_set_gtype (
+				value,
+				e_dbus_source_server_get_authenticator_type (
+				E_DBUS_SOURCE_SERVER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+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: %s", G_STRFUNC, 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->set_property = dbus_source_server_set_property;
+	object_class->get_property = dbus_source_server_get_property;
+	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;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_AUTHENTICATOR_TYPE,
+		g_param_spec_gtype (
+			"authenticator-type",
+			"Authenticator Type",
+			"GType for handling authentications",
+			E_TYPE_DBUS_SOURCE_AUTHENTICATOR,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * 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;
+	GHashTable *auth_table;
+
+	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);
+
+	auth_table = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) free_auth_queue);
+
+	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;
+	server->priv->auth_queue = g_queue_new ();
+	server->priv->auth_table = auth_table;
+
+	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_get_authenticator_type:
+ * @server: an #EDBusSourceServer
+ *
+ * Returns the type of object to create to service authentication requests.
+ *
+ * Returns: the authenticator #GType
+ *
+ * Since: 3.4
+ **/
+GType
+e_dbus_source_server_get_authenticator_type (EDBusSourceServer *server)
+{
+	g_return_val_if_fail (
+		E_IS_DBUS_SOURCE_SERVER (server), G_TYPE_INVALID);
+
+	return server->priv->authenticator_type;
+}
+
+/**
+ * e_dbus_source_server_set_authenticator_type:
+ * @server: an #EDBusSourceServer
+ * @authenticator_type: the authenticator #GType
+ *
+ * Sets the type of object to create to service authentication requests.
+ *
+ * Since: 3.4
+ **/
+void
+e_dbus_source_server_set_authenticator_type (EDBusSourceServer *server,
+                                             GType authenticator_type)
+{
+	gboolean valid_authenticator_type;
+
+	g_return_if_fail (E_IS_DBUS_SOURCE_SERVER (server));
+
+	valid_authenticator_type = g_type_is_a (
+		authenticator_type, E_TYPE_DBUS_SOURCE_AUTHENTICATOR);
+	g_return_if_fail (valid_authenticator_type);
+
+	server->priv->authenticator_type = authenticator_type;
+
+	g_object_notify (G_OBJECT (server), "authenticator-type");
+}
+
+/**
+ * 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;
+
+	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);
+
+	g_hash_table_remove (server->priv->objects, uid);
+}
+
+/**
+ * e_dbus_source_server_add_authenticator:
+ * @server: an #EDBusSourceServer
+ * @authenticator: an #EDBusSourceAuthenticator
+ *
+ * Queues an authentication session.  When its turn comes, and if necessary,
+ * the user will be prompted for a secret.  Sessions are queued this way to
+ * prevent user prompts from piling up on the screen.
+ *
+ * Since: 3.4
+ **/
+void
+e_dbus_source_server_add_authenticator (EDBusSourceServer *server,
+                                        EDBusSourceAuthenticator *authenticator)
+{
+	EDBusSourceObject *object;
+	const gchar *uid;
+	GQueue *queue;
+
+	g_return_if_fail (E_IS_DBUS_SOURCE_SERVER (server));
+	g_return_if_fail (E_IS_DBUS_SOURCE_AUTHENTICATOR (authenticator));
+
+	object = e_dbus_source_authenticator_get_object (authenticator);
+	uid = e_dbus_source_object_get_uid (object);
+	g_return_if_fail (uid != NULL);
+
+	/* Add the authenticator to the appropriate queue. */
+	queue = dbus_source_server_auth_table_lookup (server, uid);
+	g_queue_push_tail (queue, g_object_ref (authenticator));
+
+	/* Blindly push the UID onto the processing queue. */
+	g_queue_push_tail (server->priv->auth_queue, g_strdup (uid));
+
+	g_signal_connect (
+		authenticator, "complete",
+		G_CALLBACK (dbus_source_server_authenticator_complete),
+		server);
+
+	dbus_source_server_maybe_initiate_next_authenticator (server);
+}
+
+/**
+ * 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, server, 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..f6b5b13
--- /dev/null
+++ b/libebackend/e-dbus-source-server.h
@@ -0,0 +1,114 @@
+/*
+ * 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>
+#include <libebackend/e-dbus-source-authenticator.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);
+GType		e_dbus_source_server_get_authenticator_type
+						(EDBusSourceServer *server);
+void		e_dbus_source_server_set_authenticator_type
+						(EDBusSourceServer *server,
+						 GType authenticator_type);
+void		e_dbus_source_server_add_object
+						(EDBusSourceServer *server,
+						 EDBusSourceObject *object);
+void		e_dbus_source_server_remove_object
+						(EDBusSourceServer *server,
+						 EDBusSourceObject *object);
+void		e_dbus_source_server_add_authenticator
+						(EDBusSourceServer *server,
+						 EDBusSourceAuthenticator *authenticator);
+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..3500962
--- /dev/null
+++ b/services/evolution-source-registry/Makefile.am
@@ -0,0 +1,42 @@
+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_srcdir)/calendar \
+	-I$(top_srcdir)/addressbook \
+	-I$(top_builddir) \
+	-DG_LOG_DOMAIN=\"evolution-source-registry\" \
+	-DLOCALEDIR=\"$(localedir)\" \
+	$(E_DATA_SERVER_UI_CFLAGS) \
+	$(GNOME_KEYRING_CFLAGS) \
+	$(NULL)
+
+evolution_source_registry_SOURCES = \
+	evolution-source-registry.c \
+	e-authentication-dialog.c \
+	e-authentication-dialog.h \
+	e-authenticator.c \
+	e-authenticator.h \
+	$(NULL)
+
+evolution_source_registry_LDADD = \
+	$(top_builddir)/calendar/libecal/libecal-1.2.la \
+	$(top_builddir)/addressbook/libebook/libebook-1.2.la \
+	$(top_builddir)/libebackend/libebackend-1.2.la \
+	$(top_builddir)/libedataserver/libedataserver-1.2.la \
+	$(E_DATA_SERVER_UI_LIBS) \
+	$(GNOME_KEYRING_LIBS) \
+	$(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/services/evolution-source-registry/e-authentication-dialog.c b/services/evolution-source-registry/e-authentication-dialog.c
new file mode 100644
index 0000000..2ab869d
--- /dev/null
+++ b/services/evolution-source-registry/e-authentication-dialog.c
@@ -0,0 +1,679 @@
+/*
+ * e-authentication-dialog.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-authentication-dialog.h"
+
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include <libecal/e-source-calendar.h>
+#include <libebook/e-source-address-book.h>
+#include <libedataserver/e-source-authentication.h>
+#include <libedataserver/e-source-mail-account.h>
+
+#define E_AUTHENTICATION_DIALOG_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_AUTHENTICATION_DIALOG, EAuthenticationDialogPrivate))
+
+#define TRY_AGAIN_DELAY  3  /* seconds */
+
+struct _EAuthenticationDialogPrivate {
+	ESource *source;
+
+	GtkWidget *auth_button;		/* not_referenced */
+	GtkWidget *cancel_button;	/* not referenced */
+	GtkWidget *entry;		/* not referenced */
+	GtkWidget *warning;		/* not referenced */
+
+	gchar *password;
+	gchar *markup_text;
+	gboolean authenticating;
+	gboolean remember_password;
+	guint try_again_timeout;
+};
+
+enum {
+	PROP_0,
+	PROP_AUTHENTICATING,
+	PROP_PASSWORD,
+	PROP_REMEMBER_PASSWORD,
+	PROP_SOURCE
+};
+
+G_DEFINE_TYPE (
+	EAuthenticationDialog,
+	e_authentication_dialog,
+	GTK_TYPE_DIALOG)
+
+static void
+authentication_dialog_build_markup_text (EAuthenticationDialog *dialog)
+{
+	/* XXX This is kind of a hack but it should work for now.  Build a
+	 *     suitable password prompt by checking for various extensions
+	 *     in the ESource.  If no recognizable extensions are found, or
+	 *     if the result is ambiguous, just refer to the data source as
+	 *     an "account". */
+
+	ESource *source;
+	ESourceAuthentication *extension;
+	const gchar *user;
+	const gchar *display_name;
+	const gchar *extension_name;
+	gchar *markup_text;
+	gchar *name_markup;
+	gchar *user_markup;
+
+	/* Known types */
+	enum {
+		TYPE_UNKNOWN,
+		TYPE_AMBIGUOUS,
+		TYPE_ADDRESS_BOOK,
+		TYPE_CALENDAR,
+		TYPE_MAIL_ACCOUNT,
+		TYPE_MEMO_LIST,
+		TYPE_TASK_LIST
+	} type = TYPE_UNKNOWN;
+
+	/* Make sure the extensions we need are registered with GType,
+	 * otherwise the ESource won't recognize the extension names. */
+	E_TYPE_SOURCE_AUTHENTICATION;
+	E_TYPE_SOURCE_ADDRESS_BOOK;
+	E_TYPE_SOURCE_CALENDAR;
+	E_TYPE_SOURCE_MAIL_ACCOUNT;
+	E_TYPE_SOURCE_MEMO_LIST;
+	E_TYPE_SOURCE_TASK_LIST;
+
+	source = e_authentication_dialog_get_source (dialog);
+	display_name = e_source_get_display_name (source);
+
+	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+	extension = e_source_get_extension (source, extension_name);
+	user = e_source_authentication_get_user (extension);
+
+	extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+	if (e_source_has_extension (source, extension_name)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_ADDRESS_BOOK;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	extension_name = E_SOURCE_EXTENSION_CALENDAR;
+	if (e_source_has_extension (source, extension_name)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_CALENDAR;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+	if (e_source_has_extension (source, extension_name)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_MAIL_ACCOUNT;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
+	if (e_source_has_extension (source, extension_name)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_MEMO_LIST;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+	if (e_source_has_extension (source, extension_name)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_TASK_LIST;
+		else
+			type = TYPE_AMBIGUOUS;
+	}
+
+	/* Add bold tags to the "display_name" and "user" strings.  We
+	 * use separate strings here to avoid putting markup tags in the
+	 * translatable strings below. */
+	name_markup = g_markup_printf_escaped ("<b>%s</b>", display_name);
+	user_markup = g_markup_printf_escaped ("<b>%s</b>", user);
+
+	switch (type) {
+		case TYPE_ADDRESS_BOOK:
+			markup_text = g_strdup_printf (
+				_("Please enter the password for address book "
+				  "%s (user %s)."), name_markup, user_markup);
+			break;
+		case TYPE_CALENDAR:
+			markup_text = g_strdup_printf (
+				_("Please enter the password for calendar "
+				  "%s (user %s)."), name_markup, user_markup);
+			break;
+		case TYPE_MAIL_ACCOUNT:
+			markup_text = g_strdup_printf (
+				_("Please enter the password for mail account "
+				  "%s (user %s)."), name_markup, user_markup);
+			break;
+		case TYPE_MEMO_LIST:
+			markup_text = g_strdup_printf (
+				_("Please enter the password for memo list "
+				  "%s (user %s)."), name_markup, user_markup);
+			break;
+		case TYPE_TASK_LIST:
+			markup_text = g_strdup_printf (
+				_("Please enter the password for task list "
+				  "%s (user %s)."), name_markup, user_markup);
+			break;
+		default:  /* generic account prompt */
+			markup_text = g_strdup_printf (
+				_("Please enter the password for account "
+				  "%s (user %s)."), name_markup, user_markup);
+			break;
+	}
+
+	g_free (name_markup);
+	g_free (user_markup);
+
+	g_free (dialog->priv->markup_text);
+	dialog->priv->markup_text = markup_text;
+}
+
+static void
+authentication_dialog_set_source (EAuthenticationDialog *dialog,
+                                  ESource *source)
+{
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (dialog->priv->source == NULL);
+
+	dialog->priv->source = g_object_ref (source);
+
+	/* This requires an ESource.  We have it now. */
+	authentication_dialog_build_markup_text (dialog);
+}
+
+static void
+authentication_dialog_set_property (GObject *object,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SOURCE:
+			authentication_dialog_set_source (
+				E_AUTHENTICATION_DIALOG (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_REMEMBER_PASSWORD:
+			e_authentication_dialog_set_remember_password (
+				E_AUTHENTICATION_DIALOG (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+authentication_dialog_get_property (GObject *object,
+                                    guint property_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AUTHENTICATING:
+			g_value_set_boolean (
+				value,
+				e_authentication_dialog_get_authenticating (
+				E_AUTHENTICATION_DIALOG (object)));
+			return;
+
+		case PROP_PASSWORD:
+			g_value_set_string (
+				value,
+				e_authentication_dialog_get_password (
+				E_AUTHENTICATION_DIALOG (object)));
+			return;
+
+		case PROP_REMEMBER_PASSWORD:
+			g_value_set_boolean (
+				value,
+				e_authentication_dialog_get_remember_password (
+				E_AUTHENTICATION_DIALOG (object)));
+			return;
+
+		case PROP_SOURCE:
+			g_value_set_object (
+				value,
+				e_authentication_dialog_get_source (
+				E_AUTHENTICATION_DIALOG (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+authentication_dialog_dispose (GObject *object)
+{
+	EAuthenticationDialogPrivate *priv;
+
+	priv = E_AUTHENTICATION_DIALOG_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_authentication_dialog_parent_class)->
+		dispose (object);
+}
+
+static void
+authentication_dialog_finalize (GObject *object)
+{
+	EAuthenticationDialogPrivate *priv;
+
+	priv = E_AUTHENTICATION_DIALOG_GET_PRIVATE (object);
+
+	if (priv->try_again_timeout > 0)
+		g_source_remove (priv->try_again_timeout);
+
+	g_free (priv->password);
+	g_free (priv->markup_text);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_authentication_dialog_parent_class)->
+		finalize (object);
+}
+
+static void
+authentication_dialog_constructed (GObject *object)
+{
+	EAuthenticationDialogPrivate *priv;
+	GtkDialog *dialog;
+	GtkWindow *window;
+	GtkWidget *action_area;
+	GtkWidget *content_area;
+	GtkWidget *container;
+	GtkWidget *widget;
+	const gchar *text;
+	const gchar *stock_id;
+	gchar *markup;
+	gint row;
+
+	stock_id = GTK_STOCK_DIALOG_AUTHENTICATION;
+	priv = E_AUTHENTICATION_DIALOG_GET_PRIVATE (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_authentication_dialog_parent_class)->
+		constructed (object);
+
+	window = GTK_WINDOW (object);
+	gtk_window_set_title (window, _("Authenticate"));
+	gtk_window_set_position (window, GTK_WIN_POS_CENTER);
+	/*gtk_window_set_modal (window, TRUE);*/
+	gtk_window_set_resizable (window, FALSE);
+	gtk_window_set_keep_above (window, TRUE);
+	gtk_window_set_icon_name (window, stock_id);
+
+	dialog = GTK_DIALOG (object);
+	action_area = gtk_dialog_get_action_area (dialog);
+	content_area = gtk_dialog_get_content_area (dialog);
+
+	priv->cancel_button = gtk_dialog_add_button (
+		dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+	priv->auth_button = gtk_dialog_add_button (
+		dialog, _("_Authenticate"), GTK_RESPONSE_OK);
+
+	gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
+
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (content_area), 2);  /* 2 * 5 + 2 = 12 */
+	gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
+	gtk_box_set_spacing (GTK_BOX (action_area), 6);
+
+	widget = gtk_grid_new ();
+	gtk_grid_set_column_spacing (GTK_GRID (widget), 12);
+	gtk_container_set_border_width (GTK_CONTAINER (widget), 5);
+	gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
+	gtk_widget_set_valign (widget, GTK_ALIGN_START);
+	gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
+	gtk_widget_show (widget);
+
+	widget = gtk_grid_new ();
+	gtk_grid_set_row_spacing (GTK_GRID (widget), 12);
+	gtk_grid_set_column_spacing (GTK_GRID (widget), 6);
+	gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	row = 0;  /* Row 0 */
+
+	text = _("Authentication Required");
+	markup = g_markup_printf_escaped ("<big><b>%s</b></big>", text);
+	widget = gtk_label_new (markup);
+	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+	gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	gtk_widget_set_hexpand (widget, TRUE);
+	gtk_grid_attach (GTK_GRID (container), widget, 0, row, 2, 1);
+	gtk_widget_show (widget);
+	g_free (markup);
+
+	row++;  /* Row 1 */
+
+	widget = gtk_label_new (priv->markup_text);
+	gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+	gtk_grid_attach (GTK_GRID (container), widget, 0, row, 2, 1);
+	gtk_widget_show (widget);
+
+	/* Leave some extra space between the label and entry. */
+	gtk_widget_set_margin_bottom (widget, 6);
+
+	row++;  /* Row 2 */
+
+	widget = gtk_entry_new ();
+	gtk_widget_set_hexpand (widget, TRUE);
+	gtk_widget_set_can_focus (widget, TRUE);
+	gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
+	gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+	gtk_grid_attach (GTK_GRID (container), widget, 1, row, 1, 1);
+	priv->entry = widget;  /* not referenced */
+	gtk_widget_grab_focus (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new_with_mnemonic ("_Password:");
+	gtk_label_set_mnemonic_widget (GTK_LABEL (widget), priv->entry);
+	gtk_grid_attach (GTK_GRID (container), widget, 0, row, 1, 1);
+	gtk_widget_show (widget);
+
+	row++;  /* Row 3 */
+
+	text = _("Remember this password");
+	widget = gtk_check_button_new_with_label (text);
+	gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	gtk_grid_attach (GTK_GRID (container), widget, 0, row, 2, 1);
+	gtk_widget_show (widget);
+
+	g_object_bind_property (
+		object, "remember-password",
+		widget, "active",
+		G_BINDING_SYNC_CREATE |
+		G_BINDING_BIDIRECTIONAL);
+
+	row++;  /* Row 4 */
+
+	widget = gtk_label_new (" ");
+	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+	gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	gtk_widget_set_hexpand (widget, TRUE);
+	gtk_grid_attach (GTK_GRID (container), widget, 0, row, 2, 1);
+	priv->warning = widget;  /* not referenced */
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	g_object_bind_property (
+		object, "authenticating",
+		priv->auth_button, "sensitive",
+		G_BINDING_SYNC_CREATE |
+		G_BINDING_INVERT_BOOLEAN);
+
+	g_object_bind_property (
+		object, "authenticating",
+		priv->entry, "sensitive",
+		G_BINDING_SYNC_CREATE |
+		G_BINDING_INVERT_BOOLEAN);
+}
+
+static void
+authentication_dialog_response (GtkDialog *dialog,
+                                gint response)
+{
+	EAuthenticationDialogPrivate *priv;
+
+	priv = E_AUTHENTICATION_DIALOG_GET_PRIVATE (dialog);
+
+	/* GtkDialog does not implement this method.  Do not chain up. */
+
+	if (response == GTK_RESPONSE_OK) {
+		GdkCursor *cursor;
+		GdkWindow *window;
+		const gchar *text;
+
+		/* Make the cursor appear busy. */
+		cursor = gdk_cursor_new (GDK_WATCH);
+		window = gtk_widget_get_window (GTK_WIDGET (dialog));
+		gdk_window_set_cursor (window, cursor);
+		g_object_unref (cursor);
+
+		text = gtk_entry_get_text (GTK_ENTRY (priv->entry));
+
+		g_free (priv->password);
+		priv->password = g_strdup (text);
+		g_object_notify (G_OBJECT (dialog), "password");
+
+		priv->authenticating = TRUE;
+		g_object_notify (G_OBJECT (dialog), "authenticating");
+	}
+}
+
+static void
+e_authentication_dialog_class_init (EAuthenticationDialogClass *class)
+{
+	GObjectClass *object_class;
+	GtkDialogClass *dialog_class;
+
+	g_type_class_add_private (class, sizeof (EAuthenticationDialogPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = authentication_dialog_set_property;
+	object_class->get_property = authentication_dialog_get_property;
+	object_class->dispose = authentication_dialog_dispose;
+	object_class->finalize = authentication_dialog_finalize;
+	object_class->constructed = authentication_dialog_constructed;
+
+	dialog_class = GTK_DIALOG_CLASS (class);
+	dialog_class->response = authentication_dialog_response;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_AUTHENTICATING,
+		g_param_spec_boolean (
+			"authenticating",
+			"Authenticating",
+			"The user has clicked Authenticate and "
+			"the dialog is waiting for confirmation",
+			FALSE,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PASSWORD,
+		g_param_spec_string (
+			"password",
+			"Password",
+			"The user-provided password",
+			NULL,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_REMEMBER_PASSWORD,
+		g_param_spec_boolean (
+			"remember-password",
+			"Remember Password",
+			"Whether to remember the password for future "
+			"sessions once authentication is confirmed",
+			FALSE,
+			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 authenticated",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_authentication_dialog_init (EAuthenticationDialog *dialog)
+{
+	dialog->priv = E_AUTHENTICATION_DIALOG_GET_PRIVATE (dialog);
+}
+
+GtkWidget *
+e_authentication_dialog_new (ESource *source)
+{
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	return g_object_new (
+		E_TYPE_AUTHENTICATION_DIALOG,
+		"source", source, NULL);
+}
+
+gboolean
+e_authentication_dialog_get_authenticating (EAuthenticationDialog *dialog)
+{
+	g_return_val_if_fail (E_IS_AUTHENTICATION_DIALOG (dialog), FALSE);
+
+	return dialog->priv->authenticating;
+}
+
+gboolean
+e_authentication_dialog_get_remember_password (EAuthenticationDialog *dialog)
+{
+	g_return_val_if_fail (E_IS_AUTHENTICATION_DIALOG (dialog), FALSE);
+
+	return dialog->priv->remember_password;
+}
+
+void
+e_authentication_dialog_set_remember_password (EAuthenticationDialog *dialog,
+                                               gboolean remember_password)
+{
+	g_return_if_fail (E_IS_AUTHENTICATION_DIALOG (dialog));
+
+	dialog->priv->remember_password = remember_password;
+
+	g_object_notify (G_OBJECT (dialog), "remember-password");
+}
+
+ESource *
+e_authentication_dialog_get_source (EAuthenticationDialog *dialog)
+{
+	g_return_val_if_fail (E_IS_AUTHENTICATION_DIALOG (dialog), NULL);
+
+	return dialog->priv->source;
+}
+
+const gchar *
+e_authentication_dialog_get_password (EAuthenticationDialog *dialog)
+{
+	g_return_val_if_fail (E_IS_AUTHENTICATION_DIALOG (dialog), NULL);
+
+	return dialog->priv->password;
+}
+
+/* Helper for e_authentication_dialog_try_again() */
+static gboolean
+authentication_dialog_timeout_cb (EAuthenticationDialog *dialog)
+{
+	dialog->priv->try_again_timeout = 0;
+
+	/* Clear the warning message. */
+
+	gtk_label_set_markup (GTK_LABEL (dialog->priv->warning), " ");
+
+	/* Reset the authenticating state and previous password. */
+
+	dialog->priv->authenticating = FALSE;
+	g_object_notify (G_OBJECT (dialog), "authenticating");
+
+	g_free (dialog->priv->password);
+	dialog->priv->password = NULL;
+	g_object_notify (G_OBJECT (dialog), "password");
+
+	/* Put keyboard focus back on the entry. */
+
+	gtk_widget_grab_focus (dialog->priv->entry);
+
+	return FALSE;
+}
+
+void
+e_authentication_dialog_try_again (EAuthenticationDialog *dialog)
+{
+	GdkWindow *window;
+	gchar *markup;
+	gint x, y;
+	gint ii;
+	gint diff;
+
+	g_return_if_fail (E_IS_AUTHENTICATION_DIALOG (dialog));
+
+	/* Set the cursor back to normal. */
+
+	window = gtk_widget_get_window (GTK_WIDGET (dialog));
+	gdk_window_set_cursor (window, NULL);
+
+	/* Shake the window. */
+
+	gtk_window_get_position (GTK_WINDOW (dialog), &x, &y);
+
+	for (ii = 0; ii < 10; ii++) {
+		diff = (ii % 2 == 0) ? -15 : 15;
+		gtk_window_move (GTK_WINDOW (dialog), x + diff, y);
+		while (gtk_events_pending ())
+			gtk_main_iteration ();
+		g_usleep (10000);
+	}
+
+	gtk_window_move (GTK_WINDOW (dialog), x, y);
+
+	/* Show a warning message and pause for a moment. */
+
+	markup = g_markup_printf_escaped (
+		"<b>%s</b>", _("Incorrect password, please try again."));
+	gtk_label_set_markup (GTK_LABEL (dialog->priv->warning), markup);
+	g_free (markup);
+
+	if (dialog->priv->try_again_timeout > 0)
+		g_source_remove (dialog->priv->try_again_timeout);
+
+	dialog->priv->try_again_timeout = gdk_threads_add_timeout_seconds (
+		TRY_AGAIN_DELAY, (GSourceFunc)
+		authentication_dialog_timeout_cb, dialog);
+}
+
diff --git a/services/evolution-source-registry/e-authentication-dialog.h b/services/evolution-source-registry/e-authentication-dialog.h
new file mode 100644
index 0000000..b169270
--- /dev/null
+++ b/services/evolution-source-registry/e-authentication-dialog.h
@@ -0,0 +1,78 @@
+/*
+ * e-authentication-dialog.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_AUTHENTICATION_DIALOG_H
+#define E_AUTHENTICATION_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_AUTHENTICATION_DIALOG \
+	(e_authentication_dialog_get_type ())
+#define E_AUTHENTICATION_DIALOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_AUTHENTICATION_DIALOG, EAuthenticationDialog))
+#define E_AUTHENTICATION_DIALOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_AUTHENTICATION_DIALOG, EAuthenticationDialogClass))
+#define E_IS_AUTHENTICATION_DIALOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_AUTHENTICATION_DIALOG))
+#define E_IS_AUTHENTICATION_DIALOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_AUTHENTICATION_DIALOG))
+#define E_AUTHENTICATION_DIALOG_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_AUTHENTICATION_DIALOG, EAuthenticationDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAuthenticationDialog EAuthenticationDialog;
+typedef struct _EAuthenticationDialogClass EAuthenticationDialogClass;
+typedef struct _EAuthenticationDialogPrivate EAuthenticationDialogPrivate;
+
+struct _EAuthenticationDialog {
+	GtkDialog parent;
+	EAuthenticationDialogPrivate *priv;
+};
+
+struct _EAuthenticationDialogClass {
+	GtkDialogClass parent_class;
+};
+
+GType		e_authentication_dialog_get_type
+						(void) G_GNUC_CONST;
+GtkWidget *	e_authentication_dialog_new	(ESource *source);
+gboolean	e_authentication_dialog_get_authenticating
+						(EAuthenticationDialog *dialog);
+gboolean	e_authentication_dialog_get_remember_password
+						(EAuthenticationDialog *dialog);
+void		e_authentication_dialog_set_remember_password
+						(EAuthenticationDialog *dialog,
+						 gboolean remember_password);
+ESource *	e_authentication_dialog_get_source
+						(EAuthenticationDialog *dialog);
+const gchar *	e_authentication_dialog_get_password
+						(EAuthenticationDialog *dialog);
+void		e_authentication_dialog_try_again
+						(EAuthenticationDialog *dialog);
+
+G_END_DECLS
+
+#endif /* E_AUTHENTICATION_DIALOG_H */
diff --git a/services/evolution-source-registry/e-authenticator.c b/services/evolution-source-registry/e-authenticator.c
new file mode 100644
index 0000000..470a9ee
--- /dev/null
+++ b/services/evolution-source-registry/e-authenticator.c
@@ -0,0 +1,304 @@
+/*
+ * e-authenticator.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-authenticator.h"
+
+#include <libedataserver/e-source-password.h>
+
+#include "e-authentication-dialog.h"
+
+#define E_AUTHENTICATOR_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_AUTHENTICATOR, EAuthenticatorPrivate))
+
+struct _EAuthenticatorPrivate {
+	GCancellable *cancellable;
+	GtkWidget *dialog;
+	ESource *source;
+};
+
+G_DEFINE_TYPE (
+	EAuthenticator,
+	e_authenticator,
+	E_TYPE_DBUS_SOURCE_AUTHENTICATOR)
+
+static void
+authenticator_dialog_response_cb (EAuthenticationDialog *dialog,
+                                  gint response_id,
+                                  EAuthenticator *authenticator)
+{
+	if (response_id == GTK_RESPONSE_OK) {
+		const gchar *password;
+
+		password = e_authentication_dialog_get_password (dialog);
+		g_return_if_fail (password != NULL);
+
+		g_signal_emit_by_name (
+			authenticator, "authenticate", password);
+
+	} else {
+		g_signal_emit_by_name (authenticator, "dismissed");
+
+		e_dbus_source_authenticator_complete (
+			E_DBUS_SOURCE_AUTHENTICATOR (authenticator),
+			E_DBUS_SOURCE_AUTHENTICATOR_DISMISSED);
+	}
+}
+
+static gboolean
+authenticator_handle_accepted (EDBusAuthenticator *authenticator,
+                               GDBusMethodInvocation *invocation)
+{
+	EAuthenticatorPrivate *priv;
+	EAuthenticationDialog *dialog;
+	gboolean remember_password;
+	const gchar *password;
+
+	priv = E_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	dialog = E_AUTHENTICATION_DIALOG (priv->dialog);
+	password = e_authentication_dialog_get_password (dialog);
+
+	remember_password =
+		gtk_widget_get_visible (GTK_WIDGET (dialog)) &&
+		e_authentication_dialog_get_remember_password (dialog);
+
+	/* If the user wants the password remembered, store it now.
+	 * We don't really care if this works or not, so a callback
+	 * function is unnecessary.  But still do it asynchronously
+	 * in case the password service is unresponsive. */
+	if (remember_password && password != NULL)
+		e_source_password_store (
+			priv->source, password, TRUE,
+			G_PRIORITY_LOW, NULL, NULL, NULL);
+
+	e_dbus_source_authenticator_complete (
+		E_DBUS_SOURCE_AUTHENTICATOR (authenticator),
+		E_DBUS_SOURCE_AUTHENTICATOR_SUCCESSFUL);
+
+	e_dbus_authenticator_complete_accepted (authenticator, invocation);
+
+	return TRUE;
+}
+
+static gboolean
+authenticator_handle_rejected (EDBusAuthenticator *authenticator,
+                               GDBusMethodInvocation *invocation)
+{
+	EAuthenticatorPrivate *priv;
+
+	priv = E_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	/* The cached password is bad so delete it from the keyring.
+	 * We don't really care if this works or not, so a callback
+	 * function is unnecessary.  But still do it asynchronously
+	 * in case the password service is unresponsive. */
+	e_source_password_delete (
+		priv->source, G_PRIORITY_LOW, NULL, NULL, NULL);
+
+	e_dbus_authenticator_complete_rejected (authenticator, invocation);
+
+	if (gtk_widget_get_visible (priv->dialog))
+		e_authentication_dialog_try_again (
+			E_AUTHENTICATION_DIALOG (priv->dialog));
+
+	gtk_widget_show (priv->dialog);
+
+	return TRUE;
+}
+
+static void
+authenticator_password_lookup_cb (ESource *source,
+                                  GAsyncResult *result,
+                                  EAuthenticator *authenticator)
+{
+	gchar *password = NULL;
+	GError *error = NULL;
+
+	e_source_password_lookup_finish (source, result, &password, &error);
+
+	/* Ignore cancellations. */
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		goto exit;
+
+	/* If we failed to talk to the secret service, I guess we have
+	 * to prompt the user now.  Leave a breadcrumb as evidence that
+	 * something went wrong. */
+	} else if (error != NULL) {
+		g_warning ("%s: %s", G_STRFUNC, error->message);
+		g_clear_error (&error);
+	}
+
+	/* If we found a stored password, signal the client without
+	 * interrupting the user.  Note, if the client responds with
+	 * Rejected(), we'll have to interrupt the user after all. */
+	if (password != NULL) {
+		g_signal_emit_by_name (
+			authenticator, "authenticate", password);
+		e_source_password_free (password);
+		goto exit;
+	}
+
+	gtk_widget_show (authenticator->priv->dialog);
+
+exit:
+	g_object_unref (authenticator);
+}
+
+static void
+authenticator_dispose (GObject *object)
+{
+	EAuthenticatorPrivate *priv;
+
+	priv = E_AUTHENTICATOR_GET_PRIVATE (object);
+
+	if (priv->cancellable != NULL) {
+		g_object_unref (priv->cancellable);
+		priv->cancellable = NULL;
+	}
+
+	if (priv->dialog != NULL) {
+		gtk_widget_destroy (priv->dialog);
+		priv->dialog = NULL;
+	}
+
+	if (priv->source != NULL) {
+		g_object_unref (priv->source);
+		priv->source = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_authenticator_parent_class)->dispose (object);
+}
+
+static void
+authenticator_constructed (GObject *object)
+{
+	EAuthenticatorPrivate *priv;
+	EDBusSourceObject *dbus_object;
+	GError *error = NULL;
+
+	priv = E_AUTHENTICATOR_GET_PRIVATE (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_authenticator_parent_class)->constructed (object);
+
+	dbus_object = e_dbus_source_authenticator_get_object (
+		E_DBUS_SOURCE_AUTHENTICATOR (object));
+
+	/* EDBusSourceObject implements the EDBusObject interface, so we can
+	 * create a new ESource instance from it.  The server operations are
+	 * are off limits since we're already server-side, but we just need
+	 * access to the [Authentication] extension. */
+	priv->source = e_source_new (E_DBUS_OBJECT (dbus_object), &error);
+
+	/* This should never fail, but handle failures anyway. */
+	if (priv->source != NULL) {
+		g_warn_if_fail (error == NULL);
+
+		priv->dialog = e_authentication_dialog_new (priv->source);
+
+		/* Let the dialog's own response() class method
+		 * run first.  It updates the "password" property
+		 * that we need in our signal handler. */
+		g_signal_connect_after (
+			priv->dialog, "response",
+			G_CALLBACK (authenticator_dialog_response_cb),
+			object);
+	}
+
+	if (error != NULL) {
+		g_warn_if_fail (priv->source == NULL);
+		g_warning ("%s: %s", G_STRFUNC, error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+authenticator_initiate (EDBusSourceAuthenticator *authenticator)
+{
+	EAuthenticatorPrivate *priv;
+
+	/* Note, our base class won't call us until both server and
+	 * client are ready, so we don't have to worry about any of
+	 * that synchronization stuff here. */
+
+	priv = E_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	/* Chain up to parent's initiate() method. */
+	E_DBUS_SOURCE_AUTHENTICATOR_CLASS (e_authenticator_parent_class)->
+		initiate (authenticator);
+
+	g_warn_if_fail (priv->cancellable == NULL);
+	priv->cancellable = g_cancellable_new ();
+
+	e_source_password_lookup (
+		priv->source, G_PRIORITY_DEFAULT, priv->cancellable,
+		(GAsyncReadyCallback) authenticator_password_lookup_cb,
+		g_object_ref (authenticator));
+}
+
+static void
+authenticator_complete (EDBusSourceAuthenticator *authenticator,
+                        EDBusSourceAuthenticatorResult result)
+{
+	EAuthenticatorPrivate *priv;
+
+	priv = E_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	/* Chain up to parent's complete() method. */
+	E_DBUS_SOURCE_AUTHENTICATOR_CLASS (e_authenticator_parent_class)->
+		complete (authenticator, result);
+
+	if (result == E_DBUS_SOURCE_AUTHENTICATOR_CANCELLED)
+		g_cancellable_cancel (priv->cancellable);
+
+	gtk_widget_hide (priv->dialog);
+}
+
+static void
+e_authenticator_class_init (EAuthenticatorClass *class)
+{
+	GObjectClass *object_class;
+	EDBusSourceAuthenticatorClass *source_auth_class;
+
+	g_type_class_add_private (class, sizeof (EAuthenticatorPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->dispose = authenticator_dispose;
+	object_class->constructed = authenticator_constructed;
+
+	source_auth_class = E_DBUS_SOURCE_AUTHENTICATOR_CLASS (class);
+	source_auth_class->initiate = authenticator_initiate;
+	source_auth_class->complete = authenticator_complete;
+}
+
+static void
+e_authenticator_init (EAuthenticator *authenticator)
+{
+	authenticator->priv = E_AUTHENTICATOR_GET_PRIVATE (authenticator);
+
+	g_signal_connect (
+		authenticator, "handle-accepted",
+		G_CALLBACK (authenticator_handle_accepted), NULL);
+
+	g_signal_connect (
+		authenticator, "handle-rejected",
+		G_CALLBACK (authenticator_handle_rejected), NULL);
+}
+
diff --git a/services/evolution-source-registry/e-authenticator.h b/services/evolution-source-registry/e-authenticator.h
new file mode 100644
index 0000000..0939a88
--- /dev/null
+++ b/services/evolution-source-registry/e-authenticator.h
@@ -0,0 +1,63 @@
+/*
+ * e-authenticator.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_AUTHENTICATOR_H
+#define E_AUTHENTICATOR_H
+
+#include <libebackend/e-dbus-source-authenticator.h>
+
+/* Standard GObject macros */
+#define E_TYPE_AUTHENTICATOR \
+	(e_authenticator_get_type ())
+#define E_AUTHENTICATOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_AUTHENTICATOR, EAuthenticator))
+#define E_AUTHENTICATOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_AUTHENTICATOR, EAuthenticatorClass))
+#define E_IS_AUTHENTICATOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_AUTHENTICATOR))
+#define E_IS_AUTHENTICATOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_AUTHENTICATOR))
+#define E_AUTHENTICATOR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_AUTHENTICATOR, EAuthenticatorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAuthenticator EAuthenticator;
+typedef struct _EAuthenticatorClass EAuthenticatorClass;
+typedef struct _EAuthenticatorPrivate EAuthenticatorPrivate;
+
+struct _EAuthenticator {
+	EDBusSourceAuthenticator parent;
+	EAuthenticatorPrivate *priv;
+};
+
+struct _EAuthenticatorClass {
+	EDBusSourceAuthenticatorClass parent_class;
+};
+
+GType		e_authenticator_get_type	(void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* E_AUTHENTICATOR_H */
+
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..6cdfa84
--- /dev/null
+++ b/services/evolution-source-registry/evolution-source-registry.c
@@ -0,0 +1,74 @@
+/*
+ * 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 <gtk/gtk.h>
+
+#include <libebackend/e-dbus-source-server.h>
+
+#include "e-authenticator.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");
+
+	gtk_init (&argc, &argv);
+
+	server = e_dbus_source_server_new ();
+
+	e_dbus_source_server_set_authenticator_type (
+		E_DBUS_SOURCE_SERVER (server),
+		E_TYPE_AUTHENTICATOR);
+
+	/* 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]